Tool Calling: Letting the Model Use a Tool
In Lesson 1 you learned that a tool is just a function the model is allowed to ask for. In this lesson you will see exactly how that asking works. This is the single most important mechanic in the whole course, because tool calling is what turns a model into an agent.
The good news: the model never runs your code. It only requests a tool by name, with arguments. Your Python code decides whether and how to run it. That separation is what keeps you in control of everything the agent can do.
What You'll Learn
- How you describe a tool to the model
- What a "tool call" looks like when the model asks for one
- The four-step round trip of a tool call
- Why your code, not the model, runs the tool
Step one: describe the tool
You tell the model what tools exist by passing a list of tool definitions. Each definition is a name, a plain-English description, and a schema for the inputs. The description is how the model decides when to use the tool, so write it like an instruction.
tools = [
{
"name": "calculator",
"description": "Evaluate a basic arithmetic expression. Use this for any exact math instead of calculating in your head.",
"input_schema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "An arithmetic expression, e.g. '35 * 52'."
}
},
"required": ["expression"]
}
}
]
That input_schema is just JSON Schema, a standard way to describe the shape of the arguments. Here it says: the tool takes one required string called expression.
Step two: the model asks for the tool
When you include tools in your call and the model decides it needs one, it does not reply with normal text. Instead its response carries a tool_use block, and response.stop_reason is "tool_use" instead of "end_turn".
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=500,
tools=tools,
messages=[
{"role": "user", "content": "What is 4891 times 736?"}
],
)
print(response.stop_reason) # "tool_use"
A tool_use block contains three things you care about:
name: which tool the model wants ("calculator")input: the arguments it chose ({"expression": "4891 * 736"})id: a unique id you will use to match your result back to this request
The model has not done the math. It has only handed you a request: "please run calculator with this expression."
Step three: your code runs the tool
Now your Python actually executes the function. You write the real implementation; the model never sees inside it.
def calculator(expression):
# A tiny, safe-enough calculator for digits and basic operators only.
allowed = set("0123456789+-*/(). ")
if not set(expression) <= allowed:
return "Error: expression contains unsupported characters."
return str(eval(expression)) # restricted to the allowed characters above
You find the tool the model asked for, run it with the arguments it chose, and capture the result. (We will harden this function against bad input in Lesson 5; for now focus on the round trip.)
Step four: hand the result back
The model is waiting for the answer. You send the conversation again, this time with two new messages appended: the assistant's tool_use request, and a tool_result carrying your output. The tool_use_id is what links your result to the right request.
messages = [
{"role": "user", "content": "What is 4891 times 736?"},
{"role": "assistant", "content": response.content}, # the tool_use request
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use_block.id,
"content": "3599776",
}
],
},
]
final = client.messages.create(
model="claude-opus-4-8",
max_tokens=500,
tools=tools,
messages=messages,
)
print(final.content[0].text) # "4,891 times 736 is 3,599,776."
Now the model has the real result and writes a normal text answer. stop_reason is back to "end_turn".
The whole round trip
Put together, one tool call is a four-step exchange between the model and your code:
- Describe toolsYou pass tool definitions
- Model requestsReturns a tool_use block
- You run itYour code executes the function
- Send tool_resultModel writes the answer
Compare this with a plain chat call, which has no middle steps:
A tool call adds a do-this-then-tell-me step in the middle of a normal chat.
| Criteria | Plain chat | Tool call |
|---|---|---|
| What the model returns | A text answer | A request to run a tool |
| Who acts next | Nobody, you are done | Your code runs the tool |
| How it ends | stop_reason end_turn | You send tool_result, then end_turn |
Plain chat
- What the model returns
- A text answer
- Who acts next
- Nobody, you are done
- How it ends
- stop_reason end_turn
Tool call
- What the model returns
- A request to run a tool
- Who acts next
- Your code runs the tool
- How it ends
- You send tool_result, then end_turn
Why the model does not run your code
This is the part beginners often miss, and it is the most important safety idea in agents. The model cannot reach into your computer. It can only emit a request: "run calculator with this input." Your code is the gatekeeper. You decide which tools exist, you validate the inputs, and you choose whether to run them.
That means a tool that sends an email, deletes a file, or spends money should be written so your code can refuse or ask for confirmation before running it. The model proposes; your code disposes.
Practice routing a tool call (runs in your browser)
You can practice the routing logic offline. Below, a fake response stands in for the model's tool_use request. Your job is the part that matters: read which tool was requested, run it, and produce the result string.
That if tool_call["name"] == ... routing is the core of every agent's tool handling. With more tools, it becomes a dictionary lookup, which you will see in the next lesson.
Key Takeaways
- You describe tools with a name, a description, and an
input_schema. The description is how the model decides when to use it. - When the model wants a tool, it returns a
tool_useblock (name, input, id) andstop_reasonis"tool_use". - Your code runs the function, then sends a
tool_resultwith the matchingtool_use_id. - The model then writes a normal answer using the real result.
- The model only requests tools; your code runs them. That separation is what keeps the agent safe and under your control.

