Claude tool use API: complete Python guide

Claude tool use lets you give the model a set of functions it can call — and then Claude decides when calling one makes sense. You describe the tools, Claude emits a structured call, you execute it in your code and return the result, and Claude uses that result to generate its final response. This page covers the exact loop, how to define tools, tool_choice, parallel calls, and error handling — all with copy-paste Python examples using the official Anthropic SDK.

The 30-second answer

How to define a tool

Each tool is a dict with three required keys. The description is critical — Claude reads it to decide when to use the tool, so be specific about when it applies:

tools = [
    {
        "name": "get_weather",
        "description": "Get current weather for a city. Use this when the user asks about weather.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "City name, e.g. 'New York'"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "Temperature unit"
                }
            },
            "required": ["city"]
        }
    }
]

The input_schema follows JSON Schema. Mark fields as required when Claude must always provide them. Optional fields (like unit above) can be omitted — Claude may or may not include them. You can pass multiple tools in the list; Claude picks the right one based on the descriptions.

The complete 4-step call loop

This is the full working example. Every tool use interaction follows this same structure:

import anthropic
import json

client = anthropic.Anthropic()

def get_weather(city: str, unit: str = "celsius") -> str:
    # Your actual implementation here
    return json.dumps({"city": city, "temp": 22, "unit": unit, "condition": "sunny"})

# Step 1: Send message with tools
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "What's the weather in Paris?"}]
)

# Step 2: Check if Claude wants to use a tool
if response.stop_reason == "tool_use":
    tool_use_block = next(b for b in response.content if b.type == "tool_use")
    tool_name = tool_use_block.name
    tool_input = tool_use_block.input
    tool_use_id = tool_use_block.id

    # Step 3: Execute the tool
    result = get_weather(**tool_input)

    # Step 4: Send result back as tool_result, then get final response
    final_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "What's the weather in Paris?"},
            {"role": "assistant", "content": response.content},
            {
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_use_id,
                    "content": result
                }]
            }
        ]
    )
    print(final_response.content[0].text)

A few things to note: the assistant's response.content (the whole list, not just the text block) goes back verbatim as the assistant turn. The tool_result block must match the tool_use_id from Claude's response. The content field of tool_result is a plain string — JSON-serialise your result if it's structured data.

Controlling tool use with tool_choice

By default, Claude decides whether to call a tool. You can override this:

# Default: Claude decides
tool_choice = {"type": "auto"}

# Claude must call at least one tool (you choose which)
tool_choice = {"type": "any"}

# Claude must call this specific tool
tool_choice = {"type": "tool", "name": "get_weather"}

# Pass it in the create() call:
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "get_weather"},
    messages=[{"role": "user", "content": "What's the weather in Paris?"}]
)

Use "any" when you're building an agentic loop and need Claude to always take an action rather than reply with text. Use the specific "tool" type when you're using tool use for structured data extraction — force Claude to populate your schema every time.

Parallel tool calls and error handling

Claude can return multiple tool_use blocks in one response. Loop through all of them, execute each, and return all results in a single user message before requesting the final answer:

if response.stop_reason == "tool_use":
    tool_use_blocks = [b for b in response.content if b.type == "tool_use"]
    tool_results = []

    for block in tool_use_blocks:
        try:
            # Dispatch to the right function
            if block.name == "get_weather":
                result = get_weather(**block.input)
            else:
                raise ValueError(f"Unknown tool: {block.name}")

            tool_results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": result
            })
        except Exception as e:
            # Return errors gracefully — Claude will handle them
            tool_results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": str(e),
                "is_error": True
            })

    final_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "What's the weather in Paris and London?"},
            {"role": "assistant", "content": response.content},
            {"role": "user", "content": tool_results}
        ]
    )

Setting "is_error": true on a tool_result tells Claude the call failed. Claude will acknowledge the failure and either retry with different inputs, use a different tool, or explain the problem to the user — it handles the error gracefully rather than hallucinating a result.

FAQ

What is the difference between Claude tool use and function calling? They are the same concept under different names. Anthropic calls it "tool use"; OpenAI calls it "function calling" (now also "tools"). The loop logic is nearly identical — the main API differences are field names: Claude uses a tool_result content block and stop_reason: "tool_use"; OpenAI uses role: "tool" and finish_reason: "tool_calls".

How do I force Claude to use a specific tool? Set tool_choice to {"type": "tool", "name": "your_tool_name"} in your messages.create() call. To require any tool (but let Claude choose which), use {"type": "any"}.

Can Claude call multiple tools in one turn? Yes. Claude can return multiple tool_use content blocks in a single response. Loop through all of them, execute each, then return all results in a single user message containing multiple tool_result blocks before requesting the final response.

Last updated May 28, 2026. Code examples verified against the Anthropic Python SDK and Anthropic tool use documentation. API behaviour may change — confirm against the official docs before deploying to production.