Skip to content

createAgentTools

The ergonomic entry point. It resolves a config once and returns an AgentTools object with listTools() and callTool(). This is the high-level API; the core API exposes the same behavior unwrapped.

Signature

ts
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.

ts
import { createAgentTools } from "@clarvis/agent-tools";

const tools = createAgentTools({ workspaceRoot: process.cwd() });

AgentTools

ts
interface AgentTools {
  readonly config: ServerConfig;
  listTools(): ToolInfo[];
  callTool(name: string, args?: Record<string, unknown>): Promise<DispatchResult>;
}
MemberTypeDescription
configServerConfigThe 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

ts
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:

ts
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), content carries the tool's output as an array of parts. Most tools return a single text part, bounded to maxOutputBytes; read_image returns a single image part. For bash, 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.
  • meta is present only when a tool has structured data for a client to render out-of-band. The editing tools set meta.diff to a real unified diff of the change: edit_file, multi_edit, write_file (overwrite only), and replace (on apply). The content text stays the short prose summary; the diff never reaches the model. Absent when there is nothing to diff (a brand-new write_file, or an overwrite whose prior content is binary/unreadable).
  • On failure (isError: true), content is a single text part holding a JSON error envelope. An unknown tool name — or a mutating tool while readOnly is set — comes back as isError with code not_found.

Example

ts
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

Released under the MIT License.