Basic usage
Define tools using the SDK helpers, then use the tool runner to run them.
Depending on the SDK's tool signature, a tool returns its result as a string or as content blocks (text, image, or document blocks), so a tool can return multimodal results. A returned string becomes a single text content block. To return structured data, such as a JSON object or a number, encode it as a string first.
Use the `@beta_tool` decorator to define tools with type hints and docstrings.
If you're using the async client, replace `@beta_tool` with `@beta_async_tool` and define the function with `async def`.
```python
import json
from anthropic import Anthropic, beta_tool
client = Anthropic()
@beta_tool
def get_weather(location: str, unit: str = "fahrenheit") -> str:
"""Get the current weather in a given location.
Args:
location: The city and state, e.g. San Francisco, CA
unit: Temperature unit, either 'celsius' or 'fahrenheit'
"""
return json.dumps(\{"temperature": "20°C", "condition": "Sunny"\})
@beta_tool
def calculate_sum(a: int, b: int) -> str:
"""Add two numbers together.
Args:
a: First number
b: Second number
"""
return str(a + b)
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[get_weather, calculate_sum],
messages=[
\{
"role": "user",
"content": "What's the weather like in Paris? Also, what's 15 + 27?",
\}
],
)
for message in runner:
print(message)
```
The `@beta_tool` decorator inspects the function arguments and docstring to derive the JSON schema for you.
Use `betaZodTool()` for type-safe tool definitions with Zod validation, or `betaTool()` for JSON Schema-based definitions.
TypeScript offers two approaches for defining tools:
**Using Zod (recommended)** - Use `betaZodTool()` for type-safe tool definitions with Zod validation (requires Zod 3.25.0 or higher):
```typescript
import Anthropic from "@anthropic-ai/sdk";
import \{ betaZodTool \} from "@anthropic-ai/sdk/helpers/beta/zod";
import \{ z \} from "zod";
const client = new Anthropic();
const getWeatherTool = betaZodTool(\{
name: "get_weather",
description: "Get the current weather in a given location",
inputSchema: z.object(\{
location: z.string().describe("The city and state, e.g. San Francisco, CA"),
unit: z.enum(["celsius", "fahrenheit"]).default("fahrenheit").describe("Temperature unit")
\}),
run: async (input) => \{
return JSON.stringify(\{ temperature: "20°C", condition: "Sunny" \});
\}
\});
const finalMessage = await client.beta.messages.toolRunner(\{
model: "claude-opus-5",
max_tokens: 1024,
tools: [getWeatherTool],
messages: [\{ role: "user", content: "What's the weather like in Paris?" \}]
\});
for (const block of finalMessage.content) \{
if (block.type === "text") \{
console.log(block.text);
\}
\}
```
**Using JSON Schema** - Use `betaTool()` for type-safe tool definitions without Zod:
The input generated by Claude is not validated at runtime. Perform validation inside the `run` function if needed.
```typescript
import Anthropic from "@anthropic-ai/sdk";
import \{ betaTool \} from "@anthropic-ai/sdk/helpers/beta/json-schema";
const client = new Anthropic();
const calculateSumTool = betaTool(\{
name: "calculate_sum",
description: "Add two numbers together",
inputSchema: \{
type: "object",
properties: \{
a: \{ type: "number", description: "First number" \},
b: \{ type: "number", description: "Second number" \}
\},
required: ["a", "b"]
\},
run: async (input) => \{
return String(input.a + input.b);
\}
\});
const finalMessage = await client.beta.messages.toolRunner(\{
model: "claude-opus-5",
max_tokens: 1024,
tools: [calculateSumTool],
messages: [\{ role: "user", content: "What's 15 + 27?" \}]
\});
for (const block of finalMessage.content) \{
if (block.type === "text") \{
console.log(block.text);
\}
\}
```
Define each tool as a `BetaRunnableTool`, providing a `Definition` with a JSON schema and a `Run` delegate that runs when Claude calls the tool.
```csharp
using System.Text.Json;
using Anthropic;
using Anthropic.Helpers.Beta;
using Anthropic.Models.Beta.Messages;
using MessageCreateParams = Anthropic.Models.Beta.Messages.MessageCreateParams;
using InputSchema = Anthropic.Models.Beta.Messages.InputSchema;
using Role = Anthropic.Models.Beta.Messages.Role;
using Model = Anthropic.Models.Messages.Model;
var client = new AnthropicClient();
var getWeatherTool = new BetaRunnableTool
\{
Name = "get_weather",
Definition = new BetaTool
\{
Name = "get_weather",
Description = "Get the current weather in a given location.",
InputSchema = new InputSchema
\{
Properties = new Dictionary<string, JsonElement>
\{
["location"] = JsonSerializer.SerializeToElement(
new \{ type = "string", description = "The city and state, e.g. San Francisco, CA" \}
),
\},
Required = ["location"],
\},
\},
Run = (toolUse, _) =>
\{
var location = toolUse.Input["location"].GetString();
return Task.FromResult<BetaToolResultBlockParamContent>(
$"Weather in {location}: 20°C, sunny"
);
},
};
var calculateSumTool = new BetaRunnableTool
{
Name = "calculate_sum",
Definition = new BetaTool
{
Name = "calculate_sum",
Description = "Add two numbers together.",
InputSchema = new InputSchema
{
Properties = new Dictionary<string, JsonElement>
{
["a"] = JsonSerializer.SerializeToElement(new { type = "number" }),
["b"] = JsonSerializer.SerializeToElement(new { type = "number" }),
},
Required = ["a", "b"],
},
},
Run = (toolUse, _) =>
{
var a = toolUse.Input["a"].GetDouble();
var b = toolUse.Input["b"].GetDouble();
return Task.FromResult<BetaToolResultBlockParamContent>($"\{a + b\}");
\},
\};
var runner = client.Beta.Messages.ToolRunner(
new MessageCreateParams
\{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages =
[
new()
\{
Role = Role.User,
Content = "What's the weather like in Paris? Also, what's 15 + 27?",
\},
],
\},
[getWeatherTool, calculateSumTool]
);
await foreach (var message in runner)
\{
Console.WriteLine(message);
\}
```
Define a tool with `toolrunner.NewBetaToolFromJSONSchema`. The handler's input type is a struct with `jsonschema:` tags. The SDK reflects on it to generate the JSON schema.
```go
package main
import (
"context"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/toolrunner"
)
type GetWeatherInput struct \{
Location string `json:"location" jsonschema:"required,description=The city and state, e.g. San Francisco, CA"`
Unit string `json:"unit,omitempty" jsonschema:"enum=celsius,enum=fahrenheit,description=Temperature unit"`
\}
type CalculateSumInput struct \{
A int `json:"a" jsonschema:"required,description=First number"`
B int `json:"b" jsonschema:"required,description=Second number"`
\}
func main() \{
client := anthropic.NewClient()
ctx := context.Background()
getWeather, err := toolrunner.NewBetaToolFromJSONSchema(
"get_weather",
"Get the current weather in a given location.",
func(ctx context.Context, input GetWeatherInput) (anthropic.BetaToolResultBlockParamContentUnion, error) \{
return anthropic.BetaToolResultBlockParamContentUnion\{
OfText: &anthropic.BetaTextBlockParam\{Text: "20°C, Sunny"\},
\}, nil
\},
)
if err != nil \{
log.Fatal(err)
\}
calculateSum, err := toolrunner.NewBetaToolFromJSONSchema(
"calculate_sum",
"Add two numbers together.",
func(ctx context.Context, input CalculateSumInput) (anthropic.BetaToolResultBlockParamContentUnion, error) \{
return anthropic.BetaToolResultBlockParamContentUnion\{
OfText: &anthropic.BetaTextBlockParam\{Text: fmt.Sprintf("%d", input.A+input.B)\},
\}, nil
\},
)
if err != nil \{
log.Fatal(err)
\}
runner := client.Beta.Messages.NewToolRunner(
[]anthropic.BetaTool\{getWeather, calculateSum\},
anthropic.BetaToolRunnerParams\{
BetaMessageNewParams: anthropic.BetaMessageNewParams\{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.BetaMessageParam\{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(
"What's the weather like in Paris? Also, what's 15 + 27?",
)),
\},
\},
\},
)
for message, err := range runner.All(ctx) \{
if err != nil \{
log.Fatal(err)
\}
fmt.Println(message)
\}
\}
```
The `jsonschema:` struct tags generate the input schema. For example, `CalculateSumInput` becomes:
```json
\{
"name": "calculate_sum",
"description": "Add two numbers together.",
"input_schema": \{
"type": "object",
"properties": \{
"a": \{ "type": "integer", "description": "First number" \},
"b": \{ "type": "integer", "description": "Second number" \}
\},
"required": ["a", "b"]
\}
\}
```
Define each tool as a class implementing `Supplier<String>`. Annotate the class with `@JsonClassDescription` for the tool description, and each public field with `@JsonPropertyDescription` for parameter descriptions. The SDK derives the JSON schema, tool name (snake-cased class name), and input parsing from the class, and marks the tool with `strict: true` ([strict tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use)).
```java
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.helpers.BetaToolRunner;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import java.util.function.Supplier;
@JsonClassDescription("Get the current weather in a given location")
static class GetWeather implements Supplier<String> \{
@JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
public String location;
@JsonPropertyDescription("Temperature unit, either 'celsius' or 'fahrenheit'")
public String unit;
@Override
public String get() \{
return "\{\"temperature\": \"20°C\", \"condition\": \"Sunny\"\}";
\}
\}
@JsonClassDescription("Add two numbers together")
static class CalculateSum implements Supplier<String> \{
@JsonPropertyDescription("First number")
public double a;
@JsonPropertyDescription("Second number")
public double b;
@Override
public String get() \{
return String.valueOf(a + b);
\}
\}
void main() \{
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BetaToolRunner runner = client.beta()
.messages()
.toolRunner(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024)
.addBeta("structured-outputs-2025-11-13")
.addUserMessage("What's the weather like in Paris? Also, what's 15 + 27?")
.addTool(GetWeather.class)
.addTool(CalculateSum.class)
.build());
for (BetaMessage message : runner) \{
IO.println(message);
\}
\}
```
The class name `CalculateSum` becomes the tool name `calculate_sum`, and the SDK generates a JSON schema from the annotated fields:
```json
\{
"name": "calculate_sum",
"description": "Add two numbers together",
"input_schema": \{
"type": "object",
"properties": \{
"a": \{ "description": "First number", "type": "number" \},
"b": \{ "description": "Second number", "type": "number" \}
\},
"required": ["a", "b"],
"additionalProperties": false
\},
"strict": true
\}
```
Define each tool as a `BetaRunnableTool` that pairs the tool's JSON schema definition with a closure that runs it.
```php
<?php
use Anthropic\Client;
use Anthropic\Lib\Tools\BetaRunnableTool;
use Anthropic\Messages\Model;
$client = new Client();
$getWeather = new BetaRunnableTool(
definition: [
'name' => 'get_weather',
'description' => 'Get the current weather in a given location.',
'input_schema' => [
'type' => 'object',
'properties' => [
'location' => [
'type' => 'string',
'description' => 'The city and state, e.g. San Francisco, CA',
],
'unit' => [
'type' => 'string',
'enum' => ['celsius', 'fahrenheit'],
],
],
'required' => ['location'],
],
],
run: fn (array $input): string => json_encode([
'temperature' => '20°C',
'condition' => 'Sunny',
]),
);
$calculateSum = new BetaRunnableTool(
definition: [
'name' => 'calculate_sum',
'description' => 'Add two numbers together.',
'input_schema' => [
'type' => 'object',
'properties' => [
'a' => ['type' => 'number', 'description' => 'First number'],
'b' => ['type' => 'number', 'description' => 'Second number'],
],
'required' => ['a', 'b'],
],
],
run: fn (array $input): string => (string) ($input['a'] + $input['b']),
);
$runner = $client->beta->messages->toolRunner(
maxTokens: 1024,
messages: [
['role' => 'user', 'content' => "What's the weather like in Paris? Also, what's 15 + 27?"],
],
model: Model::CLAUDE_OPUS_5,
tools: [$getWeather, $calculateSum],
);
foreach ($runner as $message) {
foreach ($message->content as $block) {
if ($block->type === 'text') \{
echo $block->text, "\n";
} elseif ($block->type === 'tool_use') \{
echo "[Tool call: \{$block->name\}]\n";
\}
\}
\}
```
Use the `Anthropic::BaseTool` class to define tools with typed input schemas.
```ruby
require "anthropic"
# Initialize client
client = Anthropic::Client.new
# Define input schema
class GetWeatherInput < Anthropic::BaseModel
required :location, String, doc: "The city and state, e.g. San Francisco, CA"
optional :unit, Anthropic::InputSchema::EnumOf["celsius", "fahrenheit"],
doc: "Temperature unit"
end
# Define tool
class GetWeather < Anthropic::BaseTool
doc "Get the current weather in a given location"
input_schema GetWeatherInput
def call(input)
# In a full implementation, you'd call a weather API here
JSON.generate(\{temperature: "20°C", condition: "Sunny"\})
end
end
class CalculateSumInput < Anthropic::BaseModel
required :a, Integer, doc: "First number"
required :b, Integer, doc: "Second number"
end
class CalculateSum < Anthropic::BaseTool
doc "Add two numbers together"
input_schema CalculateSumInput
def call(input)
(input.a + input.b).to_s
end
end
# Use the tool runner
runner = client.beta.messages.tool_runner(
model: "claude-opus-5",
max_tokens: 1024,
tools: [GetWeather.new, CalculateSum.new],
messages: [
\{role: "user", content: "What's the weather like in Paris? Also, what's 15 + 27?"\}
]
)
runner.each_message do |message|
message.content.each do |block|
puts block.text if block.type == :text
end
end
```
The `Anthropic::BaseTool` class uses the `doc` method for the tool description and `input_schema` to define the expected parameters. The SDK automatically converts this to the appropriate JSON schema format.