The model chooses a tool from its description alone, and every declaration is billed on every request whether or not it is called. Assume any tool with a side effect will be invoked twice.
The description is what the model reads when choosing
A tool declaration has a name, a description and a schema. Your code reads the schema. The model reads the description.
Anthropic states the selection rule directly: "Claude determines when to call a tool based on the user's request and the tool's description." Nothing else about your tool is visible at the moment of choosing. Not the implementation, not the comments, not what the endpoint is called internally.
That reframes what a description is for. It is not documentation for whoever maintains the code. It is the input to a decision, and it should answer the question the model is asking: given this request, is this the thing to call?
// Written for a maintainer. The model cannot tell when to use it.
const vague = { name: 'lookup', description: 'Queries the orders service.' }
// Written for selection. Says what it answers and when.
const useful = {
name: 'get_order_status',
description:
'Get the current status and delivery estimate for one order by its ID. ' +
'Use when a customer asks where an order is or when it will arrive. ' +
'Does not cover refunds or returns.',
}
The second one costs more tokens and is worth them, for reasons the next section makes arithmetic.
What a tool set costs on every request
Tool definitions are input. Names, descriptions and schemas go in the tools
parameter and are billed on every request that carries them, not once at setup.
There is a second, less obvious cost. Enabling tools at all adds a system prompt
the API inserts for you, and Anthropic publishes its size per model: between 286
and 804 tokens depending on the model and the tool_choice setting. That is the
floor before any of your own declarations.
So a set of twelve tools at roughly 100 tokens each is about 1,200 tokens plus the overhead, on every single request, whether or not any tool is called. On a high-traffic feature that is a real line on the bill, and prompt caching is the obvious answer, which in turn is a reason to keep the tool list stable between requests rather than assembling it dynamically.
Sizing the set, and what to do when it is large
Tokens are the measurable cost. The harder cost is selection accuracy: every additional option is another way to pick wrong.
For small sets, the fix is editorial. Merge tools that answer the same question, delete the ones nobody has called, and make the boundaries between the survivors explicit in their descriptions, including what each one does not cover.
For large sets the problem is recognized enough to have tooling built for it.
Anthropic ships a tool_search server tool to "work with thousands of tools by
discovering and loading them on demand", which is worth reading as evidence
rather than as a product note: if loading every tool into every request worked,
nobody would need it.
The middle ground is selection in your own code. MCP host covers deciding which of several servers' tools to put in front of the model, and the same reasoning applies to your own: filter by the task at hand rather than presenting everything you have.
One parameterized tool or several specific ones
A single search tool with a type parameter, or search_orders,
search_articles and search_customers.
Specific tools make selection easier and the set larger. The model chooses between names that mean different things, which is the choice it is good at.
A parameterized tool keeps the set small and moves the choice into an argument. The model now has to get both the tool and the parameter right, and a wrong enum value is harder to notice than a wrong tool name.
The tiebreaker is whether the variants share a shape. If search_orders and
search_articles take different filters and return different fields, forcing
them into one schema produces a tool where half the parameters are irrelevant
for any given call, and irrelevant parameters are exactly what gets filled in
with plausible nonsense. Which brings us to the next point.
Assume every tool will be called twice
Two documented behaviors combine into one design rule.
The first is invention. If a request does not contain enough information to fill
a required parameter, the model may ask, and it may not: "Claude Sonnet might
ask, especially when prompted to think before outputting a tool request. But it
might also infer a reasonable value." The reference shows a weather call with a
unit nobody specified. Adding strict: true guarantees the call "always match
your schema exactly", which constrains the shape and does nothing about a
fabricated value of the correct type.
The second is repetition. Agents retry. Loops re-run. Manual implementation returns tool errors to the model precisely so it can try again, and parallel tool use means two calls can be in flight at once.
So: validate arguments in your code rather than trusting the schema, and make
anything with a side effect safe to call twice. Accept an idempotency key on
writes, or make the operation naturally repeatable. A send_email tool called
twice because the model did not see the first result is a bug your users
experience, and no amount of prompt wording prevents it.
Further reading
- Anthropic, Tool use with Claude: the selection rule, the token overhead table, strict tool use, and the missing-parameter behavior.
- Function calling: the request and response shape of a single call.
- Manual implementation: the loop these tools run inside.
- MCP host: choosing which tools to expose when several sources each offer many.
Knowledge check
Question 1 of 4
Sign in to save your progress and pick up where you left off.