Agents/Tool Use/ — Function Calling & Tool Infrastructure
60+ agent tools and the infrastructure for LLM function calling
The tool-use sub-module implements OpenClaw’s complete library of agent tools and the infrastructure that enables LLMs to call them. It handles tool call normalization, argument validation and repair, parameter type checking, action gating, error propagation, result truncation, and context-window guards — turning raw LLM function-call requests into safe, validated tool executions.
Each of the 60+ tools follows a consistent contract: validate inputs via readStringParam() and TypeBox schemas, check authorization via action gates, execute the operation, and return a structured result within configured size limits.
Function Calling Schema Validation Action Gating Error Recovery
📚 What is this & why does it exist?
The tool-use sub-module is the bridge between what an LLM wants to do and what actually happens. When an LLM decides it needs to fetch a web page, spawn a sub-agent, generate an image, or run a cron job, this module validates the request, checks permissions, repairs any malformed arguments, executes the tool, and returns the result in a format the LLM can understand. Without it, function calling would be unreliable and insecure.
It covers every stage from the moment the LLM emits a tool_call JSON object to the moment the result is injected back into the conversation context — including the surprisingly common case where the LLM produces syntactically broken JSON that needs automatic repair.
💡 Real-world analogy
Think of a Swiss Army knife with 60+ blades. Each blade is a different tool (web fetch, image generation, cron scheduling, etc.), but you can’t just yank any blade out — there’s a safety mechanism. The tool-use module is the spring-loaded locking system: it checks which blade you asked for, validates that your grip is correct (parameter validation), ensures you’re authorized to use that blade (action gating), and if you try to open the blade at a weird angle (malformed JSON), it auto-corrects your grip before the blade deploys. If something goes wrong, it safely folds the blade back and reports what happened.
Contents
Architecture
The tool execution pipeline is a linear chain with early-exit error branches. Every tool call from the LLM passes through normalization, argument repair, parameter validation, and action gating before reaching the actual tool handler. Results flow back through truncation and context guards before being returned to the LLM.
flowchart TD LLM["LLM Response"] --> NORM["Tool Call Normalization\nattempt.tool-call-normalization.ts"] NORM --> REPAIR["Argument Repair\nattempt.tool-call-argument-repair.ts"] REPAIR --> VALIDATE["Parameter Validation\nreadStringParam / TypeBox schemas"] VALIDATE --> GATE["Action Gate\ncreateActionGate()"] GATE --> DISPATCH["Tool Dispatch\nAnyAgentTool union routing"] DISPATCH --> EXEC["Tool Execution\nIndividual tool handlers"] EXEC --> TRUNC["Result Truncation\nDEFAULT_FETCH_MAX_CHARS"] TRUNC --> GUARD["Context Guard\nWindow boundary check"] GUARD --> LLM
Data Flow
The sequence diagram below shows the full lifecycle of a single tool call, from raw LLM output through normalization, gating, execution, and result delivery. Error branches (400 validation errors, 403 authorization errors) return structured error messages directly to the LLM.
sequenceDiagram
participant LLM as LLM Provider
participant Norm as Tool Call Normalizer
participant Repair as Argument Repair
participant Gate as Action Gate
participant Tool as Tool Handler
participant Trunc as Result Truncation
participant Ctx as Context Guard
LLM->>Norm: Raw tool_call JSON
Norm->>Norm: Normalize argument keys
Norm->>Repair: Normalized call
alt Malformed JSON
Repair->>Repair: Attempt JSON repair
end
Repair->>Gate: Valid tool call
alt Permission denied
Gate-->>LLM: ToolAuthorizationError (403)
end
Gate->>Tool: Authorized call
Tool->>Tool: Execute (web-fetch, spawn, cron, etc.)
alt Tool error
Tool-->>LLM: ToolInputError (400)
end
Tool->>Trunc: Raw result
Trunc->>Trunc: Enforce MAX_CHARS / MAX_BYTES
Trunc->>Ctx: Truncated result
Ctx->>Ctx: Check context window budget
Ctx-->>LLM: Final tool result
Key Classes
The core types and functions below form the backbone of the tool infrastructure. AnyAgentTool is the discriminated union through which every tool call is routed; ToolInputError and ToolAuthorizationError are the standard error types; and createActionGate() is the factory that builds permission boundaries around individual tool actions.
| Class / Function | File | Purpose |
|---|---|---|
AnyAgentTool | tools/common.ts | Union type encompassing all 60+ agent tools for type-safe dispatch |
ToolInputError | tools/common.ts | 400-level error for parameter validation failures; extends Error |
ToolAuthorizationError | tools/common.ts | 403-level error for access control violations; extends ToolInputError |
createActionGate() | tools/common.ts | Factory that creates tool-level allow/deny action gates |
readStringParam() | tools/common.ts | Validates and reads string params with trim, required, and label options |
SessionsSpawnToolSchema | sessions-spawn-tool.ts | TypeBox schema defining the spawn tool’s parameter shape |
web-fetch.ts | tools/ | Fetches web pages with configurable char/byte limits and redirect caps |
web-search.ts | tools/ | Performs web searches and returns structured results |
sessions-spawn-tool.ts | tools/ | Spawns sub-agent sessions with runtime and sandbox mode selection |
cron-tool.ts | tools/ | Manages scheduled tasks: add, update, remove, run, list, status |
attempt.tool-call-normalization.ts | run/ | Normalizes tool call argument keys and formats before dispatch |
attempt.tool-call-argument-repair.ts | run/ | Repairs malformed JSON in tool call arguments from LLM output |
message-tool.ts | tools/ | Sends structured messages between agents and users |
gateway-tool.ts | tools/ | Routes requests through the API gateway with auth propagation |
subagents-tool.ts | tools/ | Manages sub-agent lifecycle: creation, delegation, result collection |
Data Structures
Constants and type definitions that control tool behaviour at compile time and runtime. The discriminated union AnyAgentTool enables exhaustive switch statements; spawn runtime and sandbox mode literals constrain session creation; and fetch/error limit constants set safe defaults.
| Type | Name | Description |
|---|---|---|
| type | AnyAgentTool | Discriminated union of all tool types for exhaustive pattern matching |
| interface | StringParamOptions | Options for readStringParam(): required, trim, label, allowEmpty |
| const | OWNER_ONLY_TOOL_ERROR | Error constant returned when non-owners attempt owner-restricted tools |
| const | SESSIONS_SPAWN_RUNTIMES | ["subagent", "acp"] — available spawn runtime backends |
| const | SESSIONS_SPAWN_SANDBOX_MODES | ["inherit", "require"] — sandbox inheritance modes for spawned sessions |
| const | CRON_ACTIONS | ["status", "list", "add", "update", "remove", "run", "runs", "wake"] |
| const | DEFAULT_FETCH_MAX_CHARS | 50000 — character limit for web fetch tool results |
| const | DEFAULT_FETCH_MAX_RESPONSE_BYTES | 2000000 — byte limit for web fetch responses |
| const | DEFAULT_FETCH_MAX_REDIRECTS | 3 — maximum HTTP redirect hops |
| const | DEFAULT_TOOL_ALLOW | Default allowlist of 16 tool actions permitted for agents |
| const | ACP_SPAWN_MODES | ["run", "session"] — ACP spawn execution modes |
| const | ACP_SPAWN_SANDBOX_MODES | ["inherit", "require"] — ACP sandbox enforcement modes |
Tool Categories
Tools are organized into six functional categories. Each category groups tools that share a common domain, and tools within a category often share utility functions, error types, and configuration constants.
| Category | Tools | Description |
|---|---|---|
| Web | web-fetch, web-search, web-tools | HTTP fetching, search engine queries, and web utilities |
| Session | sessions-spawn, sessions-send, sessions-list, sessions-history, sessions-yield, session-status | Sub-agent session lifecycle management |
| Media | image, image-generate, canvas, pdf, tts | Image processing, generation, document handling, text-to-speech |
| Data | nodes, nodes-commands, nodes-media | Structured data node operations and media attachments |
| System | cron, gateway, agent-step, agents-list, subagents | Scheduling, API gateway, agent orchestration |
| Message | message-tool | Structured message delivery between agents and channels |
Interactions
The tool-use sub-module sits at the centre of the agent execution loop, interacting with nearly every other subsystem. Inbound interactions arrive as tool call requests from the LLM streaming pipeline; outbound interactions fan out to web APIs, session managers, sandboxes, and MCP servers.
| Direction | Partner | Interface | Description |
|---|---|---|---|
| → | LLM Providers | Function call protocol | Receives tool call requests from streaming LLM responses |
| → | Streaming Pipeline | attempt.ts | Tool calls are extracted and dispatched from the streaming attempt loop |
| → | Web APIs | HTTP / fetch | Web-fetch and web-search tools make outbound HTTP requests |
| → | Session Manager | SessionManager | Session tools (spawn, send, list) interact with session state |
| → | Sandbox Runtime | Docker / SSH | Sandboxed execution tools delegate to container or SSH backends |
| → | MCP Servers | MCP protocol | MCP bridge tools connect to external Model Context Protocol servers |
| ← | Auth System | createActionGate() | Action gates consult auth profiles for tool-level authorization |
| → | Context Engine | Result truncation | Tool results are truncated to fit within context window budgets |
Configuration
Tool behaviour is controlled through a layered configuration system. Global defaults are defined as module-level constants, while per-tool overrides can be supplied at runtime. Fetch limits, error truncation thresholds, execution host strategies, and security policies are all configurable.
| Knob / Parameter | Default | Description |
|---|---|---|
DEFAULT_FETCH_MAX_CHARS | 50000 | Maximum characters returned by the web-fetch tool |
DEFAULT_FETCH_MAX_RESPONSE_BYTES | 2000000 | Maximum response body size in bytes for web-fetch |
FETCH_MAX_RESPONSE_BYTES_MIN | 32000 | Floor value for configurable fetch byte limit |
FETCH_MAX_RESPONSE_BYTES_MAX | 10000000 | Ceiling value for configurable fetch byte limit |
DEFAULT_FETCH_MAX_REDIRECTS | 3 | Maximum HTTP redirect hops before aborting |
DEFAULT_ERROR_MAX_CHARS | 4000 | Maximum characters in error messages returned to LLM |
DEFAULT_ERROR_MAX_BYTES | 64000 | Maximum bytes in serialized error payloads |
ExecToolDefaults.host | auto | Execution host strategy: auto, local, or sandbox |
ExecToolDefaults.ask | off | User confirmation mode: off, on, or escalate |
ExecToolDefaults.security | full | Security level: full, limited, or strict |
DEFAULT_TOOL_ALLOW | 16 actions | Default allowlist: exec, process, read, write, edit, apply_patch, image, sessions_list, sessions_history, sessions_send, sessions_spawn, sessions_yield, subagents, session_status |
ACP_SPAWN_MODES | ["run","session"] | Available ACP spawn execution modes |
ACP_SPAWN_SANDBOX_MODES | ["inherit","require"] | ACP sandbox enforcement modes for spawned processes |
Directory Map
The tool source files are split across two directories: tools/ contains the individual tool implementations and shared utilities, while run/ contains the normalization and repair logic that preprocesses tool calls before dispatch.
Source tree for tool-use
tools/
├── common.ts ← AnyAgentTool, ToolInputError, createActionGate()
├── web-fetch.ts ← Web page fetching with limits
├── web-search.ts ← Search engine integration
├── web-tools.ts ← Shared web utilities
├── message-tool.ts ← Structured message delivery
├── sessions-spawn-tool.ts ← Sub-agent session spawning
├── sessions-send-tool.ts ← Send messages to sessions
├── sessions-list-tool.ts ← List active sessions
├── sessions-history-tool.ts ← Session history retrieval
├── sessions-yield-tool.ts ← Yield control to parent
├── session-status-tool.ts ← Session status queries
├── cron-tool.ts ← Cron job management (8 actions)
├── pdf-tool.ts ← PDF document handling
├── image-tool.ts ← Image processing
├── image-generate-tool.ts ← Image generation
├── canvas-tool.ts ← Canvas rendering
├── tts-tool.ts ← Text-to-speech synthesis
├── nodes-tool.ts ← Data node operations
├── nodes-commands-tool.ts ← Node command execution
├── nodes-media-tool.ts ← Node media attachments
├── gateway-tool.ts ← API gateway routing
├── subagents-tool.ts ← Sub-agent lifecycle management
├── agents-list-tool.ts ← Agent enumeration
├── agent-step-tool.ts ← Single agent step execution
run/
├── attempt.tool-call-normalization.ts ← Normalizes tool call arguments
└── attempt.tool-call-argument-repair.ts ← Repairs malformed JSON in args
Deep-Dive Highlights
Core tool-use mechanisms have been extracted into dedicated focus pages for in-depth exploration. Each page covers one subsystem with diagrams, code walks, and configuration details.
Deep-Dive Pages
These focus pages drill into the three most complex subsystems within the tool-use module. Start with Function Calling if you want to understand the end-to-end tool invocation protocol; MCP Bridge if you are integrating external tool servers; Sandbox Execution if you are working on security boundaries.
Function Calling Focus
Tool call protocol, schema validation with TypeBox, and the argument repair pipeline that recovers from malformed LLM JSON output.
MCP Bridge Focus
Model Context Protocol server integration, tool runtime helpers, and the bridge that exposes external MCP tools to agents.
Sandbox Execution Focus
Sandboxed code execution infrastructure with Docker and SSH backends, security policies, and resource isolation.
Error Handling
The tool-use module defines a two-tier error hierarchy. ToolInputError (HTTP 400 equivalent) signals parameter validation failures — missing required fields, invalid types, or out-of-range values. ToolAuthorizationError (HTTP 403 equivalent) extends ToolInputError and signals permission violations when an agent tries to invoke an owner-restricted tool or an action blocked by the gate.
Both error types are caught by the attempt loop and returned to the LLM as structured error messages, capped at DEFAULT_ERROR_MAX_CHARS (4000) characters. This approach ensures the LLM can understand what went wrong and retry with corrected parameters, rather than receiving an opaque failure. The error hierarchy is intentionally modeled after HTTP status codes for familiarity.
Code Examples
Example: ToolInputError Usage Pattern
import { ToolInputError, ToolAuthorizationError } from "./tools/common";
function validateFetchUrl(url: string): void {
if (!url || typeof url !== "string") {
throw new ToolInputError("url parameter is required and must be a string");
}
const parsed = new URL(url);
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
throw new ToolInputError(`Unsupported protocol: ${parsed.protocol}`);
}
}
function checkToolAccess(tool: string, userId: string, ownerId: string): void {
if (tool === "gateway" && userId !== ownerId) {
throw new ToolAuthorizationError(OWNER_ONLY_TOOL_ERROR);
}
}
Example: createActionGate() Factory Pattern
Action gates provide composable permission boundaries. An admin gate may allow all default tools, while a restricted user gate only permits read-only actions. Gates are checked synchronously before every tool dispatch.
import { createActionGate } from "./tools/common";
// Create a gate that allows only specific tool actions
const gate = createActionGate({
allow: ["exec", "read", "write", "sessions_spawn"],
deny: ["gateway", "subagents"],
});
// Gate returns true/false for each tool action
if (!gate.isAllowed("gateway")) {
throw new ToolAuthorizationError("gateway tool is not permitted");
}
// Gates can be composed for layered permission checks
const adminGate = createActionGate({ allow: DEFAULT_TOOL_ALLOW });
const userGate = createActionGate({
allow: ["read", "web_fetch", "web_search"],
deny: ["exec", "write", "apply_patch"],
});
Example: Web Fetch Config Resolution
The fetch configuration resolver clamps user-supplied overrides within the allowed bounds, ensuring safety limits are always respected even when callers request extreme values.
const DEFAULT_FETCH_MAX_CHARS = 50_000;
const DEFAULT_FETCH_MAX_RESPONSE_BYTES = 2_000_000;
const DEFAULT_FETCH_MAX_REDIRECTS = 3;
const FETCH_MAX_RESPONSE_BYTES_MIN = 32_000;
const FETCH_MAX_RESPONSE_BYTES_MAX = 10_000_000;
function resolveFetchConfig(overrides?: Partial<FetchConfig>): FetchConfig {
const maxBytes = Math.min(
FETCH_MAX_RESPONSE_BYTES_MAX,
Math.max(
FETCH_MAX_RESPONSE_BYTES_MIN,
overrides?.maxResponseBytes ?? DEFAULT_FETCH_MAX_RESPONSE_BYTES,
),
);
return {
maxChars: overrides?.maxChars ?? DEFAULT_FETCH_MAX_CHARS,
maxResponseBytes: maxBytes,
maxRedirects: overrides?.maxRedirects ?? DEFAULT_FETCH_MAX_REDIRECTS,
};
}