# How to implement tool use¶
¶
---¶
¶
## Choosing a model¶
¶
We recommend using the latest Claude Opus (4.6) model for complex tools and ambiguous queries; it handles multiple tools better and seeks clarification when needed.¶
¶
Use Claude Haiku models for straightforward tools, but note they may infer missing parameters.¶
¶
<Tip>¶
If using Claude with tool use and extended thinking, refer to our guide [herthe [extended thinking guide](/docs/en/build-with-claude/extended-thinking) for more information.¶
</Tip>¶
¶
## Specifying client tools¶
¶
Client tools (both Anthropic-defined and user-defined) are specified in the `tools` top-level parameter of the API request. Each tool definition includes:¶
¶
| Parameter | Description |¶
| :------------- | :-------------------------------------------------------------------------------------------------- |¶
| `name` | The name of the tool. Must match the regex `^[a-zA-Z0-9_-]{1,64}$`. |¶
| `description` | A detailed plaintext description of what the tool does, when it should be used, and how it behaves. |¶
| `input_schema` | A [JSON Schema](https://json-schema.org/) object defining the expected parameters for the tool. |¶
| `input_examples` | (Optional, beta) An array of example input objects to help Claude understand how to use the tool. See [Providing tool use examples](#providing-tool-use-examples). |¶
¶
<section title="Example simple tool definition">¶
¶
```json JSON¶
{¶
"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"],¶
"description": "The unit of temperature, either 'celsius' or 'fahrenheit'"¶
}¶
},¶
"required": ["location"]¶
}¶
}¶
```¶
¶
This tool, named `get_weather`, expects an input object with a required `location` string and an optional `unit` string that must be either "celsius" or "fahrenheit".¶
¶
</section>¶
¶
### Tool use system prompt¶
¶
When you call the Claude API with the `tools` parameter, we construct a special system prompt from the tool definitions, tool configuration, and any user-specified system prompt. The constructed prompt is designed to instruct the model to use the specified tool(s) and provide the necessary context for the tool to operate properly:¶
¶
```text¶
In this environment you have access to a set of tools you can use to answer the user's question.¶
{{ FORMATTING INSTRUCTIONS }}¶
String and scalar parameters should be specified as is, while lists and objects should use JSON format. Note that spaces for string values are not stripped. The output is not expected to be valid XML and is parsed with regular expressions.¶
Here are the functions available in JSONSchema format:¶
{{ TOOL DEFINITIONS IN JSON SCHEMA }}¶
{{ USER SYSTEM PROMPT }}¶
{{ TOOL CONFIGURATION }}¶
```¶
¶
### Best practices for tool definitions¶
¶
To get the best performance out of Claude when using tools, follow these guidelines:¶
¶
- **Provide extremely detailed descriptions.** This is by far the most important factor in tool performance. Your descriptions should explain every detail about the tool, including:¶
- What the tool does¶
- When it should be used (and when it shouldn't)¶
- What each parameter means and how it affects the tool's behavior¶
- Any important caveats or limitations, such as what information the tool does not return if the tool name is unclear. The more context you can give Claude about your tools, the better it will be at deciding when and how to use them. Aim for at least 3-4 sentences per tool description, more if the tool is complex.¶
- **Prioritize descriptions, but consider using `input_examples` for complex tools.** Clear descriptions are most important, but for tools with complex inputs, nested objects, or format-sensitive parameters, you can use the `input_examples` field (beta) to provide schema-validated examples. See [Providing tool use examples](#providing-tool-use-examples) for details.¶
¶
<section title="Example of a good tool description">¶
¶
```json JSON¶
{¶
"name": "get_stock_price",¶
"description": "Retrieves the current stock price for a given ticker symbol. The ticker symbol must be a valid symbol for a publicly traded company on a major US stock exchange like NYSE or NASDAQ. The tool will return the latest trade price in USD. It should be used when the user asks about the current or most recent price of a specific stock. It will not provide any other information about the stock or company.",¶
"input_schema": {¶
"type": "object",¶
"properties": {¶
"ticker": {¶
"type": "string",¶
"description": "The stock ticker symbol, e.g. AAPL for Apple Inc."¶
}¶
},¶
"required": ["ticker"]¶
}¶
}¶
```¶
¶
</section>¶
¶
<section title="Example poor tool description">¶
¶
```json JSON¶
{¶
"name": "get_stock_price",¶
"description": "Gets the stock price for a ticker.",¶
"input_schema": {¶
"type": "object",¶
"properties": {¶
"ticker": {¶
"type": "string"¶
}¶
},¶
"required": ["ticker"]¶
}¶
}¶
```¶
¶
</section>¶
¶
The good description clearly explains what the tool does, when to use it, what data it returns, and what the `ticker` parameter means. The poor description is too brief and leaves Claude with many open questions about the tool's behavior and usage.¶
¶
## Providing tool use examples¶
¶
You can provide concrete examples of valid tool inputs to help Claude understand how to use your tools more effectively. This is particularly useful for complex tools with nested objects, optional parameters, or format-sensitive inputs.¶
¶
<Info>¶
Tool use examples is a beta feature. Include the appropriate [beta header](/docs/en/api/beta-headers) for your provider:¶
¶
| Provider | Beta header | Supported models |¶
|----------|-------------|------------------|¶
| Claude API,<br/>Microsoft Foundry | `advanced-tool-use-2025-11-20` | All models |¶
| Vertex AI,<br/>Amazon Bedrock | `tool-examples-2025-10-29` | Claude Opus 4.6, Claude Opus 4.5 |¶
</Info>¶
¶
### Basic usage¶
¶
Add an optional `input_examples` field to your tool definition with an array of example input objects. Each example must be valid according to the tool's `input_schema`:¶
¶
<CodeGroup>¶
```python Python¶
import anthropic¶
¶
client = anthropic.Anthropic()¶
¶
response = client.beta.messages.create(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
betas=["advanced-tool-use-2025-11-20"],¶
tools=[¶
{¶
"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"],¶
"description": "The unit of temperature",¶
},¶
},¶
"required": ["location"],¶
},¶
"input_examples": [¶
{"location": "San Francisco, CA", "unit": "fahrenheit"},¶
{"location": "Tokyo, Japan", "unit": "celsius"},¶
{¶
"location": "New York, NY" # 'unit' is optional¶
},¶
],¶
}¶
],¶
messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}],¶
)¶
```¶
¶
```typescript TypeScript¶
import Anthropic from "@anthropic-ai/sdk";¶
¶
const client = new Anthropic();¶
¶
const response = await client.beta.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
betas: ["advanced-tool-use-2025-11-20"],¶
tools: [¶
{¶
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"],¶
description: "The unit of temperature"¶
}¶
},¶
required: ["location"]¶
},¶
input_examples: [¶
{¶
location: "San Francisco, CA",¶
unit: "fahrenheit"¶
},¶
{¶
location: "Tokyo, Japan",¶
unit: "celsius"¶
},¶
{¶
location: "New York, NY"¶
// Demonstrates that 'unit' is optional¶
}¶
]¶
}¶
],¶
messages: [{ role: "user", content: "What's the weather like in San Francisco?" }]¶
});¶
```¶
</CodeGroup>¶
¶
Examples are included in the prompt alongside your tool schema, showing Claude concrete patterns for well-formed tool calls. This helps Claude understand when to include optional parameters, what formats to use, and how to structure complex inputs.¶
¶
### Requirements and limitations¶
¶
- **Schema validation** - Each example must be valid according to the tool's `input_schema`. Invalid examples return a 400 error¶
- **Not supported for server-side tools** - Only user-defined tools can have input examples¶
- **Token cost** - Examples add to prompt tokens: ~20-50 tokens for simple examples, ~100-200 tokens for complex nested objects¶
¶
## Tool runner (beta)¶
¶
The tool runner provides an out-of-the-box solution for executing tools with Claude. Instead of manually handling tool calls, tool results, and conversation management, the tool runner automatically:¶
¶
- Executes tools when Claude calls them¶
- Handles the request/response cycle¶
- Manages conversation state¶
- Provides type safety and validation¶
¶
We recommend that you use the tool runner for most tool use implementations.¶
¶
<Note>¶
The tool runner is currently in beta and available in the [Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/tools.md), [TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/helpers.md#tool-helpers), and [Ruby](https://github.com/anthropics/anthropic-sdk-ruby/blob/main/helpers.md#3-auto-looping-tool-runner-beta) SDKs.¶
</Note>¶
¶
<Tip>¶
**Automatic context management with compaction**¶
¶
The tool runner supports automatic [compaction](/docs/en/build-with-claude/context-editing#client-side-compaction-sdk), which generates summaries when token usage exceeds a threshold. This allows long-running agentic tasks to continue beyond context window limits.¶
</Tip>¶
¶
### Basic usage¶
¶
Define tools using the SDK helpers, then use the tool runner to execute them.¶
¶
<Tabs>¶
<Tab title="Python">¶
¶
Use the `@beta_tool` decorator to define tools with type hints and docstrings.¶
¶
<Note>¶
If you're using the async client, replace `@beta_tool` with `@beta_async_tool` and define the function with `async def`.¶
</Note>¶
¶
```python¶
import anthropic¶
import json¶
from anthropic import beta_tool¶
¶
# Initialize client¶
client = anthropic.Anthropic()¶
¶
¶
# Define tools using the decorator¶
@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'¶
"""¶
# In a full implementation, you'd call a weather API here¶
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)¶
¶
¶
# Use the tool runner¶
runner = client.beta.messages.tool_runner(¶
model="claude-opus-4-6",¶
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.content[0].text)¶
```¶
¶
The `@beta_tool` decorator inspects the function arguments and docstring to extract a JSON schema representation. For example, `calculate_sum` becomes:¶
¶
```json¶
{¶
"name": "calculate_sum",¶
"description": "Adds two integers together.",¶
"input_schema": {¶
"additionalProperties": false,¶
"properties": {¶
"left": {¶
"description": "The first integer to add.",¶
"title": "Left",¶
"type": "integer"¶
},¶
"right": {¶
"description": "The second integer to add.",¶
"title": "Right",¶
"type": "integer"¶
}¶
},¶
"required": ["left", "right"],¶
"type": "object"¶
}¶
}¶
```¶
¶
</Tab>¶
<Tab title="TypeScript">¶
¶
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 anthropic = 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) => {¶
// In a full implementation, you'd call a weather API here¶
return JSON.stringify({ temperature: "20°C", condition: "Sunny" });¶
}¶
});¶
¶
const runner = anthropic.beta.messages.toolRunner({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [getWeatherTool],¶
messages: [{ role: "user", content: "What's the weather like in Paris?" }]¶
});¶
¶
for await (const message of runner) {¶
console.log(message.content[0].text);¶
}¶
```¶
¶
**Using JSON Schema** - Use `betaTool()` for type-safe tool definitions without Zod:¶
¶
<Note>¶
The input generated by Claude will not be validated at runtime. Perform validation inside the `run` function if needed.¶
</Note>¶
¶
```typescript¶
import { Anthropic } from "@anthropic-ai/sdk";¶
import { betaTool } from "@anthropic-ai/sdk/helpers/beta/json-schema";¶
¶
const anthropic = 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 runner = anthropic.beta.messages.toolRunner({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [calculateSumTool],¶
messages: [{ role: "user", content: "What's 15 + 27?" }]¶
});¶
¶
for await (const message of runner) {¶
console.log(message.content[0].text);¶
}¶
```¶
¶
</Tab>¶
<Tab title="Ruby">¶
¶
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-4-6",¶
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.respond_to?(: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.¶
¶
</Tab>¶
</Tabs>¶
¶
The tool function must return a content block or content block array, including text, images, or document blocks. This allows tools to return rich, multimodal responses. Returned strings will be converted to a text content block. If you want to return a structured JSON object to Claude, encode it to a JSON string before returning it. Numbers, booleans, or other non-string primitives must also be converted to strings.¶
¶
### Iterating over the tool runner¶
¶
The tool runner is an iterable that yields messages from Claude. This is often referred to as a "tool call loop". Each iteration, the runner checks if Claude requested a tool use. If so, it calls the tool and sends the result back to Claude automatically, then yields the next message from Claude to continue your loop.¶
¶
You can end the loop at any iteration with a `break` statement. The runner will loop until Claude returns a message without a tool use.¶
¶
If you don't need intermediate messages, you can get the final message directly:¶
¶
<Tabs>¶
<Tab title="Python">¶
¶
Use `runner.until_done()` to get the final message.¶
¶
```python¶
runner = client.beta.messages.tool_runner(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
tools=[get_weather, calculate_sum],¶
messages=[¶
{¶
"role": "user",¶
"content": "What's the weather like in Paris? Also, what's 15 + 27?",¶
}¶
],¶
)¶
final_message = runner.until_done()¶
print(final_message.content[0].text)¶
```¶
¶
</Tab>¶
<Tab title="TypeScript">¶
¶
Simply `await` the runner to get the final message.¶
¶
```typescript¶
const runner = anthropic.beta.messages.toolRunner({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [getWeatherTool],¶
messages: [{ role: "user", content: "What's the weather like in Paris?" }]¶
});¶
¶
const finalMessage = await runner;¶
console.log(finalMessage.content[0].text);¶
```¶
¶
</Tab>¶
<Tab title="Ruby">¶
¶
Use `runner.run_until_finished` to get all messages.¶
¶
```ruby¶
runner = client.beta.messages.tool_runner(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [GetWeather.new, CalculateSum.new],¶
messages: [¶
{role: "user", content: "What's the weather like in Paris? Also, what's 15 + 27?"}¶
]¶
)¶
¶
all_messages = runner.run_until_finished¶
all_messages.each { |msg| puts msg.content }¶
```¶
¶
</Tab>¶
</Tabs>¶
¶
### Advanced usage¶
¶
Within the loop, you can fully customize the tool runner's next request to the Messages API. The runner automatically appends tool results to the message history, so you don't need to manually manage them. You can optionally inspect the tool result for logging or debugging, and modify the request parameters before the next API call.¶
¶
<Tabs>¶
<Tab title="Python">¶
¶
Use `generate_tool_call_response()` to optionally inspect the tool result (the runner appends it automatically). Use `set_messages_params()` and `append_messages()` to modify the request.¶
¶
```python¶
runner = client.beta.messages.tool_runner(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
tools=[get_weather],¶
messages=[{"role": "user", "content": "What's the weather in San Francisco?"}],¶
)¶
for message in runner:¶
# Optional: inspect the tool response (automatically appended by the runner)¶
tool_response = runner.generate_tool_call_response()¶
if tool_response:¶
print(f"Tool result: {tool_response}")¶
¶
# Customize the next request¶
runner.set_messages_params(¶
lambda params: {¶
**params,¶
"max_tokens": 2048, # Increase tokens for next request¶
}¶
)¶
¶
# Or add additional messages¶
runner.append_messages(¶
{"role": "user", "content": "Please be concise in your response."}¶
)¶
```¶
¶
</Tab>¶
<Tab title="TypeScript">¶
¶
Use `generateToolResponse()` to optionally inspect the tool result (the runner appends it automatically). Use `setMessagesParams()` and `pushMessages()` to modify the request.¶
¶
```typescript¶
const runner = anthropic.beta.messages.toolRunner({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [getWeatherTool],¶
messages: [{ role: "user", content: "What's the weather in San Francisco?" }]¶
});¶
¶
for await (const message of runner) {¶
// Optional: inspect the tool result message (automatically appended by the runner)¶
const toolResultMessage = await runner.generateToolResponse();¶
if (toolResultMessage) {¶
console.log("Tool result:", toolResultMessage);¶
}¶
¶
// Customize the next request¶
runner.setMessagesParams(params => ({¶
...params,¶
max_tokens: 2048 // Increase tokens for next request¶
}));¶
¶
// Or add additional messages¶
runner.pushMessages(¶
{ role: "user", content: "Please be concise in your response." }¶
);¶
}¶
```¶
¶
</Tab>¶
<Tab title="Ruby">¶
¶
Use `next_message` for step-by-step control. Use `feed_messages` to inject messages and `params` to access parameters.¶
¶
```ruby¶
runner = client.beta.messages.tool_runner(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [GetWeather.new],¶
messages: [{role: "user", content: "What's the weather in San Francisco?"}]¶
)¶
¶
# Manual step-by-step control¶
message = runner.next_message¶
puts message.content¶
¶
# Inject follow-up messages¶
runner.feed_messages([¶
{role: "user", content: "Also check Boston"}¶
])¶
¶
# Access current parameters¶
puts runner.params¶
```¶
¶
</Tab>¶
</Tabs>¶
¶
#### Debugging tool execution¶
¶
When a tool throws an exception, the tool runner catches it and returns the error to Claude as a tool result with `is_error: true`. By default, only the exception message is included, not the full stack trace.¶
¶
To view full stack traces and debug information, set the `ANTHROPIC_LOG` environment variable:¶
¶
```bash¶
# View info-level logs including tool errors¶
export ANTHROPIC_LOG=info¶
¶
# View debug-level logs for more verbose output¶
export ANTHROPIC_LOG=debug¶
```¶
¶
When enabled, the SDK logs full exception details (using Python's `logging` module, the console in TypeScript, or Ruby's logger), including the complete stack trace when a tool fails.¶
¶
#### Intercepting tool errors¶
¶
By default, tool errors are passed back to Claude, which can then respond appropriately. However, you may want to detect errors and handle them differently—for example, to stop execution early or implement custom error handling.¶
¶
Use the tool response method to intercept tool results and check for errors before they're sent to Claude:¶
¶
<Tabs>¶
<Tab title="Python">¶
¶
```python¶
import json¶
¶
runner = client.beta.messages.tool_runner(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
tools=[my_tool],¶
messages=[{"role": "user", "content": "Run the tool"}],¶
)¶
¶
for message in runner:¶
tool_response = runner.generate_tool_call_response()¶
¶
if tool_response:¶
# Check if any tool result has an error¶
for block in tool_response.content:¶
if block.is_error:¶
# Option 1: Raise an exception to stop the loop¶
raise RuntimeError(f"Tool failed: {json.dumps(block.content)}")¶
¶
# Option 2: Log and continue (let Claude handle it)¶
# logger.error(f"Tool error: {json.dumps(block.content)}")¶
¶
# Process the message normally¶
print(message.content)¶
```¶
¶
</Tab>¶
<Tab title="TypeScript">¶
¶
```typescript¶
const runner = anthropic.beta.messages.toolRunner({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [myTool],¶
messages: [{ role: "user", content: "Run the tool" }]¶
});¶
¶
for await (const message of runner) {¶
const toolResultMessage = await runner.generateToolResponse();¶
¶
if (toolResultMessage) {¶
// Check if any tool result has an error¶
for (const block of toolResultMessage.content) {¶
if (block.type === "tool_result" && block.is_error) {¶
// Option 1: Throw to stop the loop¶
throw new Error(`Tool failed: ${JSON.stringify(block.content)}`);¶
¶
// Option 2: Log and continue (let Claude handle it)¶
// console.error(`Tool error: ${JSON.stringify(block.content)}`);¶
}¶
}¶
}¶
¶
// Process the message normally¶
console.log(message.content);¶
}¶
```¶
¶
</Tab>¶
<Tab title="Ruby">¶
¶
```ruby¶
runner = client.beta.messages.tool_runner(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [MyTool.new],¶
messages: [{role: "user", content: "Run the tool"}]¶
)¶
¶
runner.each_message do |message|¶
# Get the tool response to check for errors¶
# Note: The runner automatically handles tool execution and appends results¶
# This is just for error checking/logging purposes¶
tool_results = runner.params[:messages].last¶
¶
if tool_results && tool_results[:role] == "user"¶
tool_results[:content].each do |block|¶
if block[:type] == "tool_result" && block[:is_error]¶
# Option 1: Raise an exception to stop the loop¶
raise "Tool failed: #{block[:content]}"¶
¶
# Option 2: Log and continue (let Claude handle it)¶
# logger.error("Tool error: #{block[:content]}")¶
end¶
end¶
end¶
¶
puts message.content¶
end¶
```¶
¶
</Tab>¶
</Tabs>¶
¶
#### Modifying tool results¶
¶
You can modify tool results before they're sent back to Claude. This is useful for adding metadata like `cache_control` to enable [prompt caching](/docs/en/build-with-claude/prompt-caching) on tool results, or for transforming the tool output.¶
¶
Use the tool response method to get the tool result, modify it, then add your modified version to the messages:¶
¶
<Tabs>¶
<Tab title="Python">¶
¶
```python¶
runner = client.beta.messages.tool_runner(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
tools=[search_documents],¶
messages=[¶
{¶
"role": "user",¶
"content": "Search for information about the climate of San Francisco",¶
}¶
],¶
)¶
¶
for message in runner:¶
tool_response = runner.generate_tool_call_response()¶
¶
if tool_response:¶
# Modify the tool result to add cache control¶
for block in tool_response.content:¶
if block.type == "tool_result":¶
# Add cache_control to cache this tool result¶
block.cache_control = {"type": "ephemeral"}¶
¶
# Append the modified response (this prevents auto-append of original)¶
runner.append_messages(message, tool_response)¶
¶
print(message.content)¶
```¶
¶
</Tab>¶
<Tab title="TypeScript">¶
¶
```typescript¶
const runner = anthropic.beta.messages.toolRunner({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [searchDocuments],¶
messages: [{ role: "user", content: "Search for information about the climate of San Francisco" }]¶
});¶
¶
for await (const message of runner) {¶
const toolResultMessage = await runner.generateToolResponse();¶
¶
if (toolResultMessage) {¶
// Modify the tool result to add cache control¶
for (const block of toolResultMessage.content) {¶
if (block.type === "tool_result") {¶
// Add cache_control to cache this tool result¶
block.cache_control = { type: "ephemeral" };¶
}¶
}¶
¶
// Push the modified message (this prevents auto-append of original)¶
runner.pushMessages(message, toolResultMessage);¶
}¶
¶
console.log(message.content);¶
}¶
```¶
¶
</Tab>¶
<Tab title="Ruby">¶
¶
```ruby¶
runner = client.beta.messages.tool_runner(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [SearchDocuments.new],¶
messages: [{role: "user", content: "Search for information about the climate of San Francisco"}]¶
)¶
¶
loop do¶
message = runner.next_message¶
break unless message¶
¶
# Access the most recent tool results from the messages array¶
# The runner automatically adds tool results, but we can modify them¶
tool_results_message = runner.params[:messages].last¶
¶
if tool_results_message && tool_results_message[:role] == "user"¶
tool_results_message[:content].each do |block|¶
if block[:type] == "tool_result"¶
# Modify the tool result to add cache control¶
block[:cache_control] = {type: "ephemeral"}¶
end¶
end¶
end¶
¶
puts message.content¶
break if message.stop_reason != "tool_use"¶
end¶
```¶
¶
</Tab>¶
</Tabs>¶
¶
<Tip>¶
Adding `cache_control` to tool results is particularly useful when tools return large amounts of data (like document search results) that you want to cache for subsequent API calls. See [Prompt caching](/docs/en/build-with-claude/prompt-caching) for more details on caching strategies.¶
</Tip>¶
¶
### Streaming¶
¶
Enable streaming to receive events as they arrive. Each iteration yields a stream object that you can iterate for events.¶
¶
<Tabs>¶
<Tab title="Python">¶
¶
Set `stream=True` and use `get_final_message()` to get the accumulated message.¶
¶
```python¶
runner = client.beta.messages.tool_runner(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
tools=[calculate_sum],¶
messages=[{"role": "user", "content": "What is 15 + 27?"}],¶
stream=True,¶
)¶
¶
# When streaming, the runner returns BetaMessageStream¶
for message_stream in runner:¶
for event in message_stream:¶
print("event:", event)¶
print("message:", message_stream.get_final_message())¶
¶
print(runner.until_done())¶
```¶
¶
</Tab>¶
<Tab title="TypeScript">¶
¶
Set `stream: true` and use `finalMessage()` to get the accumulated message.¶
¶
```typescript¶
const runner = anthropic.beta.messages.toolRunner({¶
model: "claude-opus-4-6",¶
max_tokens: 1000,¶
messages: [{ role: "user", content: "What is the weather in San Francisco?" }],¶
tools: [getWeatherTool],¶
stream: true¶
});¶
¶
// When streaming, the runner returns BetaMessageStream¶
for await (const messageStream of runner) {¶
for await (const event of messageStream) {¶
console.log("event:", event);¶
}¶
console.log("message:", await messageStream.finalMessage());¶
}¶
¶
console.log(await runner);¶
```¶
¶
</Tab>¶
<Tab title="Ruby">¶
¶
Use `each_streaming` to iterate over streaming events.¶
¶
```ruby¶
runner = client.beta.messages.tool_runner(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: [CalculateSum.new],¶
messages: [{role: "user", content: "What is 15 + 27?"}]¶
)¶
¶
runner.each_streaming do |event|¶
case event¶
when Anthropic::Streaming::TextEvent¶
print event.text¶
when Anthropic::Streaming::ToolUseEvent¶
puts "\nTool called: #{event.tool_name}"¶
end¶
end¶
```¶
¶
</Tab>¶
</Tabs>¶
¶
<Note>¶
The SDK tool runner is in beta. The rest of this document covers manual tool implementation.¶
</Note>¶
¶
## Controlling Claude's output¶
¶
### Forcing tool use¶
¶
In some cases, you may want Claude to use a specific tool to answer the user's question, even if Claude thinks it can provide an answer without using a tool. You can do this by specifying the tool in the `tool_choice` field like so:¶
¶
```text¶
tool_choice = {"type": "tool", "name": "get_weather"}¶
```¶
¶
When working with the tool_choice parameter, we have four possible options:¶
¶
- `auto` allows Claude to decide whether to call any provided tools or not. This is the default value when `tools` are provided.¶
- `any` tells Claude that it must use one of the provided tools, but doesn't force a particular tool.¶
- `tool` allows us to force Claude to always use a particular tool.¶
- `none` prevents Claude from using any tools. This is the default value when no `tools` are provided.¶
¶
<Note>¶
When using [prompt caching](/docs/en/build-with-claude/prompt-caching#what-invalidates-the-cache), changes to the `tool_choice` parameter will invalidate cached message blocks. Tool definitions and system prompts remain cached, but message content must be reprocessed.¶
</Note>¶
¶
This diagram illustrates how each option works:¶
¶
<Frame>¶
¶
</Frame>¶
¶
Note that when you have `tool_choice` as `any` or `tool`, we will prefill the assistant message to force a tool to be used. This means that the models will not emit a natural language response or explanation before `tool_use` content blocks, even if explicitly asked to do so.¶
¶
<Note>¶
When using [extended thinking](/docs/en/build-with-claude/extended-thinking) with tool use, `tool_choice: {"type": "any"}` and `tool_choice: {"type": "tool", "name": "..."}` are not supported and will result in an error. Only `tool_choice: {"type": "auto"}` (the default) and `tool_choice: {"type": "none"}` are compatible with extended thinking.¶
</Note>¶
¶
Our testing has shown that this should not reduce performance. If you would like the model to provide natural language context or explanations while still requesting that the model use a specific tool, you can use `{"type": "auto"}` for `tool_choice` (the default) and add explicit instructions in a `user` message. For example: `What's the weather like in London? Use the get_weather tool in your response.`¶
¶
<Tip>¶
**Guaranteed tool calls with strict tools**¶
¶
Combine `tool_choice: {"type": "any"}` with [strict tool use](/docs/en/build-with-claude/structured-outputs) to guarantee both that one of your tools will be called AND that the tool inputs strictly follow your schema. Set `strict: true` on your tool definitions to enable schema validation.¶
</Tip>¶
¶
### JSON output¶
¶
Tools do not necessarily need to be client functions — you can use tools anytime you want the model to return JSON output that follows a provided schema. For example, you might use a `record_summary` tool with a particular schema. See [Tool use with Claude](/docs/en/agents-and-tools/tool-use/overview) for a full working example.¶
¶
### Model responses with tools¶
¶
When using tools, Claude will often comment on what it's doing or respond naturally to the user before invoking tools.¶
¶
For example, given the prompt "What's the weather like in San Francisco right now, and what time is it there?", Claude might respond with:¶
¶
```json JSON¶
{¶
"role": "assistant",¶
"content": [¶
{¶
"type": "text",¶
"text": "I'll help you check the current weather and time in San Francisco."¶
},¶
{¶
"type": "tool_use",¶
"id": "toolu_01A09q90qw90lq917835lq9",¶
"name": "get_weather",¶
"input": {"location": "San Francisco, CA"}¶
}¶
]¶
}¶
```¶
¶
This natural response style helps users understand what Claude is doing and creates a more conversational interaction. You can guide the style and content of these responses through your system prompts and by providing `<examples>` in your prompts.¶
¶
It's important to note that Claude may use various phrasings and approaches when explaining its actions. Your code should treat these responses like any other assistant-generated text, and not rely on specific formatting conventions.¶
¶
### Parallel tool use¶
¶
By default, Claude may use multiple tools to answer a user query. You can disable this behavior by:¶
¶
- Setting `disable_parallel_tool_use=true` when tool_choice type is `auto`, which ensures that Claude uses **at most one** tool¶
- Setting `disable_parallel_tool_use=true` when tool_choice type is `any` or `tool`, which ensures that Claude uses **exactly one** tool¶
¶
<section title="Complete parallel tool use example">¶
¶
<Note>¶
**Simpler with Tool runner**: The example below shows manual parallel tool handling. For most use cases, [tool runner](#tool-runner-beta) automatically handle parallel tool execution with much less code.¶
</Note>¶
¶
Here's a complete example showing how to properly format parallel tool calls in the message history:¶
¶
<CodeGroup>¶
```python Python¶
import anthropic¶
¶
client = anthropic.Anthropic()¶
¶
# Define tools¶
tools = [¶
{¶
"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",¶
}¶
},¶
"required": ["location"],¶
},¶
},¶
{¶
"name": "get_time",¶
"description": "Get the current time in a given timezone",¶
"input_schema": {¶
"type": "object",¶
"properties": {¶
"timezone": {¶
"type": "string",¶
"description": "The timezone, e.g. America/New_York",¶
}¶
},¶
"required": ["timezone"],¶
},¶
},¶
]¶
¶
# Initial request¶
response = client.messages.create(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
tools=tools,¶
messages=[¶
{¶
"role": "user",¶
"content": "What's the weather in SF and NYC, and what time is it there?",¶
}¶
],¶
)¶
¶
# Claude's response with parallel tool calls¶
print("Claude wants to use tools:", response.stop_reason == "tool_use")¶
print(¶
"Number of tool calls:", len([c for c in response.content if c.type == "tool_use"])¶
)¶
¶
# Build the conversation with tool results¶
messages = [¶
{¶
"role": "user",¶
"content": "What's the weather in SF and NYC, and what time is it there?",¶
},¶
{¶
"role": "assistant",¶
"content": response.content, # Contains multiple tool_use blocks¶
},¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01", # Must match the ID from tool_use¶
"content": "San Francisco: 68°F, partly cloudy",¶
},¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_02",¶
"content": "New York: 45°F, clear skies",¶
},¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_03",¶
"content": "San Francisco time: 2:30 PM PST",¶
},¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_04",¶
"content": "New York time: 5:30 PM EST",¶
},¶
],¶
},¶
]¶
¶
# Get final response¶
final_response = client.messages.create(¶
model="claude-opus-4-6", max_tokens=1024, tools=tools, messages=messages¶
)¶
¶
print(final_response.content[0].text)¶
```¶
¶
```typescript TypeScript¶
import { Anthropic } from "@anthropic-ai/sdk";¶
¶
const anthropic = new Anthropic();¶
¶
// Define tools¶
const tools = [¶
{¶
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"¶
}¶
},¶
required: ["location"]¶
}¶
},¶
{¶
name: "get_time",¶
description: "Get the current time in a given timezone",¶
input_schema: {¶
type: "object",¶
properties: {¶
timezone: {¶
type: "string",¶
description: "The timezone, e.g. America/New_York"¶
}¶
},¶
required: ["timezone"]¶
}¶
}¶
];¶
¶
// Initial request¶
const response = await anthropic.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: tools,¶
messages: [¶
{¶
role: "user",¶
content: "What's the weather in SF and NYC, and what time is it there?"¶
}¶
]¶
});¶
¶
// Build conversation with tool results¶
const messages = [¶
{¶
role: "user",¶
content: "What's the weather in SF and NYC, and what time is it there?"¶
},¶
{¶
role: "assistant",¶
content: response.content // Contains multiple tool_use blocks¶
},¶
{¶
role: "user",¶
content: [¶
{¶
type: "tool_result",¶
tool_use_id: "toolu_01", // Must match the ID from tool_use¶
content: "San Francisco: 68°F, partly cloudy"¶
},¶
{¶
type: "tool_result",¶
tool_use_id: "toolu_02",¶
content: "New York: 45°F, clear skies"¶
},¶
{¶
type: "tool_result",¶
tool_use_id: "toolu_03",¶
content: "San Francisco time: 2:30 PM PST"¶
},¶
{¶
type: "tool_result",¶
tool_use_id: "toolu_04",¶
content: "New York time: 5:30 PM EST"¶
}¶
]¶
}¶
];¶
¶
// Get final response¶
const finalResponse = await anthropic.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
tools: tools,¶
messages: messages¶
});¶
¶
console.log(finalResponse.content[0].text);¶
```¶
</CodeGroup>¶
¶
The assistant message with parallel tool calls would look like this:¶
¶
```json¶
{¶
"role": "assistant",¶
"content": [¶
{¶
"type": "text",¶
"text": "I'll check the weather and time for both San Francisco and New York City."¶
},¶
{¶
"type": "tool_use",¶
"id": "toolu_01",¶
"name": "get_weather",¶
"input": {"location": "San Francisco, CA"}¶
},¶
{¶
"type": "tool_use",¶
"id": "toolu_02",¶
"name": "get_weather",¶
"input": {"location": "New York, NY"}¶
},¶
{¶
"type": "tool_use",¶
"id": "toolu_03",¶
"name": "get_time",¶
"input": {"timezone": "America/Los_Angeles"}¶
},¶
{¶
"type": "tool_use",¶
"id": "toolu_04",¶
"name": "get_time",¶
"input": {"timezone": "America/New_York"}¶
}¶
]¶
}¶
```¶
¶
</section>¶
<section title="Complete test script for parallel tools">¶
¶
Here's a complete, runnable script to test and verify parallel tool calls are working correctly:¶
¶
<CodeGroup>¶
```python Python¶
#!/usr/bin/env python3¶
"""Test script to verify parallel tool calls with the Claude API"""¶
¶
import os¶
from anthropic import Anthropic¶
¶
# Initialize client¶
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))¶
¶
# Define tools¶
tools = [¶
{¶
"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",¶
}¶
},¶
"required": ["location"],¶
},¶
},¶
{¶
"name": "get_time",¶
"description": "Get the current time in a given timezone",¶
"input_schema": {¶
"type": "object",¶
"properties": {¶
"timezone": {¶
"type": "string",¶
"description": "The timezone, e.g. America/New_York",¶
}¶
},¶
"required": ["timezone"],¶
},¶
},¶
]¶
¶
# Test conversation with parallel tool calls¶
messages = [¶
{¶
"role": "user",¶
"content": "What's the weather in SF and NYC, and what time is it there?",¶
}¶
]¶
¶
# Make initial request¶
print("Requesting parallel tool calls...")¶
response = client.messages.create(¶
model="claude-opus-4-6", max_tokens=1024, messages=messages, tools=tools¶
)¶
¶
# Check for parallel tool calls¶
tool_uses = [block for block in response.content if block.type == "tool_use"]¶
print(f"\n✓ Claude made {len(tool_uses)} tool calls")¶
¶
if len(tool_uses) > 1:¶
print("✓ Parallel tool calls detected!")¶
for tool in tool_uses:¶
print(f" - {tool.name}: {tool.input}")¶
else:¶
print("✗ No parallel tool calls detected")¶
¶
# Simulate tool execution and format results correctly¶
tool_results = []¶
for tool_use in tool_uses:¶
if tool_use.name == "get_weather":¶
if "San Francisco" in str(tool_use.input):¶
result = "San Francisco: 68°F, partly cloudy"¶
else:¶
result = "New York: 45°F, clear skies"¶
else: # get_time¶
if "Los_Angeles" in str(tool_use.input):¶
result = "2:30 PM PST"¶
else:¶
result = "5:30 PM EST"¶
¶
tool_results.append(¶
{"type": "tool_result", "tool_use_id": tool_use.id, "content": result}¶
)¶
¶
# Continue conversation with tool results¶
messages.extend(¶
[¶
{"role": "assistant", "content": response.content},¶
{"role": "user", "content": tool_results}, # All results in one message!¶
]¶
)¶
¶
# Get final response¶
print("\nGetting final response...")¶
final_response = client.messages.create(¶
model="claude-opus-4-6", max_tokens=1024, messages=messages, tools=tools¶
)¶
¶
print(f"\nClaude's response:\n{final_response.content[0].text}")¶
¶
# Verify formatting¶
print("\n--- Verification ---")¶
print(f"✓ Tool results sent in single user message: {len(tool_results)} results")¶
print("✓ No text before tool results in content array")¶
print("✓ Conversation formatted correctly for future parallel tool use")¶
```¶
¶
```typescript TypeScript¶
#!/usr/bin/env node¶
// Test script to verify parallel tool calls with the Claude API¶
¶
import { Anthropic } from "@anthropic-ai/sdk";¶
¶
const anthropic = new Anthropic({¶
apiKey: process.env.ANTHROPIC_API_KEY¶
});¶
¶
// Define tools¶
const tools = [¶
{¶
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"¶
}¶
},¶
required: ["location"]¶
}¶
},¶
{¶
name: "get_time",¶
description: "Get the current time in a given timezone",¶
input_schema: {¶
type: "object",¶
properties: {¶
timezone: {¶
type: "string",¶
description: "The timezone, e.g. America/New_York"¶
}¶
},¶
required: ["timezone"]¶
}¶
}¶
];¶
¶
async function testParallelTools() {¶
// Make initial request¶
console.log("Requesting parallel tool calls...");¶
const response = await anthropic.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [{¶
role: "user",¶
content: "What's the weather in SF and NYC, and what time is it there?"¶
}],¶
tools: tools¶
});¶
¶
// Check for parallel tool calls¶
const toolUses = response.content.filter(block => block.type === "tool_use");¶
console.log(`\n✓ Claude made ${toolUses.length} tool calls`);¶
¶
if (toolUses.length > 1) {¶
console.log("✓ Parallel tool calls detected!");¶
toolUses.forEach(tool => {¶
console.log(` - ${tool.name}: ${JSON.stringify(tool.input)}`);¶
});¶
} else {¶
console.log("✗ No parallel tool calls detected");¶
}¶
¶
// Simulate tool execution and format results correctly¶
const toolResults = toolUses.map(toolUse => {¶
let result;¶
if (toolUse.name === "get_weather") {¶
result = toolUse.input.location.includes("San Francisco")¶
? "San Francisco: 68°F, partly cloudy"¶
: "New York: 45°F, clear skies";¶
} else {¶
result = toolUse.input.timezone.includes("Los_Angeles")¶
? "2:30 PM PST"¶
: "5:30 PM EST";¶
}¶
¶
return {¶
type: "tool_result",¶
tool_use_id: toolUse.id,¶
content: result¶
};¶
});¶
¶
// Get final response with correct formatting¶
console.log("\nGetting final response...");¶
const finalResponse = await anthropic.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [¶
{ role: "user", content: "What's the weather in SF and NYC, and what time is it there?" },¶
{ role: "assistant", content: response.content },¶
{ role: "user", content: toolResults } // All results in one message!¶
],¶
tools: tools¶
});¶
¶
console.log(`\nClaude's response:\n${finalResponse.content[0].text}`);¶
¶
// Verify formatting¶
console.log("\n--- Verification ---");¶
console.log(`✓ Tool results sent in single user message: ${toolResults.length} results`);¶
console.log("✓ No text before tool results in content array");¶
console.log("✓ Conversation formatted correctly for future parallel tool use");¶
}¶
¶
testParallelTools().catch(console.error);¶
```¶
</CodeGroup>¶
¶
This script demonstrates:¶
- How to properly format parallel tool calls and results¶
- How to verify that parallel calls are being made¶
- The correct message structure that encourages future parallel tool use¶
- Common mistakes to avoid (like text before tool results)¶
¶
Run this script to test your implementation and ensure Claude is making parallel tool calls effectively.¶
¶
</section>¶
¶
#### Maximizing parallel tool use¶
¶
While Claude 4 models have excellent parallel tool use capabilities by default, you can increase the likelihood of parallel tool execution across all models with targeted prompting:¶
¶
<section title="System prompts for parallel tool use">¶
¶
For Claude 4 models (Opus 4, and Sonnet 4), add this to your system prompt:¶
```text¶
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.¶
```¶
¶
For even stronger parallel tool use (recommended if the default isn't sufficient), use:¶
```text¶
<use_parallel_tool_calls>¶
For maximum efficiency, whenever you perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially. Prioritize calling tools in parallel whenever possible. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. When running multiple read-only commands like `ls` or `list_dir`, always run all of the commands in parallel. Err on the side of maximizing parallel tool calls rather than running too many tools sequentially.¶
</use_parallel_tool_calls>¶
```¶
¶
</section>¶
<section title="User message prompting">¶
¶
You can also encourage parallel tool use within specific user messages:¶
¶
```python¶
# Instead of:¶
"What's the weather in Paris? Also check London."¶
¶
# Use:¶
"Check the weather in Paris and London simultaneously."¶
¶
# Or be explicit:¶
"Please use parallel tool calls to get the weather for Paris, London, and Tokyo at the same time."¶
```¶
¶
</section>¶
¶
<Warning>¶
**Parallel tool use with Claude Sonnet 3.7**¶
¶
Claude Sonnet 3.7 may be less likely to make make parallel tool calls in a response, even when you have not set `disable_parallel_tool_use`. We recommend [upgrading to Claude 4 models](/docs/en/about-claude/models/migration-guide), which have built-in token-efficient tool use and improved parallel tool calling.¶
¶
If you're still using Claude Sonnet 3.7, you can enable the `token-efficient-tools-2025-02-19` [beta header](/docs/en/api/beta-headers), which helps encourage Claude to use parallel tools. You can also introduce a "batch tool" that can act as a meta-tool to wrap invocations to other tools simultaneously.¶
¶
See [this example](https://platform.claude.com/cookbook/tool-use-parallel-tools) in our cookbook for how to use this workaround.¶
¶
</Warning>¶
¶
## Handling tool use and tool result content blocks¶
¶
<Note>¶
**Simpler with Tool runner**: The manual tool handling described in this section is automatically managed by [tool runner](#tool-runner-beta). Use this section when you need custom control over tool execution.¶
</Note>¶
¶
Claude's response differs based on whether it uses a client or server tool.¶
¶
### Handling results from client tools¶
¶
The response will have a `stop_reason` of `tool_use` and one or more `tool_use` content blocks that include:¶
¶
- `id`: A unique identifier for this particular tool use block. This will be used to match up the tool results later.¶
- `name`: The name of the tool being used.¶
- `input`: An object containing the input being passed to the tool, conforming to the tool's `input_schema`.¶
¶
<section title="Example API response with a `tool_use` content block">¶
¶
```json JSON¶
{¶
"id": "msg_01Aq9w938a90dw8q",¶
"model": "claude-opus-4-6",¶
"stop_reason": "tool_use",¶
"role": "assistant",¶
"content": [¶
{¶
"type": "text",¶
"text": "I'll check the current weather in San Francisco for you."¶
},¶
{¶
"type": "tool_use",¶
"id": "toolu_01A09q90qw90lq917835lq9",¶
"name": "get_weather",¶
"input": {"location": "San Francisco, CA", "unit": "celsius"}¶
}¶
]¶
}¶
```¶
¶
</section>¶
¶
When you receive a tool use response for a client tool, you should:¶
¶
1. Extract the `name`, `id`, and `input` from the `tool_use` block.¶
2. Run the actual tool in your codebase corresponding to that tool name, passing in the tool `input`.¶
3. Continue the conversation by sending a new message with the `role` of `user`, and a `content` block containing the `tool_result` type and the following information:¶
- `tool_use_id`: The `id` of the tool use request this is a result for.¶
- `content`: The result of the tool, as a string (e.g.for example, `"content": "15 degrees"`), a list of nested content blocks (e.g.for example, `"content": [{"type": "text", "text": "15 degrees"}]`), or a list of document blocks (e.g.for example, `"content": ["type": "document", "source": {"type": "text", "media_type": "text/plain", "data": "15 degrees"}]`). These content blocks can use the `text`, `image`, or `document` types.¶
- `is_error` (optional): Set to `true` if the tool execution resulted in an error.¶
¶
<Note>¶
**Important formatting requirements**:¶
- Tool result blocks must immediately follow their corresponding tool use blocks in the message history. You cannot include any messages between the assistant's tool use message and the user's tool result message.¶
- In the user message containing tool results, the tool_result blocks must come FIRST in the content array. Any text must come AFTER all tool results.¶
¶
For example, this will cause a 400 error:¶
```json¶
{"role": "user", "content": [¶
{"type": "text", "text": "Here are the results:"}, // ❌ Text before tool_result¶
{"type": "tool_result", "tool_use_id": "toolu_01", ...}¶
]}¶
```¶
¶
This is correct:¶
```json¶
{"role": "user", "content": [¶
{"type": "tool_result", "tool_use_id": "toolu_01", ...},¶
{"type": "text", "text": "What should I do next?"} // ✅ Text after tool_result¶
]}¶
```¶
¶
If you receive an error like "tool_use ids were found without tool_result blocks immediately after", check that your tool results are formatted correctly.¶
</Note>¶
¶
<section title="Example of successful tool result">¶
¶
```json JSON¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",¶
"content": "15 degrees"¶
}¶
]¶
}¶
```¶
¶
</section>¶
¶
<section title="Example of tool result with images">¶
¶
```json JSON¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",¶
"content": [¶
{"type": "text", "text": "15 degrees"},¶
{¶
"type": "image",¶
"source": {¶
"type": "base64",¶
"media_type": "image/jpeg",¶
"data": "/9j/4AAQSkZJRg...",¶
}¶
}¶
]¶
}¶
]¶
}¶
```¶
¶
</section>¶
<section title="Example of empty tool result">¶
¶
```json JSON¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",¶
}¶
]¶
}¶
```¶
¶
</section>¶
¶
<section title="Example of tool result with documents">¶
¶
```json JSON¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",¶
"content": [¶
{"type": "text", "text": "The weather is"},¶
{¶
"type": "document",¶
"source": {¶
"type": "text",¶
"media_type": "text/plain",¶
"data": "15 degrees"¶
}¶
}¶
]¶
}¶
]¶
}¶
```¶
¶
</section>¶
¶
After receiving the tool result, Claude will use that information to continue generating a response to the original user prompt.¶
¶
### Handling results from server tools¶
¶
Claude executes the tool internally and incorporates the results directly into its response without requiring additional user interaction.¶
¶
<Tip>¶
**Differences from other APIs**¶
¶
Unlike APIs that separate tool use or use special roles like `tool` or `function`, the Claude API integrates tools directly into the `user` and `assistant` message structure.¶
¶
Messages contain arrays of `text`, `image`, `tool_use`, and `tool_result` blocks. `user` messages include client content and `tool_result`, while `assistant` messages contain AI-generated content and `tool_use`.¶
¶
</Tip>¶
¶
### Handling the `max_tokens` stop reason¶
¶
If Claude's [response is cut off due to hitting the `max_tokens` limit](/docs/en/build-with-claude/handling-stop-reasons#max-tokens), and the truncated response contains an incomplete tool use block, you'll need to retry the request with a higher `max_tokens` value to get the full tool use.¶
¶
<CodeGroup>¶
```python Python¶
# Check if response was truncated during tool use¶
if response.stop_reason == "max_tokens":¶
# Check if the last content block is an incomplete tool_use¶
last_block = response.content[-1]¶
if last_block.type == "tool_use":¶
# Send the request with higher max_tokens¶
response = client.messages.create(¶
model="claude-opus-4-6",¶
max_tokens=4096, # Increased limit¶
messages=messages,¶
tools=tools,¶
)¶
```¶
¶
```typescript TypeScript¶
// Check if response was truncated during tool use¶
if (response.stop_reason === "max_tokens") {¶
// Check if the last content block is an incomplete tool_use¶
const lastBlock = response.content[response.content.length - 1];¶
if (lastBlock.type === "tool_use") {¶
// Send the request with higher max_tokens¶
response = await anthropic.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 4096, // Increased limit¶
messages: messages,¶
tools: tools¶
});¶
}¶
}¶
```¶
</CodeGroup>¶
¶
#### Handling the `pause_turn` stop reason¶
¶
When using server tools like web search, the API may return a `pause_turn` stop reason, indicating that the API has paused a long-running turn.¶
¶
Here's how to handle the `pause_turn` stop reason:¶
¶
<CodeGroup>¶
```python Python¶
import anthropic¶
¶
client = anthropic.Anthropic()¶
¶
# Initial request with web search¶
response = client.messages.create(¶
model="claude-3-7-sonnet-latest",¶
max_tokens=1024,¶
messages=[¶
{¶
"role": "user",¶
"content": "Search for comprehensive information about quantum computing breakthroughs in 2025",¶
}¶
],¶
tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}],¶
)¶
¶
# Check if the response has pause_turn stop reason¶
if response.stop_reason == "pause_turn":¶
# Continue the conversation with the paused content¶
messages = [¶
{¶
"role": "user",¶
"content": "Search for comprehensive information about quantum computing breakthroughs in 2025",¶
},¶
{"role": "assistant", "content": response.content},¶
]¶
¶
# Send the continuation request¶
continuation = client.messages.create(¶
model="claude-3-7-sonnet-latest",¶
max_tokens=1024,¶
messages=messages,¶
tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}],¶
)¶
¶
print(continuation)¶
else:¶
print(response)¶
```¶
¶
```typescript TypeScript¶
import { Anthropic } from "@anthropic-ai/sdk";¶
¶
const anthropic = new Anthropic();¶
¶
// Initial request with web search¶
const response = await anthropic.messages.create({¶
model: "claude-3-7-sonnet-latest",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content: "Search for comprehensive information about quantum computing breakthroughs in 2025"¶
}¶
],¶
tools: [{¶
type: "web_search_20250305",¶
name: "web_search",¶
max_uses: 10¶
}]¶
});¶
¶
// Check if the response has pause_turn stop reason¶
if (response.stop_reason === "pause_turn") {¶
// Continue the conversation with the paused content¶
const messages = [¶
{ role: "user", content: "Search for comprehensive information about quantum computing breakthroughs in 2025" },¶
{ role: "assistant", content: response.content }¶
];¶
¶
// Send the continuation request¶
const continuation = await anthropic.messages.create({¶
model: "claude-3-7-sonnet-latest",¶
max_tokens: 1024,¶
messages: messages,¶
tools: [{¶
type: "web_search_20250305",¶
name: "web_search",¶
max_uses: 10¶
}]¶
});¶
¶
console.log(continuation);¶
} else {¶
console.log(response);¶
}¶
```¶
</CodeGroup>¶
¶
When handling `pause_turn`:¶
- **Continue the conversation**: Pass the paused response back as-is in a subsequent request to let Claude continue its turn¶
- **Modify if needed**: You can optionally modify the content before continuing if you want to interrupt or redirect the conversation¶
- **Preserve tool state**: Include the same tools in the continuation request to maintain functionality¶
¶
## Troubleshooting errors¶
¶
<Note>¶
**Built-in Error Handling**: [Tool runner](#tool-runner-beta) provide automatic error handling for most common scenarios. This section covers manual error handling for advanced use cases.¶
</Note>¶
¶
There are a few different types of errors that can occur when using tools with Claude:¶
¶
<section title="Tool execution error">¶
¶
If the tool itself throws an error during execution (e.g.for example, a network error when fetching weather data), you can return the error message in the `content` along with `"is_error": true`:¶
¶
```json JSON¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",¶
"content": "ConnectionError: the weather service API is not available (HTTP 500)",¶
"is_error": true¶
}¶
]¶
}¶
```¶
¶
Claude will then incorporate this error into its response to the user, e.g.. For example: "I'm sorry, I was unable to retrieve the current weather because the weather service API is not available. Please try again later."¶
¶
</section>¶
<section title="Invalid tool name">¶
¶
If Claude's attempted use of a tool is invalid (e.g.for example, missing required parameters), it usually means that the there wasn't enough information for Claude to use the tool correctly. Your best bet during development is to try the request again with more-detailed `description` values in your tool definitions.¶
¶
However, you can also continue the conversation forward with a `tool_result` that indicates the error, and Claude will try to use the tool again with the missing information filled in:¶
¶
```json JSON¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",¶
"content": "Error: Missing required 'location' parameter",¶
"is_error": true¶
}¶
]¶
}¶
```¶
¶
If a tool request is invalid or missing parameters, Claude will retry 2-3 times with corrections before apologizing to the user.¶
¶
<Tip>¶
To eliminate invalid tool calls entirely, use [strict tool use](/docs/en/build-with-claude/structured-outputs) with `strict: true` on your tool definitions. This guarantees that tool inputs will always match your schema exactly, preventing missing parameters and type mismatches.¶
</Tip>¶
¶
</section>¶
<section title="<search_quality_reflection> tags">¶
¶
To prevent Claude from reflecting on search quality with \<search_quality_reflection> tags, add "Do not reflect on the quality of the returned search results in your response" to your prompt.¶
¶
</section>¶
<section title="Server tool errors">¶
¶
When server tools encounter errors (e.g.for example, network issues with Web Search), Claude will transparently handle these errors and attempt to provide an alternative response or explanation to the user. Unlike client tools, you do not need to handle `is_error` results for server tools.¶
¶
For web search specifically, possible error codes include:¶
- `too_many_requests`: Rate limit exceeded¶
- `invalid_input`: Invalid search query parameter¶
- `max_uses_exceeded`: Maximum web search tool uses exceeded¶
- `query_too_long`: Query exceeds maximum length¶
- `unavailable`: An internal error occurred¶
¶
</section>¶
<section title="Parallel tool calls not working">¶
¶
If Claude isn't making parallel tool calls when expected, check these common issues:¶
¶
**1. Incorrect tool result formatting**¶
¶
The most common issue is formatting tool results incorrectly in the conversation history. This "teaches" Claude to avoid parallel calls.¶
¶
Specifically for parallel tool use:¶
- ❌ **Wrong**: Sending separate user messages for each tool result¶
- ✅ **Correct**: All tool results must be in a single user message¶
¶
```json¶
// ❌ This reduces parallel tool use¶
[¶
{"role": "assistant", "content": [tool_use_1, tool_use_2]},¶
{"role": "user", "content": [tool_result_1]},¶
{"role": "user", "content": [tool_result_2]} // Separate message¶
]¶
¶
// ✅ This maintains parallel tool use¶
[¶
{"role": "assistant", "content": [tool_use_1, tool_use_2]},¶
{"role": "user", "content": [tool_result_1, tool_result_2]} // Single message¶
]¶
```¶
¶
See the [general formatting requirements above](#handling-tool-use-and-tool-result-content-blocks) for other formatting rules.¶
¶
**2. Weak prompting**¶
¶
Default prompting may not be sufficient. Use stronger language:¶
¶
```text¶
<use_parallel_tool_calls>¶
For maximum efficiency, whenever you perform multiple independent operations,¶
invoke all relevant tools simultaneously rather than sequentially.¶
Prioritize calling tools in parallel whenever possible.¶
</use_parallel_tool_calls>¶
```¶
¶
**3. Measuring parallel tool usage**¶
¶
To verify parallel tool calls are working:¶
¶
```python¶
# Calculate average tools per tool-calling message¶
tool_call_messages = [¶
msg for msg in messages if any(block.type == "tool_use" for block in msg.content)¶
]¶
total_tool_calls = sum(¶
len([b for b in msg.content if b.type == "tool_use"]) for msg in tool_call_messages¶
)¶
avg_tools_per_message = total_tool_calls / len(tool_call_messages)¶
print(f"Average tools per message: {avg_tools_per_message}")¶
# Should be > 1.0 if parallel calls are working¶
```¶
¶
**4. Model-specific behavior**¶
¶
- Claude Opus 4.6, Sonnet 4.5, Opus 4.5, Opus 4.1, and Sonnet 4: Excel at parallel tool use with minimal prompting¶
- Claude Sonnet 3.7: May need stronger prompting or the `token-efficient-tools-2025-02-19` [beta header](/docs/en/api/beta-headers). Consider [upgrading to Claude 4](/docs/en/about-claude/models/migration-guide).¶
- Claude Haiku: Less likely to use parallel tools without explicit prompting¶
¶
</section>