From b631d049f6ac9854af427cac7662bc3442b0448f Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 18 Jun 2026 23:29:11 +0000 Subject: [PATCH 1/3] =?UTF-8?q?feat(server):=20add=20createRequestStateCod?= =?UTF-8?q?ec=20=E2=80=94=20opt-in=20HMAC=20sealing=20for=20the=20multi-ro?= =?UTF-8?q?und-trip=20requestState?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `createRequestStateCodec({ key, ttlSeconds?, bind? })` returns `{ mint, verify }`: `mint` HMAC-SHA256-seals a JSON-serializable payload (with TTL, default 600 s, and optional context binding) into the opaque `requestState` wire string; `verify` is the function consumers drop directly into `ServerOptions.requestState.verify` (and call again in the handler to read the payload back). WebCrypto-based and runtime-neutral; verification is fail-closed and constant-time (`subtle.verify`). Thrown reasons are fixed opaque codes (`malformed`/`mac`/`expired`/`bind`) — never decoded payload or binding values — so the seam's `onerror` relay cannot leak principal identifiers. The `ServerOptions.requestState.verify` return type is widened to `unknown | Promise` (the seam already awaited-and-discarded) so the codec's payload-returning `verify` is directly assignable. The hand-rolled HMAC in `examples/server/src/multiRoundTrip.ts` and the conformance fixture switch to the helper (the `input-required-result-tampered-state` scenario is now its conformance-side proof). --- .changeset/add-request-state-codec.md | 5 + docs/migration.md | 9 +- examples/mrtr/server.ts | 61 ++---- packages/server/src/index.ts | 4 + .../server/src/server/requestStateCodec.ts | 192 ++++++++++++++++++ packages/server/src/server/server.ts | 13 +- .../test/server/requestStateCodec.test.ts | 115 +++++++++++ test/conformance/src/everythingServer.ts | 78 +++---- 8 files changed, 383 insertions(+), 94 deletions(-) create mode 100644 .changeset/add-request-state-codec.md create mode 100644 packages/server/src/server/requestStateCodec.ts create mode 100644 packages/server/test/server/requestStateCodec.test.ts diff --git a/.changeset/add-request-state-codec.md b/.changeset/add-request-state-codec.md new file mode 100644 index 0000000000..8d93b0f9b5 --- /dev/null +++ b/.changeset/add-request-state-codec.md @@ -0,0 +1,5 @@ +--- +'@modelcontextprotocol/server': minor +--- + +Add `createRequestStateCodec({ key, ttlSeconds?, bind? })`, an opt-in HMAC-SHA256 sealing helper for the multi-round-trip `requestState`: `mint` seals a JSON-serializable payload (with TTL and optional context binding) and `verify` drops directly into `ServerOptions.requestState.verify`. WebCrypto-based and runtime-neutral; verification is fail-closed and constant-time. The `ServerOptions.requestState.verify` hook's return type is widened to `unknown | Promise` (the seam already discarded the return value) so the codec's `verify` is directly assignable. diff --git a/docs/migration.md b/docs/migration.md index ce292f18da..b358ce690a 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -1270,9 +1270,12 @@ has: only `tools/call` has a catch-all that wraps handler failures into `isError **`requestState` is untrusted input — protect it yourself.** `inputRequired({ requestState })` lets a server round-trip opaque state through the client instead of holding it in memory. The SDK treats it as an opaque string end to end: the client echoes it back byte-exact and never parses it, and the server sees the echoed value raw at `ctx.mcpReq.requestState`. The specification's requirement is the consumer's obligation: the value comes back as **attacker-controlled input**, so if it influences authorization, resource access, or business logic you -MUST integrity-protect it when minting it (for example HMAC or AEAD over the payload, bound to the principal, the originating method/parameters, and an expiry) and MUST reject state that fails verification on re-entry. The SDK does not provide or apply any sealing of its own, but -it does provide the place to put your verification: configure `ServerOptions.requestState.verify`, and the seam runs it before the handler whenever `requestState` is present — a thrown rejection answers the client with a frozen `-32602` (above the tool funnel, so it is a real -JSON-RPC error rather than an `isError` result). See `examples/server/src/multiRoundTrip.ts` for a worked HMAC example. +MUST integrity-protect it when minting it (for example HMAC or AEAD over the payload, bound to the principal, the originating method/parameters, and an expiry) and MUST reject state that fails verification on re-entry. The SDK does not apply any sealing of its own, but it does +provide the place to put your verification — configure `ServerOptions.requestState.verify`, and the seam runs it before the handler whenever `requestState` is present; a thrown rejection answers the client with a frozen `-32602` (above the tool funnel, so it is a real JSON-RPC +error rather than an `isError` result) — and an opt-in helper to drop into it: `createRequestStateCodec({ key, ttlSeconds?, bind? })` returns `{ mint, verify }` where `mint` HMAC-SHA256-seals a JSON-serializable payload (with a TTL, default 600 s, and optional context binding) +and `verify` is exactly the function you assign to the hook. The handler reads its payload back with the same `verify` (`await codec.verify(ctx.mcpReq.requestState, ctx)`). The codec is WebCrypto-based and runtime-neutral; the key must be at least 32 bytes and shared across every +instance that may receive an echoed value. Verification is fail-closed: any failure (bad MAC, expired, bind mismatch, malformed) throws with a fixed opaque reason code — the seam relays that code to `onerror` only (never the wire), and the code never carries decoded payload or +binding values, so operator logs do not pick up principal identifiers from rejections. See `examples/server/src/multiRoundTrip.ts` for a worked end-to-end example. **Client side — auto-fulfilment by default.** When a call to `tools/call`, `prompts/get`, or `resources/read` on a 2026-07-28 connection answers `input_required`, the client fulfils the embedded requests through the same handlers registered with `setRequestHandler('elicitation/create' | 'sampling/createMessage' | 'roots/list', …)` and retries the original request (fresh request id, `inputResponses`, byte-exact `requestState` echo) up to `inputRequired.maxRounds` rounds (default 10). `client.callTool()` and its siblings diff --git a/examples/mrtr/server.ts b/examples/mrtr/server.ts index db4f81105e..1907bc7fcb 100644 --- a/examples/mrtr/server.ts +++ b/examples/mrtr/server.ts @@ -11,57 +11,38 @@ * * `requestState` round-trips through the client and is therefore * attacker-controlled input on re-entry. A real server MUST integrity-protect - * it (e.g. HMAC or AEAD): this example mints `body.hmac` with a per-process - * key and rejects tampered state via the {@linkcode ServerOptions.requestState} - * `verify` hook, which answers a wire-level `-32602` Invalid Params error. + * it (e.g. HMAC or AEAD): this example uses the SDK-provided + * {@linkcode createRequestStateCodec} helper — `mint` HMAC-seals the payload + * with a per-process key and a TTL, and `verify` is dropped directly into the + * {@linkcode ServerOptions.requestState} hook so the seam rejects tampered or + * expired state with a wire-level `-32602` Invalid Params error before the + * handler runs. * * One binary, either transport (selected by the shared scaffold from argv). */ -import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto'; - import type { CallToolResult, InputRequiredResult } from '@modelcontextprotocol/server'; -import { acceptedContent, inputRequired, McpServer } from '@modelcontextprotocol/server'; +import { acceptedContent, createRequestStateCodec, inputRequired, McpServer } from '@modelcontextprotocol/server'; import * as z from 'zod/v4'; import { runServerFromArgs } from '../harness.js'; const CONFIRM_SCHEMA = { type: 'object' as const, properties: { confirm: { type: 'boolean' as const } }, required: ['confirm'] }; -// Per-process integrity key for requestState. The 2026-07-28 path serves every -// request from a fresh server instance — the state itself is the only thing -// that survives between rounds — so the key is process-local. -const STATE_KEY = randomBytes(32); - type DeployState = { step: 'confirm' | 'signed-in'; env: string }; -function mintState(payload: DeployState): string { - const body = Buffer.from(JSON.stringify(payload)).toString('base64url'); - return `${body}.${createHmac('sha256', STATE_KEY).update(body).digest('base64url')}`; -} - -function verifyState(state: string): void { - const dot = state.lastIndexOf('.'); - const body = dot > 0 ? state.slice(0, dot) : ''; - const expected = createHmac('sha256', STATE_KEY).update(body).digest(); - const provided = Buffer.from(state.slice(dot + 1), 'base64url'); - if (dot <= 0 || provided.length !== expected.length || !timingSafeEqual(provided, expected)) { - throw new Error('requestState failed integrity verification'); - } -} - -function readState(ctx: { mcpReq: { requestState?: string } }): DeployState | undefined { - // The seam-level verify hook has already proven integrity by the time the - // handler runs; this only re-reads the body. - const state = ctx.mcpReq.requestState; - return state === undefined - ? undefined - : (JSON.parse(Buffer.from(state.slice(0, state.lastIndexOf('.')), 'base64url').toString()) as DeployState); -} +// Per-process integrity key for requestState. The 2026-07-28 path serves every +// request from a fresh server instance — the state itself is the only thing +// that survives between rounds — so the key is process-local. A multi-instance +// deployment would load a shared secret here instead. +const stateCodec = createRequestStateCodec({ + key: crypto.getRandomValues(new Uint8Array(32)), + ttlSeconds: 600 +}); function buildServer(): McpServer { const server = new McpServer( { name: 'mrtr-example-server', version: '1.0.0' }, - { capabilities: { tools: {} }, requestState: { verify: verifyState } } + { capabilities: { tools: {} }, requestState: { verify: stateCodec.verify } } ); server.registerTool( @@ -74,8 +55,10 @@ function buildServer(): McpServer { async ({ env }, ctx): Promise => { // The handler reads the SAME context fields on every entry; what // changes between rounds is which input responses have arrived and - // what (verified) `requestState` was echoed back. - const state = readState(ctx); + // what (verified) `requestState` was echoed back. The seam-level + // verify hook has already proven integrity by the time the handler + // runs; calling `verify` again here just yields the payload. + const state = ctx.mcpReq.requestState === undefined ? undefined : await stateCodec.verify(ctx.mcpReq.requestState, ctx); const step = state?.step ?? 'confirm'; console.error(`[server] tools/call deploy(${env}) step=${step}`); @@ -88,7 +71,7 @@ function buildServer(): McpServer { }, // The next entry stays at the 'confirm' step until the // user actually accepts. - requestState: mintState({ step: 'confirm', env }) + requestState: await stateCodec.mint({ step: 'confirm', env }) }); } // Move to the URL-mode sign-in step. URL elicitation rides @@ -102,7 +85,7 @@ function buildServer(): McpServer { url: `https://example.com/auth?env=${env}` }) }, - requestState: mintState({ step: 'signed-in', env }) + requestState: await stateCodec.mint({ step: 'signed-in', env }) }); } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index ab24706d07..9b993eea1e 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -39,6 +39,10 @@ export type { OriginValidationResult } from './server/middleware/originValidatio export { localhostAllowedOrigins, originValidationResponse, validateOriginHeader } from './server/middleware/originValidation.js'; export type { PerRequestHTTPServerTransportOptions, PerRequestMessageExtra, PerRequestResponseMode } from './server/perRequestTransport.js'; export { PerRequestHTTPServerTransport } from './server/perRequestTransport.js'; +// Opt-in HMAC sealing for the multi-round-trip requestState (SEP-2322): the +// convenience codec consumers drop into ServerOptions.requestState.verify. +export type { RequestStateCodec, RequestStateCodecOptions } from './server/requestStateCodec.js'; +export { createRequestStateCodec } from './server/requestStateCodec.js'; export type { ServerOptions } from './server/server.js'; export { Server } from './server/server.js'; // subscriptions/listen change-event sourcing seam (protocol revision 2026-07-28). diff --git a/packages/server/src/server/requestStateCodec.ts b/packages/server/src/server/requestStateCodec.ts new file mode 100644 index 0000000000..659f2f4bf3 --- /dev/null +++ b/packages/server/src/server/requestStateCodec.ts @@ -0,0 +1,192 @@ +import type { ServerContext } from '@modelcontextprotocol/core'; + +/** + * Options for {@linkcode createRequestStateCodec}. + */ +export interface RequestStateCodecOptions { + /** + * The HMAC secret. A `string` value is UTF-8-encoded. MUST be at least + * 32 bytes (256 bits) long; a {@linkcode RangeError} is thrown at + * construction otherwise. The same key must be available to every server + * instance that may receive an echoed `requestState` (so a per-process + * random key only works when one process serves every round of a flow). + */ + key: Uint8Array | string; + + /** + * How long a minted `requestState` stays valid, in seconds. An echoed + * value past its expiry is rejected by {@linkcode RequestStateCodec.verify}. + * Defaults to `600` (ten minutes). + */ + ttlSeconds?: number; + + /** + * Optional context binding. Called at mint time and again at verify time; + * a `requestState` minted under one binding value is rejected when echoed + * under a different one. Use this to bind state to the authenticated + * principal and/or the originating method (the spec's user-binding MUST + * for state that influences authorization), for example: + * + * ```ts + * bind: ctx => `${ctx.mcpReq.method}\0${ctx.http?.authInfo?.clientId ?? ''}` + * ``` + * + * When configured, {@linkcode RequestStateCodec.mint} requires its `ctx` + * argument. + */ + bind?: (ctx: ServerContext) => string; +} + +/** + * The codec returned by {@linkcode createRequestStateCodec}: `mint` seals a + * JSON-serializable payload into the wire string a handler returns from + * `inputRequired({ requestState })`; `verify` is the function to drop into + * {@linkcode ServerOptions.requestState | ServerOptions.requestState.verify} + * (it throws on any failure, which the seam answers as the frozen `-32602`) + * AND the function a handler calls to read the payload back from + * `ctx.mcpReq.requestState` after the seam has run. + */ +export interface RequestStateCodec { + /** + * Seal `payload` into an opaque wire string. The result is what the + * handler returns from `inputRequired({ requestState })`. + * + * @param ctx The handler's context. Required when the codec was created + * with a {@linkcode RequestStateCodecOptions.bind | bind} + * callback; ignored otherwise. + */ + mint(payload: T, ctx?: ServerContext): Promise; + + /** + * Verify an echoed `requestState` and return the original payload. Throws + * on any failure (bad MAC, expired, bind mismatch, malformed). The thrown + * message is a fixed opaque reason code (`'malformed'` / `'mac'` / + * `'expired'` / `'bind'`) — never the decoded payload, the binding value, + * or any other context-derived field. + * + * Pass this directly as `ServerOptions.requestState.verify`. + */ + verify(state: string, ctx: ServerContext): Promise; +} + +const PREFIX = 'v1.'; + +// Runtime-neutral base64url (no padding) over raw bytes. `btoa`/`atob` are +// available in browsers, Cloudflare Workers, and Node 16+. +function bytesToBase64Url(bytes: Uint8Array): string { + let bin = ''; + for (const b of bytes) bin += String.fromCodePoint(b); + return btoa(bin).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, ''); +} + +function base64UrlToBytes(s: string): Uint8Array { + const b64 = s.replaceAll('-', '+').replaceAll('_', '/'); + const bin = atob(b64); + const bytes = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; i++) bytes[i] = bin.codePointAt(i)!; + return bytes; +} + +/** + * Create an opt-in HMAC-SHA256 codec for the multi-round-trip `requestState` + * (protocol revision 2026-07-28). + * + * `requestState` round-trips through the client and is attacker-controlled + * input on re-entry. The SDK applies no protection of its own; this helper is + * the convenience implementation of the spec's integrity MUST so authors don't + * hand-roll HMAC. Wire shape: + * + * "v1." b64url({"p":,"exp":,"b":?}) "." b64url(mac) + * + * Verification is fail-closed and constant-time (WebCrypto `subtle.verify`). + * See `examples/server/src/multiRoundTrip.ts` for a worked end-to-end example. + */ +export function createRequestStateCodec(options: RequestStateCodecOptions): RequestStateCodec { + const subtle = globalThis.crypto?.subtle; + if (subtle === undefined) { + throw new TypeError( + 'createRequestStateCodec requires the Web Crypto API (globalThis.crypto.subtle); ' + + 'see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/faq.md for the Node.js polyfill instructions' + ); + } + + const keyBytes = typeof options.key === 'string' ? new TextEncoder().encode(options.key) : options.key; + if (keyBytes.byteLength < 32) { + throw new RangeError(`createRequestStateCodec: key must be at least 32 bytes (got ${keyBytes.byteLength})`); + } + const ttlSeconds = options.ttlSeconds ?? 600; + const bind = options.bind; + + // The CryptoKey is imported once (lazily) and reused for every mint/verify. + // WebCrypto's `sign`/`verify` only accept BufferSource, and a Uint8Array + // backed by a SharedArrayBuffer or a resizable buffer is rejected by some + // runtimes — slice to a fresh standalone copy. + let cryptoKey: ReturnType | undefined; + const importedKey = (): ReturnType => + (cryptoKey ??= subtle.importKey('raw', Uint8Array.from(keyBytes), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'])); + + const utf8 = new TextEncoder(); + + return { + async mint(payload, ctx) { + const envelope: { p: T; exp: number; b?: string } = { + p: payload, + exp: Math.floor(Date.now() / 1000) + ttlSeconds + }; + if (bind !== undefined) { + if (ctx === undefined) { + throw new TypeError('createRequestStateCodec: mint() requires ctx when a bind callback is configured'); + } + envelope.b = bind(ctx); + } + const body = bytesToBase64Url(utf8.encode(JSON.stringify(envelope))); + const mac = new Uint8Array(await subtle.sign('HMAC', await importedKey(), utf8.encode(body))); + return `${PREFIX}${body}.${bytesToBase64Url(mac)}`; + }, + + async verify(state, ctx) { + // Envelope shape: "v1." body "." mac. The MAC is checked FIRST so + // every other rejection reason is only reachable for a value we + // minted (or a peer with the key did). + const dot = state.lastIndexOf('.'); + if (!state.startsWith(PREFIX) || dot <= PREFIX.length) { + throw new Error('malformed'); + } + const body = state.slice(PREFIX.length, dot); + let macBytes: Uint8Array; + try { + macBytes = base64UrlToBytes(state.slice(dot + 1)); + } catch { + throw new Error('malformed'); + } + // SubtleCrypto.verify is constant-time by spec — no manual byte + // compare, no `timingSafeEqual` dependency. + const ok = await subtle.verify('HMAC', await importedKey(), macBytes, utf8.encode(body)); + if (!ok) { + throw new Error('mac'); + } + // The body decoded after a good MAC is by construction the JSON we + // wrote; a parse failure here would indicate key compromise rather + // than tampering, but stays fail-closed regardless. + let envelope: { p: T; exp: number; b?: string }; + try { + envelope = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(base64UrlToBytes(body))) as { + p: T; + exp: number; + b?: string; + }; + } catch { + throw new Error('malformed'); + } + if (typeof envelope.exp !== 'number' || envelope.exp < Math.floor(Date.now() / 1000)) { + throw new Error('expired'); + } + if (bind !== undefined && envelope.b !== bind(ctx)) { + // Opaque reason only — never interpolate the expected/actual + // binding values (they may carry principal identifiers). + throw new Error('bind'); + } + return envelope.p; + } + }; +} diff --git a/packages/server/src/server/server.ts b/packages/server/src/server/server.ts index a59258a973..5689a8144f 100644 --- a/packages/server/src/server/server.ts +++ b/packages/server/src/server/server.ts @@ -137,12 +137,19 @@ export type ServerOptions = ProtocolOptions & { * `requestState`. The spec MUST for integrity-protecting state that * influences authorization, resource access, or business logic is on * the server author (basic/patterns/mrtr, server requirements 4–5); - * the SDK provides NO default verification. Leaving this option + * the SDK provides NO default verification — + * {@linkcode createRequestStateCodec} is the SDK-provided HMAC helper + * whose `verify` drops in here directly. Leaving this option * unconfigured keeps today's behavior — `ctx.mcpReq.requestState` is * passed through raw and MUST be treated as attacker-controlled * input. + * + * The return value is ignored (the seam awaits-and-discards); the + * hook signature accepts any return so a verifier that also yields + * the decoded payload — as {@linkcode RequestStateCodec.verify} does + * — is directly assignable. */ - verify?: (state: string, ctx: ServerContext) => void | Promise; + verify?: (state: string, ctx: ServerContext) => unknown | Promise; }; }; @@ -227,7 +234,7 @@ export class Server extends Protocol { private _instructions?: string; private _jsonSchemaValidator: jsonSchemaValidator; private _cacheHints?: ServerOptions['cacheHints']; - private _requestStateVerify?: (state: string, ctx: ServerContext) => void | Promise; + private _requestStateVerify?: (state: string, ctx: ServerContext) => unknown | Promise; /** * Callback for when initialization has fully completed (i.e., the client has sent an `notifications/initialized` notification). diff --git a/packages/server/test/server/requestStateCodec.test.ts b/packages/server/test/server/requestStateCodec.test.ts new file mode 100644 index 0000000000..c55f688fd9 --- /dev/null +++ b/packages/server/test/server/requestStateCodec.test.ts @@ -0,0 +1,115 @@ +/** + * `createRequestStateCodec` — the opt-in HMAC-SHA256 sealing helper for the + * multi-round-trip `requestState` (SEP-2322). Pure unit tests of the codec; + * the seam-level wiring (`ServerOptions.requestState.verify`) is covered in + * `inputRequired.test.ts`. + */ +import type { ServerContext } from '@modelcontextprotocol/core'; +import { describe, expect, it } from 'vitest'; + +import { createRequestStateCodec } from '../../src/server/requestStateCodec.js'; +import type { ServerOptions } from '../../src/server/server.js'; + +const KEY = crypto.getRandomValues(new Uint8Array(32)); + +// Minimal stand-in for the bits of ServerContext the codec's `bind` callback +// reads — the codec itself never inspects ctx beyond passing it to `bind`. +const fakeCtx = (method: string, clientId?: string) => + ({ mcpReq: { method }, http: clientId === undefined ? undefined : { authInfo: { clientId } } }) as unknown as ServerContext; + +describe('createRequestStateCodec', () => { + it('round-trips a JSON payload', async () => { + const codec = createRequestStateCodec<{ step: string; n: number }>({ key: KEY }); + const wire = await codec.mint({ step: 'confirm', n: 42 }); + expect(wire).toMatch(/^v1\.[-A-Za-z0-9_]+\.[-A-Za-z0-9_]+$/); + const payload = await codec.verify(wire, fakeCtx('tools/call')); + expect(payload).toEqual({ step: 'confirm', n: 42 }); + }); + + it('rejects a tampered body with reason "mac"', async () => { + const codec = createRequestStateCodec({ key: KEY }); + const wire = await codec.mint({ step: 'confirm' }); + // Flip a base64url character in the body segment. + const dot = wire.lastIndexOf('.'); + const tampered = `${wire.slice(0, 4)}${wire[4] === 'A' ? 'B' : 'A'}${wire.slice(5, dot)}${wire.slice(dot)}`; + await expect(codec.verify(tampered, fakeCtx('tools/call'))).rejects.toThrow('mac'); + }); + + it('rejects a tampered MAC with reason "mac"', async () => { + const codec = createRequestStateCodec({ key: KEY }); + const wire = await codec.mint({ step: 'confirm' }); + const tampered = `${wire.slice(0, -2)}${wire.at(-2) === 'A' ? 'B' : 'A'}${wire.at(-1)}`; + await expect(codec.verify(tampered, fakeCtx('tools/call'))).rejects.toThrow('mac'); + }); + + it('rejects values minted under a different key', async () => { + const codecA = createRequestStateCodec({ key: KEY }); + const codecB = createRequestStateCodec({ key: crypto.getRandomValues(new Uint8Array(32)) }); + const wire = await codecA.mint({ step: 'confirm' }); + await expect(codecB.verify(wire, fakeCtx('tools/call'))).rejects.toThrow('mac'); + }); + + it('rejects malformed envelopes (missing prefix / missing segments)', async () => { + const codec = createRequestStateCodec({ key: KEY }); + await expect(codec.verify('not-v1.body.mac', fakeCtx('tools/call'))).rejects.toThrow('malformed'); + await expect(codec.verify('v1.bodyonly', fakeCtx('tools/call'))).rejects.toThrow('malformed'); + await expect(codec.verify('v1..mac', fakeCtx('tools/call'))).rejects.toThrow('malformed'); + await expect(codec.verify('', fakeCtx('tools/call'))).rejects.toThrow('malformed'); + }); + + it('rejects an expired value with reason "expired"', async () => { + // ttlSeconds: -1 stamps an exp already in the past; the MAC still + // verifies (we minted it), so the rejection is the expiry check. + const codec = createRequestStateCodec({ key: KEY, ttlSeconds: -1 }); + const wire = await codec.mint({ step: 'confirm' }); + await expect(codec.verify(wire, fakeCtx('tools/call'))).rejects.toThrow('expired'); + }); + + describe('context binding', () => { + const bind = (ctx: ServerContext) => + `${ctx.mcpReq.method}\0${(ctx.http?.authInfo as { clientId?: string } | undefined)?.clientId ?? ''}`; + + it('round-trips when the binding value matches', async () => { + const codec = createRequestStateCodec<{ step: string }>({ key: KEY, bind }); + const wire = await codec.mint({ step: 'confirm' }, fakeCtx('tools/call', 'alice')); + const payload = await codec.verify(wire, fakeCtx('tools/call', 'alice')); + expect(payload).toEqual({ step: 'confirm' }); + }); + + it('rejects with reason "bind" when the binding value differs — message is opaque', async () => { + const codec = createRequestStateCodec({ key: KEY, bind }); + const wire = await codec.mint({ step: 'confirm' }, fakeCtx('tools/call', 'alice')); + const rejection = await codec.verify(wire, fakeCtx('tools/call', 'mallory')).catch((e: Error) => e); + expect(rejection).toBeInstanceOf(Error); + // The thrown reason is a fixed code; neither principal identifier + // appears in the message (so onerror logging cannot leak them). + expect((rejection as Error).message).toBe('bind'); + expect((rejection as Error).message).not.toContain('alice'); + expect((rejection as Error).message).not.toContain('mallory'); + }); + + it('mint without ctx throws when bind is configured', async () => { + const codec = createRequestStateCodec({ key: KEY, bind }); + await expect(codec.mint({ step: 'confirm' })).rejects.toThrow(TypeError); + }); + }); + + it('throws RangeError on a key shorter than 32 bytes', () => { + expect(() => createRequestStateCodec({ key: new Uint8Array(31) })).toThrow(RangeError); + expect(() => createRequestStateCodec({ key: 'short' })).toThrow(RangeError); + }); + + it('accepts a 32-character string key (UTF-8 length)', async () => { + const codec = createRequestStateCodec({ key: 'a'.repeat(32) }); + const wire = await codec.mint({ ok: true }); + expect(await codec.verify(wire, fakeCtx('tools/call'))).toEqual({ ok: true }); + }); + + it('codec.verify is directly assignable to ServerOptions.requestState.verify', () => { + // Type-level guard: the seam hook accepts any return so a verifier + // that also yields the decoded payload drops in directly. + const codec = createRequestStateCodec({ key: KEY }); + const opts: ServerOptions = { requestState: { verify: codec.verify } }; + expect(opts.requestState?.verify).toBe(codec.verify); + }); +}); diff --git a/test/conformance/src/everythingServer.ts b/test/conformance/src/everythingServer.ts index 4196e817d2..bb349a3f8c 100644 --- a/test/conformance/src/everythingServer.ts +++ b/test/conformance/src/everythingServer.ts @@ -7,7 +7,7 @@ * This server is designed to pass all conformance test scenarios. */ -import { createHmac, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto'; +import { randomUUID } from 'node:crypto'; import { localhostHostValidation } from '@modelcontextprotocol/express'; import { NodeStreamableHTTPServerTransport, toNodeHandler } from '@modelcontextprotocol/node'; @@ -27,6 +27,7 @@ import { classifyInboundRequest, CLIENT_CAPABILITIES_META_KEY, createMcpHandler, + createRequestStateCodec, fromJsonSchema, inputRequired, isInitializeRequest, @@ -92,36 +93,18 @@ const TEST_AUDIO_BASE64 = 'UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0Y // attacker-controlled input. The SDK treats it as an opaque string and applies // no protection of its own, so a server that lets it influence behavior MUST // integrity-protect it when minting and MUST reject state that fails -// verification (see the migration guide). This pair of helpers is the worked -// example of that obligation: the payload is HMAC-signed with a per-process -// key and verified on every retry. The key is process-local because the -// 2026-07-28 path serves every request from a fresh server instance — the -// state itself is the only thing that survives between rounds. -const REQUEST_STATE_HMAC_KEY = randomBytes(32); - -function mintRequestState(payload: Record): string { - const body = Buffer.from(JSON.stringify(payload)).toString('base64url'); - const signature = createHmac('sha256', REQUEST_STATE_HMAC_KEY).update(body).digest('base64url'); - return `${body}.${signature}`; -} - -function verifyRequestState(state: string): Record | undefined { - const separator = state.lastIndexOf('.'); - if (separator <= 0) { - return undefined; - } - const body = state.slice(0, separator); - const expected = createHmac('sha256', REQUEST_STATE_HMAC_KEY).update(body).digest(); - const provided = Buffer.from(state.slice(separator + 1), 'base64url'); - if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) { - return undefined; - } - try { - return JSON.parse(Buffer.from(body, 'base64url').toString('utf8')) as Record; - } catch { - return undefined; - } -} +// verification (see the migration guide). This fixture uses the SDK-provided +// `createRequestStateCodec` helper — `mint` HMAC-seals the payload with a +// per-process key and a TTL, and `verify` is the function dropped into +// `ServerOptions.requestState.verify` so the seam rejects tampered or expired +// state with `-32602` before the handler runs (which is what the +// `input-required-result-tampered-state` conformance scenario asserts). The +// key is process-local because the 2026-07-28 path serves every request from +// a fresh server instance — the state itself is the only thing that survives +// between rounds. +const requestStateCodec = createRequestStateCodec>({ + key: crypto.getRandomValues(new Uint8Array(32)) +}); // Function to create a new MCP server instance (one per session) function createMcpServer() { @@ -149,13 +132,7 @@ function createMcpServer() { // request that carries requestState is verified before the handler // runs. A rejection answers a wire-level -32602 with // data.reason 'invalid_request_state'. - requestState: { - verify: state => { - if (verifyRequestState(state) === undefined) { - throw new Error('requestState failed integrity verification'); - } - } - } + requestState: { verify: requestStateCodec.verify } } ); @@ -822,10 +799,13 @@ function createMcpServer() { } }) }, - requestState: mintRequestState({ tool: 'request_state', nonce: randomUUID() }) + requestState: await requestStateCodec.mint({ tool: 'request_state', nonce: randomUUID() }) }); } - const state = ctx.mcpReq.requestState === undefined ? undefined : verifyRequestState(ctx.mcpReq.requestState); + // The seam-level verify hook has already proven integrity by the + // time the handler runs; calling `verify` again here just yields + // the payload (and would re-reject if it somehow had not). + const state = ctx.mcpReq.requestState === undefined ? undefined : await requestStateCodec.verify(ctx.mcpReq.requestState, ctx); if (state === undefined) { throw new ProtocolError(ProtocolErrorCode.InvalidParams, 'Invalid requestState: missing or failed integrity verification'); } @@ -862,7 +842,7 @@ function createMcpServer() { }), client_roots: inputRequired.listRoots() }, - requestState: mintRequestState({ tool: 'multiple_inputs', nonce: randomUUID() }) + requestState: await requestStateCodec.mint({ tool: 'multiple_inputs', nonce: randomUUID() }) }); } return { content: [{ type: 'text', text: `${greeting} ${name} — ${roots.length} root(s) visible` }] }; @@ -879,7 +859,7 @@ function createMcpServer() { inputSchema: z.object({}) }, async (_args, ctx): Promise => { - const state = ctx.mcpReq.requestState === undefined ? undefined : verifyRequestState(ctx.mcpReq.requestState); + const state = ctx.mcpReq.requestState === undefined ? undefined : await requestStateCodec.verify(ctx.mcpReq.requestState, ctx); const round = state?.tool === 'multi_round' && typeof state.round === 'number' ? state.round : 0; if (round === 0) { return inputRequired({ @@ -893,7 +873,7 @@ function createMcpServer() { } }) }, - requestState: mintRequestState({ tool: 'multi_round', round: 1, nonce: randomUUID() }) + requestState: await requestStateCodec.mint({ tool: 'multi_round', round: 1, nonce: randomUUID() }) }); } if (round === 1) { @@ -909,7 +889,7 @@ function createMcpServer() { } }) }, - requestState: mintRequestState({ tool: 'multi_round', round: 2, name, nonce: randomUUID() }) + requestState: await requestStateCodec.mint({ tool: 'multi_round', round: 2, name, nonce: randomUUID() }) }); } const color = acceptedContent<{ color: string }>(ctx.mcpReq.inputResponses, 'step2')?.color ?? 'unknown'; @@ -918,10 +898,10 @@ function createMcpServer() { ); // Tampered-state rejection: the seam-level `requestState.verify` hook - // (configured on the McpServer above) rejects a retry whose requestState - // fails the fixture's HMAC before this handler runs, answering the - // wire-level -32602 the conformance scenario requires. The handler only - // sees verified state. + // (the codec's `verify`, configured on the McpServer above) rejects a + // retry whose requestState fails HMAC before this handler runs, answering + // the wire-level -32602 the conformance scenario requires. The handler + // only sees verified state. mcpServer.registerTool( 'test_input_required_result_tampered_state', { @@ -943,7 +923,7 @@ function createMcpServer() { } }) }, - requestState: mintRequestState({ tool: 'tampered_state', nonce: randomUUID() }) + requestState: await requestStateCodec.mint({ tool: 'tampered_state', nonce: randomUUID() }) }); } ); From 86bbb4a4642375c461d8a47383647d578972d53c Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 18 Jun 2026 23:39:07 +0000 Subject: [PATCH 2/3] chore(test/conformance): pin conformance to local build of main @ d70d7ad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendors a tarball built from modelcontextprotocol/conformance@main (d70d7ad, post-0.2.0-alpha.4) and points the package.json devDependency at it via a file: spec, so #347 (client-scenario mocks serve server/discover) is available without waiting for the next published release. README documents the pin and how to revert to a published version. Baseline read against d6008bcd7 (no fixture changes in this commit): client:all 345p / 14f / 1w - stale baseline: auth/scope-step-up now passes (#337 gates the SEP-2350 scope-union WARNING to 2026-07-28+) - new unexpected: request-metadata (2f) — mock now rejects with -32020/-32022 (renumbered per spec#2907 in #353); SDK still emits and recognises -32001/-32004 - sep-2322-client-request-state, http-custom-headers, http-invalid-tool-headers all pass with the withLocalDiscoverResponse shim still in place; #347 makes those mocks serve server/discover natively, so the shim is now redundant (removal is a separate change) server:draft 68p / 4f - server-stateless 23p/1f, http-custom-header-server-validation 6p/3f, http-header-validation flagged — all #353 error-code renumbering (referee now asserts -32020/-32021/-32022; SDK emits -32001/-32004) - new on main: SEP-2663 tasks lifecycle scenario (#262), pending-list only — not in the draft suite - spec-types/draft.ts re-synced (#348) --- pnpm-lock.yaml | 11 ++++++----- test/conformance/README.md | 10 ++++++++++ test/conformance/package.json | 2 +- test/conformance/vendor/.gitignore | 2 ++ ...protocol-conformance-0.2.0-main.d70d7ad.tgz | Bin 0 -> 102387 bytes 5 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 test/conformance/vendor/.gitignore create mode 100644 test/conformance/vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c112ff6f1..f062d68951 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1540,8 +1540,8 @@ importers: specifier: workspace:^ version: link:../../packages/client '@modelcontextprotocol/conformance': - specifier: 0.2.0-alpha.4 - version: 0.2.0-alpha.4(@cfworker/json-schema@4.1.1) + specifier: file:./vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz + version: file:test/conformance/vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz(@cfworker/json-schema@4.1.1) '@modelcontextprotocol/core': specifier: workspace:^ version: link:../../packages/core @@ -2552,8 +2552,9 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@modelcontextprotocol/conformance@0.2.0-alpha.4': - resolution: {integrity: sha512-WAz/Q+Fmr2XFcytLkmbNAJvUi0vCciNLQbjkHnaUUSyPcqQZEVNfsLECZWhN8hRS8oGpGDl9OLR9yBtzyGIY2Q==} + '@modelcontextprotocol/conformance@file:test/conformance/vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz': + resolution: {integrity: sha512-L61dGG8Fx+JzenEwzROovVB+tcIT8o6TzuKSVegYzgw3oIsf3aIHASJgZtKbnrhgOnSowxBpHm74d/HDKiaZnQ==, tarball: file:test/conformance/vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz} + version: 0.2.0-main.d70d7ad hasBin: true '@modelcontextprotocol/sdk@1.29.0': @@ -6442,7 +6443,7 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@modelcontextprotocol/conformance@0.2.0-alpha.4(@cfworker/json-schema@4.1.1)': + '@modelcontextprotocol/conformance@file:test/conformance/vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz(@cfworker/json-schema@4.1.1)': dependencies: '@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) '@octokit/rest': 22.0.1 diff --git a/test/conformance/README.md b/test/conformance/README.md index addd6a58e2..d2da4d56f1 100644 --- a/test/conformance/README.md +++ b/test/conformance/README.md @@ -4,6 +4,16 @@ This directory contains conformance test implementations for the TypeScript MCP > `@modelcontextprotocol/conformance` is pinned to an exact version (no `^` range) in `package.json`. New conformance releases are adopted by deliberately bumping the pin and updating `expected-failures.yaml` for any new scenarios/checks in the same change. +> **Local pin (temporary):** the pin currently points at a vendored tarball +> (`file:./vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz`) built +> from `modelcontextprotocol/conformance@main` (`d70d7ad`, post-`0.2.0-alpha.4`). +> This pulls in [#347](https://github.com/modelcontextprotocol/conformance/pull/347) +> (client-scenario mocks now serve `server/discover`) ahead of the next published +> release so the discover-shim removal can be exercised. To revert to a published +> release, replace the `file:` spec in `package.json` with the exact version +> string (e.g. `"0.2.0-alpha.5"`), delete `vendor/`, run `pnpm install`, and +> reconcile `expected-failures.yaml` in the same change. + ## Client Conformance Tests Tests the SDK's client implementation against a conformance test server. diff --git a/test/conformance/package.json b/test/conformance/package.json index 96becacab1..a2688e54ee 100644 --- a/test/conformance/package.json +++ b/test/conformance/package.json @@ -40,7 +40,7 @@ "test:conformance:all": "pnpm run test:conformance:client:all && pnpm run test:conformance:server:all" }, "devDependencies": { - "@modelcontextprotocol/conformance": "0.2.0-alpha.4", + "@modelcontextprotocol/conformance": "file:./vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz", "@modelcontextprotocol/client": "workspace:^", "@modelcontextprotocol/server": "workspace:^", "@modelcontextprotocol/core": "workspace:^", diff --git a/test/conformance/vendor/.gitignore b/test/conformance/vendor/.gitignore new file mode 100644 index 0000000000..fceadb8a3a --- /dev/null +++ b/test/conformance/vendor/.gitignore @@ -0,0 +1,2 @@ +# Allow vendored conformance tarballs (root .gitignore has *.tgz). +!*.tgz diff --git a/test/conformance/vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz b/test/conformance/vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2ef865142084210f592021c6116e561dea480ac3 GIT binary patch literal 102387 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

