# 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)