Tool Use Patterns
Quick Guide: Tool use (function calling) lets LLMs invoke external functions. The universal pattern is: define tool schemas (JSON Schema for parameters) -> send tools + message to LLM -> detect tool_use in response -> execute locally -> return result to LLM -> repeat until the model responds with text. Guard every loop with a max-step limit, validate all tool inputs before execution, and return structured errors so the model can recover. Use tool choice control (auto,required,none, specific tool) to steer model behavior.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
(You MUST guard every tool loop with a maximum step limit -- unbounded loops risk infinite API calls and runaway costs)
(You MUST validate all tool input arguments before execution -- LLM-generated arguments are untrusted input)
(You MUST return structured error messages to the model when tool execution fails -- never silently swallow errors or return empty results)
(You MUST use JSON Schema for tool parameter definitions -- all major providers require this format)
(You MUST treat tool definitions as token cost -- every tool schema is sent on every API call, so keep descriptions concise but precise)
</critical_requirements>
Auto-detection: tool use, function calling, tool_calls, tool_use, tool call loop, agent loop, tool definition, tool schema, toolChoice, tool_choice, parallel tool calls, human-in-the-loop, tool approval, agentic workflow, multi-step agent, tool result, tool error
When to use:
- Implementing LLM tool calling / function calling in any provider
- Building agent loops that call tools iteratively until a task is complete
- Handling parallel tool calls (multiple tools in one response)
- Reporting tool errors back to the model for recovery
- Controlling tool selection (auto, required, none, force specific)
- Adding human approval gates before dangerous tool execution
- Streaming responses that include tool calls
Key patterns covered:
- Tool definition schemas (JSON Schema for parameters, descriptions)
- The core tool call loop (send -> detect -> execute -> return -> re-send)
- Parallel tool calls (handling multiple calls in one response)
- Error handling (reporting tool failures back to the model)
- Tool choice control (auto, required, none, specific tool)
- Multi-step agent workflows with conversation state
- Human-in-the-loop approval patterns
- Type-safe tool definitions in TypeScript
- Security (input validation, sandboxing, least privilege)
- Streaming with tool calls
When NOT to use:
- Simple text generation without tool calling -- no tools needed
- Structured output / JSON extraction -- use your provider's structured output feature instead
- Provider-specific SDK patterns -- use your provider's SDK skill for SDK-specific APIs
Detailed Resources:
- [examples/core.md](examples/core.md) -- Tool definitions, the tool call loop, error handling, type-safe tools
- [examples/advanced.md](examples/advanced.md) -- Parallel tool calls, multi-step agents, human-in-the-loop, streaming, security
- [reference.md](reference.md) -- Decision frameworks, provider comparison, anti-pattern checklist
<philosophy>
Philosophy
Tool use is the mechanism that turns LLMs from text generators into agents. The model cannot execute code, query databases, or call APIs -- it can only _request_ that your code does so by emitting structured tool calls. Your code is the executor; the model is the planner.
Core principles:
- The model plans, you execute -- The LLM emits tool call requests with structured arguments. Your code validates, executes, and returns results. Never let the model execute arbitrary code directly.
- Agents are loops -- Every agent, from a simple weather bot to a complex coding assistant, follows the same loop: LLM decides -> system executes -> results feed back -> repeat. Complexity comes from the tools and state, not the loop itself.
- Tools are schemas -- A tool definition is a JSON Schema that tells the model what function exists, what parameters it takes, and when to use it. Better descriptions produce better tool selection and argument quality.
- Errors are information -- When a tool fails, return a structured error message to the model. The model can often recover by retrying with different arguments, choosing a different tool, or explaining the failure to the user.
- Defense in depth -- LLM-generated arguments are untrusted input. Validate schemas, enforce types, limit argument ranges, sandbox execution, and require approval for dangerous operations.
When to use tool calling:
- The task requires real-world data the model doesn't have (weather, database, APIs)
- The task requires side effects (sending email, creating records, file operations)
- The task requires multi-step reasoning with intermediate data lookups
- The task requires computation the model can't do reliably (math, code execution)
When NOT to use tool calling:
- The model can answer from its training data alone
- You only need structured JSON output (use structured output features instead)
- The "tool" is just prompt engineering disguised as a function
- You want deterministic behavior (tool calling adds non-determinism from model decisions)
</philosophy>
<patterns>
Core Patterns
Pattern 1: Tool Definition Schema
Every tool definition has three parts: a name, a description, and a parameter schema. The description is the most important part -- it guides the model's decision to call the tool and how it constructs arguments.
// Provider-agnostic tool definition shape
interface ToolDefinition {
name: string; // ^[a-zA-Z0-9_-]{1,64}$
description: string; // When and why to use this tool
parameters: JsonSchema; // JSON Schema for input arguments
}
Good Tool Definition
// Good: precise description, constrained parameters, .describe() on each property
const VALID_UNITS = ["celsius", "fahrenheit"] as const;
const getWeatherTool: ToolDefinition = {
name: "get_current_weather",
description:
"Get the current weather conditions for a specific city. " +
"Returns temperature, humidity, and conditions. " +
"Use this when the user asks about current weather, NOT forecasts.",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "City name and optional country code, e.g. 'London, UK'",
},
unit: {
type: "string",
enum: VALID_UNITS,
description: "Temperature unit. Defaults to celsius if not specified.",
},
},
required: ["location"],
},
};
Why good: Description explains what the tool returns, when to use it, and when NOT to use it. Parameters have descriptions and constraints (enum). Named constant for valid values.
Bad Tool Definition
// Bad: vague description, no parameter descriptions, no constraints
const weatherTool: ToolDefinition = {
name: "weather",
description: "Gets weather", // BAD: too vague
parameters: {
type: "object",
properties: {
loc: { type: "string" }, // BAD: no description, cryptic name
u: { type: "string" }, // BAD: no enum constraint
},
},
};
Why bad: Vague description causes incorrect tool selection, no parameter descriptions cause malformed arguments, no enum constraint causes invalid values, cryptic parameter names confuse the model
Pattern 2: The Core Tool Call Loop
The fundamental pattern for tool use. Send a message with tool definitions, check if the response contains tool calls, execute them, return results, and let the model generate a final response. See [examples/core.md](examples/core.md) for the complete implementation.
``