From 800ee0d6a56983174ba1b668ed67b89470ef5203 Mon Sep 17 00:00:00 2001 From: Sri Panyam Date: Thu, 18 Jun 2026 19:05:51 -0700 Subject: [PATCH] fix(sep-2663): use renumbered draft error-code constants in tasks scenarios The tasks extension scenarios hardcoded -32003 (Missing Required Client Capability) and -32001 (Header Mismatch). The draft spec types renumbered these to -32021 and -32020 per modelcontextprotocol#2907, so a server built against current draft.ts now returns the new codes and these checks would produce false failures. Import MISSING_REQUIRED_CLIENT_CAPABILITY and HEADER_MISMATCH from spec-types/draft instead of hardcoding, matching the existing http-standard-headers scenario. The checks now assert whatever the schema currently defines, so a future renumber propagates automatically. Also drop the now-stale code number from the affected check-ID slugs (for example sep-2663-server-returns-32003-when-required becomes sep-2663-server-returns-missing-capability-when-required) and update the sep-2663.yaml requirement text to -32021, keeping it consistent with the already-updated sep-2575 and sep-2243 manifests. Scenarios are still in the pending list so default CI is unaffected. The traceability.json manifest already predates the upstream renumber and is refreshed separately from a full suite run. --- src/scenarios/server/tasks/capability.ts | 21 ++++----- src/scenarios/server/tasks/headers.ts | 25 +++++------ .../server/tasks/required-task-error.ts | 43 ++++++++++--------- src/seps/sep-2663.yaml | 14 +++--- 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/scenarios/server/tasks/capability.ts b/src/scenarios/server/tasks/capability.ts index d807b70a..2a0bcda0 100644 --- a/src/scenarios/server/tasks/capability.ts +++ b/src/scenarios/server/tasks/capability.ts @@ -12,6 +12,7 @@ import { ClientScenario, ConformanceCheck } from '../../../types'; import type { Connection, RunContext } from '../../../connection'; +import { MISSING_REQUIRED_CLIENT_CAPABILITY } from '../../../spec-types/draft'; import { SEP_2575_REF, SEP_2663_REF } from './mrtr-helpers'; import { errMsg, failureCheck } from './mrtr-helpers'; import { TASKS_EXTENSION_ID } from './helpers'; @@ -117,16 +118,16 @@ export class TasksCapabilityNegotiationScenario implements ClientScenario { }); } - // Check 2: tasks/* methods rejected with -32003 (Missing Required - // Client Capability) when the client did not negotiate the tasks - // extension. Follows the SEP-2575 §"Missing Required Capabilities" - // pattern — same code path as the required-task-error scenario and - // (when implemented) subscriptions/listen for tasks. + // Check 2: tasks/* methods rejected with the + // MissingRequiredClientCapability error when the client did not + // negotiate the tasks extension. Follows the SEP-2575 §"Missing + // Required Capabilities" pattern — same code path as the + // required-task-error scenario and (when implemented) + // subscriptions/listen for tasks. { - const id = 'sep-2663-tasks-methods-non-declaring-32003'; + const id = 'sep-2663-tasks-methods-non-declaring'; const name = 'TasksMethodsGatedWithoutExtension'; - const description = - 'tasks/get, tasks/update, tasks/cancel return -32003 when the client did not negotiate the tasks extension (SEP-2575 Missing Required Capabilities)'; + const description = `tasks/get, tasks/update, tasks/cancel return ${MISSING_REQUIRED_CLIENT_CAPABILITY} when the client did not negotiate the tasks extension (SEP-2575 Missing Required Capabilities)`; const cases: Array<{ method: string; params: any }> = [ { method: 'tasks/get', params: { taskId: 'gate-test' } }, { @@ -141,9 +142,9 @@ export class TasksCapabilityNegotiationScenario implements ClientScenario { await withoutExt.request(tc.method, tc.params); errs.push(`${tc.method} MUST reject (it returned a result)`); } catch (e: any) { - if (e.code !== -32003) { + if (e.code !== MISSING_REQUIRED_CLIENT_CAPABILITY) { errs.push( - `${tc.method} MUST return -32003; got ${e.code ?? ''}` + `${tc.method} MUST return ${MISSING_REQUIRED_CLIENT_CAPABILITY}; got ${e.code ?? ''}` ); } } diff --git a/src/scenarios/server/tasks/headers.ts b/src/scenarios/server/tasks/headers.ts index a8bf60ac..d523994d 100644 --- a/src/scenarios/server/tasks/headers.ts +++ b/src/scenarios/server/tasks/headers.ts @@ -7,13 +7,13 @@ * to route or shape JSON-RPC traffic without parsing the body. The * server MUST reject requests where the routing headers disagree with * (or are missing for a name-carrying body) the JSON-RPC envelope, with - * HTTP 400 + JSON-RPC `-32001 HeaderMismatch`. + * HTTP 400 + JSON-RPC `-32020 HeaderMismatch`. * * This scenario exercises the validation on the tasks surface * specifically — the upstream `http-header-validation` scenario covers * the general case; here we verify mcpkit's tasks/* methods route * through the same validator (matched headers → success; mismatched - * header → -32001). + * header → -32020). * * Required server fixtures: * - greet — sync-only, returns "Hello, {name}!" @@ -24,11 +24,12 @@ import { McpError } from '@modelcontextprotocol/sdk/types.js'; import { ClientScenario, ConformanceCheck } from '../../../types'; import type { Connection, RunContext } from '../../../connection'; +import { HEADER_MISMATCH } from '../../../spec-types/draft'; import { SEP_2243_REF, SEP_2663_REF } from './mrtr-helpers'; import { errMsg, failureCheck } from './mrtr-helpers'; import { TASKS_EXTENSION_ID } from './helpers'; -const HEADER_MISMATCH_ERROR_CODE = -32001; +const HEADER_MISMATCH_ERROR_CODE = HEADER_MISMATCH; export class TasksRequestHeadersScenario implements ClientScenario { name = 'tasks-request-headers'; @@ -53,7 +54,7 @@ into the HTTP layer for routing intermediaries: Per SEP-2243 §"Server Behavior", servers that process the request body MUST validate that header values match the body. Per its "Validation Failure Conditions", both missing required headers and mismatched -values trigger rejection with JSON-RPC error code \`-32001\` +values trigger rejection with JSON-RPC error code \`-32020\` (HeaderMismatch) and HTTP 400. **Required server fixtures (\`tools/list\` MUST include all):** @@ -190,8 +191,7 @@ values trigger rejection with JSON-RPC error code \`-32001\` { const id = 'tasks-headers-reject-mismatched-method'; const name = 'TasksHeadersRejectMismatchedMethod'; - const description = - 'When Mcp-Method header disagrees with body on a tools/call, server MUST reject with -32001 HeaderMismatch (SEP-2243 §Server Validation)'; + const description = `When Mcp-Method header disagrees with body on a tools/call, server MUST reject with ${HEADER_MISMATCH_ERROR_CODE} HeaderMismatch (SEP-2243 §Server Validation)`; try { await conn.request( 'tools/call', @@ -204,8 +204,7 @@ values trigger rejection with JSON-RPC error code \`-32001\` description, status: 'FAILURE', timestamp: new Date().toISOString(), - errorMessage: - 'tools/call with Mcp-Method: tasks/get returned a successful response; SEP-2243 requires -32001 rejection', + errorMessage: `tools/call with Mcp-Method: tasks/get returned a successful response; SEP-2243 requires ${HEADER_MISMATCH_ERROR_CODE} rejection`, specReferences: [SEP_2243_REF] }); } catch (error) { @@ -227,7 +226,7 @@ values trigger rejection with JSON-RPC error code \`-32001\` description, status: 'FAILURE', timestamp: new Date().toISOString(), - errorMessage: `expected -32001 HeaderMismatch; got ${code ?? errMsg(error)}`, + errorMessage: `expected ${HEADER_MISMATCH_ERROR_CODE} HeaderMismatch; got ${code ?? errMsg(error)}`, specReferences: [SEP_2243_REF] }); } @@ -244,8 +243,7 @@ values trigger rejection with JSON-RPC error code \`-32001\` { const id = 'sep-2663-server-rejects-mismatched-mcp-name-on-tasks-get'; const name = 'Sep2663ServerRejectsMismatchedMcpNameOnTasksGet'; - const description = - 'When Mcp-Name header disagrees with params.taskId on tasks/get, server MUST reject with -32001 HeaderMismatch'; + const description = `When Mcp-Name header disagrees with params.taskId on tasks/get, server MUST reject with ${HEADER_MISMATCH_ERROR_CODE} HeaderMismatch`; if (!routingTaskId) { checks.push({ id, @@ -270,8 +268,7 @@ values trigger rejection with JSON-RPC error code \`-32001\` description, status: 'FAILURE', timestamp: new Date().toISOString(), - errorMessage: - 'tasks/get with mismatched Mcp-Name returned a result; SEP-2243 requires -32001 rejection', + errorMessage: `tasks/get with mismatched Mcp-Name returned a result; SEP-2243 requires ${HEADER_MISMATCH_ERROR_CODE} rejection`, specReferences: [SEP_2243_REF, SEP_2663_REF] }); } catch (error) { @@ -293,7 +290,7 @@ values trigger rejection with JSON-RPC error code \`-32001\` description, status: 'FAILURE', timestamp: new Date().toISOString(), - errorMessage: `expected -32001 HeaderMismatch; got ${code ?? errMsg(error)}`, + errorMessage: `expected ${HEADER_MISMATCH_ERROR_CODE} HeaderMismatch; got ${code ?? errMsg(error)}`, specReferences: [SEP_2243_REF, SEP_2663_REF] }); } diff --git a/src/scenarios/server/tasks/required-task-error.ts b/src/scenarios/server/tasks/required-task-error.ts index f33065cf..1da07c8f 100644 --- a/src/scenarios/server/tasks/required-task-error.ts +++ b/src/scenarios/server/tasks/required-task-error.ts @@ -2,12 +2,13 @@ * SEP-2663 Tasks Extension — required-task error conformance. * * SEP-2575 (Stateless MCP) §"Missing Required Capabilities" defines - * JSON-RPC error code `-32003` for the case where a server cannot - * service a request without a client capability the client did not - * declare. SEP-2663 §"Required Capabilities" applies that rule to the - * tasks extension: if a tool's declared task support is "required" and - * the client did not declare `io.modelcontextprotocol/tasks` during - * `initialize`, the server MUST reject with `-32003`. The error data + * the `MissingRequiredClientCapability` JSON-RPC error code (-32021) for + * the case where a server cannot service a request without a client + * capability the client did not declare. SEP-2663 §"Required + * Capabilities" applies that rule to the tasks extension: if a tool's + * declared task support is "required" and the client did not declare + * `io.modelcontextprotocol/tasks` during `initialize`, the server MUST + * reject with that error. The error data * SHOULD carry a `requiredCapabilities` object whose shape mirrors the * `InitializeRequest` capabilities, so the client can self-describe * what to add without needing out-of-band documentation. @@ -15,7 +16,7 @@ * This scenario verifies the failure path: * 1. Initialize a session WITHOUT declaring the tasks extension. * 2. Call a tool whose task support is `required`. - * 3. Expect a JSON-RPC error with `code: -32003` and + * 3. Expect a JSON-RPC error with `code: -32021` and * `data.requiredCapabilities.extensions["io.modelcontextprotocol/tasks"]` * present. * @@ -31,17 +32,17 @@ import { McpError } from '@modelcontextprotocol/sdk/types.js'; import { ClientScenario, ConformanceCheck } from '../../../types'; import type { Connection, RunContext } from '../../../connection'; +import { MISSING_REQUIRED_CLIENT_CAPABILITY } from '../../../spec-types/draft'; import { SEP_2575_REF, SEP_2663_REF } from './mrtr-helpers'; import { errMsg } from './mrtr-helpers'; import { TASKS_EXTENSION_ID } from './helpers'; -const MISSING_REQUIRED_CLIENT_CAPABILITY = -32003; const REQUIRED_TASK_TOOL = 'failing_job'; export class TasksRequiredTaskErrorScenario implements ClientScenario { name = 'tasks-required-task-error'; readonly source = { extensionId: 'io.modelcontextprotocol/tasks' } as const; - description = `Verify the -32003 error path for required-task tools when the + description = `Verify the -32021 error path for required-task tools when the client has not negotiated the io.modelcontextprotocol/tasks extension. **Server Implementation Requirements:** @@ -51,7 +52,7 @@ Capabilities": > If a server is unable to service a request to a client that does not > declare this extension capability without returning \`CreateTaskResult\`, -> the server MUST return an error with the code \`-32003\` (Missing +> the server MUST return an error with the code \`-32021\` (Missing > Required Client Capability), indicating the required extension in the > error response. @@ -70,7 +71,7 @@ shape mirrors \`InitializeRequest.capabilities\`, e.g. The scenario calls \`tools/call\` for a tool registered with task support \`required\` from a client that did NOT declare the extension. A -conformant server MUST reject with \`-32003\`. +conformant server MUST reject with \`-32021\`. **Required server fixtures (\`tools/list\` MUST include all):** - \`failing_job\` — registered with task support declared as \`required\`. @@ -102,10 +103,11 @@ conformant server MUST reject with \`-32003\`. return checks; } - // Check 1: tools/call for a required-task tool returns -32003. - const id = 'sep-2663-server-returns-32003-when-required'; - const name = 'Sep2663ServerReturns32003WhenRequired'; - const description = `tools/call for a TaskSupport=required tool MUST reject with code -32003 (Missing Required Client Capability) when the client did not declare ${TASKS_EXTENSION_ID}`; + // Check 1: tools/call for a required-task tool returns the + // MissingRequiredClientCapability error. + const id = 'sep-2663-server-returns-missing-capability-when-required'; + const name = 'Sep2663ServerReturnsMissingCapabilityWhenRequired'; + const description = `tools/call for a TaskSupport=required tool MUST reject with code ${MISSING_REQUIRED_CLIENT_CAPABILITY} (Missing Required Client Capability) when the client did not declare ${TASKS_EXTENSION_ID}`; let observed: { code?: number; data?: unknown } = {}; let errored = false; @@ -134,7 +136,7 @@ conformant server MUST reject with \`-32003\`. description, status: 'FAILURE', timestamp: new Date().toISOString(), - errorMessage: `tools/call for ${REQUIRED_TASK_TOOL} returned a successful response from a client that did not declare ${TASKS_EXTENSION_ID}; spec requires -32003 rejection in this case.`, + errorMessage: `tools/call for ${REQUIRED_TASK_TOOL} returned a successful response from a client that did not declare ${TASKS_EXTENSION_ID}; spec requires ${MISSING_REQUIRED_CLIENT_CAPABILITY} rejection in this case.`, specReferences: [SEP_2575_REF, SEP_2663_REF] }); await conn.close().catch(() => {}); @@ -170,9 +172,9 @@ conformant server MUST reject with \`-32003\`. // canonical error example in the spec shows this shape; flag a // FAILURE only when the field shape is broken, not when it's absent. { - const id2 = 'sep-2663-server-returns-32003-data-shape'; - const name2 = 'Sep2663ServerReturns32003DataShape'; - const description2 = `Error data for -32003 SHOULD carry data.requiredCapabilities.extensions["${TASKS_EXTENSION_ID}"]`; + const id2 = 'sep-2663-server-returns-missing-capability-data-shape'; + const name2 = 'Sep2663ServerReturnsMissingCapabilityDataShape'; + const description2 = `Error data for ${MISSING_REQUIRED_CLIENT_CAPABILITY} SHOULD carry data.requiredCapabilities.extensions["${TASKS_EXTENSION_ID}"]`; const data = observed.data as any; if (data === undefined || data === null) { checks.push({ @@ -181,8 +183,7 @@ conformant server MUST reject with \`-32003\`. description: description2, status: 'INFO', timestamp: new Date().toISOString(), - errorMessage: - 'Error returned -32003 but carried no `data` field; the spec example shows requiredCapabilities, but data is SHOULD.', + errorMessage: `Error returned ${MISSING_REQUIRED_CLIENT_CAPABILITY} but carried no \`data\` field; the spec example shows requiredCapabilities, but data is SHOULD.`, specReferences: [SEP_2575_REF, SEP_2663_REF] }); } else if ( diff --git a/src/seps/sep-2663.yaml b/src/seps/sep-2663.yaml index c1acc1b7..dec9dd93 100644 --- a/src/seps/sep-2663.yaml +++ b/src/seps/sep-2663.yaml @@ -11,17 +11,17 @@ requirements: - check: sep-2663-client-rejects-task-result-on-unsupported text: 'A client that receives CreateTaskResult in response to an unsupported request type MUST interpret this as an invalid response to the request.' - # The spec sentence "MUST return an error with the code -32003 ..., + # The spec sentence "MUST return an error with the code -32021 ..., # indicating the required extension in the error response" couples two # observable requirements: the JSON-RPC error code, and the data-shape # that names the missing extension. Split into two YAML entries so each # is verifiable independently — Check 1 fires on code mismatch, Check 2 # fires on payload shape. - - check: sep-2663-server-returns-32003-when-required - text: 'If a server is unable to service a request to a client that does not declare this extension capability without returning CreateTaskResult, the server MUST return an error with the code -32003 (Missing Required Client Capability).' + - check: sep-2663-server-returns-missing-capability-when-required + text: 'If a server is unable to service a request to a client that does not declare this extension capability without returning CreateTaskResult, the server MUST return an error with the code -32021 (Missing Required Client Capability).' - - check: sep-2663-server-returns-32003-data-shape - text: 'The -32003 error response MUST indicate the required extension in the error response payload (the spec example shows a `data.requiredCapabilities.extensions["io.modelcontextprotocol/tasks"]` shape).' + - check: sep-2663-server-returns-missing-capability-data-shape + text: 'The -32021 error response MUST indicate the required extension in the error response payload (the spec example shows a `data.requiredCapabilities.extensions["io.modelcontextprotocol/tasks"]` shape).' # ── Polymorphic Results ─────────────────────────────────────────────────── - check: sep-2663-result-type-task-on-create @@ -93,7 +93,7 @@ requirements: text: 'The resultType field MUST be set to "complete" on CancelTaskResult as it is the standard result shape for the tasks/cancel request.' # ── Task Status Notifications ───────────────────────────────────────────── - - check: sep-2663-subscribe-32003-non-declaring + - check: sep-2663-subscribe-missing-capability-non-declaring text: 'If a client requests task status notifications but does not declare the io.modelcontextprotocol/tasks extension capability, the server MUST return a JSON-RPC error specifying the missing capabilities:' - check: sep-2663-no-progress-or-message-on-task-stream @@ -124,7 +124,7 @@ requirements: - check: sep-2663-tasks-update-cancel-invalid-task-id-32602 text: 'Servers SHOULD return this error for tasks/update and tasks/cancel.' - - check: sep-2663-tasks-methods-non-declaring-32003 + - check: sep-2663-tasks-methods-non-declaring text: 'Servers MUST return this error for non-declaring clients issuing tasks/get, tasks/update, and tasks/cancel requests.' # ── Task Execution Errors ─────────────────────────────────────────────────