CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-anthropic--anthropic-java

The Anthropic Java SDK provides convenient access to the Anthropic REST API from applications written in Java

Overview
Eval results
Files

tools.mddocs/

Tool Use and Function Calling

Complete reference for the anthropic-java SDK's tool use (function calling) capabilities, enabling Claude to interact with external tools and functions.

Tool Definition

Tool Class

Custom tool definition for function calling.

package com.anthropic.models.messages;

class Tool {
    fun inputSchema(): InputSchema
    fun name(): String
    fun cacheControl(): Optional<CacheControlEphemeral>
    fun description(): Optional<String>
    fun type(): Optional<Type>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun inputSchema(inputSchema: InputSchema): Builder
        fun name(name: String): Builder
        fun cacheControl(cacheControl: CacheControlEphemeral?): Builder
        fun description(description: String): Builder
        fun build(): Tool
    }
}

Fields:

  • inputSchema: JSON schema defining tool input parameters (required)
  • name: Tool identifier used by model in tool_use blocks (required)
  • cacheControl: Cache control breakpoint for prompt caching (optional)
  • description: Detailed tool description for model guidance (optional)
  • type: Tool type, defaults to "custom" (optional)

Example:

Tool weatherTool = Tool.builder()
    .name("get_weather")
    .description("Get current weather for a location")
    .inputSchema(Tool.InputSchema.builder()
        .properties(Tool.InputSchema.Properties.builder()
            .putAdditionalProperty("location", JsonValue.from(Map.of(
                "type", "string",
                "description", "City and state, e.g. San Francisco, CA"
            )))
            .putAdditionalProperty("unit", JsonValue.from(Map.of(
                "type", "string",
                "enum", List.of("celsius", "fahrenheit")
            )))
            .build())
        .addRequired("location")
        .build())
    .build();

InputSchema

JSON schema definition for tool parameters using JSON Schema Draft 2020-12.

class InputSchema {
    fun properties(): Optional<Properties>
    fun required(): Optional<List<String>>
    fun _type(): JsonValue  // Always returns "object"

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun properties(properties: Properties?): Builder
        fun required(required: List<String>?): Builder
        fun addRequired(required: String): Builder
        fun build(): InputSchema
    }

    class Properties {
        fun _additionalProperties(): Map<String, JsonValue>

        companion object {
            @JvmStatic fun builder(): Builder
        }

        class Builder {
            fun putAdditionalProperty(key: String, value: JsonValue): Builder
            fun putAllAdditionalProperties(additionalProperties: Map<String, JsonValue>): Builder
            fun build(): Properties
        }
    }
}

Schema Structure:

  • Type is always "object"
  • Properties define parameter schemas as JSON values
  • Required lists mandatory parameters

Tool Union Types

ToolUnion

Union of all tool types (custom and built-in).

package com.anthropic.models.messages;

class ToolUnion {
    // Type checking
    fun isTool(): Boolean
    fun isBash20250124(): Boolean
    fun isTextEditor20250124(): Boolean
    fun isTextEditor20250429(): Boolean
    fun isTextEditor20250728(): Boolean
    fun isWebSearchTool20250305(): Boolean

    // Type access
    fun tool(): Optional<Tool>
    fun bash20250124(): Optional<ToolBash20250124>
    fun textEditor20250124(): Optional<ToolTextEditor20250124>
    fun textEditor20250429(): Optional<ToolTextEditor20250429>
    fun textEditor20250728(): Optional<ToolTextEditor20250728>
    fun webSearchTool20250305(): Optional<WebSearchTool20250305>

    // Type conversion
    fun asTool(): Tool
    fun asBash20250124(): ToolBash20250124
    fun asTextEditor20250124(): ToolTextEditor20250124
    fun asTextEditor20250429(): ToolTextEditor20250429
    fun asTextEditor20250728(): ToolTextEditor20250728
    fun asWebSearchTool20250305(): WebSearchTool20250305

    // Visitor pattern
    fun <T> accept(visitor: Visitor<T>): T

    companion object {
        @JvmStatic fun ofTool(tool: Tool): ToolUnion
        @JvmStatic fun ofBash20250124(bash20250124: ToolBash20250124): ToolUnion
        @JvmStatic fun ofTextEditor20250124(textEditor20250124: ToolTextEditor20250124): ToolUnion
        @JvmStatic fun ofTextEditor20250429(textEditor20250429: ToolTextEditor20250429): ToolUnion
        @JvmStatic fun ofTextEditor20250728(textEditor20250728: ToolTextEditor20250728): ToolUnion
        @JvmStatic fun ofWebSearchTool20250305(webSearchTool20250305: WebSearchTool20250305): ToolUnion
    }

