From 2e9ed913904e30ccbee6ed3e6391afc68c443f0a Mon Sep 17 00:00:00 2001 From: 7Sageer <125936732+7Sageer@users.noreply.github.com> Date: Thu, 20 Aug 2026 16:32:20 +0800 Subject: [PATCH] fix(mcp): map OAuth flow failures to wire code 40929 --- packages/kap-server/src/protocol/error-codes.ts | 1 + packages/kap-server/src/routes/v2/mcp.ts | 3 +++ packages/kap-server/test/v2Mcp.test.ts | 16 ++++++++++++++++ .../klient/src/transports/memory/dispatcher.ts | 6 +++--- packages/klient/test/helpers/conformance.ts | 4 ++-- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/kap-server/src/protocol/error-codes.ts b/packages/kap-server/src/protocol/error-codes.ts index 7807de66a..cef690ab4 100644 --- a/packages/kap-server/src/protocol/error-codes.ts +++ b/packages/kap-server/src/protocol/error-codes.ts @@ -62,6 +62,7 @@ export const ErrorCode = { RUNTIME_UNAVAILABLE: 40926, PROMPT_ID_CONFLICT: 40927, MCP_MANAGEMENT_DISABLED: 40928, + MCP_OAUTH_FAILED: 40929, APPROVAL_EXPIRED: 41001, QUESTION_EXPIRED: 41002, diff --git a/packages/kap-server/src/routes/v2/mcp.ts b/packages/kap-server/src/routes/v2/mcp.ts index 7fa3496ae..6a9681f82 100644 --- a/packages/kap-server/src/routes/v2/mcp.ts +++ b/packages/kap-server/src/routes/v2/mcp.ts @@ -199,6 +199,9 @@ function sendMappedError( errEnvelope(ErrorCode.MCP_MANAGEMENT_DISABLED, err.message, requestId, err.stack), ); return; + case ErrorCodes.MCP_OAUTH_FAILED: + reply.send(errEnvelope(ErrorCode.MCP_OAUTH_FAILED, err.message, requestId, err.stack)); + return; } } throw err; diff --git a/packages/kap-server/test/v2Mcp.test.ts b/packages/kap-server/test/v2Mcp.test.ts index 85b4a5311..8dc0b6e41 100644 --- a/packages/kap-server/test/v2Mcp.test.ts +++ b/packages/kap-server/test/v2Mcp.test.ts @@ -358,6 +358,22 @@ describe('server /api/v2/mcp', () => { expect(res.body.data).toBeNull(); }); + it('maps the engine mcp.oauth_failed rejection to 40929', async () => { + const stub = makeMcpStub(); + stub.service.completeServerAuth = async () => { + throw new Error2( + ErrorCodes.MCP_OAUTH_FAILED, + 'OAuth flow for "a" failed: OAuth callback timed out', + ); + }; + await boot(stub); + + const res = await call('POST', '/api/v2/mcp/auth:complete', { flowId: 'flow-1' }); + expect(res.status).toBe(200); + expect(res.body.code).toBe(40929); + expect(res.body.data).toBeNull(); + }); + it('maps a delete rejected with mcp.server_not_found to 40408', async () => { const stub = makeMcpStub(); stub.service.removeServer = async (name) => { diff --git a/packages/klient/src/transports/memory/dispatcher.ts b/packages/klient/src/transports/memory/dispatcher.ts index 3fa3ad58e..44f2d8aeb 100644 --- a/packages/klient/src/transports/memory/dispatcher.ts +++ b/packages/klient/src/transports/memory/dispatcher.ts @@ -68,8 +68,8 @@ const NOT_FOUND = 40404; /** kap-server wire codes mirrored so memory/ipc surface the same numeric codes as `/api/v2/mcp`. */ const MCP_SERVER_NOT_FOUND = 40408; const MCP_MANAGEMENT_DISABLED = 40928; +const MCP_OAUTH_FAILED = 40929; const PROMPT_ID_CONFLICT = 40927; -const INTERNAL_ERROR = 50001; /** Wire name of the engine's `IMcpManagementService` decorator id. */ const MCP_MANAGEMENT_SERVICE = 'mcpManagementService'; @@ -92,7 +92,7 @@ function rethrowFileErrorAsRpc(error: unknown): never { * `RPCError`s carrying the kap-server wire codes, so memory and ipc behave * identically (a raw `Error2` would cross ipc as a generic 50001) and both * match `/api/v2/mcp` — `mcp.server_not_found` → 40408, `request.invalid` / - * `config.invalid` → 40001. + * `config.invalid` → 40001, `mcp.oauth_failed` → 40929. */ function rethrowMcpManagementErrorAsRpc(error: unknown): never { if (error instanceof Error2) { @@ -103,7 +103,7 @@ function rethrowMcpManagementErrorAsRpc(error: unknown): never { case ErrorCodes.CONFIG_INVALID: throw new RPCError(REQUEST_INVALID, error.message, error.details); case ErrorCodes.MCP_OAUTH_FAILED: - throw new RPCError(INTERNAL_ERROR, error.message, error.details); + throw new RPCError(MCP_OAUTH_FAILED, error.message, error.details); } } throw error; diff --git a/packages/klient/test/helpers/conformance.ts b/packages/klient/test/helpers/conformance.ts index b5bc75366..ec6dd2b6d 100644 --- a/packages/klient/test/helpers/conformance.ts +++ b/packages/klient/test/helpers/conformance.ts @@ -473,7 +473,7 @@ export function defineKlientConformance( } }); - it('global mcp OAuth failures use transport-stable 50001 errors', async () => { + it('global mcp OAuth failures map to the 40929 wire code on every transport', async () => { const mcp = target.klient.global.mcp; const flags = target.app.accessor.get(IFlagService); flags.setConfigOverrides({ mcp_management: true }); @@ -489,7 +489,7 @@ export function defineKlientConformance( try { await expect( mcp.beginAuth({ locator: { source: 'global', name: 'conf-oauth-failure' } }), - ).rejects.toMatchObject({ name: 'RPCError', code: 50001 }); + ).rejects.toMatchObject({ name: 'RPCError', code: 40929 }); } finally { await mcp.remove({ name: 'conf-oauth-failure' }); }