Claude API Tool Use and Function (2026)
Claude API Tool Use and Function Calling Detailed look Guide
The Claude API’s tool use and function calling capabilities represent one of its most powerful features, enabling you to build AI systems that can interact with external services, execute code, and perform real-world actions. This comprehensive guide walks you through everything you need to know to use these capabilities effectively in your applications.
Understanding Tool Use in the Claude API
Tool use allows Claude to interact with external functions and services during conversations. When you define tools in your API requests, Claude can autonomously decide when and how to use them based on the conversation context and user requests.
Unlike earlier approaches where a model would simply generate text that looked like a function call, the Claude API treats tool use as a first-class concept. Claude understands not just the syntax of calling a function but the semantics. when to call it, when not to, and how to chain multiple calls together to satisfy complex user requests.
How Tool Use Works
When you include tools in your API request, Claude receives a structured description of each available function, including:
- Function name: The identifier Claude uses to call the tool
- Parameters: Expected inputs with their types and descriptions
- Description: What the function does and when to use it
Claude analyzes the conversation and decides whether to call a tool, which tool to use, and what arguments to pass. The API returns tool call requests, you execute them, and then provide the results back to continue the conversation.
The Execution Model
The Execution Model is worth being precise about what “tool use” means at the API level. Claude does not execute code. It generates a structured request that says “please run this function with these arguments.” Your application receives that request, runs the actual function, and feeds the result back. This design is intentional. it keeps Claude in a reasoning role while giving you full control over which functions are actually executed and under what conditions.
This separation matters for security. You can add authorization checks, rate limiting, and audit logging at the boundary between Claude’s request and your execution layer. Claude cannot bypass those controls because it never has direct access to your infrastructure.
Setting Up Tools in Your API Requests
To enable tool use, you need to include a tools array in your API request. Here’s a basic example:
import anthropic
client = anthropic.Anthropic(api_key="your-api-key")
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
tools=[
{
"name": "get_weather",
"description": "Get current weather for a location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or coordinates"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["location"]
}
}
],
messages=[{
"role": "user",
"content": "What's the weather like in Tokyo?"
}]
)
The input_schema field follows the JSON Schema specification. Claude uses this schema both to understand what arguments a tool accepts and to generate valid argument objects. If your schema is underspecified. for example, if you use "type": "object" without listing properties. Claude will still attempt to call the tool, but the arguments may not match your function signature.
Tool Use vs. Forced Tool Use
By default, Claude decides whether to use a tool based on context. You can override this with tool_choice:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
tools=[...],
tool_choice={"type": "tool", "name": "get_weather"}, # Force specific tool
messages=[...]
)
Options for tool_choice are "auto" (default), "any" (must use some tool), or {"type": "tool", "name": "..."} (must use this specific tool). Forced tool use is useful when you want Claude to always invoke a particular function. for example, when building structured data extraction pipelines where a text response is never the desired output.
Building Function Calling Workflows
Function calling in the Claude API follows a multi-turn pattern where Claude requests tool execution, you provide results, and the conversation continues.
The Complete Flow
Here’s a complete example demonstrating a real-world function calling workflow:
def call_claude_with_tools(user_message):
# Initial request
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
tools=[...], # Your tool definitions
messages=[{"role": "user", "content": user_message}]
)
# Check for tool calls
while response.stop_reason == "tool_use":
tool_calls = response.content
results = []
for tool_call in tool_calls:
if tool_call.type == "tool_use":
# Execute the requested function
result = execute_tool(
tool_call.name,
tool_call.input
)
results.append({
"type": "tool_result",
"tool_use_id": tool_call.id,
"content": result
})
# Continue conversation with tool results
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
tools=[...],
messages=[
{"role": "user", "content": user_message},
*response.content,
*results
]
)
return response
Notice that the message history grows with each turn. You must pass the full history including Claude’s previous tool_use blocks and your tool_result blocks. If you omit prior turns, Claude loses context and may re-request tools it already called.
Handling Tool Results
When Claude requests a tool call, you receive a tool_use block containing the function name and arguments. You must execute the function and return the results in a specific format:
Tool result format
tool_result = {
"type": "tool_result",
"tool_use_id": "toolu_xxxxx", # Match the ID from the request
"content": "The result of the function call"
}
The content field can be a plain string or a list of content blocks (text or images). If your tool returns structured data, serialize it to a JSON string rather than passing a raw dict. Claude handles text more predictably than opaque objects.
For error cases, you can signal failure explicitly:
tool_result_error = {
"type": "tool_result",
"tool_use_id": "toolu_xxxxx",
"content": "Error: database connection timed out after 5 seconds",
"is_error": True
}
Setting is_error: True helps Claude understand it should not assume the result is valid data. Claude will typically acknowledge the failure and either try a different approach or ask the user for clarification.
Best Practices for Reliable Function Calling
- Design Clear Tool Descriptions
The quality of Claude’s tool usage heavily depends on your tool definitions:
Good tool description
{
"name": "search_documents",
"description": "Search through indexed documents. Use this when the user asks about company policies, procedures, or historical information.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"},
"limit": {"type": "integer", "description": "Max results (default 10)"}
},
"required": ["query"]
}
}
A common mistake is writing a description that explains what the tool does mechanically without explaining when to use it. Claude makes tool selection decisions based on natural language reasoning, so phrases like “Use this when the user asks about…” provide much more useful signal than just “Returns documents matching a query.”
- Handle Errors Gracefully
Always implement error handling for tool executions:
def execute_tool(name, arguments):
try:
if name == "get_weather":
return get_weather(arguments)
elif name == "send_email":
return send_email(arguments)
else:
return {"error": f"Unknown tool: {name}"}
except Exception as e:
return {"error": str(e)}
Return errors as strings rather than raising exceptions. If your tool executor crashes before returning a result, you cannot provide that result back to Claude and the conversation will stall. Catch exceptions and return a descriptive error message that Claude can reason about.
- Implement Proper Authentication
When tools require authentication, pass credentials securely:
def execute_api_call(tool_name, params, auth_token):
headers = {
"Authorization": f"Bearer {auth_token}",
"Content-Type": "application/json"
}
response = requests.post(
f"https://api.example.com/{tool_name}",
json=params,
headers=headers
)
return response.json()
Credentials should never appear in tool definitions or descriptions. Claude’s context is not a secrets store. Keep authentication entirely within your execution layer where it cannot be logged or exposed through the API response.
- Set Appropriate Token Limits
Tool results can be large. If you return a 50,000-character database dump as a tool result, you will consume a significant portion of the context window before Claude has a chance to respond. Apply pagination or truncation at the tool level rather than relying on Claude to request smaller results.
def search_database(query, limit=10, offset=0):
results = db.query(query, limit=limit, offset=offset)
return {
"results": results,
"total": db.count(query),
"has_more": db.count(query) > offset + limit
}
By including pagination metadata in the response, you give Claude the information it needs to request additional pages if the user’s question requires a broader search.
Advanced Patterns
Chaining Multiple Tools
For complex workflows, you can chain multiple tool calls. Claude can request multiple tools in a single response when the tasks are independent:
Claude can request multiple tools in one response
for tool_call in response.content:
if tool_call.type == "tool_use":
# Execute and collect all results before continuing
all_results.append(execute_tool(tool_call.name, tool_call.input))
When Claude returns multiple tool_use blocks in a single response, execute all of them and return all results together in the next message. Do not make a separate API call for each result. this wastes tokens and can confuse Claude’s context tracking.
Parallel Tool Execution
If multiple tool calls in a single response are independent of each other, execute them in parallel to reduce latency:
import asyncio
async def execute_tool_async(tool_call):
result = await run_in_executor(execute_tool, tool_call.name, tool_call.input)
return {
"type": "tool_result",
"tool_use_id": tool_call.id,
"content": result
}
async def handle_tool_calls(tool_calls):
tasks = [execute_tool_async(tc) for tc in tool_calls if tc.type == "tool_use"]
return await asyncio.gather(*tasks)
For a workflow that calls three independent APIs, parallel execution cuts latency by roughly two-thirds. This matters especially for user-facing applications where response time is visible.
Tool Selection Strategies
Control which tools Claude uses by structuring your tool descriptions strategically:
- Use explicit descriptions explaining when each tool applies
- Avoid overlapping functionality between tools
- Provide fallback options in tool descriptions when appropriate
When two tools have similar descriptions, Claude may pick the wrong one or alternate between them in ways that are hard to predict. If you have a search_products tool and a search_inventory tool, make clear in both descriptions what distinguishes them. “Search products: use for customer-facing product catalog and pricing” vs “Search inventory: use for warehouse stock levels and SKU availability” removes ambiguity.
Building a Tool Registry
For applications with many tools, a registry pattern helps keep definitions organized:
TOOL_REGISTRY = {}
def register_tool(name, description, schema, handler):
TOOL_REGISTRY[name] = {
"definition": {
"name": name,
"description": description,
"input_schema": schema
},
"handler": handler
}
def get_tool_definitions():
return [v["definition"] for v in TOOL_REGISTRY.values()]
def execute_tool(name, arguments):
if name not in TOOL_REGISTRY:
return {"error": f"Unknown tool: {name}"}
return TOOL_REGISTRY[name]["handler"](arguments)
This approach makes it easy to add, remove, or modify tools without changing the main loop logic. It also enables dynamic tool loading where the available tools change based on the user’s permissions or the application’s current state.
Comparison: Tool Use Approaches
Different patterns suit different use cases. Here is a summary of the main approaches:
| Pattern | Best For | Tradeoff |
|---|---|---|
| Single tool, auto selection | Simple assistants with one external capability | Easy to implement, limited flexibility |
| Multiple tools, auto selection | General assistants needing diverse capabilities | Requires good descriptions to avoid mismatch |
| Forced tool use | Structured data extraction pipelines | Predictable but removes Claude’s judgment |
| Parallel tool execution | High-throughput workflows with independent calls | Reduces latency, adds implementation complexity |
| Tool registry | Large applications with 10+ tools | Organized, testable, higher setup cost |
Common Pitfalls to Avoid
-
Missing tool descriptions: Always include clear descriptions of what each tool does and when to use it
-
Incorrect parameter types: Ensure your input_schema matches exactly what your function expects
-
Not handling tool_use stop reason: Always check for
stop_reason == "tool_use"to detect tool calls -
Forgetting tool_use_id: When returning results, always include the matching tool_use_id
-
Blocking tool calls: Ensure your application can handle tool requests without blocking the conversation flow
-
Returning raw exceptions: Catch all exceptions in your tool executor and return error strings. uncaught exceptions break the conversation loop
-
Truncating message history: Pass the complete message history including all prior tool_use and tool_result blocks on every turn
-
Oversized tool results: Paginate or summarize large results rather than returning entire datasets
Real-World Example: A Customer Support Agent
To see these patterns working together, consider a customer support agent that can look up order status, check inventory, and escalate tickets:
tools = [
{
"name": "get_order_status",
"description": "Look up the status of a customer order by order ID. Use when the customer asks about their order, shipment, or delivery.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "The order ID from the customer's confirmation email"}
},
"required": ["order_id"]
}
},
{
"name": "check_inventory",
"description": "Check current stock levels for a product SKU. Use when the customer asks about product availability or wants to know if an item is in stock.",
"input_schema": {
"type": "object",
"properties": {
"sku": {"type": "string", "description": "Product SKU code"},
"warehouse": {"type": "string", "enum": ["east", "west", "central"], "description": "Warehouse region"}
},
"required": ["sku"]
}
},
{
"name": "create_support_ticket",
"description": "Escalate an issue to the human support team. Use when the customer's issue cannot be resolved through lookup tools, when they are frustrated, or when they explicitly request a human agent.",
"input_schema": {
"type": "object",
"properties": {
"customer_id": {"type": "string"},
"issue_summary": {"type": "string", "description": "Brief description of the issue"},
"priority": {"type": "string", "enum": ["low", "medium", "high", "urgent"]}
},
"required": ["customer_id", "issue_summary", "priority"]
}
}
]
The descriptions guide Claude toward the right tool in each scenario without requiring you to write explicit routing logic. Claude handles cases like “my package hasn’t arrived and I’m really angry” by calling get_order_status first, then escalating via create_support_ticket if the situation warrants it. all based on reading the conversation and the tool descriptions you provided.
Conclusion
Tool use and function calling transform Claude from a conversational AI into a powerful agent capable of taking real actions. By following the patterns and best practices in this guide, you can build reliable integrations that use Claude’s decision-making capabilities alongside your existing systems and services.
Start with simple tool definitions, test thoroughly, and gradually add complexity as you become comfortable with the patterns. The key to success lies in well-structured tool descriptions and solid error handling in your implementation. Pay particular attention to message history management in multi-turn workflows, and always keep authentication and authorization in your execution layer where you have full control over what actually runs.
Related Reading
- Advanced Claude Skills with Tool Use and Function Calling
- Claude Code Agent Task Queue Architecture Deep Dive
- Claude Code Astro Islands Architecture Workflow Deep Dive
- FastAPI Pydantic V2 Validation with Claude Code
Built by theluckystrike. More at zovo.one
Fix it instantly → Paste your error into our Error Diagnostic Tool for step-by-step resolution.
See Also
Estimate tokens → Calculate your usage with our Token Estimator.
Try it: Estimate your monthly spend with our Cost Calculator.