From an application's point of view, tool use is a contract: you describe tools with a name, description, and JSON Schema input_schema; Claude may return one or more tool_use blocks, each with its own id, name, and input object; your code executes the matching function and sends the result back as a tool_result content block, keyed to the original call by tool_use_id, inside a new user message. Miss that tool_use_id match and the model has no way to know which result answers which call, especially when it requested several tools in parallel — Claude can and does return multiple tool_use blocks in a single response when the tasks are independent of each other.
The description field on a tool definition is not decoration — it's the primary signal Claude uses to decide whether and when to call that tool, and a vague or misleading description is a common, entirely self-inflicted cause of a tool being called at the wrong time or not called when it should be. The input_schema should mark every field the tool genuinely needs as required; an optional field the model routinely omits produces malformed downstream calls that are easy to misdiagnose as a model problem when the actual issue is an underspecified schema.
tool_choice
tool_choice controls how much freedom Claude has: auto (default — decide whether to call a tool at all, and which one), any (must call some tool, but Claude picks which), tool with a name (force one specific tool), or none (disable tool calling for this turn even though tools are defined). Forcing a specific tool is a common pattern for structured extraction (Domain 2 covers this from the prompt-engineering side; here it's the wiring), where you don't want Claude free-texting an answer at all — the model's only valid move is to populate that tool's schema.
Handling errors inside the loop
A tool execution can fail — a downstream API times out, a lookup returns nothing, an input the model provided doesn't parse. The correct response is still a tool_result block, matched to the right tool_use_id, but with is_error: true and a content string describing what went wrong. This lets Claude reason about the failure and decide what to do next — retry with different input, try a different tool, or explain the problem to the user — instead of the application either crashing or silently swallowing the failure and leaving Claude to hallucinate a result it never actually got.
Common exam distractor
A tool_result must go in a new user message, not appended to the assistant's own turn. An answer that has the application append the result directly onto the assistant message is wrong — roles must still alternate correctly, and a tool result is something the application supplies, which makes it a user-role turn from the API's perspective even though no human typed it.
Replaying the assistant's own turn
When you send the follow-up call after executing a tool, the conversation history must include the assistant message exactly as Claude returned it — the full content array, including any text block that preceded the tool_use block, not just the tool call itself. Trimming that assistant turn down to only the parts your code cares about breaks the model's ability to see its own prior reasoning and can produce responses that contradict or repeat what it already said.
Parallel vs sequential tool calls
Claude decides on its own whether tasks are independent enough to request in parallel (multiple tool_use blocks in one response) or whether it needs one result before it can even formulate the next call (a single tool_use block, then, after seeing that result, another single-tool response). Your application loop needs to handle both shapes without assuming one or the other: code that only ever expects exactly one tool_use block per response will silently execute just the first of several parallel calls and never answer the rest, leaving the model waiting on tool_result blocks it will never receive. The robust pattern is to always collect every tool_use block in a response, execute all of them, and return a matching tool_result for each — whether that turns out to be one or several.
Designing the tool surface itself
A small number of well-scoped tools with clear, non-overlapping purposes is easier for Claude to select correctly than a large number of narrowly similar ones — two tools whose descriptions could both plausibly apply to the same request invite the model to guess, and a guess it gets wrong is a wasted round trip through the loop. Naming matters too: a tool named get_data tells the model almost nothing about when to use it, where get_customer_order_history tells it exactly. This is prompt-engineering territory as much as it is wiring, but it directly affects how reliably the loop in this lesson actually converges to a correct answer in a small number of iterations.