Tool use and function calling
Tool use is the primitive that turns Claude from a text-completion engine into an agent. The model declares a tool call via a tool_use content block; the caller executes the function (or, for server tools, Anthropic does); the result comes back as a tool_result block; Claude continues reasoning. Parallel tool use (multiple tool_use blocks in one assistant turn) is on by default for Claude 4 models. This note covers the request/response shape, every tool_choice mode, parallel execution, strict mode, fine-grained streaming, the canonical client-side tool loop, and the divide between client tools (you execute) and server tools (Anthropic executes).
See also
- claude-api-and-sdks — the
tools,tool_choice,tool_use,tool_resultparameters - computer-use-and-code-execution — Anthropic-managed server tools (web_search, code_execution, bash, text_editor, memory, computer)
- prompt-caching — caching tool definitions saves real money
- agents-and-orchestration-patterns — multi-tool orchestration patterns
- prompt-engineering-agent-systems — vendor-neutral background
1. Client tools vs server tools
Per platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works:
| Type | Where it runs | Examples | Detected via |
|---|---|---|---|
| Client tool | Your application | User-defined tools, Anthropic-schema tools (bash_20250124, text_editor_*, computer_*, memory_20250818) | stop_reason: "tool_use" + tool_use block in response |
| Server tool | Anthropic’s infrastructure | web_search_*, web_fetch_*, code_execution_*, tool_search_tool_* | server_tool_use + corresponding *_tool_result block in same response |
Client tools require you to implement the loop: read tool_use block, run your code, send back a user message containing a tool_result block. Server tools are transparent — Anthropic runs them and includes the result in the same response.
2. Defining a custom tool
{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["location"]
},
"type": "custom",
"cache_control": {"type": "ephemeral"},
"strict": true,
"defer_loading": false,
"allowed_callers": ["direct"]
}Field reference
name— alphanumeric + underscore, ≤64 charsdescription— strongly recommended. Claude uses this to decide when to call. Treat it as a doc-comment.input_schema— JSON Schema for arguments. Anthropic supports a wide subset (object, properties, required, type, enum, anyOf, oneOf, items, etc.). Always setadditionalProperties: falsewhen you want strict shapes.type: "custom"— implicit for user-defined tools; explicit for clarity. Other valid values are the server-tool type strings (web_search_20260209, etc.) which use a different namespace.cache_control— see prompt-cachingstrict: true(per Strict tool use) — guarantees output conforms to schema exactly. Required for production-grade structured-input tools. Has minor overhead.defer_loading— used with tool search; the tool isn’t loaded into Claude’s context until referenced via thetool_search_tool_*server tool.allowed_callers— restricts what context can invoke the tool.["direct"](default) means only the main model can call; values like"code_execution_20250825"allow code inside the code-execution sandbox to call your tool too. Useful for hybrid agents.
3. The tool-use loop (client tools)
Step 1: Initial call
import anthropic
client = anthropic.Anthropic()
tools = [{
"name": "get_weather",
"description": "Get current weather for a location.",
"input_schema": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
}]
messages = [{"role": "user", "content": "What's the weather in Paris?"}]
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages,
)
print(response.stop_reason) # "tool_use"Step 2: Extract the tool call
tool_use = next(b for b in response.content if b.type == "tool_use")
print(tool_use.id, tool_use.name, tool_use.input)
# "toolu_01...", "get_weather", {"location": "Paris"}Step 3: Execute, send result back
weather_result = my_weather_function(**tool_use.input) # "18°C, partly cloudy"
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": weather_result, # or list of content blocks
"is_error": False,
}],
})
continuation = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages,
)
print(continuation.content[0].text) # "The weather in Paris is..."Step 4: Repeat until stop_reason == "end_turn"
For a generic agent loop:
while response.stop_reason == "tool_use":
tool_uses = [b for b in response.content if b.type == "tool_use"]
tool_results = []
for tu in tool_uses: # parallel execution = run all at once
try:
result = dispatch(tu.name, tu.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": tu.id,
"content": result,
})
except Exception as e:
tool_results.append({
"type": "tool_result",
"tool_use_id": tu.id,
"content": str(e),
"is_error": True,
})
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
response = client.messages.create(model=model, max_tokens=1024, tools=tools, messages=messages)4. tool_choice modes
| Mode | JSON | Behavior |
|---|---|---|
| Auto (default) | {"type": "auto"} | Claude decides whether to use any tool. |
| Any | {"type": "any"} | Claude must use exactly one tool (no text-only response). |
| Specific tool | {"type": "tool", "name": "get_weather"} | Claude must use this exact tool. |
| None | {"type": "none"} | Disables tools. Equivalent to omitting tools. |
All four modes accept "disable_parallel_tool_use": true to force a single tool call per turn (default is false — parallel allowed). See section 6.
Compatibility with extended thinking
tool_choice: {"type": "auto"}and{"type": "none"}— supported with thinkingtool_choice: {"type": "any"}or{"type": "tool", ...}— error: cannot force a tool while thinking is enabled
5. Tool-use system-prompt overhead
When tools is non-empty, the API automatically prepends a hidden system prompt that teaches the model the tool-use format. The overhead in input tokens:
| Model | tool_choice: auto / none | tool_choice: any / tool |
|---|---|---|
| Claude 4 (Opus 4.7, 4.6, 4.5, 4.1, Sonnet 4.6, 4.5, 4, Haiku 4.5) | 346 | 313 |
| Haiku 3.5 | 264 | 340 |
This is additive to your own tool definitions and system prompt. Cacheable along with tools (place cache_control on the last stable tool).
6. Parallel tool use
By default on Claude 4 models, the assistant can emit multiple tool_use blocks in a single turn:
"content": [
{"type": "text", "text": "I'll check both."},
{"type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": {"location": "Paris"}},
{"type": "tool_use", "id": "toolu_02", "name": "get_weather", "input": {"location": "London"}}
]Execute both in parallel, return both results in a single user message:
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_01", "content": "Paris: 18°C"},
{"type": "tool_result", "tool_use_id": "toolu_02", "content": "London: 15°C"}
]
}Disabling parallel
Pass disable_parallel_tool_use: true in tool_choice:
tool_choice={"type": "auto", "disable_parallel_tool_use": True}When to disable: tools have side-effects whose order matters; you need single-step debugging; the LLM keeps issuing redundant parallel calls.
Parallel + thinking
Adaptive thinking on Opus 4.7 / 4.6 / Sonnet 4.6 supports interleaved thinking between tool calls automatically. For older Claude 4 models, add the interleaved-thinking-2025-05-14 beta header — Claude can then think between sequential tool results, making more sophisticated multi-step decisions.
7. Strict tool use
Per Strict tool use, set "strict": true on a tool definition. Behavior:
- Output JSON guaranteed to conform to
input_schemaexactly - Recursive: applies to nested objects
- Required:
additionalProperties: falseon every object - Limitation: subset of JSON Schema (no
$ref, nooneOfwith mixed types at the top level, nopattern)
Strict mode adds ~5-10 % to TTFT in exchange for eliminating an entire class of “missing required field” or “extra unknown field” errors. Use it for any tool that hits real infrastructure.
8. Fine-grained tool streaming
When streaming with tools, input_json_delta events arrive incrementally as Claude composes the tool’s input JSON:
event: content_block_start
data: {"type": "content_block_start", "index": 1,
"content_block": {"type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": {}}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 1,
"delta": {"type": "input_json_delta", "partial_json": "{\"location\":"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 1,
"delta": {"type": "input_json_delta", "partial_json": "\"Paris\"}"}}
event: content_block_stop
data: {"type": "content_block_stop", "index": 1}
Accumulate partial_json strings, parse the full JSON at content_block_stop. The Python SDK’s stream.text_stream handles this transparently; for full input access, iterate raw events.
9. Tool result content
The tool_result.content field accepts:
- A plain string:
"content": "18°C, partly cloudy" - A list of content blocks:
"content": [{"type": "text", "text": "..."}, {"type": "image", "source": {...}}] - An image (return screenshots from a
bashorcomputertool)
is_error: true signals failure. Claude often retries on error if there’s a sensible alternative input.
For very large tool outputs (database query dumps, log tails), truncate to a few thousand tokens with a “summary + tail” pattern; the model rarely benefits from more.
10. Error handling
Best practices from production agent loops:
- Wrap every dispatch in try/except, returning
is_error: truewith the exception message. Claude usually self-corrects. - Bound the agent loop: cap at 10-30 iterations. An unbounded loop on a malformed tool can burn tokens fast.
- Detect ping-pong: if the same
(tool_name, input_hash)repeats more than 3 times, break out — Claude is stuck. - Token-budget the loop: track cumulative
usageacross iterations; abort if exceeding a per-turn cap. - For server tools: errors come back inline as
..._tool_result_errorcontent with anerror_code. See computer-use-and-code-execution for the per-tool error codes.
11. Best practices for tool definitions
Per Anthropic’s prompting docs:
- Detailed descriptions matter more than schemas. Tell Claude when to use the tool, not just what it does.
- One purpose per tool. Avoid Swiss-army tools — narrower tools give better routing.
- Examples in description — “For example, to get NYC weather, call with
location: 'New York, NY'.” - Set
requiredaggressively — required fields surface as “Claude must ask for this if missing.” - Match the model: Opus is far better at recognizing missing required fields and asking the user; Sonnet/Haiku may guess values.
- Avoid jargon: tool descriptions are part of the prompt — write them in natural language, not code-style identifiers.
12. Server tools (Anthropic-executed)
Full coverage in computer-use-and-code-execution. Quick list:
| Server tool | Type string | What it does |
|---|---|---|
| Web Search | web_search_20260209 (latest, with dynamic filtering) or web_search_20250305 | Search the web; results inline; $10 per 1,000 searches. |
| Web Fetch | web_fetch_20260309 (latest) or earlier | Fetch a specific URL; returns parsed content. |
| Code Execution | code_execution_20260120 (latest) | Sandboxed Python + bash; persistent files within session. |
| Bash Code Execution | bash_code_execution_* | Returned inside code_execution_* blocks when bash is invoked. |
| Text Editor Code Execution | text_editor_code_execution_* | Returned inside code_execution_* blocks when text editor is invoked. |
| Tool Search (BM25 / regex) | tool_search_tool_bm25 / tool_search_tool_regex | Server-side semantic search across deferred tools; enables 100+ tool agents without context bloat. |
Server tools are invoked the same way: include them in tools[], Claude calls them, results appear inline. No client-side loop needed.
13. Best-practice examples
Strict tool with rich schema
tools = [{
"name": "create_calendar_event",
"description": (
"Create a new calendar event. Use this when the user asks to schedule, "
"book, or create an appointment, meeting, or reminder. Returns the event ID."
),
"strict": True,
"input_schema": {
"type": "object",
"additionalProperties": False,
"properties": {
"title": {"type": "string", "description": "Event title"},
"start_iso": {"type": "string", "description": "ISO 8601 start time, e.g. 2026-05-25T14:00:00"},
"duration_minutes": {"type": "integer", "minimum": 5, "maximum": 480},
"attendees": {
"type": "array",
"items": {"type": "string", "format": "email"},
"description": "List of attendee email addresses",
},
},
"required": ["title", "start_iso", "duration_minutes"],
},
}]Multi-tool agent with caching
tools = [
{"name": "search_docs", "description": "...", "input_schema": {...}},
{"name": "edit_file", "description": "...", "input_schema": {...}},
{
"name": "run_tests",
"description": "...",
"input_schema": {...},
"cache_control": {"type": "ephemeral"}, # caches all 3 tool defs above
},
]TypeScript: typed tool with discriminated narrowing
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [{
name: "get_weather",
description: "Get current weather.",
input_schema: {
type: "object",
properties: { location: { type: "string" } },
required: ["location"],
},
}];
let response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
tools,
messages: [{ role: "user", content: "Weather in Tokyo?" }],
});
while (response.stop_reason === "tool_use") {
const toolUses = response.content.filter(
(b): b is Anthropic.ToolUseBlock => b.type === "tool_use"
);
const results = await Promise.all(
toolUses.map(async (tu) => {
const r = await runMyTool(tu.name, tu.input);
return { type: "tool_result" as const, tool_use_id: tu.id, content: r };
})
);
response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
tools,
messages: [
{ role: "user", content: "Weather in Tokyo?" },
{ role: "assistant", content: response.content },
{ role: "user", content: results },
],
});
}14. Token-efficient tools (beta)
anthropic-beta: token-efficient-tools-2025-02-19 — reduces the tokens used for tool-use encoding (especially helpful with many parallel tool calls). Not yet GA; opt in if your agent loops show high overhead in usage.output_tokens proportional to the actual content.
15. Further reading
- Tool use overview
- How tool use works
- Tool reference — complete server-tool directory
- Define tools
- Handle tool calls
- Strict tool use
- Tool use with prompt caching
- Build a tool-using agent — end-to-end tutorial