createAgentTools
The ergonomic entry point. It resolves a config once and returns an
AgentToolsobject withlistTools()andcallTool(). This is the high-level API; the core API exposes the same behavior unwrapped.
Signature
function createAgentTools(options: AgentToolsOptions): AgentTools;options is the same object accepted by resolveConfig — only workspaceRoot is required. Passing an invalid config (missing/nonexistent workspace, or an out-of-range limit) throws a StartupError synchronously.
import { createAgentTools } from "@clarvis/agent-tools";
const tools = createAgentTools({ workspaceRoot: process.cwd() });AgentTools
interface AgentTools {
readonly config: ServerConfig;
listTools(): ToolInfo[];
callTool(name: string, args?: Record<string, unknown>): Promise<DispatchResult>;
}| Member | Type | Description |
|---|---|---|
config | ServerConfig | The fully-resolved, frozen config (see Configuration). |
listTools | () => ToolInfo[] | The advertised surface for the active config — respects readOnly. |
callTool | (name, args?) => Promise<DispatchResult> | Validate args, run the tool, bound the output, serialize any error. Defaults args to {}. |
The object is a thin wrapper: listTools() calls listTools(config) and callTool() calls dispatch(name, args, config) from the core API.
ToolInfo
interface ToolInfo {
name: string;
description: string;
inputSchema: Record<string, unknown>; // JSON Schema
}What listTools() returns for each tool. inputSchema is a JSON Schema you can hand directly to a model's tool-use / function-calling API. See The tools for each tool's schema.
DispatchResult
callTool / dispatch never throw for tool-level problems — they always resolve to a DispatchResult:
interface DispatchResult {
isError: boolean;
content: ContentPart[]; // TextPart { type: "text"; text } | ImagePart { type: "image"; data; mimeType }
meta?: Record<string, unknown>; // structured sidecar for a client (never shown to the model)
}- On success (
isError: false),contentcarries the tool's output as an array of parts. Most tools return a single text part, bounded tomaxOutputBytes;read_imagereturns a single image part. Forbash, the text part is a JSON object{ exit_code, stdout, stderr, signal, timed_out }— a non-zero exit is still a success.contentText(content)concatenates the text parts into a string. metais present only when a tool has structured data for a client to render out-of-band. The editing tools setmeta.diffto a real unified diff of the change:edit_file,multi_edit,write_file(overwrite only), andreplace(on apply). Thecontenttext stays the short prose summary; the diff never reaches the model. Absent when there is nothing to diff (a brand-newwrite_file, or an overwrite whose prior content is binary/unreadable).- On failure (
isError: true),contentis a single text part holding a JSON error envelope. An unknown tool name — or a mutating tool whilereadOnlyis set — comes back asisErrorwith codenot_found.
Example
import { createAgentTools, contentText } from "@clarvis/agent-tools";
const tools = createAgentTools({ workspaceRoot: process.cwd(), readOnly: true });
for (const tool of tools.listTools()) {
console.log(tool.name, "→", tool.description);
}
const res = await tools.callTool("grep", { pattern: "createAgentTools", output_mode: "content" });
const text = contentText(res.content);
console.log(res.isError ? JSON.parse(text) : text);See also
- Configuration — the
AgentToolsOptionsandServerConfigshapes - The tools — inputs, output, and errors for each tool
- Core API —
dispatch/listTools/ the registry behind this factory - Embed it in an agent loop — the intended usage pattern