    interface Visitor<out T> {
        fun visitTool(tool: Tool): T
        fun visitBash20250124(bash20250124: ToolBash20250124): T
        fun visitTextEditor20250124(textEditor20250124: ToolTextEditor20250124): T
        fun visitTextEditor20250429(textEditor20250429: ToolTextEditor20250429): T
        fun visitTextEditor20250728(textEditor20250728: ToolTextEditor20250728): T
        fun visitWebSearchTool20250305(webSearchTool20250305: WebSearchTool20250305): T
        fun unknown(json: JsonValue?): T
    }
}

Variants:

  • Tool: Custom user-defined tools
  • ToolBash20250124: Bash execution tool
  • ToolTextEditor20250124/20250429/20250728: Text editor tools (versioned)
  • WebSearchTool20250305: Web search tool

Example:

ToolUnion customTool = ToolUnion.ofTool(
    Tool.builder()
        .name("calculator")
        .description("Perform arithmetic operations")
        .inputSchema(/* ... */)
        .build()
);

// Check type
if (customTool.isTool()) {
    Tool tool = customTool.asTool();
    System.out.println("Tool name: " + tool.name());
}

// Using visitor pattern
String toolType = customTool.accept(new ToolUnion.Visitor<String>() {
    public String visitTool(Tool tool) {
        return "custom: " + tool.name();
    }
    public String visitBash20250124(ToolBash20250124 bash) {
        return "bash";
    }
    public String visitTextEditor20250124(ToolTextEditor20250124 editor) {
        return "text_editor";
    }
    // ... other visit methods
});

Built-in Tools

ToolBash

Bash command execution tool.

package com.anthropic.models.messages;

class ToolBash20250124 {
    fun name(): Name  // Returns "bash"
    fun type(): Type  // Returns "bash_20250124"
    fun cacheControl(): Optional<CacheControlEphemeral>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun cacheControl(cacheControl: CacheControlEphemeral?): Builder
        fun build(): ToolBash20250124
    }
}

ToolTextEditor

Text file editing tool (multiple versions available).

package com.anthropic.models.messages;

class ToolTextEditor20250728 {
    fun name(): Name  // Returns "str_replace_editor"
    fun type(): Type  // Returns "text_editor_20250728"
    fun cacheControl(): Optional<CacheControlEphemeral>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun cacheControl(cacheControl: CacheControlEphemeral?): Builder
        fun build(): ToolTextEditor20250728
    }
}

// Similar structure for ToolTextEditor20250124 and ToolTextEditor20250429

WebSearchTool

Web search tool for retrieving online information.

package com.anthropic.models.messages;

class WebSearchTool20250305 {
    fun name(): Name  // Returns "web_search"
    fun type(): Type  // Returns "web_search_20250305"
    fun cacheControl(): Optional<CacheControlEphemeral>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun cacheControl(cacheControl: CacheControlEphemeral?): Builder
        fun build(): WebSearchTool20250305
    }
}

Example:

// Add bash tool
MessageCreateParams params = MessageCreateParams.builder()
    .model(Model.CLAUDE_SONNET_4_5)
    .maxTokens(1024L)
    .addUserMessage("Run 'ls -la' command")
    .tools(List.of(
        ToolUnion.ofBash20250124(
            ToolBash20250124.builder().build()
        )
    ))
    .build();

Tool Choice

ToolChoice Union

Specifies how the model should use provided tools.

package com.anthropic.models.messages;

class ToolChoice {
    // Type checking
    fun isAuto(): Boolean
    fun isAny(): Boolean
    fun isTool(): Boolean
    fun isNone(): Boolean

    // Type access
    fun auto(): Optional<ToolChoiceAuto>
    fun any(): Optional<ToolChoiceAny>
    fun tool(): Optional<ToolChoiceTool>
    fun none(): Optional<ToolChoiceNone>

    // Type conversion
    fun asAuto(): ToolChoiceAuto
    fun asAny(): ToolChoiceAny
    fun asTool(): ToolChoiceTool
    fun asNone(): ToolChoiceNone

    // Visitor pattern
    fun <T> accept(visitor: Visitor<T>): T

