Use this skill when building agentic loops with Claude tool use — defining tools, handling tool calls, parallel execution, and multi-step reasoning.
64
78%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./external/anthropic-official/tool-use-orchestration/SKILL.mdComplete patterns for Claude tool use: defining tools, running the agentic loop, handling parallel tool calls, error handling, and building reliable multi-step workflows.
import anthropic
import json
client = anthropic.Anthropic()
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"]
}
}
]
def get_weather(location: str, unit: str = "celsius") -> dict:
# Replace with real weather API
return {"temp": 22, "conditions": "sunny", "location": location}def run_agent(user_message: str, tools: list, tool_handlers: dict) -> str:
"""Run a complete agentic loop until end_turn."""
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=4096,
tools=tools,
messages=messages,
)
# Collect text output
text_outputs = [
b.text for b in response.content
if hasattr(b, "text")
]
if response.stop_reason == "end_turn":
return "\n".join(text_outputs)
if response.stop_reason != "tool_use":
raise ValueError(f"Unexpected stop reason: {response.stop_reason}")
# Process all tool calls (may be parallel)
tool_results = []
for block in response.content:
if block.type == "tool_use":
handler = tool_handlers.get(block.name)
if not handler:
result = {"error": f"Unknown tool: {block.name}"}
else:
try:
result = handler(**block.input)
except Exception as e:
result = {"error": str(e)}
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result),
})
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})import asyncio
from concurrent.futures import ThreadPoolExecutor
async def execute_tools_parallel(tool_calls: list, handlers: dict) -> list:
"""Execute multiple tool calls in parallel."""
loop = asyncio.get_event_loop()
with ThreadPoolExecutor() as executor:
tasks = [
loop.run_in_executor(
executor,
lambda tc=tc: handlers[tc.name](**tc.input)
)
for tc in tool_calls
]
results = await asyncio.gather(*tasks, return_exceptions=True)
return [
{
"type": "tool_result",
"tool_use_id": tc.id,
"content": json.dumps(r) if not isinstance(r, Exception) else f"Error: {r}",
"is_error": isinstance(r, Exception),
}
for tc, r in zip(tool_calls, results)
]| Pattern | When to use |
|---|---|
| Basic loop | Single tool, sequential |
| Parallel | Multiple independent tool calls |
is_error: true | Tool failed, let Claude retry |
tool_choice: {"type": "tool", "name": "X"} | Force specific tool |
tool_choice: {"type": "none"} | Prevent tool use |
32f0050
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.