Control Tool Choice

Su Suriya Ravichandran Updated 10 Nov 2026
9 min read ·Lesson 89 of 200

What Tool Choice Controls

When you send tools, Claude normally decides for itself whether to call them (Lesson 84). The tool_choice parameter lets you control that decision:

response = client.messages.create(
    model="claude-haiku-5-5",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "auto"},
    messages=[{"role": "user", "content": "What's the weather in Chennai?"}],
)

There are four options:

tool_choiceBehavior
{"type": "auto"}Claude decides whether to call tools, and which. The default when tools are provided.
{"type": "any"}Claude must call one of the provided tools, but chooses which.
{"type": "tool", "name": "get_weather"}Claude must call this specific tool.
{"type": "none"}Claude must not call any tool. The default when no tools are provided.

any and tool are called forced tool use, because they force a tool call.


Model Support for Forced Tool Use

Not every model supports forced tool use. According to the current documentation:

Model or settingauto and noneany and tool
Claude Opus 5.5, Claude Sonnet 5.5, Claude Fable 5.1, Claude Mythos 5.1SupportedNot supported: the request fails with a 400 error
Models with adaptive thinking on by default, such as Claude Opus 5 and Claude Haiku 5.5SupportedSupported. A forced call skips thinking: the response starts with the tool call, with no thinking block.
Any model with manual extended thinking enabled (thinking={"type": "enabled", ...})SupportedNot supported

This matters because many tutorials use tool_choice={"type": "any"} or {"type": "tool", ...}. On the current flagship models, that code fails.

What to Use Instead on Models Without Forced Tool Use

The documentation recommends:

GoalAlternative
Make Claude call a toolUse auto and instruct Claude to use the tool in the user message, for example "Use the get_weather tool in your response."
Guarantee valid tool inputsSet strict: true on the tool definitions (Lesson 83)
Get a response in a fixed JSON shapeUse structured outputs (Lesson 36) instead of a forced "extraction tool"

Prompting is a strong influence on which tool auto picks, but it is not a hard guarantee. Your code should still check whether the response actually contains the expected tool call.


auto: The Default

With auto, Claude calls tools when they help and answers directly when they do not. That is the right choice for most conversational applications and agents (Lesson 88). Guide its decisions with tool descriptions and the system prompt (Lessons 82 and 84).

Disabling Parallel Tool Calls

By default, Claude may call several tools in one response. To limit that, set disable_parallel_tool_use: True inside tool_choice (it is not a top-level parameter):

tool_choice={"type": "auto", "disable_parallel_tool_use": True}
tool_choice typeEffect of disable_parallel_tool_use: True
autoAt most one tool call per response; Claude can still answer without tools
any or tool (where supported)Exactly one tool call per response

Use it when tools must run strictly one at a time, for example when each step depends on the previous one and you want Claude to see each result before deciding the next call. For independent calls, leave parallel use enabled: it saves rounds and tokens.


any and tool: Forcing a Call (Where Supported)

On models that support forced tool use, such as Claude Haiku 5.5:

response = client.messages.create(
    model="claude-haiku-5-5",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "get_weather"},
    messages=[{"role": "user", "content": "Chennai"}],
)

Important behaviors from the documentation:

  • No text before the call. With any or tool, the API prefills the assistant message to force a tool call, so the response contains the tool_use block without a preamble, even if asked for one.
  • No thinking. A forced call skips thinking: the response starts with the tool call.
  • Combine with strict: true to guarantee both that a tool is called and that its input matches the schema.

If you want Claude to explain or think before calling a specific tool, use auto with an instruction in the user message instead.


none: Tools Defined, but Not Used

{"type": "none"} prevents tool calls even though tools are included in the request. This is useful when:

  • you want a summary turn at the end of an agent run, without new tool calls, while keeping the tools in the request so the conversation history remains consistent (Lesson 88);
  • you want to keep the same tool definitions in every request for prompt caching, while temporarily disabling tools for some turns.