KM_^0&uOl}Y#!0tM9eeI|9u&B@@coJDyElwVy} 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

n^H|G- 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 literal 0 HcmV?d00001 From 47f361b4b2e98299565beaf8a174298671654a47 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Mon, 22 Jun 2026 15:04:46 +0000 Subject: [PATCH 3/3] =?UTF-8?q?fix(server):=20requestState=20codec=20harde?= =?UTF-8?q?ning=20=E2=80=94=20bind=20as=20keyed=20HMAC=20tag,=20key=20snap?= =?UTF-8?q?shot,=20prefix=20bound=20into=20MAC,=20constant-time=20bind=20c?= =?UTF-8?q?ompare,=20fail-closed=20on=20unconfigured=20bind;=20align=20{fe?= =?UTF-8?q?tch,close,notify,bus}=20doc-shape;=20reconcile=20conformance=20?= =?UTF-8?q?expected-failures=20to=20local=20main=20pin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/create-mcp-handler.md | 2 +- docs/migration-SKILL.md | 9 +- docs/migration.md | 8 +- .../server/src/server/createMcpHandler.ts | 4 +- .../server/src/server/requestStateCodec.ts | 103 +++++++++++++++--- packages/server/src/server/server.ts | 10 +- .../test/server/requestStateCodec.test.ts | 58 ++++++++++ test/conformance/README.md | 21 ++-- .../expected-failures.2026-07-28.yaml | 18 ++- test/conformance/expected-failures.yaml | 49 +++++++-- 10 files changed, 232 insertions(+), 50 deletions(-) diff --git a/.changeset/create-mcp-handler.md b/.changeset/create-mcp-handler.md index 238593202d..83906108be 100644 --- a/.changeset/create-mcp-handler.md +++ b/.changeset/create-mcp-handler.md @@ -4,7 +4,7 @@ Add `createMcpHandler(factory, { legacy?, onerror?, responseMode? })`, an HTTP entry point that serves the 2026-07-28 draft revision per request: each envelope-carrying request is classified once, served on a fresh instance from the factory bound to the claimed revision, and answered with a JSON body or a lazily-upgraded SSE stream. 2025-era serving is selected with the `legacy` option (`'stateless'` — the default — for per-request stateless serving via the existing streamable HTTP transport, `'reject'` for a modern-only strict endpoint -that answers 2025-era requests with the unsupported-protocol-version error naming its supported revisions). The handler is a web-standard `{ fetch, close, notify }` object: `fetch(request, { authInfo?, parsedBody? })` is the only request face (Node frameworks wrap it with +that answers 2025-era requests with the unsupported-protocol-version error naming its supported revisions). The handler is a web-standard `{ fetch, close, notify, bus }` object: `fetch(request, { authInfo?, parsedBody? })` is the only request face (Node frameworks wrap it with `toNodeHandler(handler)` from `@modelcontextprotocol/node`), and `close()` tears down in-flight modern exchanges. Also exported: `legacyStatelessFallback` (the same stateless legacy serving as a standalone fetch-shaped handler), the `PerRequestHTTPServerTransport` single-exchange transport and the `classifyInboundRequest` classifier for hand-wired compositions, and the supporting types. `responseMode: 'json'` never streams and drops mid-call notifications (progress, logging and other related messages emitted before the result); listen-class subscription streams are always served over SSE. The entry performs no Origin/Host validation (use the middleware packages) and no token verification — `authInfo` is pass-through and never derived from request headers. diff --git a/docs/migration-SKILL.md b/docs/migration-SKILL.md index 53979aa508..ba4414f434 100644 --- a/docs/migration-SKILL.md +++ b/docs/migration-SKILL.md @@ -561,7 +561,7 @@ The 2026-07-28 revision removes the server→client JSON-RPC request channel; se | `throw new UrlElicitationRequiredError([…])` | `return inputRequired({ inputRequests: { id: inputRequired.elicitUrl({…}) } })` | | handler shared across both eras | branch on the served era: keep the v1 push-style call toward 2025-era requests, return `inputRequired(...)` toward 2026-07-28 requests | -`inputRequired`/`acceptedContent`/`InputRequiredSpec` are exported from `@modelcontextprotocol/server`. `requestState` round-trips as an opaque string and comes back as attacker-controlled input — integrity-protect (HMAC/AEAD) and verify it yourself when relying on it. Client +`inputRequired`/`acceptedContent`/`InputRequiredSpec` are exported from `@modelcontextprotocol/server`. `requestState` round-trips as an opaque string and comes back as attacker-controlled input — integrity-protect (HMAC/AEAD) and verify it yourself when relying on it, or drop the SDK's `createRequestStateCodec({ key, ttlSeconds?, bind? })` into `ServerOptions.requestState.verify` (mint with `codec.mint`, decode on re-entry with `codec.verify`). Client side: auto-fulfilment is on by default (`ClientOptions.inputRequired`, `maxRounds` cap default 10); manual mode is `inputRequired: { autoFulfill: false }` plus per-call `allowInputRequired: true` and `withInputRequired(schema)`. ## 13. Behavioral Changes @@ -610,9 +610,10 @@ New in 2.0 — v1 has no equivalent API. How v1 Streamable HTTP hosting maps ont GET/DELETE session operations, all-legacy batches, posted responses, non-JSON bodies). Returns `false` for envelope-claiming requests AND for malformed/incomplete modern claims (the modern path answers those with `-32602`/`-32001`) — route `false` traffic to the modern handler, never to a legacy handler. The predicate classifies a clone (the body stays readable); pass the parsed body as the second argument when the stream was already consumed. - `legacyStatelessFallback(factory)` is exported as a standalone fetch-shaped handler producing the same stateless legacy serving as the default. -- The handler is web-standards-only (`{ fetch, close, notify }`). On Workers / Bun / Deno, `export default handler` works directly. On Node frameworks (Express, Fastify, plain `node:http`), wrap once with `toNodeHandler(handler)` from `@modelcontextprotocol/node`: - `app.all('/mcp', toNodeHandler(handler))`, or `const node = toNodeHandler(handler); app.all('/mcp', (req, res) => void node(req, res, req.body))` when a body parser already consumed the stream. Earlier 2.x alphas exposed this as `handler.node(req, res, req.body)` — replace with - the `toNodeHandler` wrap and add the `@modelcontextprotocol/node` import. `NodeIncomingMessageLike` / `NodeServerResponseLike` are now exported from `@modelcontextprotocol/node`, not `@modelcontextprotocol/server`. +- The handler is web-standards-only (`{ fetch, close, notify, bus }`). On Workers / Bun / Deno, `export default handler` works directly. On Node frameworks (Express, Fastify, plain `node:http`), wrap once with `toNodeHandler(handler, { onerror? })` from + `@modelcontextprotocol/node`: `app.all('/mcp', toNodeHandler(handler))`, or `const node = toNodeHandler(handler); app.all('/mcp', (req, res) => void node(req, res, req.body))` when a body parser already consumed the stream. The optional `onerror` receives the adapter-level + error fallback (request conversion / `handler.fetch` throw) before the `500` response is written. Earlier 2.x alphas exposed this as `handler.node(req, res, req.body)` — replace with the `toNodeHandler` wrap and add the `@modelcontextprotocol/node` import. + `NodeIncomingMessageLike` / `NodeServerResponseLike` are now exported from `@modelcontextprotocol/node`, not `@modelcontextprotocol/server`. ### Server (stdio / long-lived connections) diff --git a/docs/migration.md b/docs/migration.md index b358ce690a..55326a1543 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -1273,9 +1273,11 @@ never parses it, and the server sees the echoed value raw at `ctx.mcpReq.request MUST integrity-protect it when minting it (for example HMAC or AEAD over the payload, bound to the principal, the originating method/parameters, and an expiry) and MUST reject state that fails verification on re-entry. The SDK does not apply any sealing of its own, but it does provide the place to put your verification — configure `ServerOptions.requestState.verify`, and the seam runs it before the handler whenever `requestState` is present; a thrown rejection answers the client with a frozen `-32602` (above the tool funnel, so it is a real JSON-RPC error rather than an `isError` result) — and an opt-in helper to drop into it: `createRequestStateCodec({ key, ttlSeconds?, bind? })` returns `{ mint, verify }` where `mint` HMAC-SHA256-seals a JSON-serializable payload (with a TTL, default 600 s, and optional context binding) -and `verify` is exactly the function you assign to the hook. The handler reads its payload back with the same `verify` (`await codec.verify(ctx.mcpReq.requestState, ctx)`). The codec is WebCrypto-based and runtime-neutral; the key must be at least 32 bytes and shared across every -instance that may receive an echoed value. Verification is fail-closed: any failure (bad MAC, expired, bind mismatch, malformed) throws with a fixed opaque reason code — the seam relays that code to `onerror` only (never the wire), and the code never carries decoded payload or -binding values, so operator logs do not pick up principal identifiers from rejections. See `examples/server/src/multiRoundTrip.ts` for a worked end-to-end example. +and `verify` is exactly the function you assign to the hook. The handler reads its payload back with the same `verify` (`await codec.verify(ctx.mcpReq.requestState, ctx)`) — re-calling `verify` from the handler is the intended pattern (the seam already proved integrity; the +second call is the decode). The codec is **signed, not encrypted**: the body is integrity-protected but the client can base64url-decode it and read the payload in clear, so do not put secrets in the payload — use an AEAD construction if confidentiality is required (the optional +`bind` value is stored as a keyed HMAC tag, not raw, so a principal identifier in the binding does not leak). The codec is WebCrypto-based and runtime-neutral; the key must be at least 32 bytes and shared across every instance that may receive an echoed value. Verification is +fail-closed: any failure (bad MAC, expired, bind mismatch, malformed) throws with a fixed opaque reason code — the seam relays that code to `onerror` only (never the wire), and the code never carries decoded payload or binding values, so operator logs do not pick up principal +identifiers from rejections. See `examples/mrtr/server.ts` for a worked end-to-end example. **Client side — auto-fulfilment by default.** When a call to `tools/call`, `prompts/get`, or `resources/read` on a 2026-07-28 connection answers `input_required`, the client fulfils the embedded requests through the same handlers registered with `setRequestHandler('elicitation/create' | 'sampling/createMessage' | 'roots/list', …)` and retries the original request (fresh request id, `inputResponses`, byte-exact `requestState` echo) up to `inputRequired.maxRounds` rounds (default 10). `client.callTool()` and its siblings diff --git a/packages/server/src/server/createMcpHandler.ts b/packages/server/src/server/createMcpHandler.ts index 4e4b859383..691bbca1f4 100644 --- a/packages/server/src/server/createMcpHandler.ts +++ b/packages/server/src/server/createMcpHandler.ts @@ -201,8 +201,8 @@ export interface CreateMcpHandlerOptions { /** * The handler returned by {@linkcode createMcpHandler}: a web-standard - * `{ fetch, close, notify }` object — the shape Workers/Bun/Deno expect from - * `export default`. `fetch` is an arrow-assigned bound property: it can be + * `{ fetch, close, notify, bus }` object — the shape Workers/Bun/Deno expect + * from `export default`. `fetch` is an arrow-assigned bound property: it can be * detached and passed around (`const { fetch } = handler`) without losing its * binding. * diff --git a/packages/server/src/server/requestStateCodec.ts b/packages/server/src/server/requestStateCodec.ts index 659f2f4bf3..c1d7c5e516 100644 --- a/packages/server/src/server/requestStateCodec.ts +++ b/packages/server/src/server/requestStateCodec.ts @@ -6,7 +6,7 @@ import type { ServerContext } from '@modelcontextprotocol/core'; export interface RequestStateCodecOptions { /** * The HMAC secret. A `string` value is UTF-8-encoded. MUST be at least - * 32 bytes (256 bits) long; a {@linkcode RangeError} is thrown at + * 32 bytes (256 bits) long; a `RangeError` is thrown at * construction otherwise. The same key must be available to every server * instance that may receive an echoed `requestState` (so a per-process * random key only works when one process serves every round of a flow). @@ -31,6 +31,11 @@ export interface RequestStateCodecOptions { * bind: ctx => `${ctx.mcpReq.method}\0${ctx.http?.authInfo?.clientId ?? ''}` * ``` * + * The returned value is stored in the envelope as a domain-separated HMAC + * tag (keyed by the codec's `key`), not the raw string — so a principal + * identifier in the binding does not appear in the wire value the client + * holds. + * * When configured, {@linkcode RequestStateCodec.mint} requires its `ctx` * argument. */ @@ -41,7 +46,7 @@ export interface RequestStateCodecOptions { * The codec returned by {@linkcode createRequestStateCodec}: `mint` seals a * JSON-serializable payload into the wire string a handler returns from * `inputRequired({ requestState })`; `verify` is the function to drop into - * {@linkcode ServerOptions.requestState | ServerOptions.requestState.verify} + * {@linkcode server/server.ServerOptions | ServerOptions}`.requestState.verify` * (it throws on any failure, which the seam answers as the frozen `-32602`) * AND the function a handler calls to read the payload back from * `ctx.mcpReq.requestState` after the seam has run. @@ -79,6 +84,22 @@ function bytesToBase64Url(bytes: Uint8Array): string { return btoa(bin).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, ''); } +// Constant-time equality on the fixed-length base64url bind-tag string. Same +// guarantee as the body-MAC check (which uses `subtle.verify`): the +// per-character XOR accumulator does not vary its trip count with the position +// of the first mismatch. Length is fixed (22 chars for the 128-bit truncated +// tag), so the early length-check leaks nothing. +function constantTimeTagEqual(a: string, b: string): boolean { + if (a.length !== b.length) { + return false; + } + let r = 0; + for (let i = 0; i < a.length; i++) { + r |= a.codePointAt(i)! ^ b.codePointAt(i)!; + } + return r === 0; +} + function base64UrlToBytes(s: string): Uint8Array { const b64 = s.replaceAll('-', '+').replaceAll('_', '/'); const bin = atob(b64); @@ -96,10 +117,30 @@ function base64UrlToBytes(s: string): Uint8Array { * the convenience implementation of the spec's integrity MUST so authors don't * hand-roll HMAC. Wire shape: * - * "v1." b64url({"p":,"exp":,"b":?}) "." b64url(mac) + * "v1." b64url({"p":,"exp":,"b":?}) "." b64url(mac) + * + * where `bindTag` is `b64url(HMAC(key, "mcp.requestState.bind:" + bind(ctx))[:16])` + * — the binding value is never embedded raw. * - * Verification is fail-closed and constant-time (WebCrypto `subtle.verify`). - * See `examples/server/src/multiRoundTrip.ts` for a worked end-to-end example. + * The codec is **signed, not encrypted**: the body is integrity-protected but + * the client can base64url-decode it and read the payload (`p`) in clear. Do + * not put secrets in the payload; use an AEAD construction if confidentiality + * is required. The handler reads its payload back by calling `verify` again on + * `ctx.mcpReq.requestState` after the seam has run — re-calling `verify` is + * the intended pattern (the seam already proved integrity; the second call is + * the decode). + * + * Verification is fail-closed and constant-time (WebCrypto `subtle.verify` for + * the body MAC; a fixed-length XOR-accumulator compare for the bind tag). + * See `examples/mrtr/server.ts` for a worked end-to-end example. + * + * Design comparison (mcp.d `secureRequestState`, the peer SDK's reference + * implementation): mcp.d additionally offers an AES-256-GCM encrypted mode and + * derives independent cipher / bind-HMAC sub-keys from the operator secret via + * HKDF-SHA256, with an auto-generated per-process ephemeral key when none is + * supplied. This codec deliberately ships only the signed mode and a single + * keyed HMAC (domain-separated by input prefix) — HKDF sub-key derivation and + * an encrypted mode are intentionally out of scope for the initial release. */ export function createRequestStateCodec(options: RequestStateCodecOptions): RequestStateCodec { const subtle = globalThis.crypto?.subtle; @@ -110,7 +151,12 @@ export function createRequestStateCodec(options: RequestStateCodecO ); } - const keyBytes = typeof options.key === 'string' ? new TextEncoder().encode(options.key) : options.key; + // Snapshot the key bytes at construction. When `options.key` is a + // Uint8Array, holding the caller's reference would let a post-construction + // mutation (e.g. zeroing the secret for hygiene) silently change the key + // the lazy `importedKey()` reads on first mint/verify — a TOCTOU on the + // secret. The owned copy here is what every later read sees. + const keyBytes = typeof options.key === 'string' ? new TextEncoder().encode(options.key) : Uint8Array.from(options.key); if (keyBytes.byteLength < 32) { throw new RangeError(`createRequestStateCodec: key must be at least 32 bytes (got ${keyBytes.byteLength})`); } @@ -118,15 +164,25 @@ export function createRequestStateCodec(options: RequestStateCodecO const bind = options.bind; // The CryptoKey is imported once (lazily) and reused for every mint/verify. - // WebCrypto's `sign`/`verify` only accept BufferSource, and a Uint8Array - // backed by a SharedArrayBuffer or a resizable buffer is rejected by some - // runtimes — slice to a fresh standalone copy. + // `keyBytes` is already an owned standalone Uint8Array (snapshot above), so + // it satisfies WebCrypto's BufferSource requirement on every runtime. let cryptoKey: ReturnType | undefined; const importedKey = (): ReturnType => - (cryptoKey ??= subtle.importKey('raw', Uint8Array.from(keyBytes), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'])); + (cryptoKey ??= subtle.importKey('raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'])); const utf8 = new TextEncoder(); + // Domain-separated HMAC tag of the binding value, truncated to 128 bits and + // base64url'd. The label keeps the bind-tag computation separate from the + // body MAC even though both use the same key (the body MAC's input is the + // versioned wire string `"v1." + base64url(...)` and can never start with + // this label). + const BIND_LABEL = 'mcp.requestState.bind:'; + const bindTag = async (value: string): Promise => { + const mac = new Uint8Array(await subtle.sign('HMAC', await importedKey(), utf8.encode(BIND_LABEL + value))); + return bytesToBase64Url(mac.slice(0, 16)); + }; + return { async mint(payload, ctx) { const envelope: { p: T; exp: number; b?: string } = { @@ -137,10 +193,13 @@ export function createRequestStateCodec(options: RequestStateCodecO if (ctx === undefined) { throw new TypeError('createRequestStateCodec: mint() requires ctx when a bind callback is configured'); } - envelope.b = bind(ctx); + envelope.b = await bindTag(bind(ctx)); } const body = bytesToBase64Url(utf8.encode(JSON.stringify(envelope))); - const mac = new Uint8Array(await subtle.sign('HMAC', await importedKey(), utf8.encode(body))); + // The MAC covers `PREFIX + body` so the version tag is bound: a + // valid `body.mac` pair under `v1.` cannot be transplanted to a + // future `v2.` codec under the same key. + const mac = new Uint8Array(await subtle.sign('HMAC', await importedKey(), utf8.encode(PREFIX + body))); return `${PREFIX}${body}.${bytesToBase64Url(mac)}`; }, @@ -160,8 +219,9 @@ export function createRequestStateCodec(options: RequestStateCodecO throw new Error('malformed'); } // SubtleCrypto.verify is constant-time by spec — no manual byte - // compare, no `timingSafeEqual` dependency. - const ok = await subtle.verify('HMAC', await importedKey(), macBytes, utf8.encode(body)); + // compare, no `timingSafeEqual` dependency. The MAC input mirrors + // mint: `PREFIX + body`, so the version tag is authenticated. + const ok = await subtle.verify('HMAC', await importedKey(), macBytes, utf8.encode(PREFIX + body)); if (!ok) { throw new Error('mac'); } @@ -181,9 +241,18 @@ export function createRequestStateCodec(options: RequestStateCodecO if (typeof envelope.exp !== 'number' || envelope.exp < Math.floor(Date.now() / 1000)) { throw new Error('expired'); } - if (bind !== undefined && envelope.b !== bind(ctx)) { - // Opaque reason only — never interpolate the expected/actual - // binding values (they may carry principal identifiers). + if (bind !== undefined) { + const expected = await bindTag(bind(ctx)); + if (envelope.b === undefined || !constantTimeTagEqual(envelope.b, expected)) { + // Opaque reason only — never interpolate the expected/actual + // binding values (they may carry principal identifiers). + throw new Error('bind'); + } + } else if (envelope.b !== undefined) { + // Fail-closed on configuration drift: a token minted with a + // bind callback is rejected when verified by an instance that + // has none configured. Accepting it would silently drop the + // principal-binding guarantee. throw new Error('bind'); } return envelope.p; diff --git a/packages/server/src/server/server.ts b/packages/server/src/server/server.ts index 5689a8144f..eba28d80d0 100644 --- a/packages/server/src/server/server.ts +++ b/packages/server/src/server/server.ts @@ -138,16 +138,18 @@ export type ServerOptions = ProtocolOptions & { * influences authorization, resource access, or business logic is on * the server author (basic/patterns/mrtr, server requirements 4–5); * the SDK provides NO default verification — - * {@linkcode createRequestStateCodec} is the SDK-provided HMAC helper - * whose `verify` drops in here directly. Leaving this option + * {@linkcode server/requestStateCodec.createRequestStateCodec | createRequestStateCodec} + * is the SDK-provided HMAC helper whose `verify` drops in here + * directly. Leaving this option * unconfigured keeps today's behavior — `ctx.mcpReq.requestState` is * passed through raw and MUST be treated as attacker-controlled * input. * * The return value is ignored (the seam awaits-and-discards); the * hook signature accepts any return so a verifier that also yields - * the decoded payload — as {@linkcode RequestStateCodec.verify} does - * — is directly assignable. + * the decoded payload — as + * {@linkcode server/requestStateCodec.RequestStateCodec | RequestStateCodec}`.verify` + * does — is directly assignable. */ verify?: (state: string, ctx: ServerContext) => unknown | Promise; }; diff --git a/packages/server/test/server/requestStateCodec.test.ts b/packages/server/test/server/requestStateCodec.test.ts index c55f688fd9..81686bd2a4 100644 --- a/packages/server/test/server/requestStateCodec.test.ts +++ b/packages/server/test/server/requestStateCodec.test.ts @@ -92,6 +92,64 @@ describe('createRequestStateCodec', () => { const codec = createRequestStateCodec({ key: KEY, bind }); await expect(codec.mint({ step: 'confirm' })).rejects.toThrow(TypeError); }); + + it('does not embed the raw binding value in the minted state (stored as a keyed tag)', async () => { + const codec = createRequestStateCodec({ key: KEY, bind }); + const wire = await codec.mint({ step: 'confirm' }, fakeCtx('tools/call', 'alice')); + // The body segment is base64url(JSON) — decode it and confirm neither + // component of the raw `bind(ctx)` value (`tools/call\0alice`) appears. + // It is stored as a keyed HMAC tag instead, so the client cannot read + // the principal identifier out of the signed-not-encrypted envelope. + const body = wire.slice('v1.'.length, wire.lastIndexOf('.')); + const decoded = new TextDecoder().decode( + Uint8Array.from(atob(body.replaceAll('-', '+').replaceAll('_', '/')), c => c.codePointAt(0)!) + ); + expect(decoded).not.toContain('alice'); + expect(decoded).not.toContain('tools/call'); + expect(JSON.parse(decoded)).toHaveProperty('b'); + }); + + it('rejects a bound token when bind is unconfigured (fail-closed on config drift)', async () => { + const bound = createRequestStateCodec({ key: KEY, bind }); + const unbound = createRequestStateCodec({ key: KEY }); + const wire = await bound.mint({ step: 'confirm' }, fakeCtx('tools/call', 'alice')); + // Same key, MAC verifies — but the unconfigured instance must + // refuse a token that carries a `b` field rather than silently + // dropping the principal-binding guarantee. + await expect(unbound.verify(wire, fakeCtx('tools/call', 'alice'))).rejects.toThrow('bind'); + }); + }); + + it('snapshots a Uint8Array key at construction (caller mutation has no effect)', async () => { + const mutable = crypto.getRandomValues(new Uint8Array(32)); + const codec = createRequestStateCodec({ key: mutable }); + // Zero the caller's buffer AFTER construction (standard secret-hygiene). + // The codec already owns its own copy, so mint+verify still agree on + // the original key — and a second codec built from the zeroed buffer + // is a DIFFERENT key. + mutable.fill(0); + const wire = await codec.mint({ ok: true }); + expect(await codec.verify(wire, fakeCtx('tools/call'))).toEqual({ ok: true }); + const codecZero = createRequestStateCodec({ key: new Uint8Array(32) }); + await expect(codecZero.verify(wire, fakeCtx('tools/call'))).rejects.toThrow('mac'); + }); + + it('binds the version prefix into the MAC (a transplanted body.mac under a different prefix fails)', async () => { + const codec = createRequestStateCodec({ key: KEY }); + const wire = await codec.mint({ ok: true }); + // The MAC covers `"v1." + body`. The verifier hard-gates the prefix, + // so to assert the MAC binding we recompute it manually with `"v2."` + // over the same body and confirm the codec rejects that pair. + const body = wire.slice('v1.'.length, wire.lastIndexOf('.')); + const cryptoKey = await crypto.subtle.importKey('raw', KEY, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']); + const v2Mac = new Uint8Array(await crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(`v2.${body}`))); + const b64url = (b: Uint8Array) => + btoa(String.fromCodePoint(...b)) + .replaceAll('+', '-') + .replaceAll('/', '_') + .replace(/=+$/, ''); + // Same body, MAC computed for a v2 prefix, presented under v1 — MAC fails. + await expect(codec.verify(`v1.${body}.${b64url(v2Mac)}`, fakeCtx('tools/call'))).rejects.toThrow('mac'); }); it('throws RangeError on a key shorter than 32 bytes', () => { diff --git a/test/conformance/README.md b/test/conformance/README.md index d2da4d56f1..08521b21ff 100644 --- a/test/conformance/README.md +++ b/test/conformance/README.md @@ -4,15 +4,18 @@ This directory contains conformance test implementations for the TypeScript MCP > `@modelcontextprotocol/conformance` is pinned to an exact version (no `^` range) in `package.json`. New conformance releases are adopted by deliberately bumping the pin and updating `expected-failures.yaml` for any new scenarios/checks in the same change. -> **Local pin (temporary):** the pin currently points at a vendored tarball -> (`file:./vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz`) built -> from `modelcontextprotocol/conformance@main` (`d70d7ad`, post-`0.2.0-alpha.4`). -> This pulls in [#347](https://github.com/modelcontextprotocol/conformance/pull/347) -> (client-scenario mocks now serve `server/discover`) ahead of the next published -> release so the discover-shim removal can be exercised. To revert to a published -> release, replace the `file:` spec in `package.json` with the exact version -> string (e.g. `"0.2.0-alpha.5"`), delete `vendor/`, run `pnpm install`, and -> reconcile `expected-failures.yaml` in the same change. +> **Local pin (temporary):** the pin currently points at a vendored tarball (`file:./vendor/modelcontextprotocol-conformance-0.2.0-main.d70d7ad.tgz`) built from `modelcontextprotocol/conformance@main` (`d70d7ad`, post-`0.2.0-alpha.4`). This pulls in +> [#347](https://github.com/modelcontextprotocol/conformance/pull/347) (client-scenario mocks now serve `server/discover`) ahead of the next published release so the discover-shim removal can be exercised. To revert to a published release, replace the `file:` spec in +> `package.json` with the exact version string (e.g. `"0.2.0-alpha.5"`), delete `vendor/`, run `pnpm install`, and reconcile `expected-failures.yaml` in the same change. +> +> Tarball sha256: `d09d694de2d3982e511d2b6a91af93b2e09a83f107bfc8f879c090d995e8ecfe`. Rebuild (from a checkout of `modelcontextprotocol/conformance` at `d70d7ad`, with this SDK's workspace `tsdown` on `PATH` because the conformance repo's own devDeps are unavailable on the +> internal mirror): +> +> ```sh +> npm version 0.2.0-main.d70d7ad --no-git-tag-version +> tsdown # deps externalised identically to the published alpha.4 dist +> npm pack --ignore-scripts +> ``` ## Client Conformance Tests diff --git a/test/conformance/expected-failures.2026-07-28.yaml b/test/conformance/expected-failures.2026-07-28.yaml index 3a5194f209..56e173242c 100644 --- a/test/conformance/expected-failures.2026-07-28.yaml +++ b/test/conformance/expected-failures.2026-07-28.yaml @@ -11,8 +11,8 @@ # 2026 leg reads the `server:` section. Both burn down independently of the # 2025 legs. # -# Baseline established against the published @modelcontextprotocol/conformance -# release pinned in package.json. Newer conformance releases are adopted by +# Baseline established against the @modelcontextprotocol/conformance pin in +# package.json (see README.md). Newer conformance releases are adopted by # deliberately bumping the pin and reconciling this file in the same change. # # Entries are grouped by what unblocks them. As each gap closes the @@ -47,6 +47,11 @@ client: - auth/scope-step-up - auth/scope-retry-limit + # --- conformance#353 error-code renumber (spec#2907) --- + # Renumber pending Felix ruling (spec#2907 / conformance#353) — SDK still + # emits −32001/−32003/−32004. Same failure as the 2025 leg. + - request-metadata + # --- Same gaps as the 2025 baseline (fail identically when forced to 2026-07-28) --- # SEP-2106 (JSON Schema $ref handling): no fixture handler for the scenario yet. - json-schema-ref-no-deref @@ -73,3 +78,12 @@ server: # (WARNING-only; the expected-failures evaluator counts WARNINGs as # failures). Same failure as in the 2025 baseline. - sep-2164-resource-not-found + # conformance#353 error-code renumber: renumber pending Felix ruling + # (spec#2907 / conformance#353) — SDK still emits −32001/−32003/−32004; + # same failure as the 2025 leg. + - http-custom-header-server-validation + - http-header-validation + # One #353-renumber check fires in the server-stateless modern leg; the + # listChanged-on-listen WARNING was burned down separately by the listen + # arming change. + - server-stateless diff --git a/test/conformance/expected-failures.yaml b/test/conformance/expected-failures.yaml index 9fed2dee25..870af8b155 100644 --- a/test/conformance/expected-failures.yaml +++ b/test/conformance/expected-failures.yaml @@ -1,10 +1,10 @@ # Conformance scenarios not yet implemented in the v2 TypeScript SDK. # CI exits 0 if only these fail, exits 1 on unexpected failures or stale entries. # -# Baseline established against the published @modelcontextprotocol/conformance -# release pinned in package.json (0.2.0-alpha.4). Newer conformance releases -# are adopted by deliberately bumping the package.json pin and reconciling -# this file in the same change. +# Baseline established against the @modelcontextprotocol/conformance pin in +# package.json (0.2.0-main.d70d7ad — a vendored build of conformance@main; see +# README.md). Newer conformance releases are adopted by deliberately bumping +# the package.json pin and reconciling this file in the same change. # # NOTE: the SDK's modern-path rejection codes are aligned with what this # referee asserts: header/body mismatches answer -32001 (HeaderMismatch) and a @@ -36,17 +36,50 @@ client: - auth/offline-access-not-supported # --- Pre-existing scenarios that fail on checks added after conformance 0.1.15 --- - # SEP-2350 (scope step-up): WARNING-only — client does not compute the union of - # previously requested and newly challenged scopes on re-authorization; the - # expected-failures evaluator counts WARNINGs as failures. - - auth/scope-step-up # SEP-990 (enterprise-managed authorization extension): no fixture handler / # client support for the token-exchange + JWT bearer flow. - auth/enterprise-managed-authorization + # --- conformance#353 error-code renumber (spec#2907) --- + # Renumber pending Felix ruling (spec#2907 / conformance#353) — SDK still + # emits −32001/−32003/−32004. The mock now rejects with −32020/−32022 where + # the SDK still recognises −32001/−32004, so connect falls through to a + # malformed `initialize` result. + - request-metadata + server: # --- Draft-spec scenarios (in `--suite draft`; the default `active` suite is green) --- # WARNING-only entry: the scenario emits no FAILURE checks, only a SHOULD-level # WARNING, but the expected-failures evaluator counts WARNINGs as failures. # SEP-2164: server returns -32002 without the requested URI in error.data. - sep-2164-resource-not-found + # JSON Schema 2020-12 draft: vocabulary keywords the SDK's validator does not + # honor (same failure as the 2025 baseline; not a 2026-path regression). + - json-schema-2020-12 + # The referee's sse-polling scenario set is empty against the entry-hosted + # fixture (0 checks); the evaluator treats an absent-scenario result as a + # baseline mismatch. + - server-sse-polling + # --- conformance#353 error-code renumber (spec#2907) --- + # Renumber pending — SDK still emits −32001/−32003/−32004; the referee at + # this pin asserts −32020/−32021/−32022 for header/body mismatch, missing- + # required-client-capability, and unsupported-protocol-version respectively. + - http-custom-header-server-validation + # WARNING-only at this pin; same #353 cause as above. + - http-header-validation + # One #353-renumber check fires in the server-stateless modern leg; the + # listChanged-on-listen WARNING was burned down separately by the listen + # arming change. + - server-stateless + # --- SEP-2663 ext: tasks (referee scenarios added at this pin) --- + # Tasks are not implemented in the SDK; every tasks-* scenario fails on the + # capability/dispatch path. + - tasks-lifecycle + - tasks-capability-negotiation + - tasks-wire-fields + - tasks-request-state-removal + - tasks-mrtr-input + - tasks-request-headers + - tasks-dispatch-and-envelope + - tasks-required-task-error + - tasks-mrtr-composition