How Claude Tool Calling Actually Works: The Request-Execute Model
▶ Watch on YouTube & subscribe to The Stack Underflow
When developers first integrate Claude with external tools, nearly everyone starts with the same wrong mental model: the model reaches out to an API, runs a bash command, or queries a database. It cannot. Claude is a sealed box — no network, no file system, no side effects, ever. What the model does do is emit a precisely structured request, conforming exactly to the schema you gave it, asking that something be done on its behalf. Your code (or Anthropic’s servers) reads that request and acts.
That distinction is not a technicality. It is the architectural load-bearing wall of every Claude integration you will ever build. The model is brilliant at reasoning about what to do next; it depends entirely on something outside itself to actually do it. Get this inversion in your head and tool calling stops being magic and becomes a loop you control.
The one-sentence version: Claude never executes a tool — it emits a structured
tool_userequest that something outside the model executes, then consumes the result in a follow-upmessages.createcall.
The sealed box and the dotted line
The cleanest way to hold this in your head is two halves separated by a boundary that never moves:
┌──────────────────────────────────────────┐
│ Claude (the model) │
│ reasons · decides · emits tool_use │
│ blocks · waits │
└────────────────────┬─────────────────────┘
│ tool_use block
│ (a structured request,
│ never an execution)
▼
- - - - - - - - - - - - - - - - - - - - ← dotted line
│
▼
┌──────────────────────────────────────────┐
│ The executor (your code OR │
│ Anthropic's hosted infrastructure) │
│ actually runs things │
└────────────────────┬─────────────────────┘
│ tool_result block
▼
appended to messages[]
→ new messages.create call
Everything Claude “does” with tools lives above the dotted line. The work lives below. The arrow goes down as a request and comes back up as a result. The model never crosses that line.
What you hand the model: tool definitions
Every tool call starts with what you pass into the tools array of your messages.create request. Each definition has four fields that matter:
| Field | Purpose |
|---|---|
name | The identifier that appears in the emitted tool_use block |
description | What the model reads to decide which tool to invoke |
input_schema | JSON Schema (draft 2020-12) describing required and optional parameters |
strict | When true, the schema is compiled into a constrained grammar at the decoder level |
Two things are worth locking in memory here.
Description quality is the real selector. The model picks tools by reading descriptions, not names. Write a description like you are writing docs for a capable engineer who has never seen your codebase — tell them what the tool does, when to use it, and critically, when not to. Vague or overlapping descriptions are the most common source of wrong tool calls in production.
strict: true is free correctness. When you enable strict mode (available in public beta since November 2025, header anthropic-beta: structured-outputs-2025-11-13), the JSON Schema is compiled into a decoder grammar. The model physically cannot emit "2" when your schema declares an integer 2. Required fields cannot be absent. This is enforcement during generation, not validation afterward — and it costs nothing extra at inference time.
The three executor lanes
Once Claude emits a tool_use block, your orchestration layer routes it. As of mid-2026, there are three distinct execution lanes:
Lane A — User-defined client tools
The tools you built. get_weather, send_slack_message, query_orders_db. You wrote the schema, you wrote the handler, your process executes the call. This is what most tutorials show and where almost all custom integrations live.
Lane B — Anthropic-schema client tools
Tools where Anthropic publishes the schema — bash, text_editor, computer, memory — but your code still runs the execution. The published schema means the model is trained to call these reliably and consistently. The bash command runs in your environment, in your sandbox. You own the security boundary entirely. Anthropic provides the contract, not the runtime.
Lane C — Anthropic-hosted server tools
The newest category: web_search, web_fetch, code_execution, and tool_search. Here Anthropic’s own infrastructure executes the action. Your code never sees the raw outbound request — you only receive the tool_result that comes back. The model still did nothing; it asked, and Anthropic’s servers acted. Your private data never flows out unless you explicitly send it in the request.
Lane A → your handler → tool_result back to messages[]
Lane B → your sandbox → tool_result back to messages[]
Lane C → Anthropic servers → tool_result back to messages[]
↑
(web_search, web_fetch,
code_execution, tool_search)
The lane that ran does not change the message protocol. Every lane wraps its output in a tool_result block and returns it. The model sees the same structure regardless.
The full round trip, step by step
1. You call messages.create with tools[] + user message
↓
2. Claude responds:
stop_reason: "tool_use"
content: [tool_use block {name, id, input}]
↓
3. Your orchestrator reads stop_reason,
routes to the correct executor (Lane A / B / C)
↓
4. Executor runs; result is wrapped in:
{type: "tool_result", tool_use_id: id, content: "..."}
↓
5. You append BOTH the assistant's tool_use block AND
your tool_result block to messages[]
↓
6. You call messages.create again (same conversation, new turn)
↓
7. Claude reads the result, continues reasoning.
Either: stop_reason: "tool_use" → loop back to step 3
Or: stop_reason: "end_turn" → emit final text answer
For multi-step agents, steps 2 through 6 repeat. Each iteration is one tool call. The model decides at every turn whether to call another tool or to stop. The state of the conversation is the messages array — that array is the only memory the model has between calls.
One additional stop reason to know: stop_reason: "pause_turn" signals that a long-running server-tool action (such as a code_execution sandbox job) is still in progress. Your code holds the conversation open and polls until completion, then resumes.
Dynamic tool discovery: the tool_search tool
In late 2025 Anthropic introduced a fourth pattern that sits on top of the three lanes: the tool_search tool, which lets Claude discover tools on demand from large catalogs rather than having every definition loaded into the context window upfront.
The mechanism: you supply all definitions to the API, marking most with defer_loading: true. Claude only sees the tool_search tool plus your small set of always-on tools. When the model determines it needs a capability, it calls tool_search with a natural language query or a regex pattern. The server returns matching definitions, which get expanded into Claude’s context for that turn.
This matters at scale. Real enterprise MCP catalogs can exceed 10,000 tool definitions. Loading them all front-loads your context window and adds noise to every selection decision. Dynamic discovery keeps the context lean and selection accurate. The feature was integrated into Claude Code’s MCP support in 2026 (docs.anthropic.com, 2026).
Without defer_loading:
tools[] = [tool_1, tool_2, ... tool_10000] → context bloat
With defer_loading:
Claude sees: [tool_search, always_on_tool_1, ...]
Calls tool_search("query about file operations")
Server returns: [read_file, write_file, list_dir]
Context expanded only for what's needed
How many tools can you expose?
The older rule of thumb was four or five tools per agent. That was overcautious for current models (Sonnet 4.6, Opus 4.8 as of June 2026) and is largely superseded by dynamic discovery for large catalogs. The real constraint has always been the same: each tool needs a description so unambiguous that the model could not reasonably pick the wrong one. That constraint does not disappear with larger models or bigger catalogs — if anything, it becomes more important.
| Scenario | What matters |
|---|---|
| Small catalog (fewer than 10 tools) | Description quality is everything; count is not the concern |
| Medium catalog (10-50 tools) | Non-overlapping descriptions + consider grouping by domain |
| Large catalog (50+ tools) | Use defer_loading: true + tool_search; descriptions still critical |
If you have ten tools with crisp, non-overlapping descriptions, that is safer than three tools with vague ones. Tool count is secondary; description precision is the gate.
How to apply this right now
Always handle stop_reason: "tool_use" in a loop, not an if block. Agents that only handle one tool call per turn will silently drop multi-step reasoning.
Return a tool_result even on error. The Messages API does not know or care if your handler threw an exception. Catch exceptions, wrap the error message in a tool_result block, and send it back. This lets the model decide to retry, pick a different tool, or surface a clean error to the user — rather than leaving the conversation stalled.
Enable strict: true on every tool by default. There is no runtime cost. It eliminates an entire class of production incidents (missing required fields, wrong types, extra properties) before they happen.
Run parallel tool calls when the model emits multiple tool_use blocks in one response. Claude will emit independent tool calls as a batch when it determines they do not depend on each other. Execute them concurrently and return all tool_result blocks together in one follow-up call. This cuts wall-clock latency significantly in complex agents.
For MCP-backed tools, apply the same description discipline. Whether your tool definitions come from hand-written JSON Schema or from an MCP server, the model reads the description field the same way. MCP does not change the selection logic — it standardizes the plumbing, not the semantics.
Common misconceptions
-
“Claude calls my API directly.” No. Claude emits a
tool_useblock that is a structured request. Your code makes the network call — or Anthropic’s servers do, for Lane C tools. The model never touches a network socket. -
“Setting
strict: falseis fine, I’ll validate afterward.” You can validate afterward, butstrict: truecatches violations at generation time at zero extra cost. Every production tool definition should use it by default; opt out only when you have a specific reason (for example, when your schema intentionally uses patterns that constrained decoding does not yet support). -
“Lane C tools (like
web_search) mean Anthropic can see my private data.” Lane C tools fetch public content on your behalf. The data being acted on is public web content or code execution results. Your private data does not leave your system unless you explicitly include it in the request payload. -
“More tools means a smarter agent.” More tools with weak descriptions means more misrouted tool calls and a noisier context. Agent capability comes from description quality, appropriate tool scoping, and robust error handling — not raw tool count.
Frequently asked questions
Why does the model need a second messages.create call after a tool runs?
The model has no persistent memory between API calls — its world is exactly the messages array you send. After your code executes a tool, the result does not exist anywhere the model can see it until you append the tool_result block and make a new call. Each call is stateless; you own the conversation state.
What happens if my tool handler throws an exception?
The API does not know and does not care. You must return a tool_result block regardless. The right pattern is: catch all exceptions in your handler, format the error into a human-readable string, and return that as the tool_result content. The model can then reason about the error — retry, escalate, or tell the user something went wrong — rather than stalling indefinitely.
Can Claude call multiple tools in one turn?
Yes. Claude can emit multiple tool_use blocks in a single response when it determines they are independent. Run them concurrently in your executor, collect all results, and return all tool_result blocks together in one follow-up call. This is how you avoid sequential latency accumulation in complex agents.
What is the stop_reason: "pause_turn" value?
It signals that a long-running server-tool action — typically code_execution in a hosted sandbox — is still running. Your code waits, polls for completion, and then resumes the conversation once the result is ready. It is distinct from end_turn (model finished) and tool_use (model wants a client-side result).
What is MCP and how does it relate to what I just learned?
MCP (Model Context Protocol) is a standardized protocol for defining, packaging, and serving tool schemas so teams are not hand-crafting the same JSON Schema definitions for read_file, web_search, or query_db in every project. The tool-calling mechanism you learned here — tool_use block, executor, tool_result, loop — is identical whether definitions arrive from a raw JSON array or from an MCP server. MCP standardizes the plumbing; the request-execute model is unchanged. The next tutorial covers it directly.
Does this change with newer Claude models like Opus 4.8 or Sonnet 4.6?
The protocol is stable. The tool_use / tool_result round trip, the stop_reason values, and the schema fields are the same across the current model family (Sonnet 4.6, Opus 4.8, and the Fable 5 class as of June 2026). What improves across model generations is the quality of tool selection, adherence to schema constraints without strict mode, and the ability to handle larger tool catalogs gracefully. The architecture you learn here is not going to change; the model’s execution of it keeps getting better.
Where this fits in the series
This tutorial is part of How Claude Actually Works — a course that builds a mechanistic understanding of how Claude reasons, encodes context, and integrates with the world.
If you have not yet read the foundational layer, start with The Claude Stack Mental Model — it shows where tool calling sits in the full architecture before you zoom in on the mechanism. The context window tutorial explains why appending tool_use and tool_result blocks to messages[] costs tokens and why that matters for long-running agents. The stop_reason reference covers every value (end_turn, tool_use, pause_turn, max_tokens, stop_sequence, refusal) and how to handle each in production code.
Once you have the tool-calling mechanism down, the natural next step is What is MCP — the standard that stops every team from reinventing the same tool schemas by hand. After that, How Claude Code Works shows the agent loop in a real, shipped product that runs this exact round-trip on your behalf. Browse all tutorials to follow the full series.
Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.
Subscribe on YouTube →