    companion object {
        @JvmStatic fun ofAuto(auto: ToolChoiceAuto): ToolChoice
        @JvmStatic fun ofAny(any: ToolChoiceAny): ToolChoice
        @JvmStatic fun ofTool(tool: ToolChoiceTool): ToolChoice
        @JvmStatic fun ofNone(none: ToolChoiceNone): ToolChoice
    }

    interface Visitor<out T> {
        fun visitAuto(auto: ToolChoiceAuto): T
        fun visitAny(any: ToolChoiceAny): T
        fun visitTool(tool: ToolChoiceTool): T
        fun visitNone(none: ToolChoiceNone): T
        fun unknown(json: JsonValue?): T
    }
}

ToolChoice Variants

ToolChoiceAuto: Model automatically decides whether to use tools.

class ToolChoiceAuto {
    fun type(): Type  // Returns "auto"
    fun disableParallelToolUse(): Optional<Boolean>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun disableParallelToolUse(disableParallelToolUse: Boolean?): Builder
        fun build(): ToolChoiceAuto
    }
}

ToolChoiceAny: Model must use at least one available tool.

class ToolChoiceAny {
    fun type(): Type  // Returns "any"
    fun disableParallelToolUse(): Optional<Boolean>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun disableParallelToolUse(disableParallelToolUse: Boolean?): Builder
        fun build(): ToolChoiceAny
    }
}

ToolChoiceTool: Model must use the specified tool.

class ToolChoiceTool {
    fun name(): String
    fun type(): Type  // Returns "tool"
    fun disableParallelToolUse(): Optional<Boolean>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun name(name: String): Builder
        fun disableParallelToolUse(disableParallelToolUse: Boolean?): Builder
        fun build(): ToolChoiceTool
    }
}

ToolChoiceNone: Model must not use any tools.

class ToolChoiceNone {
    fun type(): Type  // Returns "none"

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun build(): ToolChoiceNone
    }
}

Example:

// Auto (model decides)
ToolChoice autoChoice = ToolChoice.ofAuto(
    ToolChoiceAuto.builder().build()
);

// Any (must use at least one tool)
ToolChoice anyChoice = ToolChoice.ofAny(
    ToolChoiceAny.builder()
        .disableParallelToolUse(true)
        .build()
);

// Specific tool (must use named tool)
ToolChoice specificTool = ToolChoice.ofTool(
    ToolChoiceTool.builder()
        .name("get_weather")
        .build()
);

// None (no tools allowed)
ToolChoice noTools = ToolChoice.ofNone(
    ToolChoiceNone.builder().build()
);

Tool Parameters

Adding Tools to MessageCreateParams

package com.anthropic.models.messages;

class MessageCreateParams {
    fun tools(): Optional<List<ToolUnion>>
    fun toolChoice(): Optional<ToolChoice>

    class Builder {
        // Tools
        fun tools(tools: List<ToolUnion>): Builder
        fun addTool(tool: Tool): Builder

        // Tool choice
        fun toolChoice(toolChoice: ToolChoice): Builder

        fun build(): MessageCreateParams
    }
}

Example:

MessageCreateParams params = MessageCreateParams.builder()
    .model(Model.CLAUDE_SONNET_4_5)
    .maxTokens(2048L)
    .addUserMessage("What's the weather in San Francisco?")
    .addTool(Tool.builder()
        .name("get_weather")
        .description("Get current weather for a location")
        .inputSchema(/* schema */)
        .build())
    .toolChoice(ToolChoice.ofAuto(
        ToolChoiceAuto.builder().build()
    ))
    .build();

Message response = client.messages().create(params);

Tool Use Blocks

ToolUseBlock

Tool use request from the model in response content.

package com.anthropic.models.messages;

class ToolUseBlock {
    fun id(): String
    fun name(): String
    fun _input(): JsonValue
    fun _type(): JsonValue  // Returns "tool_use"

    fun toParam(): ToolUseBlockParam

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun id(id: String): Builder
        fun name(name: String): Builder
        fun input(input: JsonValue): Builder
        fun build(): ToolUseBlock
    }
}

Fields:

  • id: Unique identifier for this tool use
  • name: Name of the tool to invoke
  • input: Tool parameters as JSON
  • type: Always "tool_use"

Example:

Message response = client.messages().create(params);

