The Tool Surface
The tool registry is where the model's "what can I do?" surface is defined. Claw Code registers 50 tools through a four-field struct, dispatches them through a single match statement, and lazily materializes the schemas of all but the six most common.
The ToolSpec struct
crates/tools/src/lib.rs:102:
@dataclass(frozen=True)
class ToolSpec:
name: str
description: str
input_schema: dict # JSONSchema
required_permission: PermissionMode
That's it. Four fields: name and description (interned at compile time in the Rust impl), schema as a JSON object, permission as an enum. The list is built by mvp_tool_specs() at line 393, which returns a Vec<ToolSpec> of all 50 tools.
The dispatcher is execute_tool_with_enforcer(name, input, enforcer) at line 1201, a giant match on name. Each arm calls into the appropriate function in the runtime crate, threading the optional &PermissionEnforcer through.
The 50 tools, by category
File & shell (the eager six + extras)
| Tool | Permission | Eager? | Schema highlights |
|---|---|---|---|
bash | DangerFullAccess | yes | command, timeout, description, run_in_background, dangerouslyDisableSandbox, filesystemMode |
read_file | ReadOnly | yes | path (required), offset, limit |
write_file | WorkspaceWrite | yes | path, content |
edit_file | WorkspaceWrite | yes | path, old_string, new_string, replace_all |
glob_search | ReadOnly | yes | pattern, path |
grep_search | ReadOnly | yes | pattern, path, glob, output_mode, -B/-A/-C, context, -n, -i, type, head_limit, offset, multiline |
NotebookEdit | WorkspaceWrite | deferred | notebook_path, cell_id, new_source, cell_type, edit_mode |
REPL | DangerFullAccess | deferred | code, language, timeout_ms |
PowerShell | DangerFullAccess | deferred | command, timeout, description, run_in_background |
Web
| Tool | Permission | Eager? | Notes |
|---|---|---|---|
WebFetch | ReadOnly | deferred | url, prompt — fetches and processes content with a small model |
WebSearch | ReadOnly | deferred | query (min length 2), allowed_domains, blocked_domains |
Conversation control
| Tool | Permission | Eager? | Notes |
|---|---|---|---|
Skill | ReadOnly | deferred | skill, args — invoke a registered skill |
Agent | DangerFullAccess | deferred | description, prompt, subagent_type, name, model — spawn sub-agent |
ToolSearch | ReadOnly | deferred | query, max_results — fetch deferred tool schemas on demand |
Sleep | ReadOnly | deferred | duration_ms |
SendUserMessage | ReadOnly | deferred | message, attachments, status (normal/proactive). Also exposed as Brief. |
Config | WorkspaceWrite | deferred | setting, value (string/bool/number) |
EnterPlanMode | WorkspaceWrite | deferred | (no params) |
ExitPlanMode | WorkspaceWrite | deferred | (no params) |
StructuredOutput | ReadOnly | deferred | flexible schema (additionalProperties: true) |
AskUserQuestion | ReadOnly | deferred | question, options |
TodoWrite | WorkspaceWrite | deferred | todos array of {content, activeForm, status} |
Tasks, workers, teams, crons (the multi-agent surface)
| Tool | Permission | Notes |
|---|---|---|
TaskCreate | DangerFullAccess | prompt, description |
RunTaskPacket | DangerFullAccess | full structured packet: objective, scope, repo, branch_policy, acceptance_tests, commit_policy, reporting_contract, escalation_policy |
TaskGet, TaskList, TaskOutput | ReadOnly | task introspection |
TaskStop, TaskUpdate | DangerFullAccess | task_id + state-change ops |
WorkerCreate | DangerFullAccess | cwd, trusted_roots, auto_recover_prompt_misdelivery |
WorkerGet, WorkerObserve, WorkerAwaitReady | ReadOnly | worker introspection / ready-handshake |
WorkerResolveTrust, WorkerSendPrompt, WorkerRestart, WorkerTerminate, WorkerObserveCompletion | DangerFullAccess | worker control |
TeamCreate, TeamDelete | DangerFullAccess | parallel-task team management |
CronCreate, CronDelete, CronList | DangerFullAccess / ReadOnly | scheduled triggers (5-field cron expressions stored as strings) |
MCP / remote / LSP
| Tool | Permission | Notes |
|---|---|---|
LSP | ReadOnly | action ∈ {symbols, references, diagnostics, definition, hover}, path, line, character, query |
ListMcpResources, ReadMcpResource | ReadOnly | MCP server resource listing/reading |
McpAuth | DangerFullAccess | server — kicks OAuth flow |
RemoteTrigger | DangerFullAccess | url, method (GET/POST/PUT/DELETE), headers, body — generic webhook |
MCP | DangerFullAccess | server, tool, arguments — execute server tool by name |
TestingPermission | DangerFullAccess | test-only, validates the enforcer itself |
The deferred mechanism (why this matters)
crates/tools/src/lib.rs:4944–4954 defines deferred_tool_specs(), which filters mvp_tool_specs() and excludes the eager six (bash/read/write/edit/glob/grep). searchable_tool_specs() is the deferred set; total_deferred_tools (line 337) tracks the count.
The mechanism: at session start, the model only sees full schemas for the eager six tools plus a ToolSearch tool. Every other tool is referenced by name in the system prompt or in tool-use guidance, but its parameter schema is not loaded. When the model wants to call (e.g.) WorkerCreate, it first calls ToolSearch({"query": "select:WorkerCreate"}). The harness returns the JSONSchema for WorkerCreate, which is then valid for the next turn's tool call.
This pattern is directly visible in any live Claude Code session's system prompt: "Some tools are deferred and not listed above. When a deferred tool is surfaced later in the conversation, its full schema appears as a <function>{...}</function> definition inside a <functions> block (the same encoding as the tool list above), and it is immediately callable exactly like any tool defined here."
The win: 50 tool schemas, especially for tools with rich schemas like RunTaskPacket or all the MCP tools (which can total hundreds of tokens each), would balloon the system prompt. With deferred loading, the typical session only ever materializes ~5–10 tool schemas. The cost is one extra round-trip per first-use.
Permission category as a coarse filter
Five PermissionMode levels (crates/runtime/src/permissions.rs:9–15):
ReadOnly → WorkspaceWrite → DangerFullAccess → Prompt → Allow
Two of these (Prompt, Allow) are session modes, not tool requirements. The other three are the tiers tools declare:
ReadOnly: can never write, run shells, or escape the sessionWorkspaceWrite: can write files, but only inside the workspace rootDangerFullAccess: arbitrary shell, network, file writes anywhere
Note: 21 of 50 tools require DangerFullAccess, which is a high count. In practice most of those are worker/team/cron-management tools whose risk model is "they enable spawning more agents, which may then do dangerous things," not "they are dangerous themselves." This is over-classified. Real Claude Code likely splits this into a finer enum (e.g. "spawn-agent" as a distinct category from "filesystem-write").
The full permission decision flow is covered in Permissions & Sandboxing.
Continue: Sub-agents & Context Cleanliness