The hands of the agent. How Claude Code reads files, writes code, runs commands, and interacts with the world.
β Back to Main | β Memory & Context
Claude Code isn't just a chatbot β it can do things. The tool system is what gives it hands. Every action β reading a file, editing code, running bash β goes through a unified tool execution pipeline.
graph TB
subgraph "Tool Registration"
SPEC["Tool Specifications<br/>(name, description, input_schema)"]
REG["StaticToolExecutor<br/>BTreeMap<name, handler>"]
end
subgraph "Tool Execution Pipeline"
REQ["Tool Request<br/>(name + JSON input)"]
PERM["π Permission Check"]
HOOK_PRE["β‘ PreToolUse Hook"]
EXEC["π Handler Execution"]
HOOK_POST["β‘ PostToolUse Hook"]
RES["π¨ Tool Result"]
end
SPEC --> REG
REQ --> PERM
PERM -->|"Allowed"| HOOK_PRE
PERM -->|"Denied"| DENY["β Permission Error"]
HOOK_PRE -->|"exit 0"| EXEC
HOOK_PRE -->|"exit 2"| HOOK_DENY["β Hook Denied"]
EXEC --> HOOK_POST
HOOK_POST --> RES
| Tool | Permission | Description |
|---|---|---|
read_file |
π’ ReadOnly | Read file contents with optional line range |
write_file |
π‘ WorkspaceWrite | Create or overwrite files, returns git diff |
edit_file |
π‘ WorkspaceWrite | Apply targeted string replacements |
glob_search |
π’ ReadOnly | Find files by pattern (e.g., **/*.rs) |
grep_search |
π’ ReadOnly | Search file contents with regex |
| Tool | Permission | Description |
|---|---|---|
bash |
π΄ DangerFullAccess | Run shell commands with timeout and background support |
powershell |
π΄ DangerFullAccess | Windows PowerShell execution |
repl |
π΄ DangerFullAccess | Interactive Python/Node.js REPL |
| Tool | Permission | Description |
|---|---|---|
web_search |
π΄ DangerFullAccess | Search the web |
web_fetch |
π΄ DangerFullAccess | Fetch and process URL content |
| Tool | Permission | Description |
|---|---|---|
agent |
π’ ReadOnly | Spawn sub-agent for parallel tasks |
skill |
π’ ReadOnly | Execute SKILL.md file workflows |
todo_write |
π’ ReadOnly | Task tracking and management |
| Tool | Permission | Description |
|---|---|---|
notebook_edit |
π‘ WorkspaceWrite | Edit Jupyter notebook cells |
tool_search |
π’ ReadOnly | Search for available tools |
config |
π’ ReadOnly | Inspect configuration |
sequenceDiagram
participant API as π API Response
participant RT as π§ Runtime
participant PM as π PermissionPolicy
participant HK as β‘ HookRunner
participant TE as π§ ToolExecutor
API->>RT: tool_use: { id, name, input }
RT->>PM: check(tool_name, required_permission)
alt Permission Denied
PM-->>RT: Deny(reason)
RT-->>API: tool_result: { error: "Permission denied" }
else Permission Granted
PM-->>RT: Allow
RT->>HK: run(PreToolUse, tool_name, input)
alt Hook Denies (exit code 2)
HK-->>RT: Denied(stdout_message)
RT-->>API: tool_result: { error: "Hook denied" }
else Hook Allows (exit code 0)
HK-->>RT: Allowed(optional_stdout)
RT->>TE: execute(tool_name, input_json)
TE-->>RT: Ok(output) or Err(ToolError)
RT->>HK: run(PostToolUse, tool_name, output)
HK-->>RT: Done
RT-->>API: tool_result: { output, is_error }
end
end
Every tool defines its input as a JSON Schema:
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Tool: edit_file β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Input Schema: β
β { β
β "file_path": string (required) β
β "old_string": string (required) β
β "new_string": string (required) β
β "replace_all": boolean (optional, default: false)β
β } β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Output: "Edit applied" + git diff β
β Error: "old_string not found" / "not unique" β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
classDiagram
class ToolExecutor {
<<interface>>
+execute(tool_name, input) Result
}
class ToolRegistry {
-handlers: Map of name β handler
+register(name, handler) Self
+execute(tool_name, input) Result
}
ToolExecutor <|.. ToolRegistry
Tools are registered one-by-one using a builder pattern:
ToolRegistry
.register("bash", bash_handler)
.register("read_file", read_handler)
.register("write_file", write_handler)
.register("edit_file", edit_handler)
.register("glob_search", glob_handler)
.register("grep_search", grep_handler)
... and 12 more tools
Beyond built-in tools, Claude Code can load tools from MCP (Model Context Protocol) servers:
flowchart LR
subgraph "Built-in"
B1["bash"]
B2["read_file"]
B3["write_file"]
BN["...15 more"]
end
subgraph "MCP Servers"
M1["mcp__github__create_pr"]
M2["mcp__db__query"]
M3["mcp__custom__tool"]
end
ALL["All Available Tools<br/>(sent to API)"]
B1 --> ALL
B2 --> ALL
B3 --> ALL
BN --> ALL
M1 --> ALL
M2 --> ALL
M3 --> ALL
See MCP Integration β for the full deep dive.
graph TD
subgraph "π’ ReadOnly"
T1["read_file"]
T2["glob_search"]
T3["grep_search"]
T4["agent"]
T5["todo_write"]
T6["tool_search"]
end
subgraph "π‘ WorkspaceWrite"
T7["write_file"]
T8["edit_file"]
T9["notebook_edit"]
end
subgraph "π΄ DangerFullAccess"
T10["bash"]
T11["powershell"]
T12["repl"]
T13["web_search"]
T14["web_fetch"]
end
- Permission Model β β How the permission checks work
- MCP Integration β β Extending with external tools
- Hook System β β Wrapping tools with custom logic