// Process tool use blocks
response.content().forEach(block -> {
    if (block.isToolUse()) {
        ToolUseBlock toolUse = block.asToolUse();

        System.out.println("Tool: " + toolUse.name());
        System.out.println("ID: " + toolUse.id());
        System.out.println("Input: " + toolUse._input());

        // Execute tool and prepare result
        Object result = executeToolByName(toolUse.name(), toolUse._input());
    }
});

Tool Results

ToolResultBlockParam

Tool execution result to send back to the model.

package com.anthropic.models.messages;

class ToolResultBlockParam {
    fun toolUseId(): String
    fun content(): Optional<Content>
    fun isError(): Optional<Boolean>
    fun cacheControl(): Optional<CacheControlEphemeral>
    fun _type(): JsonValue  // Returns "tool_result"

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun toolUseId(toolUseId: String): Builder
        fun content(content: String): Builder
        fun content(content: Content): Builder
        fun contentOfTextBlockParams(textBlockParams: List<TextBlockParam>): Builder
        fun contentOfImageBlockParams(imageBlockParams: List<ImageBlockParam>): Builder
        fun contentOfDocumentBlockParams(documentBlockParams: List<DocumentBlockParam>): Builder
        fun isError(isError: Boolean?): Builder
        fun cacheControl(cacheControl: CacheControlEphemeral?): Builder
        fun build(): ToolResultBlockParam
    }

    @JsonDeserialize(using = Content.Deserializer::class)
    @JsonSerialize(using = Content.Serializer::class)
    class Content {
        fun string(): Optional<String>
        fun contentBlockParams(): Optional<List<ContentBlockParam>>

        fun isString(): Boolean
        fun isContentBlockParams(): Boolean

        fun asString(): String
        fun asContentBlockParams(): List<ContentBlockParam>

        companion object {
            @JvmStatic fun ofString(string: String): Content
            @JvmStatic fun ofContentBlockParams(contentBlockParams: List<ContentBlockParam>): Content
        }
    }
}

Example:

// Simple text result
ToolResultBlockParam result = ToolResultBlockParam.builder()
    .toolUseId(toolUse.id())
    .content("Temperature: 72°F")
    .build();

// JSON result
ToolResultBlockParam jsonResult = ToolResultBlockParam.builder()
    .toolUseId(toolUse.id())
    .content(JsonValue.from(Map.of(
        "temperature", 72,
        "unit", "fahrenheit",
        "conditions", "sunny"
    )).toJsonString())
    .build();

// Error result
ToolResultBlockParam errorResult = ToolResultBlockParam.builder()
    .toolUseId(toolUse.id())
    .content("Tool execution failed: Invalid location")
    .isError(true)
    .build();

// Send result back in next message
MessageCreateParams followUp = params.toBuilder()
    .addUserMessageOfContentBlockParams(
        List.of(ContentBlockParam.ofToolResult(result))
    )
    .build();

Defining Tools from Java Classes

addTool(Class<T>) Method

Automatically derive tool schema from Java class structure.

package com.anthropic.models.beta.messages;

class MessageCreateParams {
    class Builder {
        fun <T> addTool(
            toolClass: Class<T>,
            localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES
        ): Builder
    }
}

Class-to-Schema Mapping:

  • Class name (camel case) → tool name (snake_case)
  • public fields/getters → tool parameters
  • Field types → JSON schema types
  • Jackson annotations → schema metadata

Example:

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;

@JsonClassDescription("Get the weather in a given location")
static class GetWeather {
    @JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
    public String location;

    @JsonPropertyDescription("The unit of temperature")
    public Unit unit;

    public Weather execute() {
        // Tool implementation
        return new Weather(/* ... */);
    }
}

enum Unit {
    CELSIUS, FAHRENHEIT
}

static class Weather {
    public String temperature;

    public Weather(String temperature) {
        this.temperature = temperature;
    }
}

// Add tool by class
MessageCreateParams params = MessageCreateParams.builder()
    .model(Model.CLAUDE_SONNET_4_5)
    .maxTokens(2048)
    .addTool(GetWeather.class)  // Automatically derives schema
    .addUserMessage("What's the temperature in New York?")
    .build();

Tool Class Conversion

BetaToolUseBlock.input(Class<T>): Parse tool parameters JSON to class instance.

package com.anthropic.models.beta.messages;

class BetaToolUseBlock {
    fun <T> input(toolClass: Class<T>): T?
}

BetaToolResultBlockParam.Builder.contentAsJson(Object): Convert result object to JSON.

