Tool Use
Tool use is Anthropic's name for function calling. You declare a list of tools the model is allowed to invoke, send a user message, and Claude either answers directly with text or returns a structured tool_use block describing the call it wants you to make. You execute the tool, send the result back, and the conversation continues.
The shape
A tool definition has three fields: a name, a natural-language description, and a JSON Schema for its input.
"tools": [
{
"name": "get_weather",
"description": "Get the current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"units": {"type": "string", "enum": ["c", "f"]}
},
"required": ["city"]
}
}
]
The description matters. Claude does not see your function body; it reads the description and the schema, then decides whether and how to call. Treat the description as part of your prompt.
When Claude decides to use the tool, the response contains a tool_use content block instead of a text block, and stop_reason is "tool_use":
{
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_IbId2k5Cs4dpj5vgdvJJDA",
"name": "get_weather",
"input": {"city": "Tokyo", "units": "c"}
}
]
}
You execute get_weather("Tokyo", "c"), take the string result, and append it to the conversation as a tool_result block. That gives Claude the data it asked for so it can produce the final answer.
The multi-turn round trip
This is the part that trips people up most often. A single user question can take multiple round trips between your code and the API. The conversation history grows on each one.
// Turn 1: user asks
[
{"role": "user", "content": "What's the weather in Tokyo?"}
]
// Turn 2: Claude wants to use a tool
[
{"role": "user", "content": "What's the weather in Tokyo?"},
{"role": "assistant", "content": [
{"type": "tool_use", "id": "toolu_X", "name": "get_weather",
"input": {"city": "Tokyo", "units": "c"}}
]}
]
// Turn 3: you provide the result
[
{"role": "user", "content": "What's the weather in Tokyo?"},
{"role": "assistant", "content": [
{"type": "tool_use", "id": "toolu_X", "name": "get_weather",
"input": {"city": "Tokyo", "units": "c"}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_X",
"content": "Sunny, 22 C, light breeze."}
]}
]
// Turn 4: Claude produces the final answer
"Tokyo is currently sunny and around 22 C with a light breeze."
Two rules to keep this clean: the tool_use_id in your tool_result must exactly match the id Claude returned, and the tool_result block belongs in a user-role message even though it is your code's output. Claude treats your code as a participant in the user side of the conversation.
The model can also issue multiple tool_use blocks in a single response (parallel tool calls). Your code should execute them and return all the corresponding tool_result blocks in one user message.
The three tool_choice modes
The tool_choice field controls how aggressively Claude reaches for tools. There are four values, three of which are commonly useful:
| Value | Behavior | Use when |
|---|---|---|
{"type": "auto"} | Default. Claude decides per-turn whether to use a tool or answer with text. | Most assistants. The model is good at this. |
{"type": "any"} | Claude must use one of the provided tools. It cannot answer with free text. | Structured output. The "tool" is your output schema; the model is forced to fill it. |
{"type": "tool", "name": "x"} | Claude must use a specific tool, named upfront. | You already know the call you want; you are using the model to fill in the arguments. |
{"type": "none"} | Claude cannot use tools. | Disabling tools mid-conversation while keeping the same conversation history. |
The any mode is the trick that converts Anthropic's tool-use mechanism into reliable structured output. Define a single tool whose schema is the JSON shape you want, set tool_choice to any, and the model is forced to emit a valid tool_use block matching the schema.
Streaming and tool_use
Streaming a tool call works, but the JSON arrives in fragments and your client has to reassemble it. The relevant SSE events:
event: content_block_start
data: {"type": "content_block_start", "index": 0,
"content_block": {"type": "tool_use", "id": "toolu_X", "name": "get_weather", "input": {}}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0,
"delta": {"type": "input_json_delta", "partial_json": "{\"city\":"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0,
"delta": {"type": "input_json_delta", "partial_json": " \"Tokyo\"}"}}
event: content_block_stop
data: {"type": "content_block_stop", "index": 0}
Three things to know:
- The
inputobject insidecontent_block_startis empty. Do not parse it. Wait for the deltas. - Each
input_json_deltacontains a string fragment of JSON. Concatenate the fragments and parse the result oncontent_block_stop. - If the model emits text and a tool call in the same response, you receive interleaved
content_blocksequences with differentindexvalues. Do not assume one block ends before another starts.
Because of this complexity, most clients buffer the entire tool_use block until content_block_stop before acting on it. Streaming tool calls is mainly useful for surfacing "the model is preparing to call X" UX hints, not for parsing the input early.
Common pitfalls
Mismatched tool_use_id. A tool_result with the wrong ID is a 400 error, not a silent ignore. Echo the ID exactly.
Forgetting the assistant turn. The conversation must always include the assistant's tool_use message before the user's tool_result message. Skipping the assistant turn is a 400.
Returning JSON inside tool_result.content. The content is text. If your tool returns structured data, serialize it to JSON-as-string. The model will parse it back.
Schema drift. If input_schema claims a field is required and Claude omits it, the issue is almost always an under-specified description. Tighten the description before tightening the schema.
See also
POST /v1/messages— request reference, includingtoolsandtool_choice- Prompt Caching — caching long tool catalogs
- Transparent Forwarding — why tool blocks pass through unchanged
- Claude tool use through a gateway — production patterns