Skip to main content
Every action Claude Code can take — reading a file, running a shell command, searching the web, spawning a sub-agent — is implemented as a tool: a self-contained module that declares its input schema, permission model, and execution logic. The LLM decides which tools to call; the tool system validates inputs, checks permissions, executes, and returns results.

The Tool Interface

Tools are defined by the Tool<Input, Output, P> generic type in src/Tool.ts. The three type parameters are: Core members of the Tool interface:
Input schemas use Zod v4. The schema is converted to JSON Schema for the Anthropic API and validated locally before call() is ever invoked.

Available Tools

All tools are registered in src/tools.ts and passed to QueryEngine. Some tools are conditionally included based on feature flags or environment variables.

Tool Registration

src/tools.ts assembles the complete tool list and exports it. Feature-gated tools are loaded via conditional require() so that Bun’s build system can eliminate them from the bundle entirely when the corresponding flag is inactive:
The assembled Tools array is passed directly into QueryEngine at construction time and forwarded into every ToolUseContext.

How a Tool Call Flows

1

LLM emits a tool_use block

The streamed API response includes one or more tool_use content blocks, each with a name and a JSON input object.
2

Input validation

The tool’s Zod inputSchema parses the raw JSON input. If parsing fails, an error result is returned to the model immediately — call() is never invoked.
3

validateInput() (optional)

If the tool defines validateInput(), it runs next. This handles semantic validation that Zod can’t express (e.g., “this file path must be within the project root”).
4

checkPermissions()

Every tool invocation goes through checkPermissions(). The result flows into the toolPermission hook, which either auto-resolves based on the current permission mode or shows an interactive prompt. See Permissions & Safety.
5

call() executes

With permission granted, call() runs with the validated input and a ToolUseContext that provides access to app state, the abort controller, file cache, and more.
6

Result returned to LLM

The tool’s Output is serialized via mapToolResultToToolResultBlockParam() and appended as a tool_result message. The model is then re-queried.

Tool Concurrency

Tools that declare isConcurrencySafe() returning true may be executed in parallel when the LLM emits multiple tool_use blocks in a single response. Read-only tools (e.g., GlobTool, GrepTool, FileReadTool) are typically concurrency-safe; mutating tools (e.g., FileWriteTool, BashTool) are not.

Deferred Tools

Tools with shouldDefer: true are sent to the model with defer_loading: true — their full schema is omitted from the initial system prompt. The model must call ToolSearchTool first to discover and load a deferred tool’s schema before it can be invoked. This keeps the initial context window smaller for projects with many MCP tools. Tools with alwaysLoad: true are never deferred and always appear in the initial prompt, even when ToolSearchTool is active.