defineTool():
the argument and result types are inferred from its zod input, the arguments
are validated before execute runs, and the result drops in anywhere tools are
accepted (createAgent({ tools: [...] }), ToolRegistry.register(tool),
AgentBuilder.addTool(tool)).
defineTool() options
defineTool() checks the name, the description and the zod input when it is
called, and throws an error that says how to fix a bad value. Registering two
tools with the same name throws an error naming the conflict.
A defined tool is a regular ToolDescriptor: it carries its schema as
inputSchema (the same schema as input) and its execute function directly.
The .tool field (an ai v4 { description, parameters, execute } object) is
legacy: it is still built for compatibility, and only used for hand-written
descriptors that set neither inputSchema nor execute.
What happens when the model calls a tool
- Validation first. The model’s arguments are parsed with
inputSchema(defaults, coercions and transforms applied) before hooks,needsApprovalandexecutesee them. Arguments that do not match never reachexecute: the model gets a structuredToolArgumentsValidationErrorresult and can retry. - Errors are results. A tool that throws gives the model
{ error, toolName, message, kind }(no stack trace) and the run continues. See Errors. - Parallel calls. Several calls in one model turn run concurrently; cap
them with
toolConcurrency(1for strictly sequential). Results reach the transcript in the model’s call order. See Parallel tool calls. - Cancellation. A run’s
AbortSignalreaches each call asctx.abortSignal, so long-running work can stop early. - Retries after a crash. With durable execution a tool can run more than
once across a crash; use
ctx.toolCallIdas an idempotency key. See Durable execution.
Execute context
execute(args, ctx) always gets a real second argument, typed
ToolExecutionContext (exported from the package root; it replaces the ai
SDK’s ToolExecutionOptions), on every path that
runs a tool: the main loop, the call that runs after an approval, tools that
run in a sandbox (sandboxExecute(args, sandbox, ctx)) and a flow’s tool-call
node.
A
sandboxExecute(args, sandbox) that ignores the third argument keeps working.
Errors
Every way a tool call can fail reaches the model as the same result, so one check works everywhere (including the call that runs after an approval):error: the error’s name (TypeError,ToolArgumentsValidationError, …), or the kind’s default name for a failure that is not a thrown error.toolNameandmessage: the message only, never a stack, capped at 2,000 characters (... (truncated)marks a cut).kind: why the call failed.- Some kinds add fields:
issuesforvalidation,noteforrejected,reasonfordenied.
isError: true; tool-result events, onToolResult, postToolCall hooks and tool.error events see the call as failed.
toolErrorResult({ toolName, error, kind?, toolCallId?, details? }) builds this
result; use it in your own tool wrappers so they match. A thrown error can pick
its kind by carrying a toolErrorKind property. An error extending
PropagatingToolError is not a result: it aborts the run.
Built-in tools
Built-in descriptors are passed keyed by the name the agent uses:
createAgent({ tools: { current_date: currentDateTool } }). Spec files refer to
http, current-date and day-name by name (see
Configuration).
ToolRegistry
createAgent() builds a registry for you. With AgentBuilder +
AgentExecutor.execute(), or to share tools between agents, fill one yourself:
register(tool) for a defineTool() result, register(name, descriptor) for a
raw ToolDescriptor (a built-in tool, an MCP tool, or an existing tool() from
the ai SDK), and registerMany() for a record of descriptors or an array of defined tools.
toolRegistry to AgentExecutor.execute(); the agent config’s
tools map names the tools it may use.