Note: with prompt caching, changing tool_choice between requests invalidates cached message blocks. Tool definitions and the system prompt remain cached, but the messages must be reprocessed (Lessons 121–130).


Practical Example: Requiring a Tool Call on Any Model

This helper makes Claude call a tool, using forced tool choice where the model supports it, and the documented alternative (an explicit instruction with auto and strict tools) where it does not. It then verifies that the expected tool was actually called.

"""require_tool.py: get a tool call reliably, on models with and without forced tool use."""

import anthropic
from anthropic import Anthropic

client = Anthropic()

# Models that reject tool_choice "any" and "tool" (per the current documentation)
NO_FORCED_TOOL_CHOICE = {"claude-opus-5-5", "claude-sonnet-5-5", "claude-fable-5-1", "claude-mythos-5-1"}

WEATHER_TOOL = {
    "name": "get_weather",
    "description": (
        "Gets the current weather for a city. Use it for questions about current conditions. "
        "Returns temperature in Celsius and a short description. It does not provide forecasts."
    ),
    "strict": True,
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string", "description": "City name, e.g. Chennai"}},
        "required": ["city"],
        "additionalProperties": False,
    },
}


def require_tool_call(model: str, question: str, tool: dict):
    """Return (response, method). The response should contain a call to `tool`."""
    request = {"model": model, "max_tokens": 2048, "tools": [tool]}

    if model not in NO_FORCED_TOOL_CHOICE:
        request["tool_choice"] = {"type": "tool", "name": tool["name"]}
        request["messages"] = [{"role": "user", "content": question}]
        return client.messages.create(**request), "forced tool_choice"

    instruction = f"Use the {tool['name']} tool in your response."
    request["tool_choice"] = {"type": "auto"}
    request["messages"] = [{"role": "user", "content": f"{question}\n\n{instruction}"}]
    return client.messages.create(**request), "auto + instruction"


def called(response, tool_name: str) -> list[dict]:
    return [b.input for b in response.content if b.type == "tool_use" and b.name == tool_name]


if __name__ == "__main__":
    question = "Should I carry an umbrella in Chennai this evening?"
    for model in ["claude-haiku-5-5", "claude-opus-5-5"]:
        response, method = require_tool_call(model, question, WEATHER_TOOL)
        calls = called(response, "get_weather")
        blocks = [b.type for b in response.content]
        print(f"{model:<18} via {method:<20} calls={calls} blocks={blocks}")

    try:
        client.messages.create(
            model="claude-opus-5-5", max_tokens=1024, tools=[WEATHER_TOOL],
            tool_choice={"type": "any"},
            messages=[{"role": "user", "content": question}],
        )
    except anthropic.BadRequestError as error:
        print(f"\nForced tool_choice on claude-opus-5-5 -> HTTP {error.status_code} (not supported)")

Example output:

claude-haiku-5-5   via forced tool_choice   calls=[{'city': 'Chennai'}] blocks=['tool_use']
claude-opus-5-5    via auto + instruction   calls=[{'city': 'Chennai'}] blocks=['thinking', 'text', 'tool_use']

Forced tool_choice on claude-opus-5-5 -> HTTP 400 (not supported)