class BetaToolResultBlockParam {
    class Builder {
        fun contentAsJson(content: Object): Builder
    }
}

Complete Flow:

// 1. Add tool from class
MessageCreateParams.Builder paramsBuilder = MessageCreateParams.builder()
    .model(Model.CLAUDE_SONNET_4_5)
    .maxTokens(2048)
    .addTool(GetWeather.class)
    .addUserMessage("What's the temperature in New York?");

// 2. Get response with tool use
BetaMessage response = client.beta().messages().create(paramsBuilder.build());

// 3. Process tool uses
response.content().stream()
    .flatMap(block -> block.toolUse().stream())
    .forEach(toolUse -> {
        // Parse parameters to class instance
        GetWeather tool = toolUse.input(GetWeather.class);

        // Execute tool
        Weather result = tool.execute();

        // Add tool request to conversation
        paramsBuilder.addAssistantMessageOfBetaContentBlockParams(
            List.of(BetaContentBlockParam.ofToolUse(
                BetaToolUseBlockParam.builder()
                    .name(toolUse.name())
                    .id(toolUse.id())
                    .input(toolUse._input())
                    .build()
            ))
        );

        // Add tool result to conversation
        paramsBuilder.addUserMessageOfBetaContentBlockParams(
            List.of(BetaContentBlockParam.ofToolResult(
                BetaToolResultBlockParam.builder()
                    .toolUseId(toolUse.id())
                    .contentAsJson(result)  // Converts object to JSON
                    .build()
            ))
        );
    });

// 4. Get final response
BetaMessage finalResponse = client.beta().messages().create(paramsBuilder.build());

BetaToolRunner

Automatic Tool Conversation Loop

Helper class that handles the complete tool use conversation flow.

package com.anthropic.helpers;

class BetaToolRunner : Iterable<BetaMessage> {
    // Iterate through conversation turns
    override fun iterator(): Iterator<BetaMessage>

    // Stream responses instead of buffered messages
    fun streaming(): Iterable<StreamResponse<BetaRawMessageStreamEvent>>

    // Get current request parameters
    fun params(): MessageCreateParams

    // Set parameters for next API call
    fun setNextParams(nextParams: MessageCreateParams)

    // Get last tool response (cached)
    fun lastToolResponse(): Optional<BetaMessageParam>
}

Created via MessageService:

package com.anthropic.services.blocking.beta;

interface MessageService {
    fun createToolRunner(params: ToolRunnerCreateParams): BetaToolRunner
    fun createToolRunner(
        params: ToolRunnerCreateParams,
        requestOptions: RequestOptions
    ): BetaToolRunner
}

ToolRunnerCreateParams:

package com.anthropic.models.beta.messages;

class ToolRunnerCreateParams {
    fun initialMessageParams(): MessageCreateParams
    fun maxIterations(): Optional<Long>

    companion object {
        @JvmStatic fun builder(): Builder
    }

    class Builder {
        fun initialMessageParams(initialMessageParams: MessageCreateParams): Builder
        fun maxIterations(maxIterations: Long?): Builder
        fun build(): ToolRunnerCreateParams
    }
}

Example (Basic):

// Define tool class
@JsonClassDescription("Calculate the result of an arithmetic expression")
static class Calculator {
    @JsonPropertyDescription("The arithmetic expression to evaluate")
    public String expression;

    public CalculatorResult execute() {
        // Simple eval (for demo only - use proper parser in production)
        double result = evaluateExpression(expression);
        return new CalculatorResult(result);
    }
}

static class CalculatorResult {
    public double result;

    public CalculatorResult(double result) {
        this.result = result;
    }
}

// Create initial params
MessageCreateParams initialParams = MessageCreateParams.builder()
    .model(Model.CLAUDE_SONNET_4_5)
    .maxTokens(2048)
    .addTool(Calculator.class)
    .addUserMessage("What is 15 * 23 + 42?")
    .build();

// Create tool runner
ToolRunnerCreateParams runnerParams = ToolRunnerCreateParams.builder()
    .initialMessageParams(initialParams)
    .maxIterations(5)
    .build();

BetaToolRunner runner = client.beta().messages().createToolRunner(runnerParams);

// Iterate through conversation automatically
for (BetaMessage message : runner) {
    message.content().stream()
        .flatMap(block -> block.text().stream())
        .forEach(text -> System.out.println("Claude: " + text.text()));
}

Example (Streaming):

