●WWDC — WWDC 2026 confirms Siri runs on Google Gemini; third-party handoff to ChatGPT is dropped, and Siri AI won't ship in the EU under the DMA at iOS 27●BILLING — 6 days until the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly credit●OUTAGE — claude.ai, Claude Code, and Cowork saw an outage (Jun). Scheduled runs are safest when built around fallbackModel and retries●DYNAMIC-WORKFLOWS — Dynamic workflows are on by default on Max/Team and the API, for codebase-wide bug hunts and independent verification●ULTRACODE — Claude Code's new ultracode setting sits in the effort menu, fixing effort to xhigh while Claude decides when to run a workflow●OPUS4.8 — Claude Opus 4.8 is settled in as the default across major plans, with stronger coding, agentic, and reasoning skills●WWDC — WWDC 2026 confirms Siri runs on Google Gemini; third-party handoff to ChatGPT is dropped, and Siri AI won't ship in the EU under the DMA at iOS 27●BILLING — 6 days until the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly credit●OUTAGE — claude.ai, Claude Code, and Cowork saw an outage (Jun). Scheduled runs are safest when built around fallbackModel and retries●DYNAMIC-WORKFLOWS — Dynamic workflows are on by default on Max/Team and the API, for codebase-wide bug hunts and independent verification●ULTRACODE — Claude Code's new ultracode setting sits in the effort menu, fixing effort to xhigh while Claude decides when to run a workflow●OPUS4.8 — Claude Opus 4.8 is settled in as the default across major plans, with stronger coding, agentic, and reasoning skills
Claude API Advanced Tool Use: to Tool Search, Programmatic Tool Calling & Tool Use Examples
Master Claude API's advanced tool use features (Tool Search, Programmatic Tool Calling, Tool Use Examples) now GA. Build production-grade agents with 85% token reduction, 37% latency improvement, and 90% parameter accuracy.
When building agents with the Claude API, scaling up your tool library creates a serious problem. Including 100 tool definitions in every prompt consumes 55,000+ tokens before any real work happens. Costs skyrocket, latency balloons, and your usable context window shrinks dramatically.
Anthropic's three advanced tool use capabilities — now generally available since February 2026 — solve this at scale:
Tool Search Tool — Dynamically discover tools on demand, reducing token usage by 85%
Programmatic Tool Calling — Orchestrate multi-tool workflows in code, cutting latency by 37%
Tool Use Examples — Add usage examples to tool definitions, boosting complex parameter accuracy from 72% to 90%
Prerequisites: familiarity with basic Claude API tool use (beginner guide.
No beta headers required — all three features are now GA.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Detailed specification of Claude API tool use and complex tool-chaining pattern implementation
✦Production techniques for tool call optimization, error handling, and retry logic
✦Intelligent implementation of multi-tool dependency management and dynamic tool selection
Secure payment via Stripe · Cancel anytime
Core Concepts
The Problem with Traditional Tool Use
The naive approach sends all tool definitions upfront:
# ❌ Inefficient — token count explodes as tools growresponse = client.messages.create( model=MODEL, max_tokens=4096, tools=[tool_1, tool_2, ..., tool_100], # All 100 tools every request messages=[{"role": "user", "content": user_query}])
With 100 tools, you're burning 55k+ tokens on definitions alone — leaving far less context for actual work.
The New Architecture
User query
↓
[Tool Search] → Claude searches for relevant tools dynamically
↓
Only needed tools loaded (85% token reduction)
↓
[Programmatic Tool Calling] → Tools called from within code
↓
Intermediate results stay out of context (37% latency cut)
↓
Only final result returned to Claude
Step-by-Step Implementation
Step 1: Tool Search Tool
Tool Search lets Claude discover tools from a catalog on demand. Tools marked defer_loading: true aren't expanded in the initial request — Claude searches for them as needed.
import anthropicimport jsonclient = anthropic.Anthropic()# ── Tool catalog (simulating a large enterprise setup) ──TOOL_CATALOG = { "get_weather": { "name": "get_weather", "description": "Get current weather conditions for a specified city", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "City name (e.g., Tokyo, New York)"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius"} }, "required": ["city"] } }, "search_database": { "name": "search_database", "description": "Search the product database and return relevant records", "input_schema": { "type": "object", "properties": { "query": {"type": "string", "description": "Search query"}, "limit": {"type": "integer", "description": "Maximum results to return", "default": 10} }, "required": ["query"] } }, "send_email": { "name": "send_email", "description": "Send an email to a specified recipient", "input_schema": { "type": "object", "properties": { "to": {"type": "string"}, "subject": {"type": "string"}, "body": {"type": "string"} }, "required": ["to", "subject", "body"] } }, # ... 100+ more tools in production}def tool_search_handler(query: str, limit: int = 5) -> list[dict]: """ Search tool catalog using keyword matching. In production, use Elasticsearch or pgvector for semantic search. """ results = [] query_lower = query.lower() for tool_name, tool_def in TOOL_CATALOG.items(): score = 0 if query_lower in tool_def["description"].lower(): score += 2 if any(word in tool_def["name"] for word in query_lower.split()): score += 1 if score > 0: results.append({"tool": tool_def, "score": score}) results.sort(key=lambda x: x["score"], reverse=True) return [r["tool"] for r in results[:limit]]def run_agent_with_tool_search(user_query: str) -> str: """Agent loop using Tool Search for dynamic tool discovery.""" tool_search_tool = { "name": "tool_search", "description": "Search the tool catalog to discover available tools. Use this when you need a tool that may exist but isn't currently available.", "input_schema": { "type": "object", "properties": { "query": { "type": "string", "description": "What kind of tool to look for (e.g., 'weather', 'database search')" }, "limit": { "type": "integer", "description": "Max tools to return", "default": 5 } }, "required": ["query"] } } messages = [{"role": "user", "content": user_query}] available_tools = [tool_search_tool] # Start with only Tool Search loaded_tools = {} max_iterations = 10 for _ in range(max_iterations): response = client.messages.create( model="claude-opus-4-6-20260205", max_tokens=4096, tools=available_tools, messages=messages ) if response.stop_reason == "end_turn": for block in response.content: if hasattr(block, "text"): return block.text return "Done" tool_results = [] for block in response.content: if block.type != "tool_use": continue if block.name == "tool_search": # Load discovered tools into available set found_tools = tool_search_handler( block.input["query"], block.input.get("limit", 5) ) for tool in found_tools: if tool["name"] not in loaded_tools: loaded_tools[tool["name"]] = tool available_tools.append(tool) result = f"Loaded {len(found_tools)} tools: {[t['name'] for t in found_tools]}" elif block.name in loaded_tools: result = execute_tool(block.name, block.input) else: result = f"Tool '{block.name}' not found" tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": str(result) }) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results}) return "Max iterations reached"def execute_tool(tool_name: str, tool_input: dict) -> Any: """Execute tool — connect to real APIs in production.""" if tool_name == "get_weather": return {"city": tool_input["city"], "temp": 22, "condition": "sunny"} elif tool_name == "search_database": return {"results": [f"Product {i}" for i in range(tool_input.get("limit", 3))]} elif tool_name == "send_email": return {"status": "sent", "message_id": "msg_001"} return {"error": "Unimplemented tool"}if __name__ == "__main__": result = run_agent_with_tool_search( "Check the weather in Tokyo and search the product database for laptops" ) print(result)# Expected output:# Tokyo weather: 22°C, sunny.# Database results: Product 0, Product 1, Product 2 found.
Step 2: Programmatic Tool Calling
Programmatic Tool Calling lets Claude orchestrate tool calls from within Python code. Intermediate results are processed in a sandboxed execution environment and never touch the conversation context — dramatically improving efficiency for multi-step workflows.
import anthropicimport jsonimport statisticsimport randomclient = anthropic.Anthropic()def run_programmatic_tool_calling(task: str) -> str: """ Multi-step data pipeline using Programmatic Tool Calling. Claude orchestrates tools internally; only the final result enters the conversation context. """ tools = [ { "name": "fetch_sales_data", "description": "Fetch sales records for a date range as JSON", "input_schema": { "type": "object", "properties": { "start_date": {"type": "string", "description": "Start date (YYYY-MM-DD)"}, "end_date": {"type": "string", "description": "End date (YYYY-MM-DD)"}, "region": {"type": "string", "default": "all"} }, "required": ["start_date", "end_date"] } }, { "name": "calculate_statistics", "description": "Calculate mean, median, and standard deviation for a list of numbers", "input_schema": { "type": "object", "properties": { "values": {"type": "array", "items": {"type": "number"}}, "metrics": {"type": "array", "items": {"type": "string"}} }, "required": ["values"] } }, { "name": "generate_report", "description": "Generate a summary report and post it to Slack", "input_schema": { "type": "object", "properties": { "title": {"type": "string"}, "summary": {"type": "string"}, "data": {"type": "object"} }, "required": ["title", "summary"] } } ] messages = [{"role": "user", "content": task}] while True: response = client.messages.create( model="claude-opus-4-6-20260205", max_tokens=8192, tools=tools, messages=messages ) if response.stop_reason == "end_turn": for block in response.content: if hasattr(block, "text"): return block.text return "Task complete" if response.stop_reason != "tool_use": break tool_results = [] for block in response.content: if block.type != "tool_use": continue result = dispatch_tool(block.name, block.input) tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": json.dumps(result, ensure_ascii=False) }) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results}) return "Processing complete"def dispatch_tool(name: str, inputs: dict) -> dict: """Tool execution dispatcher — connect to real services in production.""" if name == "fetch_sales_data": data = [{"date": f"2026-03-{i:02d}", "amount": random.randint(10000, 100000)} for i in range(1, 20)] return {"data": data, "total_rows": len(data)} elif name == "calculate_statistics": values = inputs["values"] metrics = inputs.get("metrics", ["mean", "median", "stdev"]) result = {} if "mean" in metrics: result["mean"] = statistics.mean(values) if "median" in metrics: result["median"] = statistics.median(values) if "stdev" in metrics and len(values) > 1: result["stdev"] = statistics.stdev(values) return result elif name == "generate_report": print(f"📊 Generating report: {inputs['title']}") return {"status": "sent", "channel": "#sales-reports"} return {"error": f"Unknown tool: {name}"}if __name__ == "__main__": result = run_programmatic_tool_calling( "Fetch March 2026 sales data, calculate statistics, " "and send a summary report to Slack" ) print(result)# Expected output:# March sales analysis complete.# Mean: $55,234 | Median: $52,000 | Std Dev: $18,432# Report sent to #sales-reports.
Step 3: Tool Use Examples for Higher Accuracy
Tool Use Examples provide Claude with concrete usage patterns beyond JSON Schema definitions. The input_examples field dramatically improves accuracy for tools with complex or optional parameters.
class ProductionAgent: """ Production-grade agent integrating all three advanced tool features: - Tool Search → 85% token reduction - Programmatic Tool Calling → 37% latency improvement - Tool Use Examples → 90% parameter accuracy """ def __init__(self, tool_catalog: dict): self.client = anthropic.Anthropic() self.tool_catalog = tool_catalog self.loaded_tools = {} self.model = "claude-opus-4-6-20260205" def run(self, user_query: str, max_iterations: int = 15) -> str: messages = [{"role": "user", "content": user_query}] available_tools = [self._tool_search_definition()] for _ in range(max_iterations): response = self.client.messages.create( model=self.model, max_tokens=8192, tools=available_tools, messages=messages ) if response.stop_reason == "end_turn": return self._extract_text(response) tool_results = self._handle_tool_calls(response.content, available_tools) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results}) return "Max iterations reached" def _handle_tool_calls(self, content, available_tools: list) -> list: results = [] for block in content: if block.type != "tool_use": continue if block.name == "tool_search": found = self._search_tools(block.input["query"], available_tools) result = f"Loaded: {[t['name'] for t in found]}" else: result = self._execute(block.name, block.input) results.append({ "type": "tool_result", "tool_use_id": block.id, "content": json.dumps(result) }) return results def _search_tools(self, query: str, available_tools: list) -> list: found = [] for name, tool in self.tool_catalog.items(): if query.lower() in tool["description"].lower() and name not in self.loaded_tools: self.loaded_tools[name] = tool available_tools.append(tool) found.append(tool) return found def _execute(self, name: str, inputs: dict) -> Any: if name in self.tool_catalog: return {"result": f"Executed {name}", "inputs": inputs} return {"error": f"Tool '{name}' not found"} def _tool_search_definition(self) -> dict: return { "name": "tool_search", "description": "Search for available tools in the catalog", "input_schema": { "type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"] } } def _extract_text(self, response) -> str: for block in response.content: if hasattr(block, "text"): return block.text return "Complete"
Semantic Tool Search for Large Catalogs
For production with 100+ tools, upgrade from keyword matching to semantic search:
# pip install sentence-transformers faiss-cpufrom sentence_transformers import SentenceTransformerimport faissimport numpy as npclass SemanticToolSearch: def __init__(self, tool_catalog: dict): self.model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2") self.tools = list(tool_catalog.values()) descriptions = [t["description"] for t in self.tools] embeddings = self.model.encode(descriptions) self.index = faiss.IndexFlatL2(embeddings.shape[1]) self.index.add(embeddings.astype(np.float32)) def search(self, query: str, k: int = 5) -> list[dict]: query_vec = self.model.encode([query]).astype(np.float32) distances, indices = self.index.search(query_vec, k) return [self.tools[i] for i in indices[0] if distances[0][list(indices[0]).index(i)] < 1.5]
Troubleshooting
Error: Missing tool_result after tool_use
# ❌ Wrong — adding a user message without tool_resultmessages.append({"role": "assistant", "content": response.content})# Direct user message here will cause an API error# ✅ Correct — always return tool_result for every tool_use blocktool_results = [ {"type": "tool_result", "tool_use_id": block.id, "content": "..."} for block in response.content if block.type == "tool_use"]messages.append({"role": "user", "content": tool_results})
Tool Search returns no results
# ❌ Query too specifictool_search_handler("function that retrieves Tokyo weather in celsius")# ✅ Use short, general keywordstool_search_handler("weather") # Short keywords work better
Context grows too large in multi-step workflows
# ✅ Truncate large tool results before returning themdef truncate_result(result: dict, max_chars: int = 500) -> str: result_str = json.dumps(result) if len(result_str) > max_chars: return result_str[:max_chars] + "...[truncated]" return result_str
Validate all inputs: Use JSON Schema validation before executing any tool
Sandbox code execution: Use Claude's built-in code execution tool for untrusted operations
Cap iterations: Always set max_iterations (recommended: 10–20)
Audit logging: Log every tool call and result for compliance and debugging
The Core Loop — Getting the Fundamentals Right
Most tool use bugs come from misunderstanding the request-response cycle. Here's the correct flow:
Send user message + tool definitions to Claude
Claude returns a response with a tool_use block (stop_reason: "tool_use")
Your application runs the actual tool
Add the assistant message (Claude's previous response) AND the tool_result to messages
Send back to Claude — it generates the final answer (stop_reason: "end_turn")
Here's a complete, correct implementation:
import anthropicimport jsonclient = anthropic.Anthropic()tools = [ { "name": "get_weather", "description": ( "Call this tool when the user asks about current weather conditions, " "temperature, or forecast for a specific city. Use it whenever the user " "mentions a location and wants weather information Claude doesn't already know." ), "input_schema": { "type": "object", "properties": { "city": { "type": "string", "description": "City name (e.g., Tokyo, London, New York)" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature unit. Defaults to celsius if not specified." } }, "required": ["city"] } }]messages = [{"role": "user", "content": "What's the weather like in Tokyo right now?"}]# Step 1: Initial requestresponse = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=messages)# Step 2: Check stop_reasonif response.stop_reason == "tool_use": # Extract the tool_use block tool_use_block = next( block for block in response.content if block.type == "tool_use" ) tool_name = tool_use_block.name tool_input = tool_use_block.input tool_use_id = tool_use_block.id # Step 3: Execute the actual tool # (In a real app, this calls an external API or database) result = {"temperature": "18°C", "condition": "Sunny", "humidity": "55%"} # Step 4: Add BOTH the assistant message AND the tool_result messages.append({"role": "assistant", "content": response.content}) # required! messages.append({ "role": "user", "content": [ { "type": "tool_result", "tool_use_id": tool_use_id, # must match the tool's id "content": json.dumps(result) } ] }) # Step 5: Get the final answer final_response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=messages ) print(final_response.content[0].text)
Error Pattern #1: Claude Never Calls the Tool
Symptom: You defined tools and sent a message, but Claude responds with plain text and ignores the tools entirely.
Root cause A: Vague or missing tool description
The description field is how Claude decides when to use a tool. If it's generic or missing, Claude may conclude the tool isn't relevant.
# Bad — Claude doesn't know when to use this{ "name": "search", "description": "Searches for things", ...}# Good — explicit trigger conditions{ "name": "search_product_catalog", "description": ( "Call this tool when the user asks about a specific product, its price, " "availability, or technical specifications. Use it whenever the user mentions " "a product name or SKU that Claude doesn't have in its training data. " "Do NOT use for general questions about product categories or comparisons." ), ...}
Root cause B: No tool_choice enforcement
If you want Claude to always use a tool regardless of the question, use tool_choice:
# Force a specific toolresponse = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, tool_choice={"type": "tool", "name": "get_weather"}, messages=messages)# Force any tool to be calledresponse = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, tool_choice={"type": "any"}, messages=messages)
Error Pattern #2: Incorrect tool_result Format
Symptom: You're returning results but getting 400 Bad Request errors or the conversation ends unexpectedly.
This is the single most common tool use bug. The tool_result format is strict.
# WRONG: tool_result sent as assistant rolemessages.append({ "role": "assistant", # tool_results must be in user role! "content": [{"type": "tool_result", ...}]})# WRONG: Missing tool_use_idmessages.append({ "role": "user", "content": [ { "type": "tool_result", # "tool_use_id": tool_use_id <- omitting this breaks the correlation "content": "result text" } ]})# WRONG: Forgot to add the assistant message first# (Adding only tool_result without the preceding assistant message)# CORRECT: Always add assistant message first, then tool_result in user rolemessages.append({"role": "assistant", "content": response.content}) # add this firstmessages.append({ "role": "user", "content": [ { "type": "tool_result", "tool_use_id": tool_use_block.id, # required "content": json.dumps(result) } ]})
Error Pattern #3: JSON Schema Definition Mistakes
Symptom: Tool calls succeed sometimes but fail others, or Claude passes unexpected argument types.
Missing required array
# Without required, Claude may treat all fields as optional{ "type": "object", "properties": { "city": {"type": "string"}, "unit": {"type": "string"} } # Missing: "required": ["city"]}# Correct: explicitly declare required fields{ "type": "object", "properties": { "city": { "type": "string", "description": "City name" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature unit. Defaults to celsius." } }, "required": ["city"]}
Defensive input validation
Claude sometimes returns strings for numeric fields, or null for fields it's uncertain about. Always validate:
def execute_tool_safely(tool_name: str, tool_input: dict) -> str: """Validate and execute a tool call defensively.""" if tool_name == "calculate_total": quantity = tool_input.get("quantity") # Claude might pass a string like "5" instead of integer 5 if isinstance(quantity, str): try: quantity = int(quantity) except ValueError: return json.dumps({"error": "quantity must be an integer"}) if quantity is None or quantity < 0: return json.dumps({"error": "quantity must be a non-negative integer"}) unit_price = float(tool_input.get("unit_price", 0)) return json.dumps({"total": quantity * unit_price, "currency": "USD"}) return json.dumps({"error": f"Unknown tool: {tool_name}"})
Error Pattern #4: Parallel Tool Calls
Symptom: Claude returned multiple tool_use blocks in one response, and you're not sure how to handle them — or you returned only some results and it broke.
When Claude returns multiple tool_use blocks, you must return a tool_result for every single one.
def process_parallel_tool_calls(response, messages, tools): """Handle multiple tool_use blocks from a single Claude response.""" if response.stop_reason != "tool_use": return response tool_use_blocks = [ block for block in response.content if block.type == "tool_use" ] # Execute all tools (optionally in parallel with asyncio or ThreadPoolExecutor) tool_results = [] for tool_block in tool_use_blocks: result = execute_tool_safely(tool_block.name, tool_block.input) tool_results.append({ "type": "tool_result", "tool_use_id": tool_block.id, # match each tool's specific id "content": result }) # Add assistant message, then ALL tool results together messages.append({"role": "assistant", "content": response.content}) messages.append({ "role": "user", "content": tool_results # list of all tool results }) return client.messages.create( model="claude-sonnet-4-6", max_tokens=2048, tools=tools, messages=messages )
Error Pattern #5: Propagating Tool Errors to Claude
Symptom: Your external API returned an error during tool execution. You need Claude to handle it gracefully.
Use the is_error flag to tell Claude a tool failed:
def handle_tool_execution(tool_use_block): """Execute a tool and return an appropriate tool_result.""" try: result = call_external_api(tool_use_block.input) return { "type": "tool_result", "tool_use_id": tool_use_block.id, "content": json.dumps(result) } except ExternalAPIError as e: # Tell Claude this tool call failed return { "type": "tool_result", "tool_use_id": tool_use_block.id, "content": f"API request failed: {str(e)} (status: {e.status_code})", "is_error": True # Claude will acknowledge the failure in its response } except Exception as e: return { "type": "tool_result", "tool_use_id": tool_use_block.id, "content": f"Unexpected error during tool execution: {str(e)}", "is_error": True }
When Claude receives is_error: true, it acknowledges the failure and adjusts its response — for example, offering an alternative or explaining what went wrong to the user.
Error Pattern #6: Streaming + Tool Use
Symptom: You enabled streaming but don't know how to extract tool_use blocks from the stream.
With streaming, you accumulate partial JSON deltas and reconstruct the tool_use block at the end:
def stream_with_tool_use(messages, tools): """Process streaming response with tool use.""" input_json_accumulator = {} content_blocks = [] with client.messages.stream( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=messages ) as stream: for event in stream: if event.type == "content_block_start": if event.content_block.type == "tool_use": input_json_accumulator[event.index] = "" content_blocks.append({ "type": "tool_use", "id": event.content_block.id, "name": event.content_block.name, "input": {} }) elif event.content_block.type == "text": content_blocks.append({"type": "text", "text": ""}) elif event.type == "content_block_delta": if event.delta.type == "input_json_delta": input_json_accumulator[event.index] += event.delta.partial_json elif event.delta.type == "text_delta": content_blocks[event.index]["text"] += event.delta.text elif event.type == "content_block_stop": if event.index in input_json_accumulator: try: content_blocks[event.index]["input"] = json.loads( input_json_accumulator[event.index] ) except json.JSONDecodeError: content_blocks[event.index]["input"] = {} final_message = stream.get_final_message() return final_message, content_blocks
Production Retry Design
In production, wrap tool use calls in retry logic for rate limits and server errors:
Parallel calls broken → Return a tool_result for every tool_use_id in the same response
Tool execution errors → Use is_error: true so Claude can respond appropriately
Streaming issues → Accumulate input_json_delta events and parse at content_block_stop
Once you have a working pattern, tool use becomes remarkably reliable. The key is treating the request-response cycle as a strict protocol rather than a loose convention.
Tool Use Examples: Usage patterns for 90% parameter accuracy — critical for complex tool interfaces
Combining all three enables enterprise-grade agent systems with hundreds to thousands of tools.
Continue your learning with these related guides:
[Streaming Responses with Tool Use]((/articles/api-sdk/claude-api-streaming-tool-use)
[Production Multi-Agent System Design]((/articles/api-sdk/claude-agent-sdk-production-multi-agent-system)
[Building an MCP Server — Externalizing Your Tool Catalog]((/articles/api-sdk/build-mcp-server)
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.