Tools
A tool is a typed function the model can call. Define one with defineTool (or the fluent ToolBuilder), backed by a Zod schema for input validation:
ts
import { defineTool, z } from "archadi-agent";
const getWeather = defineTool({
name: "get_weather",
description: "Fetches current weather for a city",
schema: z.object({ city: z.string() }),
async execute({ city }) {
const res = await fetch(`https://wttr.in/${city}?format=%C+%t`);
return { city, report: await res.text() };
},
});Attach it to an agent with .tool(...):
ts
const agent = Agent.builder().name("WeatherBot").instructions("...").model(target).tool(getWeather).build();What the Runner does for you
- Validation — input is parsed with your Zod schema before
executeruns; invalid input throwsToolValidationErrorand is reported back to the model as a tool error (it usually retries with corrected input) rather than crashing the run. - Error isolation — if
executethrows, the Runner catches it, wraps it inToolExecutionError, and feeds the error back to the model as the tool result, so one broken tool call doesn't kill the whole run. - Async support —
executecan be sync or return aPromise; both work. - JSON-schema generation —
tool.toJsonSchema()(used internally) converts your Zod schema to the JSON schema every provider needs for function calling.
Requiring approval
Mark a tool as sensitive with requiresApproval: true. The Runner will refuse to execute it unless the caller supplies an approvalHandler in RunOptions:
ts
const runShell = defineTool({
name: "run_shell_command",
description: "Runs a shell command",
requiresApproval: true,
schema: z.object({ command: z.string() }),
execute({ command }) { /* ... */ },
});
await runner.run(agent, "compile and run hello.cpp", {
approvalHandler: async ({ toolName, input }) => {
// e.g. prompt a human, check an allowlist, call another service...
return toolName === "run_shell_command"; // approve
},
});The CLI wires this to an interactive [y/N] prompt — see packages/cli/src/tools/execTool.ts.
Tool context
execute(input, ctx) receives a ToolContext:
ts
interface ToolContext {
runId: string;
agentName: string;
sessionId?: string;
metadata: Record<string, unknown>; // whatever you passed as RunOptions.metadata
signal?: AbortSignal;
}Use metadata to inject request-scoped things like a user id, a database client, or an API key id — this is how the (planned) full-stack layer authenticates tool calls without global state.