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:
ParameterTypePurpose
Inputz.ZodType (object)Validated input schema
OutputunknownReturn value from call()
PToolProgressDataProgress event payload shape
Core members of the Tool interface:
// src/Tool.ts
export type Tool<Input, Output, P> = {
  readonly name: string
  readonly inputSchema: Input          // Zod v4 schema — validated before call()
  readonly inputJSONSchema?: ToolInputJSONSchema  // MCP tools may use raw JSON Schema

  call(
    args: z.infer<Input>,
    context: ToolUseContext,
    canUseTool: CanUseToolFn,
    parentMessage: AssistantMessage,
    onProgress?: ToolCallProgress<P>,
  ): Promise<ToolResult<Output>>

  checkPermissions(
    input: z.infer<Input>,
    context: ToolUseContext,
  ): Promise<PermissionResult>

  validateInput?(
    input: z.infer<Input>,
    context: ToolUseContext,
  ): Promise<ValidationResult>

  isEnabled(): boolean
  isReadOnly(input: z.infer<Input>): boolean
  isDestructive?(input: z.infer<Input>): boolean
  isConcurrencySafe(input: z.infer<Input>): boolean
  // ... rendering methods for terminal UI
}
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.
ToolDescription
BashToolShell command execution via the system shell
FileReadToolRead files — text, images, PDFs, and notebooks
FileWriteToolCreate or fully overwrite a file
FileEditToolPartial file modification using string replacement
GlobToolFile pattern matching search
GrepToolripgrep-based content search across files
NotebookEditToolEdit Jupyter notebook cells
ToolDescription
WebFetchToolFetch and parse URL content
WebSearchToolWeb search
LSPToolLanguage Server Protocol integration (go-to-definition, hover, diagnostics)
ToolSearchToolDeferred tool discovery — find tools by keyword before invoking them
ToolDescription
AgentToolSpawn a sub-agent with its own context and tool access
SkillToolExecute a reusable skill workflow
MCPToolInvoke a tool exposed by an MCP server
SendMessageToolSend a message to another agent (inter-agent communication)
TeamCreateTool / TeamDeleteToolCreate or destroy a team of parallel agents
AskUserQuestionToolPrompt the user for input mid-task
ToolDescription
TaskCreateTool / TaskUpdateToolCreate and update tasks
TaskGetTool / TaskListToolRetrieve task details and list tasks
TaskStopToolStop a running background task
EnterPlanModeTool / ExitPlanModeToolToggle plan mode (plan without executing)
EnterWorktreeTool / ExitWorktreeToolIsolate work in a git worktree
TodoWriteToolWrite structured to-do lists
ToolDescriptionGate
SleepToolWait for a duration (proactive / background mode)PROACTIVE or KAIROS
CronCreateTool / CronDeleteTool / CronListToolScheduled trigger managementAGENT_TRIGGERS
RemoteTriggerToolRemote event triggerAGENT_TRIGGERS_REMOTE
MonitorToolMonitoring and alertingMONITOR_TOOL
SyntheticOutputToolEmit structured output for SDK consumersalways available
SuggestBackgroundPRToolSuggest opening a background PRant user type
REPLToolPersistent REPL environmentant user type
ConfigToolRead / write Claude Code configurationalways available
BriefToolGenerate a brief summary of the sessionalways available
TungstenToolInternal Anthropic toolingalways available
ListMcpResourcesTool / ReadMcpResourceToolBrowse and read MCP server resourcesalways available

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:
// src/tools.ts
const SleepTool =
  feature('PROACTIVE') || feature('KAIROS')
    ? require('./tools/SleepTool/SleepTool.js').SleepTool
    : null

// Circular-dependency-safe lazy requires
const getTeamCreateTool = () =>
  require('./tools/TeamCreateTool/TeamCreateTool.js').TeamCreateTool
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.

Build docs developers (and LLMs) love