# Implementing Parallel Tool Execution with Claude: The Batch Meta-Tool Pattern

> Implement parallel tool execution with Claude using the batch meta-tool pattern. Bundle operations to eliminate round-trip latency and force efficient execution.

- Repository: [Anthropic/claude-cookbooks](https://github.com/anthropics/claude-cookbooks)
- Tags: how-to-guide
- Published: 2026-04-14

---

**Claude supports parallel tool execution by default, but when the model serializes independent calls, wrapping multiple operations into a single batch meta-tool forces bundled execution and eliminates extra round-trip latency.**

The `anthropics/claude-cookbooks` repository provides production-ready patterns for optimizing Claude integrations. In `tool_use/parallel_tools.ipynb`, the authors demonstrate how to overcome a subtle performance limitation: even with parallel tool use enabled in the API, Claude sometimes issues sequential tool calls that require multiple network round trips to resolve.

## How Parallel Tool Execution Works in Claude

Claude's API includes specific parameters that control tool-calling behavior. Understanding these defaults is essential before implementing workarounds for serialization issues.

### Default Parameter Values

When calling `client.messages.create`, the Anthropic API applies the following defaults unless overridden:

- **`tool_choice`**: Defaults to `{ "type": "auto" }`, allowing Claude to select any available tool or combination of tools.
- **`disable_parallel_tool_use`**: Defaults to `False`, permitting the model to return multiple `tool_use` blocks in a single response.

According to `tool_use/parallel_tools.ipynb` (lines 25-27), these defaults should theoretically allow Claude to request weather and time information simultaneously. However, the notebook demonstrates that model-level optimization often overrides this capability.

### Why Claude Serializes Independent Calls

Even with `disable_parallel_tool_use=False`, Claude sometimes emits a single tool call followed by a second call only after receiving the first result. This occurs because the model optimizes for potential dependencies between operations. In `tool_use/parallel_tools.ipynb` (lines 14-15), an initial query requesting both weather and time returns only the `get_weather` call, forcing the client to respond before Claude requests `get_time`.

## The Batch Meta-Tool Pattern

The recommended solution involves creating a **batch meta-tool**—a higher-order tool that internally dispatches multiple concrete operations. This pattern guarantees that Claude bundles related requests into a single `tool_use` block.

### Define Concrete Tool Schemas

First, define the individual tools with proper JSON schemas:

```python
def get_weather(location: str) -> str:
    return f"The weather in {location} is 72 °F and sunny."

def get_time(location: str) -> str:
    return f"The time in {location} is 12:32 PM."

weather_tool = {
    "name": "get_weather",
    "description": "Gets the weather for a given location",
    "input_schema": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
    },
}

time_tool = {
    "name": "get_time",
    "description": "Gets the time for a given location",
    "input_schema": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
    },
}

```

### Create the Batch Wrapper Tool

Next, define a batch tool that accepts the common parameters and wraps both operations:

```python
batch_tool = {
    "name": "weather_and_time",
    "description": "Returns weather and time for a location in parallel",
    "input_schema": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
    },
}

```

Register this alongside individual tools when creating the message:

```python
from anthropic import Anthropic

client = Anthropic()
MODEL_NAME = "claude-sonnet-4-6"

def make_query(messages, tools):
    return client.messages.create(
        model=MODEL_NAME,
        messages=messages,
        max_tokens=500,
        tool_choice={"type": "auto"},
        tools=tools,
    )

MESSAGES = [{"role": "user", "content": "What's the weather and time in San Francisco?"}]
response = make_query(MESSAGES, tools=[weather_tool, time_tool, batch_tool])

```

### Handle Execution with Parallel Dispatch

Implement a handler that unpacks the batch call and executes sub-tools concurrently using `asyncio.gather` or thread pools:

```python
import asyncio

async def process_batch_tool(tool_name, tool_input):
    if tool_name == "weather_and_time":
        loc = tool_input["location"]
        # True parallel execution

        weather, time = await asyncio.gather(
            asyncio.to_thread(get_weather, loc),
            asyncio.to_thread(get_time, loc)
        )
        return {"weather": weather, "time": time}
    

# Process Claude's response

for block in response.content:
    if block.type == "tool_use":
        result = await process_batch_tool(block.name, block.input)
        print(f"Batch result: {result}")

```

As shown in `tool_use/parallel_tools.ipynb` (lines 201-207 and 287-293), Claude reliably selects the `weather_and_time` batch tool when available, reducing the interaction from two turns to one.

## Visualizing Tool Interactions

The repository includes utilities for debugging parallel workflows. The [`tool_use/utils/visualize.py`](https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/utils/visualize.py) module (lines 135-144) provides `render_tool_use()` and `render_tool_result()` functions that format tool calls as tree structures:

```python
from tool_use.utils.visualize import render_tool_use, render_tool_result
from rich.tree import Tree

tree = Tree("Claude Interaction")
for block in response.content:
    if block.type == "tool_use":
        render_tool_use(block, tree)
    elif block.type == "tool_result":
        render_tool_result(block, tree)

print(tree)

```

This visualization confirms whether Claude is bundling multiple operations into a single batch tool call or issuing separate sequential requests.

## Summary

- **Keep `disable_parallel_tool_use` as `False`** (the default) to allow the model to return multiple tool calls in principle.
- **Implement batch meta-tools** when Claude serializes independent operations that could execute simultaneously.
- **Register batch tools alongside individual tools** to give Claude routing flexibility while ensuring it can choose the efficient bundled path.
- **Use `asyncio.gather` or thread pools** inside batch handlers to achieve true concurrent execution of sub-operations.
- **Leverage [`tool_use/utils/visualize.py`](https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/utils/visualize.py)** to verify that batch tools are being utilized correctly.

## Frequently Asked Questions

### What is the default setting for parallel tool use in Claude?

By default, `disable_parallel_tool_use` is set to `False` and `tool_choice` defaults to `{"type": "auto"}`. These settings permit Claude to return multiple `tool_use` blocks in a single response, but do not guarantee that the model will choose to do so for independent operations.

### Why does Claude sometimes call tools one at a time instead of in parallel?

Claude optimizes for potential dependencies between tool calls. If the model determines that the result of one tool might influence whether another tool is needed, or how it should be called, it will serialize the requests regardless of parallel settings. This is a model-level decision based on the perceived relationship between operations.

### How do I force Claude to execute multiple tools simultaneously?

Expose a **batch meta-tool** that combines the inputs and outputs of multiple concrete tools into a single schema. When Claude recognizes that it needs all the information provided by the sub-tools, it will call the batch tool once, allowing your client code to execute the underlying operations in parallel using `asyncio.gather` or similar concurrency primitives.

### Can I use async/await with Claude's tool execution?

Yes. While the Anthropic Python client supports both sync and async APIs, the parallel execution happens in your tool implementation. As shown in `tool_use/parallel_tools.ipynb`, you should use `asyncio.gather()` or `asyncio.to_thread()` inside your batch tool handler to run multiple sub-tools concurrently, then return the aggregated results to Claude in a single `tool_result` block.