How 45+ tools share one interface and get composed at runtime
export type Tool<
Input extends AnyObject = AnyObject,
Output = unknown,
P extends ToolProgressData = ToolProgressData,
> = {/**
* Optional aliases for backwards compatibility when a tool is renamed.
* The tool can be looked up by any of these names in addition to its primary name.
*/
aliases?: string[]
/**
* One-line capability phrase used by ToolSearch for keyword matching.
* Helps the model find this tool via keyword search when it's deferred.
* 3–10 words, no trailing period.
* Prefer terms not already in the tool name (e.g. 'jupyter' for NotebookEdit).
*/
searchHint?: string
call(
args: z.infer<Input>,
context: ToolUseContext,
canUseTool: CanUseToolFn,
parentMessage: AssistantMessage,
onProgress?: ToolCallProgress<P>,
): Promise<ToolResult<Output>>
description(
input: z.infer<Input>,
options: {isNonInteractiveSession: boolean
toolPermissionContext: ToolPermissionContext
tools: Tools
},
): Promise<string>
readonly inputSchema: Input
// Type for MCP tools that can specify their input schema directly in JSON Schema format
// rather than converting from Zod schema
readonly inputJSONSchema?: ToolInputJSONSchema
// Optional because TungstenTool doesn't define this. TODO: Make it required.
// When we do that, we can also go through and make this a bit more type-safe.
outputSchema?: z.ZodType<unknown>
inputsEquivalent?(a: z.infer<Input>, b: z.infer<Input>): boolean
isConcurrencySafe(input: z.infer<Input>): boolean
isEnabled(): boolean
isReadOnly(input: z.infer<Input>): boolean
/** Defaults to false. Only set when the tool performs irreversible operations (delete, overwrite, send). */
isDestructive?(input: z.infer<Input>): boolean
/**
* What should happen when the user submits a new message while this tool
* is running.
*
* - `'cancel'` — stop the tool and discard its result
* - `'block'` — keep running; the new message waits
*
* Defaults to `'block'` when not implemented.
*/
interruptBehavior?(): 'cancel' | 'block'
/**
* Returns information about whether this tool use is a search or read operation
* that should be collapsed into a condensed display in the UI. Examples include
* file searching (Grep, Glob), file reading (Read), and bash commands like find,
* grep, wc, etc.
*
* Returns an object indicating whether the operation is a search or read operation:
* - `isSearch: true` for search operations (grep, find, glob patterns)
* - `isRead: true` for read operations (cat, head, tail, file read)
* - `isList: true` for directory-listing operations (ls, tree, du)
* - All can be false if the operation shouldn't be collapsed
*/
isSearchOrReadCommand?(input: z.infer<Input>): {isSearch: boolean
isRead: boolean
isList?: boolean
}
isOpenWorld?(input: z.infer<Input>): boolean
requiresUserInteraction?(): boolean
isMcp?: boolean
isLsp?: boolean
/**
* When true, this tool is deferred (sent with defer_loading: true) and requires
* ToolSearch to be used before it can be called.
*/
readonly shouldDefer?: boolean
/**
* When true, this tool is never deferred — its full schema appears in the
* initial prompt even when ToolSearch is enabled. For MCP tools, set via
* `_meta['anthropic/alwaysLoad']`. Use for tools the model must see on
* turn 1 without a ToolSearch round-trip.
*/
readonly alwaysLoad?: boolean
/**
The Tool interface in src/Tool.ts is the backbone of Claude Code's extensibility. Every capability — from running bash commands to reading PDFs to spawning subagents — implements the same generic interface.
This is the Factory + Strategy pattern: a single interface that any tool can implement, loaded into a registry at startup. Adding a new tool means implementing `Tool<MyInput, MyOutput>` — nothing else changes.
The inputSchema field is a Zod schema. When Claude returns a tool call in JSON, the schema validates and types the input before call() is invoked. Bad inputs from the model are rejected before they can cause runtime errors.
`isConcurrencySafe()` takes the tool's input as an argument — the same tool can be safe in some configurations and unsafe in others. For example, reading a file is always safe; writing is not.
Ask anything about Tool System — Factory Pattern
Powered by Groq · Enter to send, Shift+Enter for newline