D2.2 · Tool Design + Integration18% of CCA-F6 min read

Tool Choice.

tool_choice is the parameter that controls whether Claude can decide to use a tool ("auto"), must call any tool ("any"), or must call a specific tool by name. Use specific-tool forcing for mandatory-first-step operations like identity verification before refunds.

Mental model"any" lets the model choose which tool.
Tool Choice, hero illustration featuring Loop mascot in a warm gallery scene.
Share
On this page
01 · Summary

TLDR

tool_choice is the parameter that controls whether Claude can decide to use a tool ("auto"), must call any tool ("any"), or must call a specific tool by name. Use specific-tool forcing for mandatory-first-step operations like identity verification before refunds.

3
Modes
auto
Default
2
Forced use cases
D2
Exam domain
100%
Guarantee
02 · Definition

What it is

tool_choice is a request-level parameter that controls whether Claude must, may, or must not call a tool. Three modes: {type: "auto"} (default, model decides), {type: "any"} (model must call any tool), and {type: "tool", name: "X"} (forced to a specific tool). It bridges between probabilistic (the model decides) and deterministic (you decide) tool execution. The wrong choice leaves 30% of your output as unparseable text when determinism is required.

Without override, Claude reads the message history, evaluates available tools, and decides whether a tool is needed at all. In refund flows this is dangerous: 30% of the time Claude asks clarifying questions instead of calling verify_customer. With {type: "tool", name: "verify_customer"} that 30% becomes 0%. The tradeoff is inflexibility: forced tool_choice removes legitimate text-only responses. Use forced sparingly, only for mandatory architectural checkpoints.

The incompatibility with extended thinking is architectural, not cosmetic. When thinking: {type: "enabled"} is active, tool_choice: "any" and forced both return 400 validation errors. You must fall back to tool_choice: "auto". Reasoning-heavy tasks cannot guarantee tool calls; the model reasons, then decides whether to act. The fix is reframing: use extended thinking for analysis, not for deterministic control of execution.

Production fails when tool_choice is mistaken for a safety valve instead of a contract enforcer. tool_choice: "any" does not make your loop robust if tool descriptions are vague, the model still misroutes. It ensures a tool is called, not that the right tool is called. Combining unclear descriptions + forced tool_choice = guaranteed wrong action. Fix descriptions first, then use tool_choice to enforce when, not what.

03 · Mechanics

How it works

When you call messages.create() with tool_choice, the request is validated before the forward pass. If thinking is enabled and tool_choice is anything other than auto, the API returns a 400 error immediately, no model inference happens. This check happens before token accounting, so invalid combinations fail instantly and cheaply. Valid combinations proceed to the transformer.

Inside the forward pass, the model sees three elements: the system prompt, the messages, and your tool_choice specification. With auto, Claude generates and samples a stop_reason: either end_turn (text is enough) or tool_use (tools needed). With any, the model is nudged to produce a tool_use block, sampling weights shift to favor tool calls over text termination. With forced, tool generation is implicit; sampling never considers end_turn.

The stop_reason is independent of tool_choice. A forced tool call still returns stop_reason: "tool_use". The tool's name is guaranteed to be X; the input might be empty or malformed, but the structure is assured. This is why forced tool_choice is safe for mandatory architecture gates: it guarantees the attempt, not the validity. Argument validation is still your harness's job.

Tool execution remains your responsibility. Even with tool_choice: "any", a tool call might have invalid input (missing required fields, wrong types). Your harness catches it, returns a structured error, and lets Claude retry. The difference is confidence: with auto, you code for text-or-tool branches and handle tool absence; with any, you code only for tool branches. Wrong inputs are handled identically in both.

Tool Choice mechanics, painterly diagram featuring Loop mascot.
04 · In production

Where you'll see it

Mandatory verification before refunds

Refund workflow forces verify_customer as the first call via tool_choice = {type:'tool', name:'verify_customer'}. Removes probabilistic skipping. Combined with PostToolUse policy hooks for amount limits, prompt-only enforcement of the same policy lands ~70% reliable in production.

Guaranteed structured extraction from unknown documents

Document type may be invoice / receipt / contract. Use tool_choice='any' with three extraction tools (one per type). Model picks the right one if descriptions are clear. 'auto' would return free text 30% of the time.

Show 1 more examples

Subagent must take action

Research subagent must call at least one tool (search / load / query). Use tool_choice='any' so empty-text returns become impossible. Subagent always emits a tool call before returning.

05 · Implementation

Code examples

Three modes side-by-side
from anthropic import Anthropic

client = Anthropic()

# Mode 1: 'auto' (default), model picks tool OR returns text
resp_auto = client.messages.create(
    model="claude-opus-4-5", max_tokens=512, tools=tools,
    tool_choice={"type": "auto"},
    messages=[{"role": "user", "content": "Help me with a refund."}],
)
# 30% chance: text only ("Sure, can you tell me your order number?")
# 70% chance: tool_use block with verify_customer or lookup_order

