Skip to content

Commit fe3a310

Browse files
chore: renumber draft error codes per modelcontextprotocol#2907 (#353)
The error-code allocation policy in modelcontextprotocol#2907 renumbers the draft-only codes: HeaderMismatch -32001 -> -32020, MissingRequiredClientCapability -32003 -> -32021, UnsupportedProtocolVersion -32004 -> -32022. This updates every reference in the suite (mocks, scenarios, fixtures, tests, traceability yaml) and re-syncs the vendored draft schema to that commit. src/seps/traceability.json is generated and intentionally left for the scheduled refresh workflow.
1 parent 7c4bea4 commit fe3a310

15 files changed

Lines changed: 73 additions & 57 deletions

File tree

examples/clients/typescript/everything-client.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,8 @@ async function runRequestMetadataClient(serverUrl: string): Promise<void> {
265265
const clone = response.clone();
266266
try {
267267
const errorResult = await clone.json();
268-
// -32004 is UnsupportedProtocolVersionError in the draft schema;
269-
// -32001 is tolerated for servers that predate the dedicated code.
270-
if (
271-
errorResult.error?.code === -32004 ||
272-
errorResult.error?.code === -32001
273-
) {
268+
// UnsupportedProtocolVersionError is -32022 in the draft schema.
269+
if (errorResult.error?.code === -32022) {
274270
logger.debug(
275271
'Received UnsupportedProtocolVersionError, starting negotiation...'
276272
);

examples/servers/typescript/everything-server.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,7 @@ app.post('/mcp', async (req, res) => {
12601260
return res.status(400).json({
12611261
jsonrpc: '2.0',
12621262
id,
1263-
error: { code: -32001, message: 'Missing MCP-Protocol-Version header' }
1263+
error: { code: -32020, message: 'Missing MCP-Protocol-Version header' }
12641264
});
12651265
}
12661266

@@ -1283,25 +1283,25 @@ app.post('/mcp', async (req, res) => {
12831283
});
12841284
}
12851285

1286-
// Header Mismatch Verification (-32001, HTTP 400)
1286+
// Header Mismatch Verification (-32020, HTTP 400)
12871287
if (reqVersion !== metaVersion) {
12881288
return res.status(400).json({
12891289
jsonrpc: '2.0',
12901290
id,
12911291
error: {
1292-
code: -32001,
1292+
code: -32020,
12931293
message: 'Mismatched MCP-Protocol-Version header'
12941294
}
12951295
});
12961296
}
12971297

1298-
// Protocol Version Negotiation Matrix (-32004, HTTP 400)
1298+
// Protocol Version Negotiation Matrix (-32022, HTTP 400)
12991299
if (metaVersion !== '2026-07-28') {
13001300
return res.status(400).json({
13011301
jsonrpc: '2.0',
13021302
id,
13031303
error: {
1304-
code: -32004,
1304+
code: -32022,
13051305
message: 'UnsupportedProtocolVersionError',
13061306
data: {
13071307
supported: ['2026-07-28'],
@@ -1655,13 +1655,13 @@ app.post('/mcp', async (req, res) => {
16551655
if (name === 'test_missing_capability') {
16561656
const clientCaps = meta['io.modelcontextprotocol/clientCapabilities'];
16571657

1658-
// Missing Required Client Capability Check (-32003, HTTP 400)
1658+
// Missing Required Client Capability Check (-32021, HTTP 400)
16591659
if (!clientCaps?.sampling) {
16601660
return res.status(400).json({
16611661
jsonrpc: '2.0',
16621662
id,
16631663
error: {
1664-
code: -32003,
1664+
code: -32021,
16651665
message: 'MissingRequiredClientCapabilityError',
16661666
data: { requiredCapabilities: ['sampling'] }
16671667
}

src/mock-server/mock-server.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ describe('validateStatelessRequest', () => {
100100
});
101101
});
102102

103-
it('rejects versions outside the supported list with -32004 and echoes it', () => {
103+
it('rejects versions outside the supported list with -32022 and echoes it', () => {
104104
const v = validateStatelessRequest(
105105
{
106106
headers: { 'mcp-protocol-version': '2099-01-01' },
@@ -124,7 +124,7 @@ describe('validateStatelessRequest', () => {
124124
status: 400,
125125
body: {
126126
error: {
127-
code: -32004,
127+
code: -32022,
128128
data: { supported: [DRAFT_PROTOCOL_VERSION], requested: '2099-01-01' }
129129
}
130130
}
@@ -216,7 +216,7 @@ describe('createServerStateless', () => {
216216
params: { _meta: meta }
217217
});
218218
expect(status).toBe(400);
219-
expect(body.error.code).toBe(-32001);
219+
expect(body.error.code).toBe(-32020);
220220
} finally {
221221
await srv.close();
222222
}
@@ -258,7 +258,7 @@ describe('createServerStateless', () => {
258258
}
259259
});
260260

261-
it('accepts the version it was created for and rejects others with -32004', async () => {
261+
it('accepts the version it was created for and rejects others with -32022', async () => {
262262
const srv = await createServerStateless(
263263
{ 'tools/list': () => ({ tools: [] }) },
264264
DRAFT_PROTOCOL_VERSION
@@ -292,7 +292,7 @@ describe('createServerStateless', () => {
292292
{ 'mcp-protocol-version': '2099-01-01' }
293293
);
294294
expect(rejected.status).toBe(400);
295-
expect(rejected.body.error.code).toBe(-32004);
295+
expect(rejected.body.error.code).toBe(-32022);
296296
expect(rejected.body.error.data.supported).toEqual([
297297
DRAFT_PROTOCOL_VERSION
298298
]);

src/mock-server/stateless.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export type StatelessValidation =
8484
* `auth/helpers/createServer.ts`) uses the same validation as this module.
8585
*
8686
* `supportedVersions` is the list of wire protocolVersion strings this
87-
* endpoint accepts; anything else is rejected with -32004 carrying
87+
* endpoint accepts; anything else is rejected with -32022 carrying
8888
* `{ supported, requested }` in the error data, and the list is echoed in
8989
* the `server/discover` result.
9090
*/
@@ -108,7 +108,7 @@ export function validateStatelessRequest(
108108

109109
const headerVersion = req.headers['mcp-protocol-version'];
110110
if (!headerVersion) {
111-
return reject(400, -32001, 'Missing MCP-Protocol-Version header');
111+
return reject(400, -32020, 'Missing MCP-Protocol-Version header');
112112
}
113113
const missing = META_KEYS.filter((k) => meta?.[k] === undefined);
114114
if (missing.length > 0) {
@@ -121,7 +121,7 @@ export function validateStatelessRequest(
121121
if (meta?.[META_KEYS[0]] !== headerVersion) {
122122
return reject(
123123
400,
124-
-32001,
124+
-32020,
125125
'MCP-Protocol-Version header does not match _meta.protocolVersion'
126126
);
127127
}
@@ -136,7 +136,7 @@ export function validateStatelessRequest(
136136
jsonrpc: '2.0',
137137
id,
138138
error: {
139-
code: -32004,
139+
code: -32022,
140140
message: 'Unsupported protocol version',
141141
data: {
142142
supported: supportedVersions,

src/scenarios/client/draft-result-fields.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ describe('request-metadata mock results (2026-07-28)', () => {
238238
);
239239
const headers = { 'MCP-Protocol-Version': DRAFT_PROTOCOL_VERSION };
240240
try {
241-
// The first request is always answered with the simulated -32004
241+
// The first request is always answered with the simulated -32022
242242
// rejection (retry probe); results are served from the second on.
243243
const first = await post(
244244
serverUrl,

src/scenarios/client/request-metadata.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ async function incompatibleVersionClient(serverUrl: string) {
105105

106106
if (response.status === 400) {
107107
const body = await response.json();
108-
if (body.error?.code === -32004) {
108+
if (body.error?.code === -32022) {
109109
return body; // Abort cleanly
110110
}
111111
}

src/scenarios/client/request-metadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export class RequestMetadataScenario implements Scenario {
282282
id: request.id ?? null,
283283
error: {
284284
// UnsupportedProtocolVersionError per the draft schema.
285-
code: -32004,
285+
code: -32022,
286286
message: 'Unsupported protocol version',
287287
data: {
288288
supported: [DRAFT_PROTOCOL_VERSION],

src/scenarios/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const pendingClientScenariosList: ClientScenario[] = [
120120

121121
// HTTP Standardization (SEP-2243)
122122
// Pending until the everything-server fully implements SEP-2243
123-
// header validation (case-insensitive names, whitespace trimming, -32001 error code)
123+
// header validation (case-insensitive names, whitespace trimming, -32020 error code)
124124
new HttpHeaderValidationScenario(),
125125
new HttpCustomHeaderServerValidationScenario()
126126
];

src/scenarios/server/http-standard-headers.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* - Reject case variations of header values (case-sensitive)
99
* - Handle whitespace trimming per HTTP spec
1010
* - Validate Base64-encoded custom header values
11-
* - Return 400 Bad Request with error code -32001 (HeaderMismatch)
11+
* - Return 400 Bad Request with error code -32020 (HeaderMismatch)
1212
*
1313
* This is a ClientScenario (connects to a server under test and validates
1414
* its behavior).
@@ -66,8 +66,8 @@ const REJECT_STATUS_CHECK_ID = 'sep-2243-server-reject-invalid-headers';
6666
const REJECT_ERROR_CODE_CHECK_ID = 'sep-2243-server-reject-error-code';
6767

6868
// CUSTOM-header (Mcp-Param-*) rejections map to the param-validation
69-
// requirements instead. The -32001 error-code half of every custom-header
70-
// rejection is the "reject with 400 + JSON-RPC error code -32001 if any
69+
// requirements instead. The -32020 error-code half of every custom-header
70+
// rejection is the "reject with 400 + JSON-RPC error code -32020 if any
7171
// validation fails" requirement.
7272
const PARAM_REJECT_ERROR_CODE_CHECK_ID =
7373
'sep-2243-server-reject-param-mismatch';
@@ -148,8 +148,8 @@ async function sendRawRequest(
148148

149149
/**
150150
* Builds two checks for a rejection case: one for the HTTP 400 status, one for
151-
* the -32001 JSON-RPC error code. Per SEP-2243 §Server Validation, 400 is MUST
152-
* but -32001 is SHOULD for *standard* headers (and MUST for *custom* headers,
151+
* the -32020 JSON-RPC error code. Per SEP-2243 §Server Validation, 400 is MUST
152+
* but -32020 is SHOULD for *standard* headers (and MUST for *custom* headers,
153153
* §Server Behavior for Custom Headers) — so a server returning 400 with a
154154
* different error code is compliant for standard headers and must not FAIL.
155155
*
@@ -196,7 +196,7 @@ function createRejectionChecks(
196196
{
197197
id: errorCodeId,
198198
name: `${name}ErrorCode`,
199-
description: `${description} — uses JSON-RPC error code -32001 (HeaderMismatch)`,
199+
description: `${description} — uses JSON-RPC error code -32020 (HeaderMismatch)`,
200200
status: codeOk ? 'SUCCESS' : opts.errorCodeSeverity,
201201
timestamp: ts,
202202
errorMessage: codeOk
@@ -265,7 +265,7 @@ export class HttpHeaderValidationScenario implements ClientScenario {
265265
- Server MUST reject case-mismatched header values (method values are case-sensitive)
266266
- Server MUST accept extra whitespace around header values (per HTTP spec)
267267
- Server MUST return HTTP 400 Bad Request for validation failures
268-
- Server MUST return JSON-RPC error with code -32001 (HeaderMismatch)`;
268+
- Server MUST return JSON-RPC error with code -32020 (HeaderMismatch)`;
269269

270270
async run(ctx: RunContext): Promise<ConformanceCheck[]> {
271271
const { serverUrl } = ctx;
@@ -507,7 +507,7 @@ export class HttpHeaderValidationScenario implements ClientScenario {
507507
...extraHeaders
508508
});
509509
if (expectation === 'reject') {
510-
// Standard-header rejection: 400 is MUST, -32001 is SHOULD. All
510+
// Standard-header rejection: 400 is MUST, -32020 is SHOULD. All
511511
// standard-header rejection cases collapse onto the coarse requirement
512512
// ids; checkId/checkName still distinguish the case via name/details.
513513
checks.push(
@@ -926,10 +926,10 @@ export class HttpCustomHeaderServerValidationScenario implements ClientScenario
926926
)
927927
);
928928
} else {
929-
// Custom-header rejection: both 400 and -32001 are MUST per
929+
// Custom-header rejection: both 400 and -32020 are MUST per
930930
// §Server Behavior for Custom Headers. The status half maps to the
931931
// param-validation requirement this case exercises (checkId); the
932-
// error-code half is the "400 + -32001 if any validation fails"
932+
// error-code half is the "400 + -32020 if any validation fails"
933933
// requirement.
934934
checks.push(
935935
...createRejectionChecks(
@@ -992,7 +992,7 @@ export class HttpCustomHeaderServerValidationScenario implements ClientScenario
992992
}
993993
);
994994

995-
// Custom-header rejection: both 400 and -32001 are MUST. A header
995+
// Custom-header rejection: both 400 and -32020 are MUST. A header
996996
// omitted while the body carries a value is a header/body mismatch, so
997997
// the status half maps to the validate-param-match requirement.
998998
checks.push(

src/scenarios/server/stateless.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ describe('Stateless Server Scenario Negative Tests', () => {
180180
jsonrpc: '2.0',
181181
id: reqBody.id,
182182
error: {
183-
code: -32004,
183+
code: -32022,
184184
message: 'Unsupported protocol version',
185185
// Spec Violation: data.requested is a required member
186186
data: { supported: ['2026-07-28'] }

0 commit comments

Comments
 (0)