Tools Overview
The tool system is the backbone of Claude Code's ability to interact with the filesystem, shell, web, and external services. Every action Claude takes (reading files, running commands, searching code) is mediated through a typed tool interface defined in src/Tool.ts, registered via src/tools.ts, and executed through the streaming tool executor.
Tool Base Type
Every tool implements the Tool type, a generic interface parameterized over input schema, output type, and progress data:
type Tool<
Input extends AnyObject = AnyObject,
Output = unknown,
P extends ToolProgressData = ToolProgressData,
> = {
name: string
aliases?: string[]
searchHint?: string
inputSchema: Input
outputSchema?: z.ZodType<unknown>
call(args, context, canUseTool, parentMessage, onProgress?): Promise<ToolResult<Output>>
description(input, options): Promise<string>
prompt(options): Promise<string>
checkPermissions(input, context): Promise<PermissionResult>
validateInput?(input, context): Promise<ValidationResult>
isReadOnly(input): boolean
isConcurrencySafe(input): boolean
isEnabled(): boolean
isDestructive?(input): boolean
maxResultSizeChars: number
// ... rendering and UI methods
}Key Properties
Unique identifier for the tool. Used in permission rules, analytics, and tool lookup.
Optional alternative names for backward compatibility when a tool is renamed. The tool can be found by any alias.
A 3-10 word capability phrase used by ToolSearch for keyword matching when the tool is deferred.
Zod schema defining the tool's input parameters. Used for validation and API schema generation.
Maximum characters for a tool result before it gets persisted to disk. Set to Infinity for tools like Read whose output must never be persisted.
When true, the tool is sent with defer_loading: true and requires ToolSearch before it can be called.
Enables strict mode for the tool, causing the API to more strictly adhere to the parameter schema.
Core Methods
Executes the tool. Receives parsed input, the ToolUseContext, a canUseTool callback, the parent message, and an optional progress callback. Returns a ToolResult containing data, optional new messages, and an optional context modifier.
Determines whether the user must be prompted for permission. Called after validateInput() passes. Returns a PermissionDecision with behaviors: allow, deny, ask, or passthrough.
Pre-execution validation. Checks file existence, path validity, size limits, and security constraints. Returns { result: true } or { result: false, message, errorCode }.
Whether the tool modifies state for the given input. Read-only tools may skip certain permission checks.
Whether this tool can safely run in parallel with other tools. Used by the streaming executor to decide parallel vs. sequential execution.
Whether this tool is currently available. Checked at registry assembly time. Tools can be disabled by environment variables, feature flags, or API provider constraints.
Tool Registry
The tool registry in src/tools.ts is the single source of truth for which tools are available.
getAllBaseTools()
Returns the complete exhaustive list of all tools that could be available in the current environment. This respects feature flags and environment variables:
function getAllBaseTools(): Tools {
return [
AgentTool,
TaskOutputTool,
BashTool,
// Glob/Grep conditionally included based on embedded search tools
...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
ExitPlanModeV2Tool,
FileReadTool,
FileEditTool,
FileWriteTool,
// ... many more tools, conditionally included
]
}getTools(permissionContext)
Filters getAllBaseTools() by:
- Removing tools blanket-denied by permission rules
- Hiding primitive tools when REPL mode is active (they are accessible inside the REPL VM)
- Filtering out disabled tools via
isEnabled() - Supporting "simple mode" (
CLAUDE_CODE_SIMPLE) which restricts to Bash, Read, and Edit
assembleToolPool(permissionContext, mcpTools)
The primary entry point for building the final tool list. Combines built-in tools from getTools() with MCP tools, deduplicates by name (built-in tools take precedence), and sorts for prompt-cache stability:
function assembleToolPool(permissionContext, mcpTools): Tools {
const builtInTools = getTools(permissionContext)
const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)
// Sort each partition, built-ins first for cache stability
return uniqBy(
[...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
'name',
)
}Built-in tools are sorted as a contiguous prefix before MCP tools. This layout is critical for prompt-cache stability: interleaving MCP tools with built-ins would invalidate cache keys whenever an MCP tool is added or removed.
Permission Context
The ToolPermissionContext controls what tools can do:
type ToolPermissionContext = DeepImmutable<{
mode: PermissionMode // 'default' | 'plan' | 'auto' | 'bypassPermissions'
additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>
alwaysAllowRules: ToolPermissionRulesBySource
alwaysDenyRules: ToolPermissionRulesBySource
alwaysAskRules: ToolPermissionRulesBySource
isBypassPermissionsModeAvailable: boolean
shouldAvoidPermissionPrompts?: boolean
prePlanMode?: PermissionMode
}>Permission rules are organized by source (user settings, project settings, session overrides) and matched against tool names and input content. A blanket deny rule (no ruleContent) removes the tool entirely from the model's view.
ToolUseContext
The ToolUseContext is passed to every tool invocation and provides:
- options: Commands, debug flags, model info, MCP clients, agent definitions
- abortController: For cancellation support
- readFileState: File state cache for staleness detection
- App state access:
getAppState()/setAppState()for reading and updating global state - messages: The current conversation history
- File reading limits: Token and size limits for file reads
- Tool decisions: Map tracking accept/reject decisions for tool uses
Tool Execution Flow
The streaming tool executor processes tool calls through this pipeline:
- Input parsing: The tool's
inputSchemavalidates and parses the raw input from the API - Input validation:
validateInput()checks tool-specific constraints (file exists, valid path, size limits) - Permission check:
checkPermissions()determines if user approval is needed, consulting permission rules, hooks, and the auto-mode classifier - Execution:
call()runs the tool with progress reporting viaonProgress - Result handling: Output is sized against
maxResultSizeChars; oversized results are persisted to disk with a preview returned to the model - Context modification: Non-concurrency-safe tools may return a
contextModifierthat updates theToolUseContextfor subsequent tools
Tools that are not concurrency-safe (isConcurrencySafe() === false) will block other tool executions and may modify the shared context via contextModifier. The executor serializes these tools.
Tool Deferred Loading
When many tools are available (especially with MCP servers), ToolSearch enables deferred loading. Tools marked with shouldDefer: true are sent to the API with defer_loading: true, so the model sees only their names, not full schemas. The model must call ToolSearch to fetch the full schema before using a deferred tool.
Tools can opt out with alwaysLoad: true to ensure their schema is always visible on turn 1 without a ToolSearch round-trip.
Key Source Files
src/Tool.ts: Tool type definitions, ToolUseContext, ToolPermissionContextsrc/tools.ts: Tool registry:getAllBaseTools(),getTools(),assembleToolPool()src/utils/permissions/permissions.ts: Permission matching and rule evaluation