invokeTool
Invoke a single tool directly as a typed step.
Quick Example
import { z } from 'zod';
import { invokeTool, tool } from '@noetic-tools/core';
const calculator = tool({
name: 'add',
description: 'Add two numbers',
input: z.object({
a: z.number(),
b: z.number(),
}),
output: z.object({
sum: z.number(),
}),
execute: async (args) => ({
sum: args.a + args.b,
}),
});
const addStep = invokeTool({
id: 'add-numbers',
tool: calculator,
args: {
a: 10,
},
});What It Does
invokeTool invokes a single tool without going through an LLM. This is useful when you know exactly which tool to call -- for example, after a conditional routes to a known action, or as part of a deterministic pipeline.
The optional args field lets you preset some or all of the tool's inputs. At runtime, preset args are merged with the step input.
The Tool Interface
Every tool in Noetic follows the same interface, whether it is passed to callModel or invoked directly via invokeTool.
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tool name (used in LLM function calling) |
description | string | Yes | Human-readable description for the model |
input | StandardSchemaV1<I> | Yes | Schema defining the tool's input — any Standard Schema v1 validator (Zod, Valibot, ArkType, …); Zod is the default |
output | StandardSchemaV1<O> | Yes | Schema defining the tool's output |
inputJsonSchema | Record<string, unknown> | No | Explicit JSON Schema override/fallback for non-Zod tool input. Zod uses its native converter; other validators can implement StandardJSONSchemaV1. A validation-only schema must provide this field or conversion throws MISSING_JSON_SCHEMA |
execute | (args: I, toolCtx: ToolExecutionContext) => Promise<O> | Yes | The function that runs the tool. The second argument is a ToolExecutionContext — { ctx, harness, fs, shell, context, assembledView, lastStepMeta } — not the step Context itself (which is available as toolCtx.ctx) |
needsApproval | boolean | No | If true, the runtime should ask for human approval before executing |
context | ToolContextDeclaration | No | Declares tool-owned state; the runtime generates a context layer from it |
ui | ToolUiDeclaration | No | Render functions (call / progress / result / error) so the tool's calls carry their own UI — see Generative UI |
API Reference
invokeTool Options
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique step identifier |
tool | Tool<StandardSchemaV1<unknown, I>, StandardSchemaV1<unknown, O>> | Yes | The tool to invoke |
args | Partial<I> | No | Preset arguments merged with step input |
Defining a Tool
import { z } from 'zod';
import type { Tool } from '@noetic-tools/core';
const fetchUrl: Tool<typeof UrlInput, typeof UrlOutput> = {
name: 'fetch-url',
description: 'Fetch the contents of a URL',
input: z.object({
url: z.string().url(),
}),
output: z.object({
body: z.string(),
status: z.number(),
}),
execute: async (args) => {
const res = await fetch(args.url);
return {
body: await res.text(),
status: res.status,
};
},
};
const UrlInput = z.object({
url: z.string().url(),
});
const UrlOutput = z.object({
body: z.string(),
status: z.number(),
});Standard Schema v1
Tool schemas accept any Standard Schema v1 validator, not just Zod. Runtime validation uses Zod safeParse or ~standard.validate (sync and Promise results are supported), and execute receives the parsed/transformed value.
Model-facing JSON Schema uses three tiers: Zod's native converter, the Standard JSON Schema v1 companion trait, then inputJsonSchema. For non-Zod inputs, an explicit inputJsonSchema overrides the trait and falls back when the trait converter throws. Zod 4.2+, ArkType 2.1.28+, Zod Mini, VineJS, and Sury implement the trait; Valibot adds it with toStandardJsonSchema() from @valibot/to-json-schema. Validation-only schemas still require inputJsonSchema. Noetic adds no per-validator runtime dependency.
import { toStandardJsonSchema } from '@valibot/to-json-schema';
import * as v from 'valibot';
import { tool } from '@noetic-tools/core';
const search = tool({
name: 'search',
description: 'Search the knowledge base',
input: toStandardJsonSchema(v.object({ query: v.string() })),
output: v.object({ results: v.array(v.string()) }),
execute: async (args) => ({ results: ['Result 1', 'Result 2'] }),
});Standard Schema support covers tool input/output/event schemas and callModel / ACP agent structured output only. Channels, context-layer schemas, item extension schemas, and JSON workflow schemas remain Zod-specific.
Human-in-the-Loop Approval
Set needsApproval: true on a tool to signal that the runtime should pause and request human confirmation before executing it.
import { z } from 'zod';
import { tool } from '@noetic-tools/core';
declare const db: { delete(recordId: string): Promise<void> };
const deleteTool = tool({
name: 'delete-record',
description: 'Permanently delete a database record',
input: z.object({
recordId: z.string(),
}),
output: z.object({
deleted: z.boolean(),
}),
execute: async (args) => {
// Only runs after approval
await db.delete(args.recordId);
return {
deleted: true,
};
},
needsApproval: true,
});Related
- callModel -- pass tools to an LLM and let the model decide when to call them.
- runCode -- for arbitrary async logic that is not a tool.
- Steps overview -- comparison of all three step types.