Tool Use Intro
What Tool Use Is
On its own, Claude can only work with what is in the conversation and what it learned during training. It cannot look up today's weather, query your database, check an order's status, or send an email.
Tool use (also called function calling) changes that. You describe tools, functions your application can run, and Claude can decide to call them when it needs information or wants to take an action. Your code runs the tool and sends the result back, and Claude uses the result to continue.
User: "Is it a good day for a walk in Chennai?"
Claude: (needs current weather) → calls get_weather({"city": "Chennai"})
Your code: runs get_weather → returns "31°C, humid, light rain expected at 5 PM"
Claude: "It's warm and humid, with rain expected around 5 PM, so a morning walk is best."
Claude never runs your code itself. It produces a structured request: the tool's name and its input. Your application decides whether and how to execute it. That separation keeps you in control of what actually happens.
Why Tool Use Matters
| Capability | Example tools |
|---|---|
| Access current or private data | Weather, stock prices, your product catalog, a user's orders |
| Take actions | Create a support ticket, send a notification, update a record |
| Calculate precisely | Exact arithmetic, date calculations, unit conversions |
| Connect to other systems | Search engines, internal APIs, databases |
| Build agents | Multi-step workflows where Claude chooses which tools to use, in which order |
Tool use turns Claude from a system that only answers into one that can act on your behalf, within the limits you define.
Client Tools and Server Tools
| Client tools | Server tools | |
|---|---|---|
| Who runs them | Your application | Anthropic's servers |
| Examples | Any function you define: get_weather, lookup_order | Web search, web fetch, code execution |
| What you do | Define the tool, run it, send back the result | Enable the tool in the request; results appear in the response |
| Covered in | Lessons 81–100 | Lesson 155 |
This section focuses on client tools: tools you define and run yourself.
How Tool Use Works: The Loop
Tool use is a conversation with extra steps:
┌─────────────────────────────────────────────────────────────────┐
│ 1. You send: messages + tool definitions │
│ 2. Claude replies with stop_reason = "tool_use" and tool_use │
│ blocks: [{id, name, input}, ...] │
│ 3. Your code runs each requested tool │
│ 4. You send back: Claude's reply + a user message containing │
│ tool_result blocks [{tool_use_id, content}, ...] │
│ 5. Claude continues: another tool call (back to step 2), │
│ or a final answer with stop_reason = "end_turn" │
└─────────────────────────────────────────────────────────────────┘
| Step | Lesson |
|---|---|
| Define tools and their input schemas | 82, 83 |
| Send tools with a request | 84 |
Read tool_use blocks | 85 |
| Run the matching Python function | 86 |
Return tool_result blocks | 87 |
| Loop until Claude finishes | 88 |
| Control whether and how tools are used | 89 |
| Handle failures | 90 |
The SDK also offers a tool runner that automates this loop (Lessons 91–92). Learning the manual loop first makes clear what the runner does for you.
The Building Blocks
A Tool Definition
weather_tool = {
"name": "get_weather",
"description": "Get the current weather for a city. Use it when the user asks about "
"current conditions or whether the weather suits an activity. "
"Returns temperature in Celsius and a short description.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. Chennai"},
},
"required": ["city"],
},
}
A Tool Call (in Claude's Response)
{"type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather",
"input": {"city": "Chennai"}}
A Tool Result (in Your Next Message)
{"type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "31°C, humid, light rain expected at 5 PM"}
The tool_use_id links each result to the call that requested it.
Practical Example: Your First Tool Call
This complete example defines one tool, lets Claude call it, runs a simple local implementation, sends the result back, and prints Claude's final answer.
"""first_tool.py: a complete tool use round trip."""
from anthropic import Anthropic
client = Anthropic()
MODEL = "claude-opus-5-5"
WEATHER = {
"chennai": "31°C, humid, light rain expected at 5 PM",
"bengaluru": "24°C, partly cloudy, no rain expected",
}
TOOLS = [{
"name": "get_weather",
"description": (
"Get the current weather for a city. Use it whenever the user asks about current "
"conditions, or whether the weather suits an activity. Returns the temperature in "
"Celsius and a short description. It does not provide forecasts beyond today."
),
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string", "description": "City name, e.g. Chennai"}},
"required": ["city"],
},
}]
def get_weather(city: str) -> str:
return WEATHER.get(city.strip().lower(), f"No weather data available for {city}.")
messages = [{"role": "user", "content": "Is it a good day for an evening walk in Chennai?"}]
# Step 1–2: send the question with the tool; Claude decides to call it
response = client.messages.create(model=MODEL, max_tokens=2048, tools=TOOLS, messages=messages)
print("stop_reason:", response.stop_reason)
# Step 3: run every requested tool
tool_results = []
for block in response.content:
if block.type == "tool_use":
print(f"Claude called {block.name}({block.input})")
result = get_weather(**block.input)
tool_results.append({"type": "tool_result", "tool_use_id": block.id, "content": result})
# Step 4: send Claude's reply and the results back
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
# Step 5: Claude continues with the tool's information
final = client.messages.create(model=MODEL, max_tokens=2048, tools=TOOLS, messages=messages)
print("stop_reason:", final.stop_reason)
print("".join(b.text for b in final.content if b.type == "text"))
Example output:
stop_reason: tool_use
Claude called get_weather({'city': 'Chennai'})
stop_reason: end_turn
It's warm and humid in Chennai (31°C), and light rain is expected around 5 PM, so an evening walk may get wet. If you can, go earlier in the afternoon, or take an umbrella.
How It Works
TOOLSdescribes one tool: its name, a detailed description of what it does and when to use it, and aninput_schema(a JSON Schema) for its input. Claude only sees this description, not your Python code.- The first request includes
tools=TOOLS. Claude reads the question, decides it needs current weather, and responds with atool_useblock.stop_reasonis"tool_use", which means Claude is waiting for your code. - The loop over
response.contentfinds everytool_useblock. For each one,get_weather(**block.input)runs the local function, using the input dictionary as keyword arguments. - Each result becomes a
tool_resultblock, withtool_use_idset to the call'sid. - The conversation continues: Claude's response is appended unchanged as an assistant message (it may include thinking blocks, Lesson 16), followed by a user message containing the tool results.
- The second request includes the same tools. Claude reads the result and writes its final answer, with
stop_reason"end_turn".
This example handles one round of tool calls. Lesson 88 builds a loop that handles any number of rounds.
Tool Use and Costs
Tools add input tokens to every request that includes them:
- Tool definitions (names, descriptions, schemas) are sent with each request.
- When tools are present, the API also adds a tool-use system prompt that explains how to call them.
- Tool calls are output tokens, and tool results are input tokens in the next request.
Use count_tokens() with your tools to measure the overhead (Lesson 44), and send only the tools a request needs.
Common Mistakes
Expecting Claude to Run the Code
What happens: developers wait for Claude to "execute" a function, but nothing happens.
Fix: your application runs tools. Claude only requests them.
Treating stop_reason == "tool_use" as a Final Answer
What happens: the user sees a short preamble, such as "I'll check the weather", instead of an answer.
Fix: when the stop reason is tool_use, run the tools and continue the conversation.
Mismatched tool_use_id Values
What happens: a 400 error, because results do not correspond to the calls.
Fix: set each result's tool_use_id to the id of the call it answers.
Modifying Claude's Tool-Use Response
What happens: 400 errors in tool-use conversations, because thinking blocks from the latest assistant turn must be returned unchanged.
Fix: append response.content exactly as received.
Vague Tool Descriptions
What happens: Claude calls the tool at the wrong times, or not at all.
Fix: describe what the tool does, when to use it, and what it returns (Lesson 82).
Best Practices
- Write detailed tool descriptions. They are the most important factor in tool performance.
- Keep your application in control: validate inputs and decide what tools may do.
- Return all tool results together in one user message.
- Pass Claude's responses back unchanged.
- Include the same tools in every request of a tool-use conversation.
- Measure token overhead, and send only relevant tools.
- Use the SDK's tool runner once you understand the manual loop (Lessons 91–92).
Exercise
- Ask
first_tool.pyabout Bengaluru instead of Chennai. How does the answer change? - Ask about a city that is not in
WEATHER. What does Claude say? - Ask a question that does not need weather, such as "What is a Python list?". Does Claude call the tool?
- Ask "Compare the weather in Chennai and Bengaluru." How many
tool_useblocks are in the first response?
Step 3: Claude answers directly, without calling the tool. The first response has stop_reason == "end_turn" and no tool_use blocks, so the loop finds nothing to run. Your code should check for this case: in first_tool.py, the second request would be unnecessary. Lesson 88 handles it properly.
Step 4: typically two tool_use blocks in the same response, one for each city. Claude makes parallel tool calls by default when a request benefits from several. The example's loop already handles this: it runs every call and returns all the results together in a single user message, which is the required format.