/** * MCPJam Public API — v1 contract (shared source of truth). * * Framework-agnostic: NO Hono, Convex, or Node imports, and no runtime * dependencies, so every public-API surface can import it cheaply (the same * discipline as `@mcpjam/sdk/host-config/internal`). It defines the wire * contract — the error-code union, the code -> HTTP-status map, the * internal -> public code mapping, and the envelope/pagination builders. * * Consumers: * - the Inspector Hono gateway (`mcpjam-inspector/server/routes/v1`) imports * this and adapts it to `c.json(...)` in its `envelope.ts`. * - the Convex backend (`mcpjam-backend/convex/publicApi`) will consume this * once its pinned `@mcpjam/sdk` version includes the subpath; until then it * keeps a byte-identical local copy, cross-checked by golden fixtures. * * Envelope rules: * - success (single resource) -> the resource object directly * - success (collection) -> { items, nextCursor? } (cursor-based) * - error -> { code, message, details? } + HTTP status * * Framework-specific response helpers (Hono `c.json`, platform `Response`) * intentionally live with each consumer, not here. So does anything a * particular HOST needs: the agent types below carry a kind and a severity, * never Slack wording or Block Kit — a second wrapper must be a rendering * file, not a protocol change. */ /** * Canonical v1 public error-code union. * * Reconciliation note: the Inspector Node already ships * UNAUTHORIZED/FORBIDDEN/NOT_FOUND/VALIDATION_ERROR/RATE_LIMITED/ * FEATURE_NOT_SUPPORTED/SERVER_UNREACHABLE/TIMEOUT/INTERNAL_ERROR (see * routes/web/errors.ts `ErrorCode`). The public union adopts those verbatim and * adds OAUTH_REQUIRED so callers (our MCP worker, CLI, agents) can distinguish * "this server needs an OAuth grant" from a generic 401. Draft-only codes * UPSTREAM_ERROR/TOOL_TIMEOUT are NOT public; they collapse to * SERVER_UNREACHABLE/TIMEOUT at the boundary (see INTERNAL_TO_V1_CODE). Adding * codes is backward-compatible; removing or repurposing one is breaking. * * CONFLICT (409) covers a write rejected because the resource is not in a state * that accepts it — the caller's request was well-formed, so VALIDATION_ERROR * would misreport it, and retrying verbatim will not help. Project Environments * are the first surface to need it: they use optimistic concurrency, so a stale * `expectedRevision` must be distinguishable from bad input. It also carries the * environment name collisions and archive-state rejections the backend reports * with the same code, and the run-start `ENVIRONMENT_REVISION_CONFLICT`, which * previously had no public mapping and so surfaced as a 500. */ declare const V1_ERROR_CODES: readonly ["UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "CONFLICT", "VALIDATION_ERROR", "RATE_LIMITED", "FEATURE_NOT_SUPPORTED", "SERVER_UNREACHABLE", "TIMEOUT", "OAUTH_REQUIRED", "INTERNAL_ERROR"]; type V1ErrorCode = (typeof V1_ERROR_CODES)[number]; declare function isV1ErrorCode(value: unknown): value is V1ErrorCode; /** Canonical error body. `details` is an opaque, JSON-serializable bag. */ interface V1ErrorBody { code: V1ErrorCode; message: string; details?: Record; } /** Canonical collection envelope. Cursor is opaque to the caller. */ interface V1Page { items: T[]; nextCursor?: string; } /** Canonical code -> HTTP status mapping. */ declare const V1_ERROR_STATUS: Record; /** * Internal/draft code -> public v1 code. Used at the surface boundary to map * whatever an internal handler threw (Inspector `ErrorCode`) onto the public * union. The 9 shipped Inspector codes map to themselves; the draft-only codes * collapse onto their canonical equivalents. */ declare const INTERNAL_TO_V1_CODE: Record; declare function mapInternalCode(code: string | undefined | null): V1ErrorCode; /** Build a canonical error body (drops an empty `details` bag). */ declare function v1ErrorBody(code: V1ErrorCode, message: string, details?: Record): V1ErrorBody; /** Build a canonical collection body (omits `nextCursor` when absent). */ declare function v1Page(items: T[], nextCursor?: string): V1Page; /** * What an approved action DOES, coarsely, so a host can word its confirmation * and its after-the-fact announcement truthfully. * * The announcement is the reason this exists rather than the host switching on * the operation name: "it's away" is true of a run and false of a cancellation, * and telling someone their cancel started something is the kind of small lie * that costs trust in every later message. Hosts that do not recognise a kind * must fall back to neutral copy, never to a guess. * * - `start` — begins work that will run for a while (a suite/case run). * - `cancel` — stops work that is already running. * - `generate` — authors content in the background. * - `schedule` — changes a recurring setting; nothing starts right now. * - `external` — reaches a third-party system whose effects MCPJam cannot * describe, undo, or bound. */ declare const PROPOSED_ACTION_KINDS: readonly ["start", "cancel", "generate", "schedule", "external"]; type ProposedActionKind = (typeof PROPOSED_ACTION_KINDS)[number]; /** * What the host should warn about, beyond "you are approving something". * * Absent means "the ordinary approval prompt is honest enough". Present names * the specific claim the host must make, and `none` exists because a default * prompt is not neutral: hosts word theirs around cost, so an action that * COSTS NOTHING needs to say so rather than inherit a warning that is false. * * - `spend` — consumes quota or credits, possibly repeatedly. * - `external` — runs somewhere MCPJam does not control and cannot undo. * - `none` — costs nothing (e.g. turning a schedule OFF). The host must not * claim it uses quota. */ declare const PROPOSED_ACTION_SEVERITIES: readonly ["spend", "external", "none"]; type ProposedActionSeverity = (typeof PROPOSED_ACTION_SEVERITIES)[number]; /** * An action the turn wants to take but may not take on its own. * * `actionId` is the ONLY thing a host's approval control needs to carry. What * the action does is held server-side against that id, because an approval * control's payload is mintable by anyone who can post in the host workspace — * a click may say WHICH proposal to run, never what it does. The rest of the * fields are for RENDERING and nothing else; a host that echoed `operation` * back as an instruction would be re-opening the hole `actionId` closes. */ interface ProposedAction { actionId: string; /** Platform operation name. Display/telemetry only — never an instruction. */ operation: string; /** Short, concrete summary of the target. Host-escaped before rendering. */ description: string; /** Verb for the approval control, e.g. "Run it". Host-capped. */ buttonLabel: string; kind: ProposedActionKind; /** Absent ⇒ the host's default confirmation copy is sufficient. */ confirmSeverity?: ProposedActionSeverity; /** * What the proposal is ABOUT, for hosts that correlate it with other turn * output — e.g. suppressing a duplicate run affordance on exactly the * created resource this proposal already offers to run. Display/dedup only, * never an instruction. Absent on operations with no meaningful target and * on servers that predate the field; a host must treat absence as "match * unknown" and fall back to its coarser behavior. */ target?: ProposedActionTarget; } /** * The resource a proposal targets, in the proposing operation's own selector * vocabulary: `selector` is whatever the validated input named — an id where * the server minted the proposal itself, possibly a name where the model * authored it — so hosts should match it against both. */ interface ProposedActionTarget { type: string; selector: string; } /** A resource the turn created, with an app deep link. */ interface AgentCreatedResource { type: string; id: string; name?: string; url: string; } /** The completed-turn envelope. */ interface AgentTurnResponse { reply: string; toolCalls: Array<{ operation: string; }>; createdResources: AgentCreatedResource[]; proposedActions: ProposedAction[]; usage: { inputTokens: number; outputTokens: number; }; } /** * What an approved action produced, when it produced something a host can * link to. * * Built SERVER-SIDE from the operation registry, never assembled by the host * from a result payload: a host that synthesised URLs would have to know each * operation's result shape, and would silently link to nothing the moment one * changed. */ interface ExecutedActionResource { type: string; id: string; url: string; } /** The response to executing an approved action. */ interface ExecuteProposedActionResponse { actionId: string; operation: string; status: "succeeded"; kind: ProposedActionKind; /** Absent when the action produced nothing linkable (e.g. a cancellation). */ resource?: ExecutedActionResource; /** The raw operation result. Opaque to the contract. */ result: unknown; } declare function classifyRuntimeError(error: unknown): { code: V1ErrorCode; message: string; }; export { type AgentCreatedResource, type AgentTurnResponse, type ExecuteProposedActionResponse, type ExecutedActionResource, INTERNAL_TO_V1_CODE, PROPOSED_ACTION_KINDS, PROPOSED_ACTION_SEVERITIES, type ProposedAction, type ProposedActionKind, type ProposedActionSeverity, type ProposedActionTarget, type V1ErrorBody, type V1ErrorCode, type V1Page, V1_ERROR_CODES, V1_ERROR_STATUS, classifyRuntimeError, isV1ErrorCode, mapInternalCode, v1ErrorBody, v1Page };