From fd5b0edb6483917391a98d4efdfee1527108e8f4 Mon Sep 17 00:00:00 2001 From: jju1s <123896289+cig13zs@users.noreply.github.com> Date: Thu, 15 Oct 2026 11:26:19 +0800 Subject: [PATCH] fix(sep-2164): grade the resource-not-found error code as a MUST The released 2026-07-28 resources page says servers MUST return -32602 for a resource that does not exist (the spec text was aligned with the SEP in modelcontextprotocol/modelcontextprotocol#2863), and basic/index lists -32002 among the codes 2026-07-28 implementations MUST NOT emit. sep-2164-error-code still graded the older SHOULD wording, so a server answering -32002 or -32603 only drew a WARNING. Report FAILURE instead and quote the current text in sep-2164.yaml. --- src/new-sep/index.test.ts | 4 +- src/scenarios/server/negative.test.ts | 53 ++++++++++++++++++++++++++- src/scenarios/server/resources.ts | 12 +++--- src/seps/sep-2164.yaml | 2 +- 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/new-sep/index.test.ts b/src/new-sep/index.test.ts index acc01bb7..023112be 100644 --- a/src/new-sep/index.test.ts +++ b/src/new-sep/index.test.ts @@ -53,7 +53,7 @@ requirements: check: 'sep-2164-no-empty-contents' }, { - text: 'Servers SHOULD return standard JSON-RPC errors for common failure cases: Resource not found: -32602 (Invalid Params)', + text: 'If the requested resource does not exist, servers MUST return a JSON-RPC error with code -32602 (Invalid Params).', check: 'sep-2164-error-code' }, { @@ -70,7 +70,7 @@ requirements: - check: sep-2164-no-empty-contents text: 'Servers MUST NOT return an empty contents array for a non-existent resource' - check: sep-2164-error-code - text: 'Servers SHOULD return standard JSON-RPC errors for common failure cases: Resource not found: -32602 (Invalid Params)' + text: 'If the requested resource does not exist, servers MUST return a JSON-RPC error with code -32602 (Invalid Params).' - text: 'clients SHOULD also accept -32002 as a resource not found error' excluded: 'Client-side error handling is implementation-defined; not protocol-observable' diff --git a/src/scenarios/server/negative.test.ts b/src/scenarios/server/negative.test.ts index 746cf9ce..cf5b920d 100644 --- a/src/scenarios/server/negative.test.ts +++ b/src/scenarios/server/negative.test.ts @@ -317,7 +317,7 @@ describe('Server scenario negative tests', () => { await stopServer(serverProcess); }); - it('emits FAILURE for no-empty-contents and WARNING for error-code against a server returning empty contents', async () => { + it('emits FAILURE for no-empty-contents and error-code against a server returning empty contents', async () => { const scenario = new ResourcesNotFoundErrorScenario(); const checks = await scenario.run( testContext(`http://localhost:${PORT}/mcp`, DRAFT_PROTOCOL_VERSION) @@ -327,7 +327,56 @@ describe('Server scenario negative tests', () => { expect(noEmpty?.status).toBe('FAILURE'); const errorCode = checks.find((c) => c.id === 'sep-2164-error-code'); - expect(errorCode?.status).toBe('WARNING'); + expect(errorCode?.status).toBe('FAILURE'); + }, 10000); + + it('emits FAILURE for error-code against a server answering with the retired -32002 code', async () => { + const server = createServer(async (req, res) => { + const body = await readJsonBody(req); + const result = + body.method === 'server/discover' + ? { + result: { + resultType: 'complete', + ttlMs: 0, + cacheScope: 'private', + supportedVersions: [DRAFT_PROTOCOL_VERSION], + capabilities: { resources: {} }, + serverInfo: { name: 'sep-2164-legacy-code', version: '1.0.0' } + } + } + : { + error: { + code: -32002, + message: 'Resource not found', + data: { + uri: (body.params as { uri?: string } | undefined)?.uri + } + } + }; + res + .writeHead(200, { 'Content-Type': 'application/json' }) + .end(JSON.stringify({ jsonrpc: '2.0', id: body.id, ...result })); + }); + + const serverUrl = await listenOnRandomPort(server); + try { + const checks = await new ResourcesNotFoundErrorScenario().run( + testContext(serverUrl, DRAFT_PROTOCOL_VERSION) + ); + + expect( + checks.find((c) => c.id === 'sep-2164-no-empty-contents')?.status + ).toBe('SUCCESS'); + const errorCode = checks.find((c) => c.id === 'sep-2164-error-code'); + expect(errorCode?.status).toBe('FAILURE'); + expect(errorCode?.errorMessage).toContain('-32002'); + expect(checks.find((c) => c.id === 'sep-2164-data-uri')?.status).toBe( + 'SUCCESS' + ); + } finally { + await closeHttpServer(server); + } }, 10000); }); diff --git a/src/scenarios/server/resources.ts b/src/scenarios/server/resources.ts index 382b0db8..7c280367 100644 --- a/src/scenarios/server/resources.ts +++ b/src/scenarios/server/resources.ts @@ -457,7 +457,7 @@ export class ResourcesNotFoundErrorScenario implements ClientScenario { When a client requests a URI that does not correspond to any resource, the server: - **MUST NOT** return a result with an empty \`contents\` array -- **SHOULD** return a JSON-RPC error with code \`-32602\` (Invalid Params) +- **MUST** return a JSON-RPC error with code \`-32602\` (Invalid Params) - **SHOULD** include the requested \`uri\` in the error \`data\` field Example error response: @@ -540,20 +540,20 @@ This scenario does not require the server to register any specific resource — } }); - // Check 2: SHOULD return JSON-RPC error with code -32602 + // Check 2: MUST return JSON-RPC error with code -32602 const rpcError = caughtError instanceof JsonRpcError ? caughtError : undefined; const errorCode = rpcError?.code; let errorCodeMessage: string | undefined; if (result !== undefined) { - errorCodeMessage = `Server returned a result instead of an error (contents length: ${result.contents?.length ?? 'undefined'}). Servers SHOULD return a JSON-RPC error for non-existent resources.`; + errorCodeMessage = `Server returned a result instead of an error (contents length: ${result.contents?.length ?? 'undefined'}). Servers MUST return a JSON-RPC error for non-existent resources.`; } else if (rpcError === undefined) { errorCodeMessage = `Expected a JSON-RPC error, got: ${caughtError instanceof Error ? caughtError.message : String(caughtError)}`; } else if (errorCode !== -32602) { errorCodeMessage = `Expected error code -32602 (Invalid Params), got ${errorCode}. ` + (errorCode === -32002 - ? 'Code -32002 was used in earlier spec versions but SEP-2164 standardizes on -32602.' + ? 'Code -32002 is the pre-2026-07-28 resource-not-found code; implementations of 2026-07-28 MUST NOT emit it.' : ''); } @@ -561,8 +561,8 @@ This scenario does not require the server to register any specific resource — id: 'sep-2164-error-code', name: 'ResourcesNotFoundErrorCode', description: - 'Server returns -32602 (Invalid Params) for non-existent resource (SHOULD)', - status: errorCodeMessage === undefined ? 'SUCCESS' : 'WARNING', + 'Server returns -32602 (Invalid Params) for non-existent resource', + status: errorCodeMessage === undefined ? 'SUCCESS' : 'FAILURE', timestamp: new Date().toISOString(), errorMessage: errorCodeMessage, specReferences, diff --git a/src/seps/sep-2164.yaml b/src/seps/sep-2164.yaml index 83c11ce9..e43db171 100644 --- a/src/seps/sep-2164.yaml +++ b/src/seps/sep-2164.yaml @@ -4,7 +4,7 @@ requirements: - check: sep-2164-no-empty-contents text: 'Servers MUST NOT return an empty contents array for a non-existent resource' - check: sep-2164-error-code - text: 'Servers SHOULD return standard JSON-RPC errors for common failure cases: Resource not found: -32602 (Invalid Params)' + text: 'If the requested resource does not exist, servers MUST return a JSON-RPC error with code -32602 (Invalid Params).' - text: 'clients SHOULD also accept -32002 as a resource not found error' excluded: 'Client-side error handling is implementation-defined; not protocol-observable'