Lesson 07 Tool Use & Function Calling
Theme:
Lesson 07

Tool Use & Function Calling

Give Claude the ability to take actions in the world — call APIs, run code, query databases — and close the loop between language and execution.

Objectives

Concept

What Is Tool Use?

Claude cannot browse the web or run code on its own. Tool use lets you extend Claude with callable functions — Claude decides when and how to call them based on the conversation.

The Flow

  1. User message arrives — "What's 17 times 843?"
  2. Claude decides — a calculator tool would be more accurate than mental math
  3. Tool call emitted — Claude returns a tool_use block with arguments
  4. Your code executes — you run the tool, get the result
  5. Result returned — you send a tool_result block back to Claude
  6. Claude responds — it incorporates the result into a natural-language reply
Key insight: Claude never executes code itself. Your application is always in control of tool execution — Claude only requests tool calls.
Schema

Tool Schema

A tool is a Python dict with three required keys. The description is the most important part — Claude reads it to decide whether to use this tool.

calculator_tool = {
    "name": "calculator",
    "description": (
        "Perform arithmetic operations on two numbers. "
        "Use this whenever you need to compute a mathematical result precisely. "
        "Supports add, subtract, multiply, and divide."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "operation": {
                "type": "string",
                "enum": ["add", "subtract", "multiply", "divide"],
                "description": "The arithmetic operation to perform"
            },
            "a": {"type": "number", "description": "First operand"},
            "b": {"type": "number", "description": "Second operand"}
        },
        "required": ["operation", "a", "b"]
    }
}
Description quality matters: Vague descriptions lead to wrong tool selection. Be specific about when to use the tool, what it does, and any limitations.
Architecture

The Tool Loop

Tool use is inherently multi-turn. You must loop until Claude produces a response with no tool calls.

  1. Send user message + tools list to client.messages.create()
  2. Check response.stop_reason == "tool_use"
  3. Extract all tool_use blocks from response.content
  4. Execute each tool call — call your Python function with the inputs
  5. Build a tool_result message and append to messages
  6. Call the API again — go to step 2
  7. When stop_reason == "end_turn" — extract and return the text
while response.stop_reason == "tool_use":
    tool_calls = [b for b in response.content if b.type == "tool_use"]
    results = []
    for tc in tool_calls:
        output = execute_tool(tc.name, tc.input)
        results.append({"type": "tool_result", "tool_use_id": tc.id,
                        "content": str(output)})
    messages.append({"role": "assistant", "content": response.content})
    messages.append({"role": "user", "content": results})
    response = client.messages.create(model=MODEL, tools=tools,
                                      messages=messages, max_tokens=1024)
Parallel Calls

Parallel Tool Calls

Claude can request multiple tool calls in a single response when they are independent. This is more efficient than sequential calls.

Parallel (1 round)

"What's the weather in Paris AND what meetings do I have today?"

Claude emits: get_weather(Paris) + get_calendar(today) simultaneously
Sequential (2 rounds)

"Search for Python docs, then summarize the top result."

Claude must see search results before deciding what to summarize.

When handling parallel calls, collect all tool_use blocks from response.content, execute each, and return all results in a single tool_result message — one entry per tool call.

Reliability

Error Handling

Tool execution can fail. A divide-by-zero, file not found, or network timeout should not crash your loop. Return the error in the tool_result — Claude will adapt its response.

def execute_tool(name: str, inputs: dict) -> str:
    try:
        if name == "calculator":
            return str(run_calculator(**inputs))
        elif name == "unit_converter":
            return str(run_converter(**inputs))
        else:
            return f"Error: unknown tool '{name}'"
    except ZeroDivisionError:
        return "Error: division by zero"
    except Exception as e:
        return f"Error: {type(e).__name__}: {e}"
Pattern: Return a string starting with "Error:". Claude recognises this, tells the user what went wrong, and may try an alternative approach.

Also set a max_iterations guard

Prevent infinite loops by limiting how many tool rounds you allow. 10 is a safe default for most tasks.

Demo

Live Demo

Run the standalone demo to see a complete tool loop in action.

cd lesson07-tool-use
python demo/tool_demo.py
Presenter notes: Run demo/tool_demo.py. The script shows a calculator and unit_converter being called. Walk through the printed output — note the tool_use block with the inputs, then the tool_result, then Claude's final natural-language answer. Try asking a question that triggers both tools at once.

📥 Demo Files

⬇ tool_demo.py
Next Steps

Lab & Homework

Lab (lab.pdf)

Homework (homework.html)

1 / ?