From 212edf31ed7835c240f73c9f65d04cc05d2786fc Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Tue, 14 Apr 2026 13:24:21 -0400 Subject: [PATCH] chore(mcp): check MCP error code over brittle string match (#25381) --- packages/core/src/tools/mcp-client.test.ts | 22 ++++++++++++++++++++++ packages/core/src/tools/mcp-client.ts | 16 +++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/core/src/tools/mcp-client.test.ts b/packages/core/src/tools/mcp-client.test.ts index 4a14b671a0..50b17aa735 100644 --- a/packages/core/src/tools/mcp-client.test.ts +++ b/packages/core/src/tools/mcp-client.test.ts @@ -20,6 +20,8 @@ import { MCPOAuthTokenStorage } from '../mcp/oauth-token-storage.js'; import { OAuthUtils } from '../mcp/oauth-utils.js'; import type { PromptRegistry } from '../prompts/prompt-registry.js'; import { + ErrorCode, + McpError, PromptListChangedNotificationSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, @@ -35,6 +37,7 @@ import { isEnabled, McpClient, populateMcpServerCommand, + discoverPrompts, type McpContext, } from './mcp-client.js'; import type { ToolRegistry } from './tool-registry.js'; @@ -320,6 +323,25 @@ describe('mcp-client', () => { ); }); + it('should return empty array for discoverPrompts on MethodNotFound error without diagnostic', async () => { + const mockedClient = { + getServerCapabilities: vi.fn().mockReturnValue({ prompts: {} }), + listPrompts: vi + .fn() + .mockRejectedValue( + new McpError(ErrorCode.MethodNotFound, 'Method not supported'), + ), + }; + const result = await discoverPrompts( + 'test-server', + mockedClient as unknown as ClientLib.Client, + MOCK_CONTEXT, + ); + expect(result).toEqual([]); + // MethodNotFound errors should be silently ignored regardless of message text + expect(MOCK_CONTEXT.emitMcpDiagnostic).not.toHaveBeenCalled(); + }); + it('should not discover tools if server does not support them', async () => { const mockedClient = { connect: vi.fn(), diff --git a/packages/core/src/tools/mcp-client.ts b/packages/core/src/tools/mcp-client.ts index a7852050fc..0441063f81 100644 --- a/packages/core/src/tools/mcp-client.ts +++ b/packages/core/src/tools/mcp-client.ts @@ -27,6 +27,8 @@ import { ReadResourceResultSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, + ErrorCode, + McpError, PromptListChangedNotificationSchema, ProgressNotificationSchema, type GetPromptResult, @@ -1250,6 +1252,10 @@ export async function connectAndDiscover( } } +function isMcpMethodNotFoundError(error: unknown): boolean { + return error instanceof McpError && error.code === ErrorCode.MethodNotFound; +} + /** * Discovers and sanitizes tools from a connected MCP client. * It retrieves function declarations from the client, filters out disabled tools, @@ -1329,10 +1335,7 @@ export async function discoverTools( } return discoveredTools; } catch (error) { - if ( - error instanceof Error && - !error.message?.includes('Method not found') - ) { + if (!isMcpMethodNotFoundError(error)) { cliConfig.emitMcpDiagnostic( 'error', `Error discovering tools from ${mcpServerName}: ${getErrorMessage( @@ -1456,8 +1459,7 @@ export async function discoverPrompts( ), })); } catch (error) { - // It's okay if the method is not found, which is a common case. - if (error instanceof Error && error.message?.includes('Method not found')) { + if (isMcpMethodNotFoundError(error)) { return []; } cliConfig.emitMcpDiagnostic( @@ -1505,7 +1507,7 @@ async function listResources( cursor = response.nextCursor ?? undefined; } while (cursor); } catch (error) { - if (error instanceof Error && error.message?.includes('Method not found')) { + if (isMcpMethodNotFoundError(error)) { return []; } cliConfig.emitMcpDiagnostic(