Claude misreads a tool mainly when the tool’s own description leaves a decision up to guesswork: when to call it, what a parameter means, or what it won’t do. Anthropic’s own documentation for the Claude API calls the description “by far the most important factor in tool performance,” ahead of the JSON schema itself. The fix is almost never more code; it’s a more complete sentence.
Why the description carries more weight than the schema
A tool definition gives Claude two different kinds of information: a JSON schema that constrains the shape of the input, and a natural-language description that explains the meaning behind that shape. The schema can reject a malformed call, but it can’t tell Claude when to call the tool or why one parameter exists instead of another. That judgment call is made entirely from the description, which is why Anthropic’s guide to defining tools asks developers to “aim for at least 3â4 sentences for each tool description, more if the tool is complex.”
Most tool definitions that misfire in production aren’t failing because of a bad schema. They’re failing because the description reads like a code comment instead of an explanation a new teammate could follow without seeing the implementation.
The commented checklist
Run every tool definition through these seven checks before you ship it. Each one is followed by the reason it matters, not just the rule.
- 1. Does the name declare its namespace? Prefix names like
github_list_prsorzendesk_create_ticketinstead of generic verbs likecreateorlist. Anthropic’s docs note this “makes tool selection unambiguous as your library grows, and is especially important when using tool search” â once an agent has thirty tools, two calledsearchare a coin flip. - 2. Does the description say what the tool does, in one clear sentence? Not what it’s named, what it returns. “Retrieves the current stock price for a given ticker symbol” tells Claude the output type before it reads another word.
- 3. Does the description say when to use it and when not to? A tool that only covers US exchanges should say so. Leaving the boundary unstated means Claude finds it by trial and error, in front of a user.
- 4. Does every parameter have its own description, with an example value? A bare
{"type": "string"}for a field liketickerleaves Claude to infer the format. “The stock ticker symbol, e.g. AAPL for Apple Inc.” removes the guess. - 5. Are constrained values declared with
enuminstead of described in prose? If astatusfield only ever takes three values, put those three values in the schema. Prose lists drift out of sync with the backend; an enum can’t. - 6. Does the tool ask for data, not reasoning? A parameter that asks Claude to explain its chain of thought, rather than state a short justification or the evidence it found, risks a
refusalwith the stop reasonreasoning_extraction. Ask “what did you find,” not “how did you decide.” - 7. Could this tool be merged with three siblings instead of standing alone? Separate
create_ticket,update_ticket, andclose_tickettools triple the number of names Claude has to disambiguate. Onemanage_tickettool with anactionenum cuts that back to a single decision: which action.
A before-and-after rewrite
Here’s a support-ticket tool that passes its own tests but still gets called at the wrong moments, next to a version rewritten against the checklist above. Neither example is copied from Anthropic’s documentation; they’re written to show the same failure pattern in a different tool than the one the docs use.
Before
create_ticket â description: “Creates a support ticket.” Parameters: subject (string), body (string), priority (string).
This passes schema validation on every call. It also gets invoked when a user is only asking how to file a ticket, because nothing in the description tells Claude the tool is for filing one, not explaining the process. priority has no enum, so Claude has produced “urgent,” “Urgent,” and “P1” for the same situation across different conversations â three strings a downstream system now has to normalize.
After
zendesk_create_ticket â description: “Files a new support ticket in Zendesk on the user’s behalf. Use this only when the user has described a concrete problem and confirmed they want it filed; do not use it to explain what a ticket is or how filing works. Returns the new ticket’s ID and a link to it. This tool cannot update or close an existing ticket â use zendesk_manage_ticket for that.” Parameters: subject (string, “a short summary a support agent would see first, e.g. ‘Checkout button not responding on mobile'”), body (string, “the full description of the problem, in the user’s own words where possible”), priority (string, enum: low, normal, high, urgent, “urgent means the user is blocked right now with no workaround”).
Nothing about the input shape changed. What changed is that a developer reading only the description â no code, no docstring elsewhere â now knows exactly when to reach for this tool, what each field is for, and where the boundary with a neighboring tool sits.
Steering with tool_choice instead of hoping the description is enough
A well-written description still leaves Claude deciding, each turn, whether to call a tool at all. The tool_choice parameter overrides that judgment call when you need to. {"type": "auto"} is the default: Claude decides. {"type": "any"} forces a tool call but lets Claude pick which tool. {"type": "tool", "name": "..."} forces one specific tool. {"type": "none"} blocks tool use entirely for that turn.
Two caveats are easy to miss. First, on several current models â Claude Opus 5.5, Claude Sonnet 5.5, and Claude Fable 5.1 among them â any and tool return a 400 error; Anthropic’s guidance for those models is to combine auto with strict tool use instead. Second, forcing a tool with any or tool prefills the assistant turn, so Claude emits no explanatory text before the call â if you want both a forced call and a sentence of context, prompt for it in the user turn instead (“use the get_weather tool in your response”), rather than relying on tool_choice to produce it.
What strict mode fixes, and what it doesn’t
Setting "strict": true on a tool definition guarantees the input matches your schema exactly â it closes the gap the documentation flags elsewhere: that Claude may add a parameter you didn’t ask for, such as defaulting unit to "fahrenheit" when a prompt didn’t specify one. Strict mode stops that drift at the schema level. It does not stop Claude from calling the right tool at the wrong moment, or the wrong tool at the right one â that’s still entirely a function of how well the description draws the boundary, which is the checklist’s job, not the schema’s.
Auditing an existing library without an eval suite
You don’t need a benchmark to find the descriptions that are causing trouble; you need three real transcripts where the agent picked the wrong tool, called the right tool at the wrong moment, or sent a parameter value nobody expected. Pull up each one and read only the tool definition involved, not the code that implements it, as if you were seeing the library for the first time.
Three questions tend to surface the fix fast. Could a reasonable person, reading just this description, know not to call it here? If the answer is no, that’s a missing “when not to use this” sentence, not a model problem. Does the parameter that got the bad value have an example or an enum next to it? If it only has a bare type, that’s check 4 or 5 from the list above. And is there a second tool in the library whose description overlaps with this one closely enough that a human skimming both would also hesitate? If so, the fix is usually check 7: merge them into one tool with an action parameter, rather than writing an even more detailed description to keep them apart.
This audit is also where namespacing earns its keep as your tool count grows. Anthropic’s tool search feature lets Claude look up relevant tools by name and description rather than holding every definition in context at once, but it can only do that well if names already sort themselves by service â github_list_prs and github_create_pr read as a family at a glance, where list_prs and create_pr next to a dozen other bare verbs don’t.
None of this requires re-running an eval suite to notice a problem in the first place. If two tools in your library keep getting swapped, or one tool keeps firing on requests it shouldn’t, read its description out loud without the code behind it. If a question is left unanswered, that’s the line to rewrite.
For the mechanics of how Claude emits a call once it decides to make one, see what function calling actually is, and for how the same decision plays out differently across vendors, how Claude, OpenAI, and Gemini each define function calling and how each one handles calling several tools in the same turn. If your tools return data in more than one shape, choosing between structured output modes covers the related decision of constraining the output instead of the tool call. And if you’re packaging a set of tools and instructions together rather than defining one at a time, building your first Claude Skill walks through that structure.



