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.
This commit is contained in:
parent
7c4bea4188
commit
fe3a3103bc
15 changed files with 73 additions and 57 deletions
|
|
@ -265,12 +265,8 @@ async function runRequestMetadataClient(serverUrl: string): Promise<void> {
|
||||||
const clone = response.clone();
|
const clone = response.clone();
|
||||||
try {
|
try {
|
||||||
const errorResult = await clone.json();
|
const errorResult = await clone.json();
|
||||||
// -32004 is UnsupportedProtocolVersionError in the draft schema;
|
// UnsupportedProtocolVersionError is -32022 in the draft schema.
|
||||||
// -32001 is tolerated for servers that predate the dedicated code.
|
if (errorResult.error?.code === -32022) {
|
||||||
if (
|
|
||||||
errorResult.error?.code === -32004 ||
|
|
||||||
errorResult.error?.code === -32001
|
|
||||||
) {
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Received UnsupportedProtocolVersionError, starting negotiation...'
|
'Received UnsupportedProtocolVersionError, starting negotiation...'
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1260,7 +1260,7 @@ app.post('/mcp', async (req, res) => {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id,
|
id,
|
||||||
error: { code: -32001, message: 'Missing MCP-Protocol-Version header' }
|
error: { code: -32020, message: 'Missing MCP-Protocol-Version header' }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1283,25 +1283,25 @@ app.post('/mcp', async (req, res) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header Mismatch Verification (-32001, HTTP 400)
|
// Header Mismatch Verification (-32020, HTTP 400)
|
||||||
if (reqVersion !== metaVersion) {
|
if (reqVersion !== metaVersion) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id,
|
id,
|
||||||
error: {
|
error: {
|
||||||
code: -32001,
|
code: -32020,
|
||||||
message: 'Mismatched MCP-Protocol-Version header'
|
message: 'Mismatched MCP-Protocol-Version header'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protocol Version Negotiation Matrix (-32004, HTTP 400)
|
// Protocol Version Negotiation Matrix (-32022, HTTP 400)
|
||||||
if (metaVersion !== '2026-07-28') {
|
if (metaVersion !== '2026-07-28') {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id,
|
id,
|
||||||
error: {
|
error: {
|
||||||
code: -32004,
|
code: -32022,
|
||||||
message: 'UnsupportedProtocolVersionError',
|
message: 'UnsupportedProtocolVersionError',
|
||||||
data: {
|
data: {
|
||||||
supported: ['2026-07-28'],
|
supported: ['2026-07-28'],
|
||||||
|
|
@ -1655,13 +1655,13 @@ app.post('/mcp', async (req, res) => {
|
||||||
if (name === 'test_missing_capability') {
|
if (name === 'test_missing_capability') {
|
||||||
const clientCaps = meta['io.modelcontextprotocol/clientCapabilities'];
|
const clientCaps = meta['io.modelcontextprotocol/clientCapabilities'];
|
||||||
|
|
||||||
// Missing Required Client Capability Check (-32003, HTTP 400)
|
// Missing Required Client Capability Check (-32021, HTTP 400)
|
||||||
if (!clientCaps?.sampling) {
|
if (!clientCaps?.sampling) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id,
|
id,
|
||||||
error: {
|
error: {
|
||||||
code: -32003,
|
code: -32021,
|
||||||
message: 'MissingRequiredClientCapabilityError',
|
message: 'MissingRequiredClientCapabilityError',
|
||||||
data: { requiredCapabilities: ['sampling'] }
|
data: { requiredCapabilities: ['sampling'] }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ describe('validateStatelessRequest', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects versions outside the supported list with -32004 and echoes it', () => {
|
it('rejects versions outside the supported list with -32022 and echoes it', () => {
|
||||||
const v = validateStatelessRequest(
|
const v = validateStatelessRequest(
|
||||||
{
|
{
|
||||||
headers: { 'mcp-protocol-version': '2099-01-01' },
|
headers: { 'mcp-protocol-version': '2099-01-01' },
|
||||||
|
|
@ -124,7 +124,7 @@ describe('validateStatelessRequest', () => {
|
||||||
status: 400,
|
status: 400,
|
||||||
body: {
|
body: {
|
||||||
error: {
|
error: {
|
||||||
code: -32004,
|
code: -32022,
|
||||||
data: { supported: [DRAFT_PROTOCOL_VERSION], requested: '2099-01-01' }
|
data: { supported: [DRAFT_PROTOCOL_VERSION], requested: '2099-01-01' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +216,7 @@ describe('createServerStateless', () => {
|
||||||
params: { _meta: meta }
|
params: { _meta: meta }
|
||||||
});
|
});
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body.error.code).toBe(-32001);
|
expect(body.error.code).toBe(-32020);
|
||||||
} finally {
|
} finally {
|
||||||
await srv.close();
|
await srv.close();
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +258,7 @@ describe('createServerStateless', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts the version it was created for and rejects others with -32004', async () => {
|
it('accepts the version it was created for and rejects others with -32022', async () => {
|
||||||
const srv = await createServerStateless(
|
const srv = await createServerStateless(
|
||||||
{ 'tools/list': () => ({ tools: [] }) },
|
{ 'tools/list': () => ({ tools: [] }) },
|
||||||
DRAFT_PROTOCOL_VERSION
|
DRAFT_PROTOCOL_VERSION
|
||||||
|
|
@ -292,7 +292,7 @@ describe('createServerStateless', () => {
|
||||||
{ 'mcp-protocol-version': '2099-01-01' }
|
{ 'mcp-protocol-version': '2099-01-01' }
|
||||||
);
|
);
|
||||||
expect(rejected.status).toBe(400);
|
expect(rejected.status).toBe(400);
|
||||||
expect(rejected.body.error.code).toBe(-32004);
|
expect(rejected.body.error.code).toBe(-32022);
|
||||||
expect(rejected.body.error.data.supported).toEqual([
|
expect(rejected.body.error.data.supported).toEqual([
|
||||||
DRAFT_PROTOCOL_VERSION
|
DRAFT_PROTOCOL_VERSION
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ export type StatelessValidation =
|
||||||
* `auth/helpers/createServer.ts`) uses the same validation as this module.
|
* `auth/helpers/createServer.ts`) uses the same validation as this module.
|
||||||
*
|
*
|
||||||
* `supportedVersions` is the list of wire protocolVersion strings this
|
* `supportedVersions` is the list of wire protocolVersion strings this
|
||||||
* endpoint accepts; anything else is rejected with -32004 carrying
|
* endpoint accepts; anything else is rejected with -32022 carrying
|
||||||
* `{ supported, requested }` in the error data, and the list is echoed in
|
* `{ supported, requested }` in the error data, and the list is echoed in
|
||||||
* the `server/discover` result.
|
* the `server/discover` result.
|
||||||
*/
|
*/
|
||||||
|
|
@ -108,7 +108,7 @@ export function validateStatelessRequest(
|
||||||
|
|
||||||
const headerVersion = req.headers['mcp-protocol-version'];
|
const headerVersion = req.headers['mcp-protocol-version'];
|
||||||
if (!headerVersion) {
|
if (!headerVersion) {
|
||||||
return reject(400, -32001, 'Missing MCP-Protocol-Version header');
|
return reject(400, -32020, 'Missing MCP-Protocol-Version header');
|
||||||
}
|
}
|
||||||
const missing = META_KEYS.filter((k) => meta?.[k] === undefined);
|
const missing = META_KEYS.filter((k) => meta?.[k] === undefined);
|
||||||
if (missing.length > 0) {
|
if (missing.length > 0) {
|
||||||
|
|
@ -121,7 +121,7 @@ export function validateStatelessRequest(
|
||||||
if (meta?.[META_KEYS[0]] !== headerVersion) {
|
if (meta?.[META_KEYS[0]] !== headerVersion) {
|
||||||
return reject(
|
return reject(
|
||||||
400,
|
400,
|
||||||
-32001,
|
-32020,
|
||||||
'MCP-Protocol-Version header does not match _meta.protocolVersion'
|
'MCP-Protocol-Version header does not match _meta.protocolVersion'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +136,7 @@ export function validateStatelessRequest(
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id,
|
id,
|
||||||
error: {
|
error: {
|
||||||
code: -32004,
|
code: -32022,
|
||||||
message: 'Unsupported protocol version',
|
message: 'Unsupported protocol version',
|
||||||
data: {
|
data: {
|
||||||
supported: supportedVersions,
|
supported: supportedVersions,
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ describe('request-metadata mock results (2026-07-28)', () => {
|
||||||
);
|
);
|
||||||
const headers = { 'MCP-Protocol-Version': DRAFT_PROTOCOL_VERSION };
|
const headers = { 'MCP-Protocol-Version': DRAFT_PROTOCOL_VERSION };
|
||||||
try {
|
try {
|
||||||
// The first request is always answered with the simulated -32004
|
// The first request is always answered with the simulated -32022
|
||||||
// rejection (retry probe); results are served from the second on.
|
// rejection (retry probe); results are served from the second on.
|
||||||
const first = await post(
|
const first = await post(
|
||||||
serverUrl,
|
serverUrl,
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ async function incompatibleVersionClient(serverUrl: string) {
|
||||||
|
|
||||||
if (response.status === 400) {
|
if (response.status === 400) {
|
||||||
const body = await response.json();
|
const body = await response.json();
|
||||||
if (body.error?.code === -32004) {
|
if (body.error?.code === -32022) {
|
||||||
return body; // Abort cleanly
|
return body; // Abort cleanly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ export class RequestMetadataScenario implements Scenario {
|
||||||
id: request.id ?? null,
|
id: request.id ?? null,
|
||||||
error: {
|
error: {
|
||||||
// UnsupportedProtocolVersionError per the draft schema.
|
// UnsupportedProtocolVersionError per the draft schema.
|
||||||
code: -32004,
|
code: -32022,
|
||||||
message: 'Unsupported protocol version',
|
message: 'Unsupported protocol version',
|
||||||
data: {
|
data: {
|
||||||
supported: [DRAFT_PROTOCOL_VERSION],
|
supported: [DRAFT_PROTOCOL_VERSION],
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ const pendingClientScenariosList: ClientScenario[] = [
|
||||||
|
|
||||||
// HTTP Standardization (SEP-2243)
|
// HTTP Standardization (SEP-2243)
|
||||||
// Pending until the everything-server fully implements SEP-2243
|
// Pending until the everything-server fully implements SEP-2243
|
||||||
// header validation (case-insensitive names, whitespace trimming, -32001 error code)
|
// header validation (case-insensitive names, whitespace trimming, -32020 error code)
|
||||||
new HttpHeaderValidationScenario(),
|
new HttpHeaderValidationScenario(),
|
||||||
new HttpCustomHeaderServerValidationScenario()
|
new HttpCustomHeaderServerValidationScenario()
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
* - Reject case variations of header values (case-sensitive)
|
* - Reject case variations of header values (case-sensitive)
|
||||||
* - Handle whitespace trimming per HTTP spec
|
* - Handle whitespace trimming per HTTP spec
|
||||||
* - Validate Base64-encoded custom header values
|
* - Validate Base64-encoded custom header values
|
||||||
* - Return 400 Bad Request with error code -32001 (HeaderMismatch)
|
* - Return 400 Bad Request with error code -32020 (HeaderMismatch)
|
||||||
*
|
*
|
||||||
* This is a ClientScenario (connects to a server under test and validates
|
* This is a ClientScenario (connects to a server under test and validates
|
||||||
* its behavior).
|
* its behavior).
|
||||||
|
|
@ -66,8 +66,8 @@ const REJECT_STATUS_CHECK_ID = 'sep-2243-server-reject-invalid-headers';
|
||||||
const REJECT_ERROR_CODE_CHECK_ID = 'sep-2243-server-reject-error-code';
|
const REJECT_ERROR_CODE_CHECK_ID = 'sep-2243-server-reject-error-code';
|
||||||
|
|
||||||
// CUSTOM-header (Mcp-Param-*) rejections map to the param-validation
|
// CUSTOM-header (Mcp-Param-*) rejections map to the param-validation
|
||||||
// requirements instead. The -32001 error-code half of every custom-header
|
// requirements instead. The -32020 error-code half of every custom-header
|
||||||
// rejection is the "reject with 400 + JSON-RPC error code -32001 if any
|
// rejection is the "reject with 400 + JSON-RPC error code -32020 if any
|
||||||
// validation fails" requirement.
|
// validation fails" requirement.
|
||||||
const PARAM_REJECT_ERROR_CODE_CHECK_ID =
|
const PARAM_REJECT_ERROR_CODE_CHECK_ID =
|
||||||
'sep-2243-server-reject-param-mismatch';
|
'sep-2243-server-reject-param-mismatch';
|
||||||
|
|
@ -148,8 +148,8 @@ async function sendRawRequest(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds two checks for a rejection case: one for the HTTP 400 status, one for
|
* Builds two checks for a rejection case: one for the HTTP 400 status, one for
|
||||||
* the -32001 JSON-RPC error code. Per SEP-2243 §Server Validation, 400 is MUST
|
* the -32020 JSON-RPC error code. Per SEP-2243 §Server Validation, 400 is MUST
|
||||||
* but -32001 is SHOULD for *standard* headers (and MUST for *custom* headers,
|
* but -32020 is SHOULD for *standard* headers (and MUST for *custom* headers,
|
||||||
* §Server Behavior for Custom Headers) — so a server returning 400 with a
|
* §Server Behavior for Custom Headers) — so a server returning 400 with a
|
||||||
* different error code is compliant for standard headers and must not FAIL.
|
* different error code is compliant for standard headers and must not FAIL.
|
||||||
*
|
*
|
||||||
|
|
@ -196,7 +196,7 @@ function createRejectionChecks(
|
||||||
{
|
{
|
||||||
id: errorCodeId,
|
id: errorCodeId,
|
||||||
name: `${name}ErrorCode`,
|
name: `${name}ErrorCode`,
|
||||||
description: `${description} — uses JSON-RPC error code -32001 (HeaderMismatch)`,
|
description: `${description} — uses JSON-RPC error code -32020 (HeaderMismatch)`,
|
||||||
status: codeOk ? 'SUCCESS' : opts.errorCodeSeverity,
|
status: codeOk ? 'SUCCESS' : opts.errorCodeSeverity,
|
||||||
timestamp: ts,
|
timestamp: ts,
|
||||||
errorMessage: codeOk
|
errorMessage: codeOk
|
||||||
|
|
@ -265,7 +265,7 @@ export class HttpHeaderValidationScenario implements ClientScenario {
|
||||||
- Server MUST reject case-mismatched header values (method values are case-sensitive)
|
- Server MUST reject case-mismatched header values (method values are case-sensitive)
|
||||||
- Server MUST accept extra whitespace around header values (per HTTP spec)
|
- Server MUST accept extra whitespace around header values (per HTTP spec)
|
||||||
- Server MUST return HTTP 400 Bad Request for validation failures
|
- Server MUST return HTTP 400 Bad Request for validation failures
|
||||||
- Server MUST return JSON-RPC error with code -32001 (HeaderMismatch)`;
|
- Server MUST return JSON-RPC error with code -32020 (HeaderMismatch)`;
|
||||||
|
|
||||||
async run(ctx: RunContext): Promise<ConformanceCheck[]> {
|
async run(ctx: RunContext): Promise<ConformanceCheck[]> {
|
||||||
const { serverUrl } = ctx;
|
const { serverUrl } = ctx;
|
||||||
|
|
@ -507,7 +507,7 @@ export class HttpHeaderValidationScenario implements ClientScenario {
|
||||||
...extraHeaders
|
...extraHeaders
|
||||||
});
|
});
|
||||||
if (expectation === 'reject') {
|
if (expectation === 'reject') {
|
||||||
// Standard-header rejection: 400 is MUST, -32001 is SHOULD. All
|
// Standard-header rejection: 400 is MUST, -32020 is SHOULD. All
|
||||||
// standard-header rejection cases collapse onto the coarse requirement
|
// standard-header rejection cases collapse onto the coarse requirement
|
||||||
// ids; checkId/checkName still distinguish the case via name/details.
|
// ids; checkId/checkName still distinguish the case via name/details.
|
||||||
checks.push(
|
checks.push(
|
||||||
|
|
@ -926,10 +926,10 @@ export class HttpCustomHeaderServerValidationScenario implements ClientScenario
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Custom-header rejection: both 400 and -32001 are MUST per
|
// Custom-header rejection: both 400 and -32020 are MUST per
|
||||||
// §Server Behavior for Custom Headers. The status half maps to the
|
// §Server Behavior for Custom Headers. The status half maps to the
|
||||||
// param-validation requirement this case exercises (checkId); the
|
// param-validation requirement this case exercises (checkId); the
|
||||||
// error-code half is the "400 + -32001 if any validation fails"
|
// error-code half is the "400 + -32020 if any validation fails"
|
||||||
// requirement.
|
// requirement.
|
||||||
checks.push(
|
checks.push(
|
||||||
...createRejectionChecks(
|
...createRejectionChecks(
|
||||||
|
|
@ -992,7 +992,7 @@ export class HttpCustomHeaderServerValidationScenario implements ClientScenario
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Custom-header rejection: both 400 and -32001 are MUST. A header
|
// Custom-header rejection: both 400 and -32020 are MUST. A header
|
||||||
// omitted while the body carries a value is a header/body mismatch, so
|
// omitted while the body carries a value is a header/body mismatch, so
|
||||||
// the status half maps to the validate-param-match requirement.
|
// the status half maps to the validate-param-match requirement.
|
||||||
checks.push(
|
checks.push(
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ describe('Stateless Server Scenario Negative Tests', () => {
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id: reqBody.id,
|
id: reqBody.id,
|
||||||
error: {
|
error: {
|
||||||
code: -32004,
|
code: -32022,
|
||||||
message: 'Unsupported protocol version',
|
message: 'Unsupported protocol version',
|
||||||
// Spec Violation: data.requested is a required member
|
// Spec Violation: data.requested is a required member
|
||||||
data: { supported: ['2026-07-28'] }
|
data: { supported: ['2026-07-28'] }
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ export class ServerStatelessScenario implements ClientScenario {
|
||||||
- Dynamically checks prompt capability declaration constraints, validates that active RPC handlers match advertised discovery capacities.
|
- Dynamically checks prompt capability declaration constraints, validates that active RPC handlers match advertised discovery capacities.
|
||||||
3. **Version Negotiation & Headers (3 Checks)**
|
3. **Version Negotiation & Headers (3 Checks)**
|
||||||
- Mismatched or unknown protocol versions must return an \`UnsupportedProtocolVersionError\` (HTTP status code \`400 Bad Request\`) carrying precise version tracking arrays.
|
- Mismatched or unknown protocol versions must return an \`UnsupportedProtocolVersionError\` (HTTP status code \`400 Bad Request\`) carrying precise version tracking arrays.
|
||||||
- Absent or altered protocol version header metadata must trigger a \`-32001 Header Mismatch\` error with an HTTP 400 boundary state.
|
- Absent or altered protocol version header metadata must trigger a \`-32020 Header Mismatch\` error with an HTTP 400 boundary state.
|
||||||
4. **Client Capability Constraints (2 Checks)**
|
4. **Client Capability Constraints (2 Checks)**
|
||||||
- Accessing platform capabilities without explicit declaration drops requests with a \`-32003 MissingRequiredClientCapabilityError\` containing needed capabilities, returning an HTTP status code \`400 Bad Request\`.
|
- Accessing platform capabilities without explicit declaration drops requests with a \`-32021 MissingRequiredClientCapabilityError\` containing needed capabilities, returning an HTTP status code \`400 Bad Request\`.
|
||||||
5. **Methods & Routing Mechanics (5 Checks)**
|
5. **Methods & Routing Mechanics (5 Checks)**
|
||||||
- Removed legacy endpoints (\`initialize\`, \`ping\`, \`logging/setLevel\`, etc.) or generic unknown methods must cleanly yield an HTTP status code \`404 Not Found\` alongside a JSON-RPC \`-32601 Method not found\` payload. All error returns must preserve original request ID mappings.
|
- Removed legacy endpoints (\`initialize\`, \`ping\`, \`logging/setLevel\`, etc.) or generic unknown methods must cleanly yield an HTTP status code \`404 Not Found\` alongside a JSON-RPC \`-32601 Method not found\` payload. All error returns must preserve original request ID mappings.
|
||||||
- Validates response streams contain only \`IncompleteResult\` chunks and never independent top-level JSON-RPC requests, while enforcing that no log messages are emitted when \`_meta.../logLevel\` is omitted.
|
- Validates response streams contain only \`IncompleteResult\` chunks and never independent top-level JSON-RPC requests, while enforcing that no log messages are emitted when \`_meta.../logLevel\` is omitted.
|
||||||
|
|
@ -608,9 +608,9 @@ export class ServerStatelessScenario implements ClientScenario {
|
||||||
() => {
|
() => {
|
||||||
if (!resAbsent)
|
if (!resAbsent)
|
||||||
return { error: 'Header verification endpoint network hit failed' };
|
return { error: 'Header verification endpoint network hit failed' };
|
||||||
if (resAbsent.status !== 400 || dataAbsent?.error?.code !== -32001) {
|
if (resAbsent.status !== 400 || dataAbsent?.error?.code !== -32020) {
|
||||||
return {
|
return {
|
||||||
error: `Expected HTTP 400 and JSON-RPC error -32001, got status ${resAbsent.status} with code ${dataAbsent?.error?.code}`
|
error: `Expected HTTP 400 and JSON-RPC error -32020, got status ${resAbsent.status} with code ${dataAbsent?.error?.code}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return { details: { response: dataAbsent } };
|
return { details: { response: dataAbsent } };
|
||||||
|
|
@ -631,12 +631,12 @@ export class ServerStatelessScenario implements ClientScenario {
|
||||||
if (data401) checkErrorId(data401, 401);
|
if (data401) checkErrorId(data401, 401);
|
||||||
|
|
||||||
// Determine if this server actively enforces client capabilities
|
// Determine if this server actively enforces client capabilities
|
||||||
const serverRequiresCapability = data401?.error?.code === -32003;
|
const serverRequiresCapability = data401?.error?.code === -32021;
|
||||||
|
|
||||||
await runCheck(
|
await runCheck(
|
||||||
'sep-2575-server-rejects-undeclared-capability',
|
'sep-2575-server-rejects-undeclared-capability',
|
||||||
'ServerRejectsUndeclaredCapability',
|
'ServerRejectsUndeclaredCapability',
|
||||||
'A server MUST NOT rely on capabilities the client has not declared. If processing a request requires a capability the client did not include in io.modelcontextprotocol/clientCapabilities, the server MUST return a MissingRequiredClientCapabilityError (-32003).',
|
'A server MUST NOT rely on capabilities the client has not declared. If processing a request requires a capability the client did not include in io.modelcontextprotocol/clientCapabilities, the server MUST return a MissingRequiredClientCapabilityError (-32021).',
|
||||||
() => {
|
() => {
|
||||||
if (!res401)
|
if (!res401)
|
||||||
return {
|
return {
|
||||||
|
|
@ -645,22 +645,22 @@ export class ServerStatelessScenario implements ClientScenario {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!serverRequiresCapability) {
|
if (!serverRequiresCapability) {
|
||||||
// The server didn't return -32003, so this requirement isn't
|
// The server didn't return -32021, so this requirement isn't
|
||||||
// exercised for this method. Report SKIPPED rather than a green PASS.
|
// exercised for this method. Report SKIPPED rather than a green PASS.
|
||||||
return {
|
return {
|
||||||
skipped: true,
|
skipped: true,
|
||||||
details: {
|
details: {
|
||||||
note: 'Skipped requirement tracking: Server returned a non-32003 response, indicating it does not require explicit client capability authorization constraints for this method.',
|
note: 'Skipped requirement tracking: Server returned a non-32021 response, indicating it does not require explicit client capability authorization constraints for this method.',
|
||||||
response: data401
|
response: data401
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it DOES return -32003, strictly validate the requirement payload structure
|
// If it DOES return -32021, strictly validate the requirement payload structure
|
||||||
const reqCaps = data401?.error?.data?.requiredCapabilities;
|
const reqCaps = data401?.error?.data?.requiredCapabilities;
|
||||||
if (!Array.isArray(reqCaps) || !reqCaps.includes('sampling')) {
|
if (!Array.isArray(reqCaps) || !reqCaps.includes('sampling')) {
|
||||||
return {
|
return {
|
||||||
error: `Server responded with error code -32003 but failed to provide an array containing the expected 'sampling' capability in error.data.requiredCapabilities`,
|
error: `Server responded with error code -32021 but failed to provide an array containing the expected 'sampling' capability in error.data.requiredCapabilities`,
|
||||||
details: { response: data401 }
|
details: { response: data401 }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -680,7 +680,7 @@ export class ServerStatelessScenario implements ClientScenario {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!serverRequiresCapability) {
|
if (!serverRequiresCapability) {
|
||||||
// No -32003 means the HTTP-400 requirement doesn't apply here.
|
// No -32021 means the HTTP-400 requirement doesn't apply here.
|
||||||
// Report SKIPPED rather than a green PASS.
|
// Report SKIPPED rather than a green PASS.
|
||||||
return {
|
return {
|
||||||
skipped: true,
|
skipped: true,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ requirements:
|
||||||
- check: sep-2243-server-reject-invalid-headers
|
- check: sep-2243-server-reject-invalid-headers
|
||||||
text: 'Servers that process the request body MUST reject requests with mismatched or missing standard-header values, returning HTTP 400 Bad Request.'
|
text: 'Servers that process the request body MUST reject requests with mismatched or missing standard-header values, returning HTTP 400 Bad Request.'
|
||||||
- check: sep-2243-server-reject-error-code
|
- check: sep-2243-server-reject-error-code
|
||||||
text: 'When rejecting a request due to header validation failure, servers SHOULD include a JSON-RPC error response using error code -32001.'
|
text: 'When rejecting a request due to header validation failure, servers SHOULD include a JSON-RPC error response using error code -32020.'
|
||||||
- check: sep-2243-client-supports-custom-headers
|
- check: sep-2243-client-supports-custom-headers
|
||||||
text: 'MCP clients MUST support this feature [custom headers via x-mcp-header].'
|
text: 'MCP clients MUST support this feature [custom headers via x-mcp-header].'
|
||||||
- check: sep-2243-client-mirrors-designated-params
|
- check: sep-2243-client-mirrors-designated-params
|
||||||
|
|
@ -45,7 +45,7 @@ requirements:
|
||||||
- check: sep-2243-server-validate-param-match
|
- check: sep-2243-server-validate-param-match
|
||||||
text: 'Any server that processes the message body MUST validate that encoded header values, after decoding if Base64-encoded, match the corresponding parameter values in the body.'
|
text: 'Any server that processes the message body MUST validate that encoded header values, after decoding if Base64-encoded, match the corresponding parameter values in the body.'
|
||||||
- check: sep-2243-server-reject-param-mismatch
|
- check: sep-2243-server-reject-param-mismatch
|
||||||
text: 'Servers MUST reject requests with a 400 Bad Request HTTP status and JSON-RPC error code -32001 if any validation fails.'
|
text: 'Servers MUST reject requests with a 400 Bad Request HTTP status and JSON-RPC error code -32020 if any validation fails.'
|
||||||
|
|
||||||
- text: 'Clients SHOULD log a warning when rejecting a tool definition due to invalid x-mcp-header, including the tool name and the reason.'
|
- text: 'Clients SHOULD log a warning when rejecting a tool definition due to invalid x-mcp-header, including the tool name and the reason.'
|
||||||
excluded: 'Log output is not wire-observable.'
|
excluded: 'Log output is not wire-observable.'
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ requirements:
|
||||||
text: 'Every client request MUST include the following io.modelcontextprotocol/* fields in _meta: protocolVersion, clientInfo, clientCapabilities.'
|
text: 'Every client request MUST include the following io.modelcontextprotocol/* fields in _meta: protocolVersion, clientInfo, clientCapabilities.'
|
||||||
url: https://modelcontextprotocol.io/specification/draft/basic/index#meta
|
url: https://modelcontextprotocol.io/specification/draft/basic/index#meta
|
||||||
- check: sep-2575-server-rejects-undeclared-capability
|
- check: sep-2575-server-rejects-undeclared-capability
|
||||||
text: 'A server MUST NOT rely on capabilities the client has not declared. If processing a request requires a capability the client did not include in io.modelcontextprotocol/clientCapabilities, the server MUST return a MissingRequiredClientCapabilityError (-32003).'
|
text: 'A server MUST NOT rely on capabilities the client has not declared. If processing a request requires a capability the client did not include in io.modelcontextprotocol/clientCapabilities, the server MUST return a MissingRequiredClientCapabilityError (-32021).'
|
||||||
url: https://modelcontextprotocol.io/specification/draft/basic/index#meta
|
url: https://modelcontextprotocol.io/specification/draft/basic/index#meta
|
||||||
- check: sep-2575-missing-capability-http-400
|
- check: sep-2575-missing-capability-http-400
|
||||||
text: 'On HTTP, the response status MUST be 400 Bad Request [for MissingRequiredClientCapabilityError].'
|
text: 'On HTTP, the response status MUST be 400 Bad Request [for MissingRequiredClientCapabilityError].'
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
modelcontextprotocol@2fb207da428f43a4981513ec2aef218b7533c5b3
|
modelcontextprotocol@f817239f4d6b1efff2c4dfc2f7af85c985d73076
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ export interface InvalidRequestError extends Error {
|
||||||
*
|
*
|
||||||
* In MCP, a server returns this error when a client invokes a method the server does not implement — either a genuinely unknown method, or one gated behind a server capability the server did not advertise (e.g., calling `prompts/list` when the `prompts` capability was not advertised).
|
* In MCP, a server returns this error when a client invokes a method the server does not implement — either a genuinely unknown method, or one gated behind a server capability the server did not advertise (e.g., calling `prompts/list` when the `prompts` capability was not advertised).
|
||||||
*
|
*
|
||||||
* A request that requires a client capability the client did not declare is signalled instead by {@link MissingRequiredClientCapabilityError} (`-32003`).
|
* A request that requires a client capability the client did not declare is signalled instead by {@link MissingRequiredClientCapabilityError} (`-32021`).
|
||||||
*
|
*
|
||||||
* @see {@link https://www.jsonrpc.org/specification#error_object | JSON-RPC 2.0 Error Object}
|
* @see {@link https://www.jsonrpc.org/specification#error_object | JSON-RPC 2.0 Error Object}
|
||||||
*
|
*
|
||||||
|
|
@ -380,6 +380,26 @@ export interface InternalError extends Error {
|
||||||
code: typeof INTERNAL_ERROR;
|
code: typeof INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCP error codes.
|
||||||
|
*
|
||||||
|
* JSON-RPC 2.0 reserves `-32000` to `-32099` for implementation-defined
|
||||||
|
* server errors. MCP partitions that range:
|
||||||
|
*
|
||||||
|
* - `-32000` to `-32019`: implementation-defined. Existing SDKs and
|
||||||
|
* implementations use codes here for their own purposes; the specification
|
||||||
|
* will never define codes in this sub-range, and receivers must not assign
|
||||||
|
* cross-implementation semantics to them.
|
||||||
|
* - `-32020` to `-32099`: reserved for error codes defined by the MCP
|
||||||
|
* specification. Every code allocated here is recorded in this file.
|
||||||
|
* Codes are allocated sequentially starting at `-32020` and proceeding
|
||||||
|
* toward `-32099`.
|
||||||
|
*
|
||||||
|
* Codes defined by earlier protocol versions remain reserved and are never
|
||||||
|
* reused: `-32002` (resource not found, 2025-11-25 and earlier; replaced by
|
||||||
|
* `-32602`) and `-32042` (URL elicitation required, 2025-11-25 only).
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code returned when the HTTP headers of a request do not match the
|
* Error code returned when the HTTP headers of a request do not match the
|
||||||
* corresponding values in the request body, or required headers are
|
* corresponding values in the request body, or required headers are
|
||||||
|
|
@ -387,7 +407,7 @@ export interface InternalError extends Error {
|
||||||
*
|
*
|
||||||
* @category Errors
|
* @category Errors
|
||||||
*/
|
*/
|
||||||
export const HEADER_MISMATCH = -32001;
|
export const HEADER_MISMATCH = -32020;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code returned when a server requires a client capability that was
|
* Error code returned when a server requires a client capability that was
|
||||||
|
|
@ -395,7 +415,7 @@ export const HEADER_MISMATCH = -32001;
|
||||||
*
|
*
|
||||||
* @category Errors
|
* @category Errors
|
||||||
*/
|
*/
|
||||||
export const MISSING_REQUIRED_CLIENT_CAPABILITY = -32003;
|
export const MISSING_REQUIRED_CLIENT_CAPABILITY = -32021;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code returned when the request's protocol version is not supported
|
* Error code returned when the request's protocol version is not supported
|
||||||
|
|
@ -403,7 +423,7 @@ export const MISSING_REQUIRED_CLIENT_CAPABILITY = -32003;
|
||||||
*
|
*
|
||||||
* @category Errors
|
* @category Errors
|
||||||
*/
|
*/
|
||||||
export const UNSUPPORTED_PROTOCOL_VERSION = -32004;
|
export const UNSUPPORTED_PROTOCOL_VERSION = -32022;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returned when a server rejects a request because the values in the HTTP
|
* Returned when a server rejects a request because the values in the HTTP
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue