Core API
The building blocks under
createAgentTools: dispatch and listing against a config, the raw tool registry, the spill sweeper, and the error contract. Use these to build a custom transport — see The core API guide for the how-to.
dispatch
function dispatch(
name: string,
args: Record<string, unknown>,
config: ServerConfig,
): Promise<DispatchResult>;Looks up name in the surface for config (respecting readOnly), validates args against the tool's JSON Schema with ajv, runs the handler, bounds the output to maxOutputBytes, and serializes any thrown error. It never throws — an unknown tool, invalid arguments, or a tool-level failure all come back as an isError result.
callTool / dispatch never throw for tool-level problems — they always resolve to a DispatchResult:
interface DispatchResult {
isError: boolean;
text: string;
}- On success (
isError: false),textis the tool's output, already bounded tomaxOutputBytes. It is plain text for every tool exceptbash, whose successtextis a JSON object{ exit_code, stdout, stderr, signal, timed_out }— a non-zero exit is still a success. - On failure (
isError: true),textis a JSON error envelope. An unknown tool name — or a mutating tool whilereadOnlyis set — comes back asisErrorwith codenot_found.
listTools
function listTools(config: ServerConfig): ToolInfo[];The advertised surface for config — { name, description, inputSchema } per tool, honoring readOnly. See ToolInfo.
The registry
const tools: ToolDef[]; // all nine
const readOnlyTools: ToolDef[]; // read_file, list_dir, glob, grep
function selectSurface(readOnly: boolean): ToolDef[];
function getTool(name: string, surface?: ToolDef[]): ToolDef | undefined;tools— everyToolDef, in registration order.readOnlyTools— the non-mutating subset.selectSurface(readOnly)—readOnlyToolswhentrue, elsetools. This is whatdispatch/listToolsuse to honorconfig.readOnly.getTool(name, surface = tools)— find one tool by name within a surface, orundefined.
ToolDef
interface ToolDef {
name: string;
description: string;
inputSchema: Record<string, unknown>; // JSON Schema, ajv-validated on dispatch
bounded?: boolean; // when true, the handler bounds its own output (dispatch skips re-bounding)
handler: (args: Record<string, unknown>, config: ServerConfig) => Promise<string>;
}listTools projects a ToolDef down to ToolInfo (drops handler/bounded). If you call a handler yourself, you bypass validation and output bounding — prefer dispatch unless you have a reason not to.
sweepSpillDir
function sweepSpillDir(workspaceRoot: string): Promise<void>;Removes stale bash spill files from the .clarvis/ directory under workspaceRoot. Safe to call periodically in a long-lived process. See Limits & spill.
Errors
type ErrorCode =
| "invalid_input" | "not_found" | "not_a_file" | "is_binary"
| "no_match" | "ambiguous_match" | "patch_failed" | "io_error"
| "timeout" | "output_limit" | "too_large" | "path_escape" | "internal";
class ToolError extends Error {
readonly code: ErrorCode;
readonly fields: Record<string, unknown>;
constructor(code: ErrorCode, message: string, fields?: Record<string, unknown>);
}
function serializeError(err: unknown): string;
function fsError(err: NodeJS.ErrnoException, path: string): ToolError;ToolError— the structured error a tool handler throws.codeis the stable error code;fieldsare merged into the serialized envelope.serializeError(err)— turns aToolErrorinto the JSON envelope{ error, message, ...fields }. Any non-ToolErroris logged to stderr and returned as{ "error": "internal", "message": "internal error" }, so raw internals never leak to the caller.fsError(err, path)— maps a Nodefserrno (ENOENT→not_found,EISDIR/ENOTDIR→not_a_file, elseio_error) to aToolError. Useful when writing your own tool handlers.
StartupError (thrown by resolveConfig / buildConfig on bad config) is a separate class, exported from the package root.
See also
- The core API guide — building a transport with these primitives
- createAgentTools — the factory that wraps
dispatch/listTools - Error codes — the envelope and every
errorvalue - Configuration —
resolveConfig/buildConfig/ServerConfig