8.1 Tool Use and the Function-Calling Loop
8.1.1 The Tool-Use Loop: Who Actually Executes the Tool
Every extension of Claude beyond plain text starts with the same primitive: tool use, also called function calling. You pass a `tools` array in your request; each entry has a `name`, a `description`, and an `input_schema` written as JSON Schema. Claude reads those definitions alongside the conversation and decides, on its own, whether one of them would help it answer. If it decides yes, it doesn't just say so in prose — it stops with `stop_reason: "tool_use"` and emits a `tool_use` content block carrying the tool's `name` and the `input` arguments it wants to pass.
Here is the detail the exam leans on hardest: Claude's turn ends there. It has only made a REQUEST. Your application code is what actually runs the function, hits the API, queries the database, or does whatever the tool represents. You then package the outcome as a `tool_result` block, place it inside a new `user` message, and — critically — tag it with the same `tool_use_id` that Claude's request carried, so Claude can match the answer to the question it asked. Only after that round trip does Claude continue the turn, possibly requesting another tool, until it reaches a natural `end_turn`.
Claude only ever emits a tool_use request. Your application executes the tool and feeds the outcome back as a tool_result tagged with the matching tool_use_id, closing the loop.
The one idea to hold onto
Claude never executes a tool — it only requests one by emitting a tool_use block (name + input). Your application code is what actually runs the function and returns a tool_result tagged with the matching tool_use_id. Without that matching ID, the loop cannot continue.
8.1.2 Client-Side vs. Server-Side Execution, and the Approval Pattern
Not every tool is executed the same way. The default pattern — and the one most function-calling questions assume unless told otherwise — is client-side: your application is the one that runs the code behind the tool. But some built-in tools are server-side, meaning they execute within Anthropic's own infrastructure rather than in your process. Knowing which category a given tool belongs to tells you where the executing code lives and who is responsible for it.
Inside an actual agent, this dispatch is usually handled by a harness: a small piece of routing logic that looks at the `name` on each incoming `tool_use` block and calls the corresponding function in your codebase. For tools that are sensitive or destructive — deleting a record, sending a payment, force-pushing to a repository — that dispatcher is also where you insert an approval pattern: pause and require a human (or a hook-based automated check) to sign off before the tool actually runs. This is the same idea as the permission gates covered elsewhere in the agentic-workflow material, applied specifically to the tool-execution step.
- •Client-side tools: your application executes the code (the default assumption for custom function calling).
- •Server-side tools: certain Anthropic-provided built-ins run within Anthropic's own infrastructure, not your process.
- •Agentic harness dispatch: a router maps each tool_use name to its implementing function.
- •Approval pattern: gate sensitive or destructive tool calls behind human or hook-based sign-off before execution.
8.1.2 — Exam Trap
Exam trap: don't assume every tool call is client-side just because that's the default pattern. Some built-in tools execute server-side, inside Anthropic's infrastructure. The exam-relevant judgment is knowing which category a described tool falls into, not memorizing a single universal rule.
8.1.3 Writing Tool Descriptions That Drive Correct Selection
If there is one lever that matters more than any other in tool design, it's the natural-language `description`. Claude decides WHETHER to call a tool and WHICH tool to call almost entirely based on how well the description explains what the tool does, when it should be used, and what each parameter means. A vague description — "looks things up" — gets a tool ignored when it would have helped, or misused when it shouldn't have been called at all. A precise one — "searches the internal product catalog by SKU or free-text name; use this instead of web search for any question about products this company sells" — gives Claude exactly the signal it needs to pick correctly.
This is worth sitting with because it's counterintuitive to engineers who default to thinking in schemas and types: the schema shapes the ARGUMENTS once a tool has already been chosen, but the description is what drives the choice itself. Spend your effort accordingly. If Claude keeps picking the wrong tool, or ignoring the right one, the fix is almost always to rewrite the description — not to add more fields to the schema.
| Description style | Example | Outcome |
|---|---|---|
| Vague | "get_data: retrieves data" | Claude can't tell when to use it vs. other tools; gets ignored or misused |
| Precise | "get_order_status: look up the current shipping status of a customer order by order ID; use this whenever a user asks where their order is" | Claude reliably selects it exactly when it's the right fit |
8.1.4 Precise Input Schemas and Structured Error Handling
Once Claude has picked a tool, the `input_schema` is what shapes the arguments it produces. Write it as real JSON Schema: give every field a type, mark the ones that are `required`, constrain free-form choices with `enum` where the valid values are known, and add a per-field description so ambiguous parameters (what format is this date in? what units?) don't have to be guessed. A precise schema reduces malformed calls, but it's a different lever from the description — the description governs whether and when the tool gets called; the schema governs how well-formed the arguments are once it has.
The other half of a well-behaved tool is what happens when it fails. Don't return a bare failure or a raw stack trace. Return a structured, informative error — commonly an `is_error` flag on the `tool_result` plus a clear message describing what went wrong ("order ID not found" vs. "invalid date format, expected YYYY-MM-DD"). That lets Claude reason about the failure and recover intelligently: retry with corrected arguments, try a different tool, or explain the limitation to the user — instead of guessing blindly at what broke.
8.1.4 — Key Concept
Two different levers, two different jobs: the description drives WHETHER and WHICH tool gets called; the input_schema shapes HOW WELL-FORMED the arguments are once it's chosen. Structured errors (is_error + message) let Claude recover instead of guessing.
8.1.5 disable_parallel_tool_use: Forcing One Tool Call Per Turn
Current Claude models default to requesting multiple independent tool calls in a single turn whenever the subtasks don't depend on each other -- Claude emits several tool_use blocks in one assistant turn, your code executes them concurrently, and you return all the corresponding tool_result blocks together in a single follow-up user message. This is a throughput win when the calls really are independent: three unrelated lookups don't need three separate round trips.
That default assumes independence, though, and not every pair of tool calls is independent. When one tool's output feeds the next call's arguments -- the second call literally cannot be constructed until the first result comes back -- letting the model issue both as if they were parallel produces malformed or guessed arguments for the second call. `disable_parallel_tool_use` is the fix: set it to force the model to request at most one tool call per turn, so a genuine dependency gets modeled as separate turns instead of a single batch.
# Force strictly sequential tool calls when one call's output feeds the next
response = client.messages.create(
model="claude-sonnet-4-6", max_tokens=2048,
tools=tool_definitions,
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=messages,
)
# Claude will now request at most one tool_use block per turn, even when it
# would otherwise have batched several independent-looking calls together.- •Genuine sequential dependency -- each tool call's result determines the next call's arguments, so batching them would require guessing inputs that don't exist yet.
- •Auditability or replay invariants -- some systems require a strict one-call-per-turn trace for logging, replay, or compliance reasons, independent of whether the calls are logically independent.
8.1.5 -- Key Concept
Don't confuse disable_parallel_tool_use with tool_choice itself. tool_choice controls whether/which tool must be called; disable_parallel_tool_use controls how many calls can appear in a single turn. And don't reach for a better tool description to fix a sequencing bug -- description quality drives which tool gets picked, not whether the model batches two calls that have a real ordering dependency.
8.1.6 Right-Sizing the Tool Set
It's tempting to think more tools is strictly more capability. In practice, a large catalog of overlapping tools actively hurts selection: every tool definition sits in context on every turn, and when two or three tools could plausibly handle the same request, Claude has to guess among near-duplicates instead of picking confidently. The fix is to right-size the tool set — give Claude a focused, non-overlapping list scoped to the actual task, rather than exposing every capability your system happens to have.
Put the whole lesson together and a pattern emerges: every piece of tool design — the loop, the execution model, the description, the schema, the error handling, the set size — exists to make one thing true as often as possible: Claude picks the right tool, calls it with well-formed arguments, and can recover cleanly if something goes wrong. That's the entire job of tool implementation.
8.1.5 — Key Concept
Too many overlapping tools cause confusion in selection and bloat the context window with definitions Claude must weigh on every turn. A focused, non-overlapping tool set sized to the actual task outperforms a large, redundant one — more tools is not automatically better.
Key Takeaways
- ✓The tool-use loop: you send a tools array → Claude emits a tool_use block (name + input) with stop_reason "tool_use" → your code executes it → you return a tool_result matched by tool_use_id → Claude continues or reaches end_turn.
- ✓Claude never executes a tool itself; it only requests the call. Your application code is what actually runs it — this is the single most tested fact in this lesson.
- ✓Client-side tools are executed by your app (the default); server-side tools run within Anthropic's own infrastructure. Sensitive or destructive tools should sit behind a human or hook-based approval pattern.
- ✓Tool description quality — what it does, when to use it, what each parameter means — is the single biggest driver of correct tool selection; vague descriptions get tools misused or ignored.
- ✓A precise input_schema (types, required, enums, per-field descriptions) governs how well-formed the arguments are, but doesn't substitute for a good description — the two are different levers.
- ✓Return structured, informative errors (an is_error flag or clear message) so Claude can recover or retry instead of guessing at a bare failure.
- ✓Right-size the tool set: a focused, non-overlapping list beats a large catalog of overlapping tools, which bloats context and confuses selection.
- ✓Current models default to parallel tool calling -- multiple tool_use blocks in one turn, executed concurrently. disable_parallel_tool_use forces exactly one tool call per turn, for a genuine sequential dependency (one call's result determines the next call's arguments) or a strict auditability/replay invariant.
Check Your Understanding
Test what you learned in this lesson.
Q1.Claude responds to a request with `stop_reason: "tool_use"` and a `tool_use` block. What happens next?
Q2.A team keeps seeing Claude call the wrong tool among several similar-looking options, even though each tool's input_schema is precise and fully typed. What is the most likely fix?
Q3.A tool call fails because a required record doesn't exist. What should the tool_result contain?
Q4.An application exposes fifteen overlapping tools that all perform slightly different variations of "look up a customer." What is the most likely consequence?
Q5.A tool-use design has one call whose result must determine the arguments of the next call, but Claude keeps issuing both as parallel tool_use blocks in the same turn, producing malformed arguments for the second call. What's the fix?
Practice This Lesson