BetaToolRunner runner = client.beta().messages().createToolRunner(runnerParams);

// Stream responses
for (StreamResponse<BetaRawMessageStreamEvent> streamResponse : runner.streaming()) {
    streamResponse.stream()
        .flatMap(event -> event.contentBlockDelta().stream())
        .flatMap(delta -> delta.delta().text().stream())
        .forEach(text -> System.out.print(text.text()));

    System.out.println();  // New line after each turn
}

Example (Custom Tool Execution):

// Custom tool handler with external API calls
static class WeatherAPI {
    @JsonClassDescription("Get real-time weather data")
    public String location;

    public WeatherData execute() {
        // Call external weather API
        return callWeatherAPI(location);
    }
}

// Runner with error handling
BetaToolRunner runner = client.beta().messages().createToolRunner(runnerParams);

try {
    for (BetaMessage message : runner) {
        // Access tool response for logging/debugging
        runner.lastToolResponse().ifPresent(toolResponse -> {
            System.out.println("Tool executed: " + toolResponse);
        });

        // Process message
        message.content().forEach(block -> {
            if (block.isText()) {
                System.out.println(block.asText().text());
            } else if (block.isToolUse()) {
                BetaToolUseBlock toolUse = block.asToolUse();
                System.out.println("Using tool: " + toolUse.name());
            }
        });

        // Modify params for next iteration if needed
        if (needsAdjustment(message)) {
            MessageCreateParams nextParams = runner.params()
                .toBuilder()
                .temperature(0.7)
                .build();
            runner.setNextParams(nextParams);
        }
    }
} catch (Exception e) {
    System.err.println("Tool runner error: " + e.getMessage());
}

Annotations for Tools

Jackson Annotations

Use Jackson Databind annotations to enhance tool schemas.

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonTypeName;

@JsonClassDescription: Tool description.

@JsonClassDescription("Get the weather in a given location")
class GetWeather {
    // ...
}

@JsonPropertyDescription: Parameter description.

class GetWeather {
    @JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
    public String location;
}

@JsonProperty: Include non-public fields or custom names.

class GetWeather {
    @JsonProperty("loc")  // Use "loc" instead of "location" in schema
    private String location;

    @JsonProperty  // Include private field
    private String apiKey;
}

@JsonIgnore: Exclude public fields from schema.

class GetWeather {
    public String location;

    @JsonIgnore
    public String internalCache;  // Excluded from tool schema
}

@JsonTypeName: Override tool name.

@JsonTypeName("weather_api")  // Use "weather_api" instead of "get_weather"
class GetWeather {
    // ...
}

Swagger/OpenAPI Annotations

Use Swagger annotations for type-specific constraints.

import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.ArraySchema;

@Schema: Property constraints.

class Article {
    @Schema(minimum = "1", maximum = "1000")
    public int pageCount;

    @Schema(pattern = "^[A-Z].*$")
    public String title;

    @Schema(format = "date")
    public String publicationDate;

    @Schema(description = "ISBN-13 identifier", minLength = 13, maxLength = 13)
    public String isbn;
}

@ArraySchema: Array constraints.

class BookList {
    @ArraySchema(minItems = 1, maxItems = 100)
    public List<Book> books;
}

Supported Constraints:

  • Numeric: minimum, maximum, exclusiveMinimum, exclusiveMaximum
  • String: minLength, maxLength, pattern, format
  • Array: minItems, maxItems, uniqueItems

Example:

@JsonClassDescription("Search for books in a library catalog")
class LibrarySearch {
    @JsonPropertyDescription("Search query terms")
    @Schema(minLength = 1, maxLength = 200)
    public String query;

    @JsonPropertyDescription("Publication year range")
    @Schema(minimum = "1800", maximum = "2100")
    public Integer year;

    @ArraySchema(
        schema = @Schema(description = "Genre filters"),
        minItems = 0,
        maxItems = 5
    )
    public List<String> genres;

    @Schema(format = "date")
    public String searchDate;
}

JSON Schema Validation

Local Validation

Validate tool schemas locally before sending to API.

package com.anthropic.core;

enum class JsonSchemaLocalValidation {
    YES,  // Enable local validation (default)
    NO    // Disable local validation
}

Enable/Disable:

// Default: validation enabled
MessageCreateParams params = MessageCreateParams.builder()
    .addTool(GetWeather.class)  // Validates schema
    .build();

