/** * A machine-readable classification for an MCP call that failed, carried across the host/plugin * bridge alongside the human-readable message. * * ## Why this exists * * The bridge used to reduce every failure to `err.message`, a bare string. A plugin therefore could * not tell "you lack permission for this tool" from "the host is unreachable" without matching on * host error prose, and the practical consequence was that plugins swallowed *all* failures into a * successful empty result to keep a permission denial from looking like a crash. That turns a * transient outage into "you have no connectors" - a false statement about the user's data, and one * that also suppresses the query layer's retry. * * A closed vocabulary lets the plugin branch on the one case it wants to tolerate and rethrow the * rest. * * ## Deliberately small, and deliberately not HTTP * * These are the distinctions a *caller* acts on differently, not a mirror of any status enum. Codes * that would prompt the same handling are folded together, because a vocabulary nobody can apply is * just a wider surface to get wrong. `unavailable` and `timeout` stay separate only because retry * policy differs between them. */ type McpErrorCode = /** The caller is not authenticated, or its token expired. Re-authentication may fix it. */ "unauthorized" /** Authenticated, but not permitted this tool or resource. Retrying will not help. */ | "forbidden" /** The tool, resource, or addressed entity does not exist. */ | "not_found" /** The arguments were rejected. A caller bug, or stale client-side validation. */ | "invalid_request" /** A concurrency or state conflict - a row version, or a duplicate. */ | "conflict" /** Throttled. Retry later, with backoff. */ | "rate_limited" /** The call did not complete in time. Safe to retry only if the operation is idempotent. */ | "timeout" /** The host or an upstream dependency is down. Retryable. */ | "unavailable" /** Anything else, including an unclassifiable failure. The default - never a claim. */ | "internal"; /** * Every valid {@link McpErrorCode}. Used to validate a code arriving over the wire: an unknown * string is downgraded rather than trusted, so a newer host cannot make an older plugin branch on a * code it has never heard of. */ declare const MCP_ERROR_CODES: readonly McpErrorCode[]; /** True when `value` is a code this build understands. */ declare function isMcpErrorCode(value: unknown): value is McpErrorCode; /** True when the failure is worth retrying as-is. */ declare function isRetryableMcpErrorCode(code: McpErrorCode): boolean; /** * An MCP call rejected by the host, carrying its {@link McpErrorCode}. * * `name` stays `"McpHostError"` - the string the bridge has always set - so code that matches on the * name keeps working. Prefer {@link isMcpToolError}, which survives a name change and works across * realm boundaries where `instanceof` does not. */ declare class McpToolError extends Error { /** Machine-readable classification. `internal` when the host sent none. */ readonly code: McpErrorCode; /** * Marks the instance for {@link isMcpToolError}. A structural marker rather than a prototype * check because the error can be constructed in one bundle and inspected in another, where * `instanceof` compares two different class objects and answers false. */ readonly isMcpToolError: true; constructor(message: string, code?: McpErrorCode); /** True when this failure is worth retrying unchanged. */ get retryable(): boolean; } /** True when `value` is an {@link McpToolError}, including one from another bundle. */ declare function isMcpToolError(value: unknown): value is McpToolError; /** * Maps an HTTP status onto a code. Split out because host MCP clients are commonly HTTP clients, so * a status is the classification most of them already have. * * Unrecognised statuses (including every 2xx and 3xx, which should not be reaching an error path) * yield `internal` rather than a guess. */ declare function mcpErrorCodeFromHttpStatus(status: number): McpErrorCode; /** * Derives a code from a host MCP client's RETURNED failure, as opposed to a thrown one. * * Both paths exist and both have to be covered. A client that throws is handled by * {@link classifyHostError}; a client that reports failure as `{ ok: false, error, status }` - the * common shape for anything wrapping HTTP - comes through here. Covering only the throw path leaves * the majority of real failures arriving as `internal`, which is the same blindness the code was * added to remove. * * Returns `undefined` for a successful response, so a caller can spread it without putting a * meaningless code on the happy path. */ declare function classifyHostResponse(response: { readonly ok: boolean; readonly status?: number; readonly mcpErrorCode?: unknown; }): McpErrorCode | undefined; /** * Derives a code from an arbitrary thrown value, for the host side of the bridge. * * Precedence, most explicit first: * * 1. An `mcpErrorCode` property holding a known code. The intended contract: a host MCP client that * knows why a call failed says so directly. * 2. A numeric `status` / `statusCode`, mapped by {@link mcpErrorCodeFromHttpStatus}. Covers the * HTTP clients that already carry one without asking every host to adopt the field above. * 3. `name === "AbortError"` / `"TimeoutError"`, which is how the platform's own aborts surface. * 4. `internal`. * * **Never infers from the message.** Matching prose would make the classification depend on wording * nobody treats as a contract, and it would silently reclassify itself the day someone improves an * error string. An unclassifiable failure is `internal`, which is honest. */ declare function classifyHostError(err: unknown): McpErrorCode; export { MCP_ERROR_CODES as M, type McpErrorCode as a, McpToolError as b, classifyHostError as c, classifyHostResponse as d, isMcpToolError as e, isRetryableMcpErrorCode as f, isMcpErrorCode as i, mcpErrorCodeFromHttpStatus as m };