Skip to content
Learn Agentic AI12 min read1 views

Claude Tool Use Patterns: Sequential, Parallel, and Nested Tool Calls

Master Claude's tool calling patterns including sequential chains, parallel execution, nested tool calls, forced tool use, and auto vs any mode for complex agent orchestration.

Understanding Claude Tool Call Patterns

Claude does not just call one tool at a time. Depending on the task, it may invoke multiple tools in parallel, chain tool calls sequentially across turns, or use the output of one tool as input to another. Understanding these patterns is critical for building efficient and reliable agents.

The three core patterns are sequential (one tool per turn, results feed forward), parallel (multiple tools in a single turn), and nested (a tool call triggers another agent or sub-workflow). Each pattern has different performance, cost, and reliability characteristics.

Sequential Tool Calls

Sequential calls happen naturally when each step depends on the previous result. Claude calls one tool, receives the result, reasons about it, then calls the next tool.

import anthropic
import json

client = anthropic.Anthropic()

tools = [
    {
        "name": "get_user",
        "description": "Fetch user details by user ID",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string", "description": "The user ID"}
            },
            "required": ["user_id"]
        }
    },
    {
        "name": "get_orders",
        "description": "Fetch recent orders for a user email address",
        "input_schema": {
            "type": "object",
            "properties": {
                "email": {"type": "string", "description": "User email"}
            },
            "required": ["email"]
        }
    },
]

# Claude will first call get_user, then use the returned email
# to call get_orders — a natural sequential chain

Claude decides the ordering automatically. If it needs user email before fetching orders, it calls get_user first and extracts the email from the result.

Parallel Tool Calls

When tools are independent, Claude calls them simultaneously in a single response. This reduces round trips and speeds up the agent loop.

# Claude will call both tools in ONE response when the
# information requests are independent
tools = [
    {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            },
            "required": ["city"]
        }
    },
    {
        "name": "get_exchange_rate",
        "description": "Get currency exchange rate",
        "input_schema": {
            "type": "object",
            "properties": {
                "from_currency": {"type": "string"},
                "to_currency": {"type": "string"}
            },
            "required": ["from_currency", "to_currency"]
        }
    },
]

# "What's the weather in Tokyo and the USD to JPY rate?"
# Claude returns TWO tool_use blocks in a single response

When processing parallel tool calls, you must return a tool_result for every tool_use block, matching them by tool_use_id:

def process_parallel_tools(response):
    tool_results = []
    for block in response.content:
        if block.type == "tool_use":
            result = execute_tool(block.name, block.input)
            tool_results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": json.dumps(result),
            })
    return tool_results

Controlling Tool Use with tool_choice

The tool_choice parameter controls whether and how Claude uses tools:

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

# Let Claude decide (default)
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "auto"},
    messages=messages,
)

# Force Claude to use ANY tool (must call at least one)
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "any"},
    messages=messages,
)

# Force Claude to use a SPECIFIC tool
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "get_weather"},
    messages=messages,
)

Use "any" when you know the user's request requires a tool call but you want Claude to pick which one. Use "tool" with a specific name when you need deterministic behavior — for example, always extracting structured data before reasoning about it.

Nested Tool Calls with Sub-Agents

Nested patterns emerge when a tool itself runs another Claude agent. This creates a hierarchy where a top-level agent delegates specialized tasks:

def research_tool(query: str) -> dict:
    """This tool runs a sub-agent to do deep research."""
    sub_agent_tools = [web_search_tool, summarize_tool]

    messages = [{"role": "user", "content": f"Research: {query}"}]

    # Sub-agent loop
    while True:
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            system="You are a research specialist. Search and summarize.",
            tools=sub_agent_tools,
            messages=messages,
        )

        if response.stop_reason == "end_turn":
            return {"research_summary": response.content[0].text}

        # Process sub-agent tool calls
        messages.append({"role": "assistant", "content": response.content})
        results = process_parallel_tools(response)
        messages.append({"role": "user", "content": results})

The outer agent sees research_tool as a single tool, unaware that it internally runs a full agent loop with its own tools.

Error Handling in Tool Results

Always return errors gracefully in tool results. Claude can reason about errors and retry or try alternative approaches:

def execute_tool(name: str, inputs: dict) -> dict:
    try:
        result = TOOL_REGISTRY[name](**inputs)
        return {"status": "success", "data": result}
    except KeyError:
        return {"status": "error", "message": f"Unknown tool: {name}"}
    except Exception as e:
        return {"status": "error", "message": str(e)}

When Claude receives an error result, it often tries a different approach or asks the user for clarification rather than failing silently.

FAQ

When does Claude choose parallel vs sequential tool calls?

Claude calls tools in parallel when they are independent — neither tool's input depends on the other's output. If the user asks "What is the weather in Tokyo and New York?", Claude will issue two parallel get_weather calls. If the user asks "Get user 123's email and then their orders," Claude will call get_user first and get_orders second.

Can I disable parallel tool calls?

There is no direct SDK parameter to disable parallel calls. However, you can structure your tool descriptions to imply dependencies, or process them sequentially on your side even if Claude sends them in parallel. In practice, parallel calls are usually desirable because they reduce latency.

What happens if a tool_result is missing for a tool_use block?

The API will return an error. Every tool_use block in Claude's response must have a corresponding tool_result in the next user message. If a tool fails, return an error message as the content rather than omitting the result entirely.


#Claude #ToolUse #AgentPatterns #Orchestration #Python #AgenticAI #LearnAI #AIEngineering

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.