# 3. Tool System > How 42 built-in tools are defined, validated, orchestrated, and rendered. --- ## Overview Every capability Claude Code has — reading files, running bash, editing code, searching the web — is a **Tool**. Tools are the bridge between the model's intentions and the real world. ```mermaid %%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'primaryBorderColor': '#28a745', 'lineColor': '#28a745', 'secondaryColor': '#16213e', 'tertiaryColor': '#0f3460'}}}%% graph TB subgraph Interface["Tool Interface — Tool.ts"] direction LR IS["inputSchema
Zod validation"] CP["checkPermissions"] CALL["call — execute"] PROMPT["prompt — model instructions"] RENDER["render — terminal UI"] end IS --> CP --> CALL --> PROMPT --> RENDER subgraph FileOps["File Operations"] FR["FileRead"] FW["FileWrite"] FE["FileEdit"] GL["Glob"] GR["Grep"] NE["NotebookEdit"] end subgraph Exec["Execution"] BA["Bash"] PS["PowerShell"] end subgraph Web["Web"] WF["WebFetch"] WS["WebSearch"] end subgraph AgentTools["Agent and Task"] AG["Agent — spawn sub-agent"] TC["TaskCreate"] TG["TaskGet"] TU["TaskUpdate"] TL["TaskList"] TS["TaskStop"] SM["SendMessage"] end subgraph Meta["Meta Tools"] AQ["AskUserQuestion"] SK["SkillTool"] TW["TodoWrite"] EP["EnterPlanMode"] XP["ExitPlanMode"] TSR["ToolSearch"] end subgraph Dynamic["Dynamic — Runtime Loaded"] MCP_T["MCP Tools
from external servers"] LSP_T["LSP Tool
language server queries"] end subgraph Orchestration["Orchestration Layer"] RUN["toolOrchestration.ts
runTools — parallel dispatch"] STE["StreamingToolExecutor
execute as blocks stream in"] TEX["toolExecution.ts — 60KB
single tool lifecycle"] THK["toolHooks.ts
Pre/Post hook dispatch"] end Interface --> FileOps Interface --> Exec Interface --> Web Interface --> AgentTools Interface --> Meta Interface --> Dynamic FileOps --> Orchestration Exec --> Orchestration Web --> Orchestration AgentTools --> Orchestration Meta --> Orchestration Dynamic --> Orchestration RUN --> STE RUN --> TEX TEX --> THK ``` --- ## The Tool Interface — `Tool.ts` (793 lines) Every tool implements the `Tool` type. Here are the key methods: ```mermaid %%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#28a745', 'primaryBorderColor': '#28a745'}}}%% flowchart LR subgraph Definition["Tool Definition"] NAME["name: string"] SCHEMA["inputSchema: Zod"] ALIASES["aliases?: string array"] HINT["searchHint?: string"] end subgraph Lifecycle["Lifecycle Methods"] VAL["validateInput
pre-execution check"] PERM["checkPermissions
allow / deny / prompt"] CALL["call
execute the tool"] DESC["description
model-facing summary"] end subgraph Rendering["Rendering Methods"] RUM["renderToolUseMessage
show input in terminal"] RRM["renderToolResultMessage
show output in terminal"] RPM["renderToolUseProgressMessage
spinner / progress bar"] GRP["renderGroupedToolUse
parallel display"] end subgraph Metadata["Metadata Methods"] RO["isReadOnly
does it write?"] CS["isConcurrencySafe
parallel safe?"] EN["isEnabled
available now?"] DS["isDestructive
irreversible?"] AC["toAutoClassifierInput
safety classifier text"] end Definition --> Lifecycle --> Rendering Definition --> Metadata ``` ### The `buildTool` Factory All tools go through `buildTool()` which provides safe defaults: ```typescript const TOOL_DEFAULTS = { isEnabled: () => true, isConcurrencySafe: () => false, // Assume not safe isReadOnly: () => false, // Assume writes isDestructive: () => false, checkPermissions: (input) => // Defer to general system Promise.resolve({ behavior: 'allow', updatedInput: input }), toAutoClassifierInput: () => '', // Skip classifier userFacingName: () => '', } export function buildTool(def) { return { ...TOOL_DEFAULTS, userFacingName: () => def.name, ...def } } ``` This "fail-closed" design means a tool that forgets to implement `isConcurrencySafe` defaults to `false` (not safe for parallel execution). --- ## The 42 Built-in Tools ```mermaid %%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#17a2b8', 'primaryBorderColor': '#17a2b8'}}}%% graph TB subgraph FileOps["File Operations — Read + Write + Search"] FR["FileRead
Read files, images,
PDFs, notebooks"] FW["FileWrite
Create or overwrite
entire files"] FE["FileEdit
Partial string
replacement edits"] GL["Glob
File pattern
matching search"] GR["Grep
ripgrep content
search"] NE["NotebookEdit
Jupyter notebook
cell editing"] end subgraph Exec["Execution — Run Commands"] BA["Bash
Shell command
execution"] PS["PowerShell
Windows shell
execution"] REPL["REPL
Persistent JS/TS
runtime context"] end subgraph Web["Web — Fetch and Search"] WF["WebFetch
HTTP GET to URLs
HTML to markdown"] WS["WebSearch
Web search via
Brave or similar"] end subgraph AgentTask["Agent and Task Management"] AG["Agent
Spawn sub-agent
with forked context"] TC["TaskCreate
Background task"] TG["TaskGet
Check task status"] TU["TaskUpdate
Update task state"] TL["TaskList
List all tasks"] TS["TaskStop
Terminate task"] SM["SendMessage
Inter-agent
messaging"] TmC["TeamCreate
Create agent team"] TmD["TeamDelete
Remove agent team"] end subgraph Meta["Meta Tools — Control Claude's Behavior"] AQ["AskUserQuestion
Interactive prompt"] SK["SkillTool
Execute skills"] TW["TodoWrite
Manage task lists"] EP["EnterPlanMode
Switch to read-only"] XP["ExitPlanMode
Resume full access"] TSR["ToolSearch
Find deferred tools"] BF["Brief
Toggle concise mode"] SL["Sleep
Idle wait for
proactive mode"] SO["SyntheticOutput
Structured JSON
output"] end subgraph Dynamic["Dynamic — Loaded at Runtime"] MCP["MCP Tools
From external
MCP servers"] LSP["LSP Tool
Language server
queries"] end subgraph Special["Special Purpose"] EW["EnterWorktree
Git worktree
isolation"] XW["ExitWorktree
Leave worktree"] RT["RemoteTrigger
Remote execution"] SC["ScheduleCron
Timed triggers"] CF["Config
Settings management"] end ``` --- ## Tool Orchestration — Parallel Execution When the model returns multiple `tool_use` blocks, Claude Code can execute them **in parallel**: ```mermaid %%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%% sequenceDiagram participant Q as query.ts participant O as toolOrchestration.ts participant STE as StreamingToolExecutor participant T1 as Tool 1 — FileRead participant T2 as Tool 2 — Grep participant T3 as Tool 3 — Bash participant P as Permission System Q->>O: runTools(3 tool_use blocks) activate O Note over O: Check concurrency safety O->>STE: FileRead — isConcurrencySafe = true O->>STE: Grep — isConcurrencySafe = true O->>STE: Bash — isConcurrencySafe = false par Parallel Execution STE->>P: checkPermissions(FileRead) P-->>STE: allow STE->>T1: call(input) T1-->>STE: result STE->>P: checkPermissions(Grep) P-->>STE: allow STE->>T2: call(input) T2-->>STE: result end Note over STE: Wait for parallel tools STE->>P: checkPermissions(Bash) P-->>STE: prompt user STE->>T3: call(input) T3-->>STE: result O-->>Q: yield all tool_result messages deactivate O ``` Key files in the orchestration layer: - **`toolOrchestration.ts`** — `runTools()`: dispatches tools, handles parallel vs. sequential - **`StreamingToolExecutor`** — Starts permission checks while model is still streaming - **`toolExecution.ts`** (60KB) — Single tool lifecycle: validate → permissions → execute → hooks - **`toolHooks.ts`** — Dispatches PreToolUse and PostToolUse hooks --- ## Single Tool Lifecycle ```mermaid %%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#28a745', 'primaryBorderColor': '#28a745'}}}%% flowchart TD BLOCK["tool_use block arrives
from model stream"]:::start PARSE["Parse + validate input
via Zod inputSchema"]:::step VAL{"validateInput?"}:::check DENY_VAL["Return error to model
with validation message"]:::deny PRE_HOOK["Run PreToolUse hooks
user-defined scripts"]:::hook HOOK_R{"Hook result?"}:::check PERM["Check permissions
deny → allow → tool → hooks → classifier → dialog"]:::step PERM_R{"Permission?"}:::check EXEC["tool.call(input, context)
execute the operation"]:::step RESULT["Map output to tool_result
via mapToolResultToToolResultBlockParam"]:::step SIZE{"Result exceeds
maxResultSizeChars?"}:::check PERSIST["Persist to disk
return file path + preview"]:::step POST_HOOK["Run PostToolUse hooks"]:::hook RENDER["Render in terminal
renderToolResultMessage"]:::step YIELD["Yield tool_result
to query loop"]:::done DENY_PERM["Return permission_denied
error to model"]:::deny BLOCK --> PARSE --> VAL VAL -->|"pass"| PRE_HOOK VAL -->|"fail"| DENY_VAL PRE_HOOK --> HOOK_R HOOK_R -->|"approve"| PERM HOOK_R -->|"deny"| DENY_PERM HOOK_R -->|"modify input"| PERM PERM --> PERM_R PERM_R -->|"allow"| EXEC PERM_R -->|"deny"| DENY_PERM EXEC --> RESULT --> SIZE SIZE -->|"within limit"| POST_HOOK SIZE -->|"exceeds limit"| PERSIST --> POST_HOOK POST_HOOK --> RENDER --> YIELD classDef start fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px classDef step fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px classDef check fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px classDef hook fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px classDef deny fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px classDef done fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px ``` --- ## ToolSearch — Deferred Tool Loading With 42+ tools, sending all schemas to the model wastes tokens. **ToolSearch** defers tools that aren't immediately needed: ```mermaid %%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#ffc107', 'primaryBorderColor': '#ffc107'}}}%% flowchart LR ALL["42+ Tools"]:::input SPLIT{"shouldDefer?"}:::check EAGER["~15 Eager Tools
Always in prompt
FileRead, Bash, Grep..."]:::eager DEFER["~27 Deferred Tools
Schema not sent initially
TaskCreate, WebSearch..."]:::defer ALWAYS["alwaysLoad Tools
Forced eager by MCP meta"]:::eager SEARCH["ToolSearch Tool
Model searches by keyword
using searchHint"]:::tool FOUND["Tool schema injected
into next request"]:::result ALL --> SPLIT SPLIT -->|"no"| EAGER SPLIT -->|"yes"| DEFER SPLIT -->|"alwaysLoad"| ALWAYS DEFER --> SEARCH SEARCH --> FOUND classDef input fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px classDef check fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px classDef eager fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px classDef defer fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px classDef tool fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px classDef result fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px ``` --- ## Dynamic Tools — MCP and LSP Beyond built-in tools, Claude Code loads tools dynamically at runtime: ```mermaid %%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#17a2b8', 'primaryBorderColor': '#17a2b8'}}}%% flowchart TD subgraph MCP["MCP Tools — Model Context Protocol"] SRV["External MCP Servers
configured in settings"] CONN["MCPConnectionManager
stdio / SSE transport"] DISC["Discover tools
via tools/list"] WRAP["Wrap as Tool objects
name: mcp__server__tool"] end subgraph LSP["LSP Tool — Language Server Protocol"] LS["Language Server
runtime type info"] QUERY_LSP["Query definitions,
references, diagnostics"] end SRV --> CONN --> DISC --> WRAP LS --> QUERY_LSP MERGE["Merged into tool pool
via useMergedTools hook"]:::merge WRAP --> MERGE QUERY_LSP --> MERGE classDef merge fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px ``` MCP tools are prefixed with `mcp____` unless running in SDK no-prefix mode. They go through the same permission system as built-in tools. --- ## Key Design Decisions ### 1. Self-Contained Modules Each tool directory (`src/tools//`) contains everything: - `index.ts` — Tool definition via `buildTool()` - `prompt.ts` — Model-facing instructions - `*.test.ts` — Tests - Additional helpers as needed ### 2. Fail-Closed Defaults `buildTool()` defaults are conservative: - `isConcurrencySafe = false` — Won't run in parallel unless explicitly safe - `isReadOnly = false` — Assumed to write unless stated otherwise - `checkPermissions` defaults to `allow` — But the general permission system still applies ### 3. Result Size Budgets Each tool has `maxResultSizeChars`. Oversized results are persisted to disk and the model gets a truncated preview + file path. This prevents single tool results from consuming the entire context window. ### 4. Observable Input Backfilling `backfillObservableInput()` adds derived fields to tool inputs for SDK consumers and transcripts, without mutating the API-bound input (which would break prompt caching): ```typescript // The API sees: { file_path: "src/foo.ts" } // SDK/transcript sees: { file_path: "src/foo.ts", resolved_path: "/abs/path/src/foo.ts" } ``` --- **Previous:** [← The Agentic Loop](./02-agentic-loop.md) · **Next:** [Permission System →](./04-permission-system.md)