---
title: "Claude API Tool Use: Building Custom AI Workflows"
description: "Complete guide to implementing tool use (function calling) with the Claude API. Covers tool definitions, execution patterns, multi-turn conversations, and production best practices."
canonical: https://callsphere.ai/blog/claude-api-tool-use-guide
category: "Agentic AI"
tags: ["Claude API", "Tool Use", "Function Calling", "AI Workflows", "Anthropic", "Python"]
author: "CallSphere Team"
published: 2026-01-25T00:00:00.000Z
updated: 2026-05-07T00:28:44.429Z
---

# Claude API Tool Use: Building Custom AI Workflows

> Complete guide to implementing tool use (function calling) with the Claude API. Covers tool definitions, execution patterns, multi-turn conversations, and production best practices.

## What Is Tool Use in the Claude API?

Tool use -- also called function calling -- is the mechanism that allows Claude to interact with external systems. Instead of only generating text, Claude can request that your application execute a specific function with specific arguments. Your application runs the function, returns the result, and Claude incorporates that result into its reasoning.

This is the foundation of every agentic application. Without tool use, Claude is limited to its training data and the content you provide in the prompt. With tool use, Claude can query databases, call APIs, read files, send emails, and perform any operation you expose as a tool.

## Defining Tools

Tools are defined as JSON schemas that describe the function name, description, and parameters. The quality of your tool definitions directly impacts how reliably Claude uses them.

```mermaid
flowchart LR
    USER(["User message"])
    LOOP{"messages.create
agent loop"}
    THINK["Extended thinking
optional"]
    TOOL{"stop_reason
tool_use?"}
    EXEC["Execute tool
append tool_result"]
    DONE(["stop_reason
end_turn"])
    USER --> LOOP --> THINK --> TOOL
    TOOL -->|Yes| EXEC --> LOOP
    TOOL -->|No| DONE
    style LOOP fill:#4f46e5,stroke:#4338ca,color:#fff
    style THINK fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
    style DONE fill:#059669,stroke:#047857,color:#fff
```

```python
from anthropic import Anthropic

client = Anthropic()

tools = [
    {
        "name": "get_weather",
        "description": "Get current weather conditions for a specific location. Returns temperature, humidity, and conditions.",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City name, e.g. 'San Francisco, CA'"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "Temperature unit. Defaults to fahrenheit."
                }
            },
            "required": ["location"]
        }
    },
    {
        "name": "search_database",
        "description": "Search the product database by name, category, or price range. Returns matching products with details.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search query string"
                },
                "category": {
                    "type": "string",
                    "enum": ["electronics", "clothing", "books", "home"],
                    "description": "Product category filter"
                },
                "max_price": {
                    "type": "number",
                    "description": "Maximum price in USD"
                },
                "limit": {
                    "type": "integer",
                    "description": "Maximum number of results to return. Default 10."
                }
            },
            "required": ["query"]
        }
    }
]
```

### Tool Description Best Practices

The tool description is arguably more important than the schema itself. Claude uses it to decide when and whether to use the tool.

- **Be specific about what the tool returns**, not just what it does
- **Include edge cases**: "Returns an empty array if no results match"
- **Specify units and formats**: "Prices are in USD", "Dates are ISO 8601"
- **Note limitations**: "Only searches products added in the last 30 days"

## The Tool Use Conversation Loop

A complete tool use interaction involves multiple API calls:

```python
import json

def run_tool_loop(user_message: str, tools: list, max_iterations: int = 10) -> str:
    messages = [{"role": "user", "content": user_message}]

    for _ in range(max_iterations):
        response = client.messages.create(
            model="claude-sonnet-4-5-20250514",
            max_tokens=4096,
            tools=tools,
            messages=messages,
        )

        # Check if Claude wants to use a tool
        if response.stop_reason == "tool_use":
            # Extract tool use blocks
            tool_results = []
            assistant_content = response.content

            for block in response.content:
                if block.type == "tool_use":
                    # Execute the tool
                    result = execute_tool(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": json.dumps(result),
                    })

            # Add assistant message and tool results
            messages.append({"role": "assistant", "content": assistant_content})
            messages.append({"role": "user", "content": tool_results})

        elif response.stop_reason == "end_turn":
            # Claude is done -- extract final text
            return "".join(
                block.text for block in response.content if block.type == "text"
            )

    return "Max iterations reached without final response."

def execute_tool(name: str, input_data: dict):
    """Route tool calls to actual implementations."""
    if name == "get_weather":
        return fetch_weather(input_data["location"], input_data.get("unit", "fahrenheit"))
    elif name == "search_database":
        return search_products(**input_data)
    else:
        return {"error": f"Unknown tool: {name}"}
```

