/*
RFC-0290: the supported MCP protocol versions and the minimal JSON-RPC 2.0
shapes the gate speaks. Owning this small subset (rather than a full MCP SDK)
bounds the protocol surface to exactly what conformance fixtures pin.
RFC-1113: the gate speaks the 2026-07-28 stateless era only — per-request
version via _meta["io.modelcontextprotocol/protocolVersion"], server/discover,
and UnsupportedProtocolVersionError; the legacy initialize handshake is gone.
- Do not model batching, notifications, sessions, or any transport beyond
one request → one response (the stateless Streamable HTTP subset).
- Do not retain legacy-era (≤ 2025-11-25) initialize negotiation —
forward-only per RFC-1113.
- RFC-0290: initial protocol constants.
- RFC-1113: replace PINNED_MCP_PROTOCOL_VERSION with SUPPORTED_MCP_PROTOCOL_VERSIONS; add UNSUPPORTED_PROTOCOL_VERSION (-32022) + unsupportedProtocolVersion helper.
- RFC-1113: align agent discovery surface with RFC 9727 and MCP 2026-07-28 stateless era
*/
/**
* RFC-1113: the MCP protocol versions this gate accepts, newest first.
* Modern-only (2026-07-28 stateless era) — upgrading is one reviewed change:
* extend the list, regenerate fixtures from the new spec, fix the handler
* until they pass (AS-5/AS-7).
*/
export const SUPPORTED_MCP_PROTOCOL_VERSIONS = ["2026-07-28"] as const;
/** The _meta key carrying the per-request protocol version (2026-07-28 spec). */
export const MCP_PROTOCOL_VERSION_META_KEY = "io.modelcontextprotocol/protocolVersion";
/** The HTTP header that MUST carry the same version on Streamable HTTP. */
export const MCP_PROTOCOL_VERSION_HEADER = "MCP-Protocol-Version";
export type JsonRpcId = string | number | null;
export interface JsonRpcRequest {
jsonrpc: "2.0";
id?: JsonRpcId;
method: string;
params?: unknown;
}
export interface JsonRpcSuccess {
jsonrpc: "2.0";
id: JsonRpcId;
result: unknown;
}
export interface JsonRpcErrorBody {
code: number;
message: string;
data?: unknown;
}
export interface JsonRpcError {
jsonrpc: "2.0";
id: JsonRpcId;
error: JsonRpcErrorBody;
}
export type JsonRpcResponse = JsonRpcSuccess | JsonRpcError;
/** Standard JSON-RPC 2.0 codes, plus -32000 (server error range) for dispatch failures. */
export const JSON_RPC_ERROR = {
PARSE_ERROR: -32700,
INVALID_REQUEST: -32600,
METHOD_NOT_FOUND: -32601,
INVALID_PARAMS: -32602,
INTERNAL_ERROR: -32603,
SERVER_ERROR: -32000,
/** MCP 2026-07-28: the request's protocol version is not supported. */
UNSUPPORTED_PROTOCOL_VERSION: -32022,
} as const;
export function isJsonRpcRequest(value: unknown): value is JsonRpcRequest {
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
const v = value as Record;
return v.jsonrpc === "2.0" && typeof v.method === "string";
}
export function jsonRpcError(
id: JsonRpcId,
code: number,
message: string,
data?: unknown,
): JsonRpcError {
return { jsonrpc: "2.0", id, error: { code, message, ...(data !== undefined ? { data } : {}) } };
}
export function jsonRpcSuccess(id: JsonRpcId, result: unknown): JsonRpcSuccess {
return { jsonrpc: "2.0", id, result };
}
/**
* RFC-1113 / MCP 2026-07-28: UnsupportedProtocolVersionError — the spec-defined
* diagnostic for a request whose declared protocol version is unknown or
* unsupported. `data.supported` lists the versions the client may retry with.
* On Streamable HTTP this response is served with status 400.
*/
export function unsupportedProtocolVersion(id: JsonRpcId, requested: unknown): JsonRpcError {
return jsonRpcError(
id,
JSON_RPC_ERROR.UNSUPPORTED_PROTOCOL_VERSION,
"Unsupported protocol version",
{
supported: [...SUPPORTED_MCP_PROTOCOL_VERSIONS],
requested: typeof requested === "string" ? requested : null,
},
);
}
/** Read the per-request protocol version from `params._meta` (2026-07-28 spec). */
export function readRequestProtocolVersion(request: JsonRpcRequest): string | undefined {
const params = request.params;
if (typeof params !== "object" || params === null) return undefined;
const meta = (params as Record)._meta;
if (typeof meta !== "object" || meta === null) return undefined;
const version = (meta as Record)[MCP_PROTOCOL_VERSION_META_KEY];
return typeof version === "string" ? version : undefined;
}