# Mode 2: 'any', model MUST call a tool, picks which one
resp_any = client.messages.create(
    model="claude-opus-4-5", max_tokens=512, tools=tools,
    tool_choice={"type": "any"},
    messages=[{"role": "user", "content": "Help me with a refund."}],
)
# 100% chance: tool_use block. Model picks (likely verify_customer first).

# Mode 3: forced, model MUST call this specific tool
resp_forced = client.messages.create(
    model="claude-opus-4-5", max_tokens=512, tools=tools,
    tool_choice={"type": "tool", "name": "verify_customer"},
    messages=[{"role": "user", "content": "Help me with a refund."}],
)
# 100% chance: tool_use block calling verify_customer.
Use forced for mandatory steps (verification before refunds). Use 'any' when a tool must be called but choice is open. Use 'auto' when text is a valid response.
06 · Distractor patterns

Looks right, isn't

Each row pairs a plausible-looking pattern with the failure it actually creates. These are the shapes exam distractors are built from.

01Use 'any' to guarantee deterministic
× Looks right
Use 'any' to guarantee deterministic output.
✓ What wins
'any' forces a tool call but the model still picks which one.

If two tool descriptions overlap, the wrong one fires. For true determinism, use forced ({type:'tool', name:X}).

02Forced tool_choice removes the need
× Looks right
Forced tool_choice removes the need for hooks.
✓ What wins
Forced ensures the tool is called, it does NOT validate inputs.

Add PreToolUse hooks for argument validation (e.g., refund amount within policy).

03Use 'any' or forced when
× Looks right
Use 'any' or forced when extended_thinking is on for safer behavior.
✓ What wins
Both modes fail validation when thinking is enabled.

Only 'auto' is compatible. If you need both thinking and a guaranteed tool call, drop thinking or accept text fallback.

04tool_choice: "any"
× Looks right
Set tool_choice: "any" once at session start and Claude will keep calling tools every turn.
✓ What wins
tool_choice is per-request, not per-session.

Each messages.create() call evaluates it independently. If turn 5 omits tool_choice, the default auto returns and Claude may emit text. Pin it on every call that needs the constraint, or wrap the SDK in a helper that injects it automatically.

05disable_parallel_tool_use: true
× Looks right
Setting disable_parallel_tool_use: true is unrelated to tool_choice.
✓ What wins
It's actually nested inside the tool_choice object: {type: "auto", disable_parallel_tool_use: true}.

The flag forces sequential tool calls, useful when downstream side effects must happen in order. Most exam-tested gotcha: developers expect a top-level parameter, never find it, and ship buggy parallel writes.

07 · Compare

Side-by-side

↔ scroll to compare
ModeBehaviorUse caseExtended thinking
auto (default)Model picks: tool OR textOpen-ended assistanceCompatible
anyMust call a tool; model picks whichSubagent must act; structured-only flowsIncompatible
{type:'tool', name:X}Must call this exact toolMandatory first steps (verify before refund)Incompatible
noneTools available but model returns textRare: only allow tools as fallbackCompatible
auto + disable_parallel_tool_usePicks 0 or 1 tool, never multiple in parallelSequential side-effect tools (writes, payments)Compatible
any + disable_parallel_tool_useMust call exactly one tool, no parallelismMandatory single action; no fan-outIncompatible
08 · When to use

Decision tree

01

Is a SPECIFIC tool mandatory (verify-before-refund, identity gate)?

YesUse forced: tool_choice={type:'tool', name:'X'}. Combine with PreToolUse hooks for argument validation.
NoGo to next question.
02

Must the model take SOME tool action (any tool)?

YesUse tool_choice={type:'any'}. Ensure tool descriptions disambiguate clearly.
NoUse tool_choice={type:'auto'}. Text fallback is acceptable.
03

Are you using extended_thinking?

YesOnly 'auto' is compatible. Drop forced/any or drop thinking.
NoAll modes available. Pick based on the policy questions above.
04

Do you need to prevent Claude from calling multiple tools in one turn?

YesAdd disable_parallel_tool_use: true to the tool_choice object. Critical when tool results have ordered side effects (payments, writes, sends).
NoLeave it off; parallel tool use cuts latency on independent reads.
05

Are you in an agentic loop where the FIRST turn must be a tool but later turns can return text?

YesPin forced or 'any' on the first call only; switch to 'auto' for subsequent turns. `tool_choice` is per-request, exploit the per-turn flexibility.
NoUse the same mode every turn for consistency.
09 · On the exam

Question patterns

Tool Choice exam trap, painterly cautionary scene featuring Loop mascot.

6 V2 questions wired to this concept. Tap an answer to check it instantly - you'll see whether it's right and why - then expand the full breakdown for the mental model and all four rationales.

Question 1 of 6 · D1Choose the best answer

A tool throws an exception inside your harness; on the next iteration Claude requests the same tool again with the same arguments. Why?

10 · FAQ

Frequently asked

Showing 10 of 10 questions

Help someone pass

Share this concept.

One share is one less person stuck on the same question.

Last reviewed: 2026-05-04·Refresh cadence: monthly