How Claude Code’s tool interface works — lifecycle, capabilities, rendering, and the complete built-in tool inventory.
Every capability Claude Code can exercise — reading a file, running a shell command, fetching a URL, spawning a sub-agent — is a tool. Tools are the atomic unit of action in the agent loop. When the Claude API returns a tool_use block, the loop looks up the matching tool by name, validates the input, checks permissions, and calls it.
Every tool is a TypeScript object that satisfies the Tool<Input, Output, Progress> type defined in src/Tool.ts. The interface is divided into four groups of methods.
These methods let the executor and permission system know how to handle a tool safely.
Method
Default
Description
isEnabled()
true
Feature gate — return false to hide the tool entirely from the model.
isConcurrencySafe(input)
false
When true, this tool can run in parallel with other concurrency-safe tools. Defaults to false (assume writes).
isReadOnly(input)
false
When true, the tool has no side effects. Informs the permission dialog.
isDestructive(input)
false
When true, the operation is irreversible (delete, overwrite, send). Shown prominently in the permission UI.
interruptBehavior()
'block'
What happens when the user submits a new message while this tool runs. 'cancel' stops it immediately; 'block' lets it finish while the new message waits.
All tools are created through the buildTool() factory function in src/Tool.ts. It merges safe defaults for the commonly-stubbed methods so that callers never need ?.() ?? default.
// src/Tool.tsexport function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> { return { ...TOOL_DEFAULTS, userFacingName: () => def.name, ...def, } as BuiltTool<D>}
The defaults applied by buildTool() are all fail-closed where it matters:
Every tool in the codebase goes through buildTool(). The factory also ensures that userFacingName always returns the tool’s name even when the tool definition omits it.
Every tool sets a maxResultSizeChars property. When a result exceeds this limit, the output is saved to a temporary file and Claude receives a preview with the file path instead of the full content. This prevents single large tool results from consuming the entire context window.FileReadTool sets maxResultSizeChars to Infinity — it self-bounds through its own read limits and persisting would create a circular Read → file → Read loop.
When the shouldDefer flag is true on a tool, it is sent to the model with defer_loading: true and its full schema is omitted from the initial system prompt. The model must call ToolSearchTool first to locate and load the tool’s schema. This reduces prompt size when many tools are registered.Set alwaysLoad: true on a tool to force its schema into the initial prompt regardless of deferral settings. For MCP tools, this is controlled via _meta['anthropic/alwaysLoad'].