buildTool Factory
- Entity ID:
ent-20260410-88893730f5a0 - Type:
concept - Scope:
shared - Status:
active - Aliases: buildTool, Tool.ts, tool definition, ToolDef
Description
The buildTool() factory function (src/Tool.ts, line 783) is the single entry point for creating tool objects in Claude Code. It takes a ToolDef configuration with required fields (name, schema, execution logic) and optional behavioral declarations, applies fail-closed defaults, and produces a standardized Tool object consumable by the QueryEngine and streaming tool executor.
How it works
buildTool<D extends AnyToolDef>(def: D) performs a simple spread: { ...TOOL_DEFAULTS, userFacingName: () => def.name, ...def }. The caller's overrides win over defaults.
Fail-closed defaults
| Method | Default | Rationale |
|---|---|---|
isEnabled |
() => true |
Tools are enabled unless explicitly disabled |
isConcurrencySafe |
() => false |
Assume NOT safe for parallel execution |
isReadOnly |
() => false |
Assume tool writes |
isDestructive |
() => false |
Assume not destructive |
checkPermissions |
Returns { behavior: 'allow', updatedInput } |
Defers to general permission system |
toAutoClassifierInput |
() => '' |
Skip security classifier |
userFacingName |
() => def.name |
Display name matches wire name |
The type DefaultableToolKeys defines which methods have defaults: isEnabled, isConcurrencySafe, isReadOnly, isDestructive, checkPermissions, toAutoClassifierInput, userFacingName.
Required fields in ToolDef
Every tool must provide:
- name — wire name for API communication
- inputSchema — Zod schema for input validation
- call(args, context, canUseTool, parentMessage, onProgress) — execution logic
- prompt(options) — generates system prompt text
- description(input, options) — generates tool description for the model
- maxResultSizeChars — threshold for persisting output to disk (Infinity to never persist)
Optional behavioral flags
aliases— backwards-compatible name lookupsshouldDefer/alwaysLoad— control lazy loading via ToolSearchstrict— enables strict mode (gated bytengu_tool_pear)searchHint— 3-10 word keyword phrase for ToolSearch matchingisMcp/isLsp— discriminator flagsmcpInfo—{ serverName, toolName }for MCP toolsinterruptBehavior()— returns'cancel'or'block'(default:'block')inputJSONSchema— raw JSON Schema for MCP tools that skip Zod conversion
Rendering methods
Tools provide UI rendering: renderToolUseMessage, renderToolResultMessage, renderToolUseProgressMessage, renderGroupedToolUse. These produce React (Ink) components for terminal display.
Helper utilities
toolMatchesName(tool, name)— checks both name and aliasesfindToolByName(tools, name)— lookup by name or aliasfilterToolProgressMessages(progressMessages)— filters outhook_progresstypegetEmptyToolPermissionContext()— returns default context withmode: 'default'
Trade-offs
- Simple spread — no validation, no warnings for unusual configurations. A tool with
isReadOnly: truebutisDestructive: truewould pass silently. - Fail-closed defaults — new tools are safe by default (not concurrent, not read-only) but developers must explicitly opt in to parallelism, which can be forgotten.
- Single factory — all tools (built-in, MCP, LSP) go through the same path, ensuring consistency. But MCP tools need workarounds like
inputJSONSchemasince they don't use Zod. - No runtime type checking — TypeScript generics enforce types at compile time, but the spread at runtime doesn't validate.
Depends on
- Zod — input schema validation
- tool-system — registers tools built by this factory
- permission-pipeline —
checkPermissionsintegrates with permission system
Key claims
- All 40+ tools are created through
buildTool()— it's the only factory - Defaults are fail-closed: tools assumed not concurrent-safe, not read-only until declared
- Simple spread implementation — no validation beyond TypeScript types
- MCP tools use
inputJSONSchemato bypass Zod conversion
Relations
part_oftool-systemused_bymcp-subsystem (for MCP tool creation)related_topermission-pipeline
Sources
src-20260409-a5fc157bc756, source code analysis of src/Tool.ts