/** * MCP tool response hardening (spec-10). * * A single set of guards every tool runs through in the CallTool handler, so the * whole surface has one uniform input-validation, timeout, output-cap, and * error-normalization path: * * - Input validation BEFORE the handler runs, against the tool's own declared * `inputSchema` (reusing the hand-written JSON-Schema-subset validator from * spec-05 — no Ajv). Invalid args are a Tool Execution Error (`isError: true`) whose text * names the parameter, the expected shape, and a corrected example call, so the calling * model can self-correct (change: adopt-mcp-protocol-conformance; SEP-1303). * - Per-tool timeout via Promise.race, with slow tools overridden. * - Output size cap: oversized results are truncated DETERMINISTICALLY with a * `truncated: true` note telling the agent how to narrow the query — never a * silent drop. * - Error normalization to a stable code taxonomy, distinguishing "repo not * analyzed yet" (actionable) from real failures. */ /** Stable MCP tool error-code taxonomy. */ export type McpToolErrorCode = 'INVALID_ARGS' | 'NOT_ANALYZED' | 'TIMEOUT' | 'OUTPUT_TRUNCATED' | 'INTERNAL'; /** Reject the first unknown own property with a bounded, deterministic hint. */ export declare function validateKnownProperties(value: unknown, knownKeys: readonly string[]): string | null; /** * A minimal argument object that satisfies a schema's `required` list, each value an example of its * declared shape — the "corrected example" half of an actionable validation error. */ export declare function exampleToolArguments(inputSchema: unknown, depth?: number): Record; /** * The text of an actionable Tool Execution Error for rejected arguments: the tool, what is wrong * (parameter path and expected shape), and a corrected example call to retry with. The detail echoes * caller-supplied values, so it is bounded, secret-redacted, and stripped of terminal controls like * every other error result. */ export declare function invalidArgumentsMessage(toolName: string, detail: string, inputSchema: unknown, exampleOverrides?: Record): string; type InvalidArgumentsResult = { content: Array<{ type: 'text'; text: string; }>; isError: true; }; /** * Check a tool call's arguments before anything runs (change: adopt-mcp-protocol-conformance): schema * validation, then the `directory`. A rejection is a Tool Execution Error (`isError: true`) the model * can act on, never a JSON-RPC protocol error; it creates nothing, so no telemetry is written for it. */ export declare function checkToolArguments(toolName: string, args: unknown, inputSchema: unknown, options: { hadExplicitDirectory: boolean; validateDirectory: (directory: string) => Promise; }): Promise<{ ok: true; directory: string; } | { ok: false; result: InvalidArgumentsResult; }>; /** * Validate args against a tool's inputSchema. Returns a human-readable message on * failure, or null when valid (or when no schema is declared). */ export declare function validateToolArgs(args: unknown, inputSchema: unknown): string | null; /** Thrown when a tool exceeds its timeout — classified as TIMEOUT downstream. */ export declare class ToolTimeoutError extends Error { readonly toolName: string; readonly ms: number; constructor(toolName: string, ms: number); } /** The timeout budget for a tool (per-tool override or the default). */ export declare function toolTimeoutMs(toolName: string): number; /** Race a tool's work against its timeout. Rejects with ToolTimeoutError on expiry. */ export declare function withToolTimeout(work: Promise, toolName: string, msOverride?: number): Promise; /** * Deterministically cap a result string to a byte budget. When over budget, cut on * a UTF-8-safe boundary and append a note explaining how to narrow the query. */ export declare function capOutput(text: string, maxBytes: number): { text: string; truncated: boolean; }; /** * Cap a tool RESULT to a byte budget while keeping it parseable. * * Tool results are serialized to JSON before being sent to the agent. Naively * byte-truncating that JSON (what capOutput does to a raw string) cuts mid-string- * literal and yields UNPARSEABLE JSON — the agent can't use any of it (this is how * get_spec on a >256 KB spec broke). Instead: * - string results (raw-text tools) → capOutput, unchanged; * - object results → re-serialize with the single largest top-level string field * truncated to fit (shape preserved, valid JSON, marked `truncated: true`); * - anything else over budget → a valid JSON envelope wrapping the partial. * Binary search keeps the result within the byte budget despite JSON-escaping overhead. * * The string branch also SANITIZES, which the object branches do not need to: an object * result is emitted through `JSON.stringify`, which escapes control characters to * `\uXXXX` and renders them inert on the way to the agent's terminal. A string result * is emitted verbatim, so its control characters survive — and the one string-returning * handler (`get_signatures`) returns extracted source, code COMMENTS included. That * makes a comment in an analyzed repository a way to clear the agent's terminal and * print a forged OpenLore verdict. Newlines are the signature listing's own structure, * so they are kept. */ export declare function capStructuredResult(result: unknown, maxBytes: number): { text: string; truncated: boolean; }; /** Map an error to the stable taxonomy code (actionable vs real failure). */ export declare function classifyToolError(err: unknown): McpToolErrorCode; export {}; //# sourceMappingURL=tool-guard.d.ts.map