How It Works

  1. NO_FORCED_TOOL_CHOICE lists the models that the documentation says reject any and tool. Keep this list in one place, and update it when you adopt new models (check each model's documentation).
  2. On supported models, the helper sets tool_choice={"type": "tool", "name": ...}. The response contains only the tool call: no thinking block and no preamble, exactly as documented for forced calls.
  3. On other models, the helper uses auto and appends an explicit instruction to the user message, the documented alternative. Claude thinks, writes a short preamble, and calls the tool.
  4. strict: True with "additionalProperties": False guarantees that, whenever the tool is called, its input matches the schema (Lesson 83).
  5. called() checks the response for the expected tool. With auto, a call is very likely but not guaranteed, so production code should handle the case where calls is empty, for example by retrying once with a firmer instruction, or by continuing without the tool.
  6. The final block shows the failure mode: sending tool_choice={"type": "any"} to Claude Opus 5.5 raises anthropic.BadRequestError (HTTP 400).

Choosing the Right Option

SituationRecommended setting
Chat assistants and agentsauto (the default)
One action at a time, each depending on the previous resultauto with disable_parallel_tool_use: True
A specific tool must be called, on a model that supports forcingtool (with strict: true)
A specific tool must be called, on Opus 5.5, Sonnet 5.5, Fable 5.1, or Mythos 5.1auto + an explicit instruction + strict: true, then verify
You need a fixed JSON output, not an actionStructured outputs (Lesson 36), not a forced tool
Tools stay defined, but this turn must not call themnone

Common Mistakes

Using Forced Tool Choice on Models That Reject It

What happens: a 400 error on Claude Opus 5.5, Sonnet 5.5, Fable 5.1, or Mythos 5.1.

Fix: use auto with an instruction and strict tools, or structured outputs.

Forcing Tools for Structured Output

What happens: older "fake extraction tool" patterns break on current models, or skip thinking unnecessarily.

Fix: use structured outputs for fixed JSON shapes.

Putting disable_parallel_tool_use at the Top Level

What happens: the request fails, because the field belongs inside tool_choice.

Fix: tool_choice={"type": "auto", "disable_parallel_tool_use": True}.

Expecting Explanations with Forced Calls

What happens: no preamble text appears, even when the prompt asks for one.

Fix: use auto with an instruction if you want text or thinking before the call.

Assuming auto + an Instruction Always Calls the Tool

What happens: code that reads response.content[...] for a tool call fails on the rare response without one.

Fix: check for the tool call, and handle its absence.


Best Practices

  • Use auto by default, and guide tool use with descriptions and the system prompt.
  • Know your model's tool-choice support, and keep that knowledge in one place.
  • Prefer instructions plus strict: true over forced tool choice on current flagship models.
  • Use structured outputs for fixed JSON responses.
  • Disable parallel calls only when steps truly depend on each other.
  • Use none to pause tool use without changing the tool list.
  • Always verify that the response contains the tool call your code expects.

Exercise

  1. Run require_tool_call() with claude-sonnet-5-5. Which method does it use?
  2. Send a request to Claude Opus 5.5 that asks about the weather in two cities, with tool_choice={"type": "auto", "disable_parallel_tool_use": True}. How many tool calls does the first response contain?
  3. Extend require_tool_call() to retry once with a firmer instruction when the auto path returns no call to the expected tool.
  4. Explain why structured outputs are a better fit than a forced tool when you only need Claude to return data in a fixed JSON format.
Show answers for steps 2 and 4

Step 2: at most one tool call, for one of the cities. After you return its result, Claude makes the second call in the next round. Disabling parallel use trades extra rounds for strictly sequential calls.

Step 4: a tool call represents an action your application performs, and requires the full tool-use flow: reading the call, returning a result, and continuing. When you only need data in a fixed shape, structured outputs return it directly as the response, guaranteed to match your schema, in a single request, without fake tools or extra rounds. They also work on every current model, including those that reject forced tool choice.

0 Comments

Reviewed before they appear

No comments yet.

Anthropic SDK Using Python
Anthropic SDK Intro Claude API Intro SDK Requirements Install Anthropic SDK Upgrade the SDK Create an API Key Set API Keys Use Environment Variables Import Anthropic Create a Client Send Your First Message Choose a Claude Model Set Max Tokens Read a Response Print Response Text Understand Message Blocks Use System Prompts Send User Messages Set Temperature Handle Stop Reasons Messages API Intro Create Messages Use Message Roles Send Conversation History Continue a Conversation Build a Chat Loop Reset Chat History Use Multiple Turns Inspect Message IDs Read Usage Data Write Clear Prompts Set a System Prompt Add Context to Prompts Use Prompt Templates Add Examples to Prompts Request JSON Output Use XML Tags Control Response Length Improve Prompt Quality Avoid Prompt Injection Read Text Blocks Read Content Blocks Check Stop Reasons Count Input Tokens Count Output Tokens Count Tokens Before Sending Limit Output Tokens Use Stop Sequences Read Request IDs Parse API Responses Streaming Intro Stream Text Output Use Stream Events Use Stream Helpers Get Final Messages Stream Async Responses Handle Stream Errors Build a Live Chat Stream Long Responses Close Streams Safely Build a Chatbot Keep Chat History Manage Chat Sessions Add Chat Memory Build a CLI Chatbot Build a Web Chatbot Use Flask with Claude Use FastAPI with Claude Return JSON from an API Add Chat Error Messages Async SDK Intro Create an Async Client Send Async Messages Use asyncio Run Parallel Requests Stream Async Text Handle Async Errors Set Async Timeouts Close Async Clients Choose Sync or Async Tool Use Intro Define a Tool Create Tool Schemas Send Tools to Claude Read Tool Calls Run Python Tools Return Tool Results Continue After Tool Use Control Tool Choice Handle Tool Errors Use the beta_tool Decorator Create a Tool Runner Add Tool Docstrings Validate Tool Inputs Use Multiple Tools Limit Tool Calls Log Tool Activity Build a Weather Tool Build a Search Tool Build a Calculator Tool Structured Output Intro Request JSON Data Validate JSON Output Use Pydantic Models Handle Invalid JSON Extract Fields from Text Generate Typed Data Use Image URLs Send Base64 Images Read PDFs with Claude Use Output Schemas Build Data Extraction Test Structured Responses Send Image Inputs Upload Files Use File IDs Process Text Files Summarize Documents Extract Table Data Handle File Errors Read Cache Usage Reduce Repeated Input Costs Debug Cache Misses Prompt Caching Intro Enable Prompt Caching Cache System Prompts Cache Long Context Test Cache Performance Use Cache Breakpoints Cache Best Practices Message Batches Intro Create a Message Batch Set Batch IDs Check Batch Status List Message Batches Get Batch Results Process Batch Outputs Handle Failed Batch Items Use Batches for Bulk Tasks Compare Batches and Requests Handle API Errors Handle Rate Limits Retry Failed Requests Set Request Timeouts Handle Network Errors Use Exponential Backoff Build Reliable API Calls Use Extended Thinking Log API Errors Track Request IDs Paginate API Results Set Thinking Budgets Read Thinking Blocks Use Tool Search Use Web Search Tools Use Computer Tools Use Citations Use Token Counting Read Raw Responses Set Custom Headers Use Claude on AWS Bedrock Use Claude on Vertex AI Use Claude on Microsoft Foundry Configure Cloud Credentials Choose a Cloud Provider Compare API Integrations Configure Provider Clients Handle Cloud Errors Use Cloud SDK Extras Deploy Cloud Apps Protect API Keys Use a .env File Ignore Secrets in Git Validate User Input Reduce Prompt Injection Limit Tool Permissions Avoid Unsafe Tool Calls Sanitize Tool Results Set Usage Limits Review Security Logs Test SDK Functions Mock API Responses Test Error Handling Test Streaming Code Measure Token Usage Track API Costs Configure Logging Deploy with Docker Deploy a FastAPI App Monitor Production Apps Build a Claude Chatbot Build a Text Summarizer Build a Code Explainer Build a JSON Extractor Build a PDF Assistant Build a Streaming Chat App Build a Tool-Calling Agent Build a Batch Processor Build a Research Assistant Build a Claude API Dashboard
Ask about this post
AI Ask about this post

Ask questions about Control Tool Choice and get answers drawn from it.

Signed-in readers only.