Vault / wiki/201/tool-use.md
updated 2026-05-28Tool Use (Function Calling)
The loop
Tool use is a multi-turn loop driven by stop_reason:
flowchart LR
Start([User request]) --> Req[messages.create<br/>with tools schema]
Req --> SR{stop_reason?}
SR -- end_turn --> Done([Return to user])
SR -- tool_use --> Run[Run each tool_use block]
Run --> Append[Append assistant turn + user turn with tool_result blocks]
Append --> Req
- Send messages + tools schema.
- Claude returns content with one or more
tool_useblocks;stop_reason = "tool_use". - Execute each tool.
- Append the assistant turn (with
tool_useblocks) and a user turn containingtool_resultblocks. - Loop until
stop_reason = "end_turn".
Skeleton:
messages = [{"role": "user", "content": "What's the weather in Berlin?"}]
while True:
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=[weather_tool_schema],
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
break
tool_results = []
for block in resp.content:
if block.type == "tool_use":
output = run_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
"is_error": False,
})
messages.append({"role": "user", "content": tool_results})
Tool schema
{
"name": "get_weather",
"description": "Get the current weather in a specific location. Use when the user asks about temperature, precipitation, or conditions.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and country, e.g., 'Berlin, Germany'"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature unit"}
},
"required": ["location"]
}
}
The description is the most important field. Claude decides whether to call based on the description — not the name. Be specific about when to use the tool, not just what it does.
tool_choice
auto— Claude decides (default).any— Claude must call some tool, no plain-text answer.{type: "tool", name: "X"}— Force a specific tool. Useful for structured extraction.none— Disable tool use for this turn.
Parallel tool use
Claude can return multiple tool_use blocks in one turn. Run them in parallel and return all tool_results in a single user turn. This is a major latency win.
To allow Claude to do this, your tools should be independent — no result depends on another.
Tool result content
tool_result content can be:
- A string.
- A list of blocks (text + image), enabling tools that return screenshots.
is_error: truefor failed tool calls — Claude will see the error and may retry or escalate.
Common pitfalls
| Pitfall | Fix |
|---|---|
| Vague description ("does stuff") | Specify when to use AND when NOT to use |
| Required vs optional confused | List only truly-required params in required |
| Returning huge blobs | Summarize / truncate; consider returning a resource URI |
| Silent failure | Always return is_error: true with a clear message |
| Infinite loop | Cap loop iterations (e.g., 20) and surface the cap |