// Explicitly enable
MessageCreateParams params = MessageCreateParams.builder()
    .addTool(GetWeather.class, JsonSchemaLocalValidation.YES)
    .build();

// Disable validation
MessageCreateParams params = MessageCreateParams.builder()
    .addTool(GetWeather.class, JsonSchemaLocalValidation.NO)
    .build();

Validation Checks:

  • Schema structure compliance with JSON Schema Draft 2020-12
  • Anthropic-specific restrictions (supported types, constraints)
  • Required properties defined
  • Valid constraint values

When to Disable:

  • SDK version older than API schema restrictions
  • Custom schema extensions
  • Troubleshooting compatibility issues

Note: Remote API always validates schemas regardless of local validation setting.

Complete Tool Use Example

Full workflow showing tool definition, execution, and result handling.

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.messages.*;
import com.anthropic.models.messages.Model;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import java.util.List;

public class ToolUseExample {

    // 1. Define tool classes with annotations
    enum Unit {
        CELSIUS, FAHRENHEIT;

        public String toString() {
            return this == CELSIUS ? "°C" : "°F";
        }

        public double fromKelvin(double k) {
            return this == CELSIUS ? k - 273.15 : (k - 273.15) * 1.8 + 32.0;
        }
    }

    @JsonClassDescription("Get the weather in a given location")
    static class GetWeather {
        @JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
        public String location;

        @JsonPropertyDescription("The unit of temperature")
        public Unit unit;

        public Weather execute() {
            // Simulate weather lookup
            double tempK = switch (location) {
                case "San Francisco, CA" -> 300.0;
                case "New York, NY" -> 310.0;
                case "Dallas, TX" -> 305.0;
                default -> 295.0;
            };

            return new Weather(
                String.format("%.0f%s", unit.fromKelvin(tempK), unit)
            );
        }
    }

    static class Weather {
        public String temperature;

        public Weather(String temperature) {
            this.temperature = temperature;
        }
    }

    public static void main(String[] args) {
        // 2. Create client
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        // 3. Build initial message params with tool
        MessageCreateParams.Builder paramsBuilder = MessageCreateParams.builder()
            .model(Model.CLAUDE_SONNET_4_5)
            .maxTokens(2048)
            .addTool(GetWeather.class)  // Add tool from class
            .addUserMessage("What's the temperature in New York?");

        // 4. Create message and get tool use
        client.beta().messages()
            .create(paramsBuilder.build())
            .content()
            .stream()
            .flatMap(block -> block.toolUse().stream())
            .forEach(toolUse -> {
                System.out.println("Tool requested: " + toolUse.name());

                // 5. Parse tool parameters
                GetWeather tool = toolUse.input(GetWeather.class);
                System.out.println("Location: " + tool.location);
                System.out.println("Unit: " + tool.unit);

                // 6. Execute tool
                Weather result = tool.execute();
                System.out.println("Result: " + result.temperature);

                // 7. Add assistant message with tool use
                paramsBuilder.addAssistantMessageOfBetaContentBlockParams(
                    List.of(BetaContentBlockParam.ofToolUse(
                        BetaToolUseBlockParam.builder()
                            .name(toolUse.name())
                            .id(toolUse.id())
                            .input(toolUse._input())
                            .build()
                    ))
                );

                // 8. Add user message with tool result
                paramsBuilder.addUserMessageOfBetaContentBlockParams(
                    List.of(BetaContentBlockParam.ofToolResult(
                        BetaToolResultBlockParam.builder()
                            .toolUseId(toolUse.id())
                            .contentAsJson(result)  // Convert to JSON
                            .build()
                    ))
                );
            });

        // 9. Get final response from Claude
        client.beta().messages()
            .create(paramsBuilder.build())
            .content()
            .stream()
            .flatMap(block -> block.text().stream())
            .forEach(text -> System.out.println("Claude: " + text.text()));
    }
}

Output:

Tool requested: get_weather
Location: New York, NY
Unit: FAHRENHEIT
Result: 99°F
Claude: The temperature in New York is 99°F.

See Also:

  • Messages API - Core messaging functionality
  • Streaming - Streaming responses
  • [Structured Outputs](README section on structured outputs) - JSON schema outputs

Install with Tessl CLI

npx tessl i tessl/maven-com-anthropic--anthropic-java@2.11.1

docs

client-setup.md

errors.md

index.md

messages.md

platform-adapters.md

streaming.md

structured-outputs.md

tools.md

tile.json