Overview
Tool use lets a model call functions your application (or the provider) executes rather than only generating text: you declare each tool's name, description, and input schema, the model decides mid-response whether a tool fits the request, and if so returns a structured tool-call block instead of (or alongside) prose. For a client tool, your own code executes the call and sends the result back in a follow-up request referencing the original call's ID, only then does the model produce a final answer grounded in the actual result. A server tool skips that entirely, running on the provider's own infrastructure so your application just sees the outcome. What's easy to miss is that every declared tool, called or not, adds real input tokens to every request that includes it, plus a fixed per-model overhead for enabling tool use at all.
Quick Reference
| Concept | What it means |
|---|---|
| Client tool | Your application executes it; you write the handler and error handling |
| Server tool | Provider's infrastructure executes it; you only declare it |
tool_choice: {"type": "auto"} | Model decides per turn whether to call a tool |
tool_choice: {"type": "any"} | Model must call some tool, any of them |
tool_choice: {"type": "tool", "name": "get_weather"} | Forces that exact named tool to be called |
tool_choice: {"type": "none"} | Model cannot call a tool |
| Tool round trip | Request → model returns tool call → your code executes → result sent back → model answers |
Syntax
tools = [{
"name": "get_weather",
"description": "Get the current weather for a given location.",
"input_schema": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
}]
response = client.messages.create(model="claude-sonnet-5", tools=tools, messages=messages)Examples
# The round trip: the model's tool call has to be executed by your
# code, then the result sent back before it can actually answer.
tool_use = next(b for b in response.content if b.type == "tool_use")
result = run_weather_lookup(tool_use.input["location"]) # your own code
messages += [
{"role": "assistant", "content": response.content},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": tool_use.id, "content": result}
]},
]
followup = client.messages.create(model="claude-sonnet-5", tools=tools, messages=messages)Declared tools cost tokens even when never called
Every tool's name, description, and schema in the request counts as input tokens on every single call, plus a fixed system-prompt overhead for enabling tool use at all. A large, rarely-used tool library has a real, continuous cost across every request, not just when a tool actually gets invoked.
Visual Diagram
Common Mistakes
- Assuming the model executes the tool itself; for client tools, your application code is entirely responsible for actually running the call.
- Declaring a large library of tools "just in case," paying their token cost on every request regardless of whether they're ever called that turn.
- Not handling the case where the model omits a required parameter, particularly on prompts too ambiguous to have supplied one.
- Treating
tool_choice: autoas a guarantee the model will call a tool when you expect it to; the boundary is steerable via the system prompt, not fixed.
Performance
- Every declared tool adds real input tokens on every request that includes it, whether or not it's called, this scales directly with the number and verbosity of tool definitions in play.
- Enabling tool use at all adds a fixed, model-specific system-prompt token overhead on top of whatever the tool definitions themselves cost.
- For applications with many available tools, loading only the tool definitions relevant to the current task (rather than every tool, every request) is a direct lever on both cost and latency.
Best Practices
- Write clear, specific tool names and descriptions; the model decides whether to call a tool based substantially on how well the description matches the request.
- Use
tool_choiceto force a specific tool when the application logic already knows one is needed, rather than relying onautoin situations where ambiguity is costly. - Prune or dynamically load tool definitions for applications with a large tool library, instead of declaring every tool on every request regardless of relevance.
- Design a clear error-signaling convention for tool results (success vs. failure) so the model can react appropriately rather than treating every result as a success.