Tool Use & Function Calling
Tool use lets Claude call external functions during a conversation — databases, APIs, code execution, file systems, web search, or any custom function you define. This page covers the tool definition schema, how Claude decides whether to call a tool, result handling, parallel calls, and error recovery.
Tool Definition Schema
Each tool is defined as a JSON object with three required fields:
{
"name": "get_weather",
"description": "Get the current weather for a specific city. Returns temperature in Celsius and a condition description.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name, e.g. 'London' or 'New York'"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature units"
}
},
"required": ["city"]
}
}The description field is the most important — Claude reads it to decide when to call the tool. Write descriptions that clearly explain what the tool does, what inputs it expects, and what it returns. Claude uses this description, not the input schema, to decide whether to call the tool.
Tool call round-trip — Claude drives the loop
How Claude Decides Whether to Call a Tool
Claude decides whether to call a tool based on:
- Whether the task requires information or actions the tool provides
- The tool's description — a good description prevents both over-calling and under-calling
- The
tool_choiceparameter in the API request, which can force or prevent tool use:auto(default) — Claude decides whether to call a toolany— Claude must call at least one tooltool— Claude must call a specific tool by namenone— Claude may not call any tools
Receiving and Processing a Tool Call
When Claude decides to call a tool, it returns a response with stop_reason: "tool_use" and one or more tool_use content blocks:
# Claude's response when calling a tool:
{
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_01A2B3C4",
"name": "get_weather",
"input": { "city": "London", "units": "celsius" }
}
]
}
# Send the result back:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A2B3C4",
"content": "{ \"temperature\": 18, \"condition\": \"Partly cloudy\" }"
}
]
}The tool_use_id in the result must match the id from Claude's tool call — this is how Claude links results to calls.
Parallel Tool Calls
Claude can request multiple tool calls in a single response when they are independent. The response will contain multiple tool_use blocks:
# Claude requests two tools in one response:
"content": [
{ "type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": {"city": "London"} },
{ "type": "tool_use", "id": "toolu_02", "name": "get_weather", "input": {"city": "Paris"} }
]
# Execute both in parallel, then send both results:
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "..." },
{ "type": "tool_result", "tool_use_id": "toolu_02", "content": "..." }
]Always send results for all requested tool calls in a single user message — Claude expects all results before continuing.
Error Handling: When a Tool Call Fails
If a tool call fails (network error, invalid input, permission denied), return a tool result with is_error: true:
{
"type": "tool_result",
"tool_use_id": "toolu_01",
"content": "Error: City 'Lndon' not found. Check spelling.",
"is_error": true
}Claude will read the error message and adapt — it may try a different input, use an alternative approach, or inform the user of the failure. Provide descriptive error messages; generic "error occurred" messages leave Claude without useful information for recovery.
Checklist: Do You Understand This?
- Tool definition: name + description (most important) + input_schema (JSON Schema)
- Claude decides whether to call based on the description — write descriptions clearly about what the tool does and returns
- Tool call response has
stop_reason: "tool_use"— always send results back before continuing - Parallel tool calls: Claude can request multiple tools in one response — execute in parallel, return all results together
- Error handling: return
is_error: truewith a descriptive message — Claude will adapt based on the error content