Tool Use Basics
10 examples to get you started with Tool Use & Function Calling - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Tool Use & Function Calling - 7 basic and 3 intermediate.
pip install anthropic.ANTHROPIC_API_KEY in your environment - the client reads it automatically.from anthropic import Anthropic; client = Anthropic() is all the setup these examples need.Describe a tool with a name, a description, and a JSON Schema for its input.
weather_tool = {
"name": "get_weather",
"description": "Get current weather for a location. Call this when the user asks about current conditions.",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and state, e.g., San Francisco, CA",
}
},
"required": ["location"],
},
}name is the identifier Claude uses to request the tool and that you match against in your own code.description is the main signal Claude uses to decide when to call the tool - write it like documentation for a teammate, not a label.input_schema is standard JSON Schema; required and per-field description values both improve how reliably Claude fills in arguments.Related: Defining Tool Schemas - the full schema reference
Pass your tool list to messages.create alongside the conversation.
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=[weather_tool],
messages=[
{"role": "user", "content": "What's the weather in San Francisco, CA?"}
],
)tools parameter accepts a list, so you can offer Claude several tools in the same call.tools does not force a call.max_tokens around 1024 is plenty for a single tool-call turn; raise it if the final answer will be long.model="claude-sonnet-5" is the default model for general-purpose tool use on this stack.Check stop_reason before assuming you have a plain text answer.
if response.stop_reason == "tool_use":
print("Claude wants to call a tool")
else:
print(response.content[0].text)stop_reason == "tool_use" is how the SDK tells you Claude paused to request a tool call instead of finishing its answer.stop_reason rather than guessing from the content shape - it is the documented signal for this state.response.content holds ordinary text blocks you can read directly.Related: How Claude Decides When to Call a Tool - why some turns skip tool use entirely
Find the tool_use content block and read its generated arguments.
tool_use_block = next(
block for block in response.content if block.type == "tool_use"
)
tool_name = tool_use_block.name
tool_input = tool_use_block.input
tool_id = tool_use_block.id
print(tool_name, tool_input, tool_id)response.content is a list of content blocks; a tool_use turn can mix a text block with one or more tool_use blocks.block.input is already a parsed Python dict matching your input_schema - no JSON decoding needed.block.id is the value you must echo back later in the matching tool_result, so capture it now.block.name tells you which tool to run when you have more than one defined.Run your own code with the arguments Claude generated.
def get_weather(location: str) -> str:
# Replace with a real weather API call.
return f"72F and sunny in {location}"
if tool_name == "get_weather":
result = get_weather(**tool_input)tool_input with ** works cleanly when your function's parameter names match the schema's properties keys.tool_input like any other external input - validate it before using it in file paths, queries, or shell commands.tool_result.Send the tool's output back to Claude as a new user message.
messages = [
{"role": "user", "content": "What's the weather in San Francisco, CA?"},
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_id,
"content": result,
}
],
},
]
final_response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=[weather_tool],
messages=messages,
)
print(final_response.content[0].text)response.content verbatim - it preserves the exact tool_use block Claude generated, including its id.tool_use_id must match the id of the tool_use block it answers, so Claude can pair the request with its result.tool_result block always goes in a user-role message, even though it's really a machine response, not something the human typed.messages.create call is a fresh request with the full history attached - Claude only sees prior turns you explicitly include.Related: Handling Tool Use Requests and Returning tool_result Blocks - the full round-trip reference
Tell Claude a tool call failed instead of silently dropping it.
try:
result = get_weather(**tool_input)
tool_result_content = result
is_error = False
except Exception as exc:
tool_result_content = f"Tool execution failed: {exc}"
is_error = True
tool_result_block = {
"type": "tool_result",
"tool_use_id": tool_id,
"content": tool_result_content,
"is_error": is_error,
}"is_error": true on the tool_result block when your function raises or the underlying API call fails.tool_result for a tool_use block, even on failure - the conversation is malformed if a tool call is left unanswered.try/except keeps a single bad API call from crashing your whole request handler.Chain every step above into one working loop.
from anthropic import Anthropic
client = Anthropic()
weather_tool = {
"name": "get_weather",
"description": "Get current weather for a location. Call this when the user asks about current conditions.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and state, e.g., San Francisco, CA"}
},
"required": ["location"],
},
}
def get_weather(location: str) -> str:
return f"72F and sunny in {location}"
messages = [{"role": "user", "content": "Should I bring an umbrella in Austin, TX today?"}]
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=[weather_tool],
messages=messages,
)
if response.stop_reason == "tool_use":
tool_use_block = next(b for b in response.content if b.type == "tool_use")
result = get_weather(**tool_use_block.input)
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": tool_use_block.id, "content": result}
],
})
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=[weather_tool],
messages=messages,
)
print(response.content[0].text)tool_use, execute, append tool_result, create again - written as one script.messages list with .append() keeps the full conversation history intact for the second call.messages.create call still passes tools=[weather_tool], since Claude may legitimately need to call the tool again.while response.stop_reason == "tool_use": loop to handle any number of tool calls.Related: Building a Multi-Turn Tool Use Loop - generalizing this into a reusable loop
Loop over every tool_use block, since Claude can request several tools in a single turn.
tool_result_blocks = []
for block in response.content:
if block.type != "tool_use":
continue
if block.name == "get_weather":
output = get_weather(**block.input)
else:
output = f"Unknown tool: {block.name}"
tool_result_blocks.append(
{"type": "tool_result", "tool_use_id": block.id, "content": output}
)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_result_blocks})tool_use block - filter response.content for type == "tool_use" rather than assuming exactly one.tool_use block needs its own matching tool_result block in the follow-up user message, each with its own tool_use_id.tool_result blocks for one turn go together in a single user message's content list, not as separate messages.block.name is how you dispatch to the right Python function when multiple tools are defined.Related: Executing Parallel Tool Calls in a Single Turn - more on batching tool results
Use tool_choice to force, restrict, or leave open which tool Claude picks.
calculate_tool = {
"name": "calculate",
"description": "Evaluate a basic arithmetic expression. Call this for any math question.",
"input_schema": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "e.g., 12 * (4 + 1)"}
},
"required": ["expression"],
},
}
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=[weather_tool, calculate_tool],
tool_choice={"type": "tool", "name": "calculate"},
messages=[{"role": "user", "content": "What is 12 times 5?"}],
)tool_choice={"type": "tool", "name": "calculate"} forces Claude to call that specific tool instead of choosing on its own.tool_choice unset is equivalent to {"type": "auto"} - Claude decides whether and which tool to use based on the descriptions.{"type": "any"} requires some tool call but lets Claude pick which one; {"type": "none"} disables tool calls for that turn.Related: Tool Choice Options Reference - every tool_choice mode in detail
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.
Reviewed by Chris St. John·Last updated Jul 19, 2026