# Tool use and structured outputs
A model that can only emit text is a dead end the moment you need it to check a live database, hit an APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition →, or hand clean JSON to the next service in your pipelinepipelineAll active sales opportunities across the stages of the sales process, together with their combined potential value and probability of closing.View full definition →. Tool use and structured outputs are how you close that gap with Claude: you describe capabilities, Claude decides when to invoke them, and you get back data your code can actually consume.
This lesson covers both mechanics on the
Tool use (sometimes called function calling) is a protocol, not magic. Claude never runs your code. You hand it a list of tool definitions, each with a name, a description, and a JSON SchemaSchemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition → for its inputs. When Claude decides a tool would help, it pauses generating prose and instead emits a structured request: "call get_weather with {"location": "Paris"}." Your application runs that function, sends the result back, and Claude continues.
That back and forth is the tool-use loop. Understanding it is the whole game.
1. You send a user message plus your tools array.
2. Claude responds with stop_reason: "tool_use" and a tool_use content block.
3. You execute the tool and send the answer back as a tool_result.
4. Claude reads the result and produces its final text answer.
get_weather loopHere is the full loop in Python with the official SDK. Read it once, then we will break down each shape.
import anthropic
client = anthropic.Anthropic()
tools = [{
"name": "get_weather",
"description": "Get current temperature for a given city.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name, e.g. 'Paris'"}
},
"required": ["location"],
},
}]
def get_weather(location):
# In real life, call a weather API here.
return {"location": location, "temp_c": 14, "conditions": "cloudy"}
messages = [{"role": "user", "content": "What's the weather in Paris right now?"}]
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
# Step 2: Claude asked for a tool.
if response.stop_reason == "tool_use":
tool_call = next(b for b in response.content if b.type == "tool_use")
result = get_weather(**tool_call.input)
# Step 3: append Claude's request AND your result.
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_call.id,
"content": str(result),
}],
})
# Step 4: Claude reads the result and writes the answer.
final = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
print(final.content[0].text)When Claude wants a tool, response.content is a list of blocks. The relevant one looks like this:
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": {"location": "Paris"}
}Three fields matter. name tells you which function to run. input is already a parsed object matching your schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition →, so no string parsing. id is the handle you must echo back.
The critical detail people miss: you must append two messages, in order. First, Claude's own assistant turn (the full response.content, including the tool_use block). Second, a user turn carrying the tool_result. The tool_use_id in your result must exactly match the id Claude sent, or the loop breaks.
If your tool fails, set "is_error": true in the result block. Claude will see the error and can apologize, retry with different inputs, or pick another tool.
You rarely ship one tool. Pass several in the tools array and Claude routes to the right one based on the descriptions. Write those descriptions like documentation, because they are the only thing Claude uses to decide. "Get current temperature for a given city" beats "weather tool" every time.
Claude can also request several tools in a single turn (parallel tool use). When that happens, response.content contains multiple tool_use blocks. Run them all, then return all the tool_result blocks together in one user message before calling the APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → again.
To loop until Claude is done (it may chain several tools), wrap the call in a while response.stop_reason == "tool_use" loop instead of a single if.
Tool use solves "do something." Structured outputs solve "give me data in an exact shape." Say you are extracting fields from a support email and need {"category": ..., "urgency": ..., "summary": ...} every single time, no prose, no markdown fences.
The most reliable trick is to use the tool mechanism itself. Define a tool that represents your desired output schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition →, then force Claude to use it.
import anthropic
client = anthropic.Anthropic()
extract_tool = {
"name": "record_ticket",
"description": "Record the structured fields of a support ticket.",
"input_schema": {
"type": "object",
"properties": {
"category": {"type": "string", "enum": ["billing", "bug", "feature", "other"]},
"urgency": {"type": "string", "enum": ["low", "medium", "high"]},
"summary": {"type": "string", "description": "One sentence."},
},
"required": ["category", "urgency", "summary"],
},
}
email = "I was charged twice this month and need this fixed before Friday!"
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[extract_tool],
tool_choice={"type": "tool", "name": "record_ticket"},
messages=[{"role": "user", "content": email}],
)
ticket = next(b.input for b in response.content if b.type == "tool_use")
print(ticket)
# {'category': 'billing', 'urgency': 'high', 'summary': 'Customer charged twice and wants a fix by Friday.'}The tool_choice parameter is the lever. Set it to {"type": "tool", "name": "record_ticket"} and you force Claude to call that exact tool, which means it must produce input matching your schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition →. You never run a function here. You just read b.input, which is already a validated object. No fenced code blocks to strip, no half-formed JSON.
The enum constraints are doing real work too. They stop Claude from inventing a fourth urgency level. Tighten your schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition → and you tighten your outputs.
Other useful tool_choice values:
{"type": "auto"} (default): Claude decides whether to use a tool.{"type": "any"}: Claude must use *some* tool but picks which.{"type": "tool", "name": "..."}: force one specific tool, as above.Anthropic's docs cover schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition → edge cases and streaming in depth. See the tool use guide for the authoritative reference.
Knowledge check
1. In the tool-use loop, what does Claude do when it decides a tool would help answer a request?
2. After you execute a tool, how do you return its output to Claude so it can produce a final answer?
3. What stop_reason does Claude return when it wants to invoke a tool?
4. Select ALL correct answers about what a tool definition must include in the tools array.
Sélectionnez toutes les réponses correctes.
5. Select ALL correct answers about why tool use and structured outputs matter for production pipelines.
Sélectionnez toutes les réponses correctes.
Everything above is the raw APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition →. You define tools inline and run them yourself. That is perfect for a handful of bespoke tools inside one service.
But you will quickly want reusable tools that any Claude surface can call: the desktop app, Claude Code, your own agent. That is what MCP (Model Context Protocol) standardizes. An MCP server exposes tools (and resources and prompts) over a defined protocol, so you write a tool once and plug it into many clients. The Claude apps surface these as Connectors, and there is a connector marketplace for common ones like Google Drive or GitHub.
The relationship is clean: MCP tools and APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → tools are the *same idea* at different scopes. An MCP server's tool list ultimately becomes entries in a tools array, and the call/result handshake mirrors the loop you just learned. Learn the protocol at modelcontextprotocol.io.
The Claude Agent SDK sits one level higher again. It runs the tool-use loop for you, manages context, and handles multi-step agent behavior, so you describe tools and goals rather than hand-coding the while stop_reason == "tool_use" machinery. When you build a real agent, reachreachThe number of unique people exposed to your message in a given period. Unlike impressions, reach counts each person once, no matter how often they see it.View full definition → for the SDK. When you need surgical control over a single exchange, drop to the Messages APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → as shown above. Both speak the same tool-use language, which is exactly why learning the raw loop pays off.
A few things that will bite you in production:
tool_use block, run the function, return a tool_result with the matching tool_use_id. Echo Claude's assistant turn back before the result, or the loop breaks.tool_choice to that tool, and read the already-parsed input. Use enum and required to lock the shape.inputtool_choice to a specific tool, Claude jumps straight to the call. If you need reasoning first, leave it on auto or split into two calls.is_error: true with a useful message often lets Claude self-correct on the next turn, which is more robust than crashing your loop.