## Parallel Tool Use

Claude can request multiple tools in a single response. When it does, the tool use blocks appear as separate items in the `content` array. You should execute them all and return all results.

```python
# Claude might return content like:
# [TextBlock("Let me check both..."), ToolUseBlock(get_weather, ...), ToolUseBlock(search_database, ...)]

# Execute all tool calls (potentially in parallel)
import asyncio

async def execute_tools_parallel(tool_blocks):
    tasks = [
        execute_tool_async(block.name, block.input)
        for block in tool_blocks
        if block.type == "tool_use"
    ]
    return await asyncio.gather(*tasks)
```

## Forcing Tool Use

Sometimes you want Claude to always use a specific tool rather than answering from its own knowledge. Use the `tool_choice` parameter:

```python
# Force Claude to use a specific tool
response = client.messages.create(
    model="claude-sonnet-4-5-20250514",
    max_tokens=4096,
    tools=tools,
    tool_choice={"type": "tool", "name": "search_database"},
    messages=[{"role": "user", "content": "Find me headphones under $100"}]
)

# Force Claude to use any tool (must use at least one)
response = client.messages.create(
    model="claude-sonnet-4-5-20250514",
    max_tokens=4096,
    tools=tools,
    tool_choice={"type": "any"},
    messages=[{"role": "user", "content": "What is the weather in Tokyo?"}]
)

# Let Claude decide (default behavior)
response = client.messages.create(
    model="claude-sonnet-4-5-20250514",
    max_tokens=4096,
    tools=tools,
    tool_choice={"type": "auto"},
    messages=[{"role": "user", "content": "Tell me about Claude"}]
)
```

## Error Handling in Tool Results

When a tool execution fails, return the error as a tool result with `is_error: true`. Claude will see the error and can decide how to proceed -- often retrying with different parameters or informing the user.

```python
def execute_tool_safe(name: str, input_data: dict) -> dict:
    try:
        result = execute_tool(name, input_data)
        return {
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": json.dumps(result),
        }
    except Exception as e:
        return {
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": f"Error executing {name}: {str(e)}",
            "is_error": True,
        }
```

## Advanced Pattern: Tool Chains

Some workflows require Claude to call tools in a specific sequence. Instead of hardcoding the sequence, describe the workflow in the system prompt and let Claude orchestrate:

```python
system_prompt = """You are a customer support agent with access to these tools:
- lookup_customer: Find a customer by email or phone
- get_order_history: Get recent orders for a customer ID
- create_ticket: Create a support ticket
- send_email: Send an email to a customer

Workflow for order issues:
1. First lookup the customer
2. Then check their order history
3. Create a ticket with the relevant details
4. Send confirmation email to the customer

Always follow this sequence. Do not skip steps."""
```

## Token Cost Implications

Tool definitions consume input tokens on every API call in the conversation. A large tool schema adds significant cost over multi-turn conversations.

| Number of Tools | Avg Schema Size | Tokens Per Call | Cost at Sonnet Rate |
| --- | --- | --- | --- |
| 5 tools | 200 tokens each | 1,000 | $0.003 |
| 20 tools | 200 tokens each | 4,000 | $0.012 |
| 50 tools | 200 tokens each | 10,000 | $0.030 |

For applications with many tools, consider using prompt caching to cache the tool definitions. With caching, you pay full price on the first call and 90% less on subsequent calls.

## Production Checklist

Before deploying tool use in production:

- **Validate all tool inputs** before execution (do not trust Claude's output blindly)
- **Set timeouts** on tool execution to prevent hung API calls
- **Log every tool call** with input, output, and duration for debugging
- **Rate-limit tool execution** to prevent runaway loops
- **Sanitize tool outputs** before returning them (strip sensitive data)
- **Test with adversarial prompts** to verify Claude does not misuse tools
- **Monitor token usage** to catch unexpected cost spikes from tool-heavy conversations

---

Source: https://callsphere.ai/blog/claude-api-tool-use-guide
