How Claude chooses a tool
- You send toolsname, description,
input_schema - API builds the prompttool definitions + your system prompt
- Claude picks a toolby matching each described purpose
- Claude fills inputs
tool_useblock, per the schema
The description is the interface
When you pass tools to the Messages API, the API assembles a special system prompt from your tool definitions, your tool configuration and your own system prompt. With the default tool_choice of auto, Claude decides on each turn whether a request maps to a tool’s described capability. Nothing else about the tool — its code, its database, its author’s intentions — reaches the model. That is why Anthropic’s documentation calls detailed descriptions “by far the most important factor” in tool performance.
| Field | Required? | What it does for tool selection |
|---|---|---|
name | Yes | Must match ^[a-zA-Z0-9_-]{1,128}$. The first thing Claude — and tool search — scans |
description | Yes | What the tool does, when to use it, when not to, and what it returns |
input_schema | Yes | A JSON Schema for the inputs; each property can carry its own description |
input_examples | No | Schema-valid example inputs for complex or format-sensitive tools |
strict | No | true constrains Claude’s inputs to match the schema exactly |
The docs list what a good description covers, and recommend at least three to four sentences — more for a complex tool:
- What the tool does, in terms of the task rather than the implementation.
- When to use it — and when not to, naming the sibling tool to use instead.
- What each parameter means and how it changes the tool’s behaviour, including formats.
- Caveats and limits: scope, freshness, maximum sizes, permissions.
- What it returns — and what it does not, so Claude does not expect data the tool never supplies.
orders_lookup = {
"name": "orders_lookup",
"description": (
"Look up one customer order by its order number. Returns status, items, "
"carrier and tracking number. Use it when the customer gives an order "
"number or asks where a specific order is. Do not use it to find orders "
"by name or email: call crm_find_customer first to get order numbers. "
"It returns no payment or refund data; use billing_get_payment for that."
),
"input_schema": {
"type": "object",
"properties": {
"order_number": { # not "id" or "order"
"type": "string",
"description": "Order number from the receipt, e.g. A-1042.",
},
},
"required": ["order_number"],
"additionalProperties": False,
},
"input_examples": [{"order_number": "A-1042"}], # must be schema-valid
"strict": True, # inputs always match the schema
}Read the description as Claude would. It says what the tool is for, points away from two neighbouring jobs by name, and warns what is missing from the result. Anthropic’s engineering team suggests writing it the way you would brief a new hire: make explicit the context you carry in your head — query formats, in-house terminology, how records relate to each other.
Why overlapping tools misroute
Misrouting rarely comes from a single bad description. It comes from two descriptions that could both honestly answer the same request. Given “Searches documents” and “Finds records matching a query”, Claude has no basis for choosing, so surface wording in each request tips it one way or the other — and the behaviour looks random. Anthropic’s troubleshooting guide puts the fix precisely: differentiate tools by when to use them, not only by what they do.
Overlap versus clear boundaries
Before: overlapping
search_documents
"Searches documents."
find_records
"Finds records matching a query."After: distinct jobs
contracts_search_clauses
"Searches clauses in contracts the
firm has signed or drafted. Input:
client_name, optional clause_type.
Returns clause text, document, page.
Not for court filings: use
court_filings_search."
court_filings_search
"Searches public court dockets by
case_number or party name. Returns
filing title, date and docket entry.
Not for the firm’s own contracts:
use contracts_search_clauses."Where to draw the line: split or consolidate
Two pieces of Anthropic advice look contradictory at first. The tool-use docs say to consolidate related operations — rather than create_pr, review_pr and merge_pr, offer one tool with an action parameter. The engineering post goes further: a schedule_event tool that finds availability and books the meeting beats separate list_users, list_events and create_event tools. Yet a task statement about clear boundaries often points the other way: splitting a generic tool into purpose-specific ones. Both are true, because the unit is neither “an endpoint” nor “a verb”. It is one distinct purpose with a predictable contract.
| Signal in the transcripts | What it means | Move |
|---|---|---|
| One tool’s output shape changes with how the request is phrased | It is hiding several jobs behind one name | Split into purpose-specific tools, each with a defined input and output |
| Claude always chains the same three calls to finish one task | The task is one job cut into pieces | Consolidate into one workflow tool |
| Several tools differ only by a verb on the same resource | Near-synonyms Claude must choose between | Consolidate behind an action enum |
| Two tools could both answer the same request | Their boundary is undefined | Rename and rewrite both descriptions by when to use them |
Which fix does this tool set need?
- Two tools fit one requestRename and differentiatedescribe when, and when not
- One tool, many output shapesSplit by purposeone contract per tool
- Same chain of calls every timeConsolidate the workflowone tool per real task
- Right tool, malformed inputsFix the parametersclear names, examples,
strict
Parameters are part of the boundary
Choosing the right tool is half the job; calling it with the right inputs is the other half. Anthropic’s advice is concrete. Name inputs unambiguously — user_id, not user. Give every property a description with its format and an example. Use an enum when the set of values is closed. For inputs whose shape is hard to convey in prose, add input_examples: each example must validate against the schema or the request fails with a 400, and each costs prompt tokens, so keep them for tools that need them.
Better still, design inputs so the wrong call is impossible — the “poka-yoke” idea from Anthropic’s Building effective agents. Anthropic’s SWE-bench agent kept making mistakes with relative file paths after changing directory; requiring absolute paths removed the whole class of error. Setting strict: true goes one step further for types: Claude’s inputs are constrained to match the schema, so you get 2, not "two".
When the system prompt fights the description
Your system prompt and your tool definitions end up in the same prompt, and the system prompt can steer tool use — the docs note that a light instruction such as “use the tools to investigate before responding” increases tool calls. That power cuts both ways. A rule keyed to a word, such as “whenever the customer mentions a refund, call billing_issue_refund”, fires on the word, not the intent: “what is your refund policy?” now triggers a refund attempt, however carefully the tool’s description says it is only for approved refunds.
- Search the system prompt for tool names and trigger words (“whenever”, “if the user mentions”, “always call”).
- For each, ask whether it restates a boundary the description should own. If so, move it into the description and delete the rule.
- Keep system-prompt guidance at the level of intent and policy — for example, that refunds need a verified order and a manager’s approval — not keyword routing.
- Re-test with requests that contain the trigger word but not the intent.
A related boundary: keep your own instructions out of tool results. Claude treats instructions that arrive inside a tool_result as potentially untrusted third-party content, so a tool that returns “now call X” may be ignored or questioned. Tools should return data; instructions belong in the prompt.
Boundaries at scale: names, namespaces and tool search
Selection gets harder as the tool set grows: Anthropic’s docs say accuracy degrades once more than 30–50 tools are loaded at once. The first defence is namespacing — prefix each tool with its service or resource (github_list_prs, slack_send_message, asana_projects_search) so the name alone says which system it touches. The engineering post adds that prefix versus suffix naming has measurable effects, so test your choice.
The second defence is tool search. With defer_loading: true, a tool’s full definition stays out of context until Claude finds it with a search over tool names, descriptions, argument names and argument descriptions. That makes wording matter even more: the Agent SDK docs note that a name like search_slack_messages surfaces for more requests than query_slack, and a description with specific keywords beats a generic one.
Every layer of a tool’s boundary
From what Claude reads to what your system enforces
- Namenamespaced, specific, searchable
- Descriptiondoes, when, when not, returns
- Input schemaclear names, enums, examples,
strict - Annotations
readOnlyHintand others — hints only - Handler and permissionsvalidates inputs, enforces access
Tools from MCP servers follow the same rules. An MCP tool declares a name, a description and an inputSchema, and may add an outputSchema and behavioural annotations such as readOnlyHint and destructiveHint. The MCP specification says clients must treat annotations as untrusted unless the server is trusted, and the Agent SDK docs call them metadata, not enforcement. In the Agent SDK, a tool from a server registered as crm appears to Claude as mcp__crm__<tool> — the server name becomes a namespace for free, but the tool’s own name and description still have to do the work.
Traps the wrong answers are built from
| Tempting but wrong | Do this instead |
|---|---|
| One-line descriptions such as “Gets the stock price” | Three to four sentences or more: what it does, when and when not to use it, parameters, limits, what it returns. |
| Two tools whose descriptions both fit the same request | Differentiate by when to use each, rename to show scope, and point each to its sibling. |
| A generic tool whose output depends on how the request is phrased | Split it into purpose-specific tools, each with a defined input and output contract. |
| Patching misrouting with keyword rules or routing examples in the system prompt | Fix the tool definitions, and remove keyword triggers that override them. |
| Wrapping every API endpoint as its own tool | Design tools around the agent’s tasks; consolidate steps that always run together. |
Treating strict or annotations as the boundary | They shape inputs and describe behaviour; enforce access in the handler and permissions. |
You should now be able to
- Write tool descriptions that state purpose, when and when not to use, inputs, limits and outputs.
- Diagnose misrouting between tools with overlapping descriptions and fix it by renaming and rewriting.
- Decide whether a tool set needs splitting into purpose-specific tools or consolidating into workflow tools.
- Design parameters with unambiguous names, formats, enums and examples, and know what
strictguarantees. - Find system-prompt instructions keyed to words that override well-written tool descriptions.
- Use namespacing and tool search so large tool sets stay selectable.