Tool definition
Complete contract for schemas, effects, approvals, retries, execution context, output validation, and projection.
type ToolDefinition<I = unknown, O = unknown> = {
description: string
input: InputSchema<I>
resolveInputJsonSchema?(): JsonSchema | Promise<JsonSchema>
output?: InputSchema<O>
project?(output: O, context: ToolContext): unknown | Promise<unknown>
maxOutputBytes?: number
effect?: 'read' | 'write' | 'destructive' | 'external-message'
approval?: 'never' | 'requester' | 'always'
retry?: 'safe' | 'idempotent' | 'never'
approvalSummary?(input: I): string | Promise<string>
execute(input: I, context: ToolContext): O | Promise<O>
}InputSchema
An input schema has a JSON Schema for the model and a parse(value) function for runtime validation. resolveInputJsonSchema supports a schema whose enum or allowed resources must be loaded per call; keep it bounded and deterministic.
ToolContext
The context contains runId, optional tool-call ID, actor, channel, current attachments, model messages, raw input, stable idempotencyKey, abort signal, connector clients, progress(text), and resolveAttachment(id, options).
Defaults
Do not rely on defaults for consequential tools. Declare effect, approval, and retry so a reviewer can understand the operation from code. Provider-native tools are normalized conservatively because names are not proof of safety.
Output handling
If output exists, Noir parses the execution result. project receives the validated output and returns the model-visible value. The resulting JSON is bounded by maxOutputBytes or the agent limit. Projection should remove internal state and preserve only what the next reasoning step needs.
Naming
Tool names are stable API. Connector tools become connector.tool, plugin tools become plugin.tool, and top-level groups become group.tool. Duplicate names fail during construction. Groups cannot nest recursively.