buildTool Factory

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

Rendering methods

Tools provide UI rendering: renderToolUseMessage, renderToolResultMessage, renderToolUseProgressMessage, renderGroupedToolUse. These produce React (Ink) components for terminal display.

Helper utilities

Trade-offs

  1. Simple spread — no validation, no warnings for unusual configurations. A tool with isReadOnly: true but isDestructive: true would pass silently.
  2. 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.
  3. Single factory — all tools (built-in, MCP, LSP) go through the same path, ensuring consistency. But MCP tools need workarounds like inputJSONSchema since they don't use Zod.
  4. No runtime type checking — TypeScript generics enforce types at compile time, but the spread at runtime doesn't validate.

Depends on

Key claims

Relations

Sources

src-20260409-a5fc157bc756, source code analysis of src/Tool.ts