From 61fef527500dbd7f314f0a23bded8631208938a1 Mon Sep 17 00:00:00 2001 From: Matteo Gobbo Date: Sat, 20 Dec 2025 12:31:22 +0100 Subject: [PATCH 1/4] feat: add UnsupportedCapabilityError and update ErrorCode enum --- packages/core/src/types/types.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/src/types/types.ts b/packages/core/src/types/types.ts index 35b04745d..80e1df913 100644 --- a/packages/core/src/types/types.ts +++ b/packages/core/src/types/types.ts @@ -238,7 +238,8 @@ export enum ErrorCode { InternalError = -32603, // MCP-specific error codes - UrlElicitationRequired = -32042 + UrlElicitationRequired = -32042, + UnsupportedCapability = -32043 } /** @@ -2369,6 +2370,12 @@ export class UrlElicitationRequiredError extends McpError { } } +export class UnsupportedCapabilityError extends McpError { + constructor(message: string) { + super(ErrorCode.UnsupportedCapability, message); + } +} + type Primitive = string | number | boolean | bigint | null | undefined; type Flatten = T extends Primitive ? T From fa105b0d6a461c8e1a191947c90b903c17fd37e3 Mon Sep 17 00:00:00 2001 From: Matteo Gobbo Date: Sat, 20 Dec 2025 12:32:49 +0100 Subject: [PATCH 2/4] refactor: replace Error with UnsupportedCapabilityError in client and helpers --- packages/client/src/client/client.ts | 27 ++++++++++--------- .../core/src/experimental/tasks/helpers.ts | 16 ++++++----- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/client/src/client/client.ts b/packages/client/src/client/client.ts index 8d96ba0bc..957b9782a 100644 --- a/packages/client/src/client/client.ts +++ b/packages/client/src/client/client.ts @@ -66,7 +66,8 @@ import { ResourceListChangedNotificationSchema, safeParse, SUPPORTED_PROTOCOL_VERSIONS, - ToolListChangedNotificationSchema + ToolListChangedNotificationSchema, + UnsupportedCapabilityError } from '@modelcontextprotocol/core'; import { ExperimentalClientTasks } from '../experimental/tasks/client.js'; @@ -479,7 +480,7 @@ export class Client< protected assertCapability(capability: keyof ServerCapabilities, method: string): void { if (!this._serverCapabilities?.[capability]) { - throw new Error(`Server does not support ${capability} (required for ${method})`); + throw new UnsupportedCapabilityError(`Server does not support ${capability} (required for ${method})`); } } @@ -562,14 +563,14 @@ export class Client< switch (method as ClientRequest['method']) { case 'logging/setLevel': if (!this._serverCapabilities?.logging) { - throw new Error(`Server does not support logging (required for ${method})`); + throw new UnsupportedCapabilityError(`Server does not support logging (required for ${method})`); } break; case 'prompts/get': case 'prompts/list': if (!this._serverCapabilities?.prompts) { - throw new Error(`Server does not support prompts (required for ${method})`); + throw new UnsupportedCapabilityError(`Server does not support prompts (required for ${method})`); } break; @@ -579,11 +580,11 @@ export class Client< case 'resources/subscribe': case 'resources/unsubscribe': if (!this._serverCapabilities?.resources) { - throw new Error(`Server does not support resources (required for ${method})`); + throw new UnsupportedCapabilityError(`Server does not support resources (required for ${method})`); } if (method === 'resources/subscribe' && !this._serverCapabilities.resources.subscribe) { - throw new Error(`Server does not support resource subscriptions (required for ${method})`); + throw new UnsupportedCapabilityError(`Server does not support resource subscriptions (required for ${method})`); } break; @@ -591,13 +592,13 @@ export class Client< case 'tools/call': case 'tools/list': if (!this._serverCapabilities?.tools) { - throw new Error(`Server does not support tools (required for ${method})`); + throw new UnsupportedCapabilityError(`Server does not support tools (required for ${method})`); } break; case 'completion/complete': if (!this._serverCapabilities?.completions) { - throw new Error(`Server does not support completions (required for ${method})`); + throw new UnsupportedCapabilityError(`Server does not support completions (required for ${method})`); } break; @@ -615,7 +616,7 @@ export class Client< switch (method as ClientNotification['method']) { case 'notifications/roots/list_changed': if (!this._capabilities.roots?.listChanged) { - throw new Error(`Client does not support roots list changed notifications (required for ${method})`); + throw new UnsupportedCapabilityError(`Client does not support roots list changed notifications (required for ${method})`); } break; @@ -643,19 +644,19 @@ export class Client< switch (method) { case 'sampling/createMessage': if (!this._capabilities.sampling) { - throw new Error(`Client does not support sampling capability (required for ${method})`); + throw new UnsupportedCapabilityError(`Client does not support sampling capability (required for ${method})`); } break; case 'elicitation/create': if (!this._capabilities.elicitation) { - throw new Error(`Client does not support elicitation capability (required for ${method})`); + throw new UnsupportedCapabilityError(`Client does not support elicitation capability (required for ${method})`); } break; case 'roots/list': if (!this._capabilities.roots) { - throw new Error(`Client does not support roots capability (required for ${method})`); + throw new UnsupportedCapabilityError(`Client does not support roots capability (required for ${method})`); } break; @@ -664,7 +665,7 @@ export class Client< case 'tasks/result': case 'tasks/cancel': if (!this._capabilities.tasks) { - throw new Error(`Client does not support tasks capability (required for ${method})`); + throw new UnsupportedCapabilityError(`Client does not support tasks capability (required for ${method})`); } break; diff --git a/packages/core/src/experimental/tasks/helpers.ts b/packages/core/src/experimental/tasks/helpers.ts index 34b15188f..69f9fc6ec 100644 --- a/packages/core/src/experimental/tasks/helpers.ts +++ b/packages/core/src/experimental/tasks/helpers.ts @@ -5,6 +5,8 @@ * @experimental */ +import { UnsupportedCapabilityError } from "../../types/types.js"; + /** * Type representing the task requests capability structure. * This is derived from ClientTasksCapability.requests and ServerTasksCapability.requests. @@ -22,7 +24,7 @@ interface TaskRequestsCapability { * @param requests - The task requests capability object * @param method - The method being checked * @param entityName - 'Server' or 'Client' for error messages - * @throws Error if the capability is not supported + * @throws UnsupportedCapabilityError if the capability is not supported * * @experimental */ @@ -32,13 +34,13 @@ export function assertToolsCallTaskCapability( entityName: 'Server' | 'Client' ): void { if (!requests) { - throw new Error(`${entityName} does not support task creation (required for ${method})`); + throw new UnsupportedCapabilityError(`${entityName} does not support task creation (required for ${method})`); } switch (method) { case 'tools/call': if (!requests.tools?.call) { - throw new Error(`${entityName} does not support task creation for tools/call (required for ${method})`); + throw new UnsupportedCapabilityError(`${entityName} does not support task creation for tools/call (required for ${method})`); } break; @@ -55,7 +57,7 @@ export function assertToolsCallTaskCapability( * @param requests - The task requests capability object * @param method - The method being checked * @param entityName - 'Server' or 'Client' for error messages - * @throws Error if the capability is not supported + * @throws UnsupportedCapabilityError if the capability is not supported * * @experimental */ @@ -65,19 +67,19 @@ export function assertClientRequestTaskCapability( entityName: 'Server' | 'Client' ): void { if (!requests) { - throw new Error(`${entityName} does not support task creation (required for ${method})`); + throw new UnsupportedCapabilityError(`${entityName} does not support task creation (required for ${method})`); } switch (method) { case 'sampling/createMessage': if (!requests.sampling?.createMessage) { - throw new Error(`${entityName} does not support task creation for sampling/createMessage (required for ${method})`); + throw new UnsupportedCapabilityError(`${entityName} does not support task creation for sampling/createMessage (required for ${method})`); } break; case 'elicitation/create': if (!requests.elicitation?.create) { - throw new Error(`${entityName} does not support task creation for elicitation/create (required for ${method})`); + throw new UnsupportedCapabilityError(`${entityName} does not support task creation for elicitation/create (required for ${method})`); } break; From 6dede8e181bb0205aac2daa60c34789a041dbcd2 Mon Sep 17 00:00:00 2001 From: Matteo Gobbo Date: Sat, 20 Dec 2025 12:33:48 +0100 Subject: [PATCH 3/4] fix: update error message in tests --- test/integration/test/client/client.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/test/client/client.test.ts b/test/integration/test/client/client.test.ts index 5574a2d84..9b9095431 100644 --- a/test/integration/test/client/client.test.ts +++ b/test/integration/test/client/client.test.ts @@ -645,7 +645,7 @@ test('should respect client notification capabilities', async () => { await clientWithoutCapability.connect(clientTransport); // This should throw because the client doesn't have the roots.listChanged capability - await expect(clientWithoutCapability.sendRootsListChanged()).rejects.toThrow(/^Client does not support/); + await expect(clientWithoutCapability.sendRootsListChanged()).rejects.toThrow(/^MCP error -32043: Client does not support/); }); /*** From 78bb55bacf0d4dfbf9328c017d62c692611af926 Mon Sep 17 00:00:00 2001 From: Matteo Gobbo Date: Sat, 20 Dec 2025 12:41:28 +0100 Subject: [PATCH 4/4] style: lint errors --- packages/client/src/client/client.ts | 4 +++- packages/core/src/experimental/tasks/helpers.ts | 14 ++++++++++---- packages/core/src/types/types.ts | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/client/src/client/client.ts b/packages/client/src/client/client.ts index 957b9782a..3fa948aba 100644 --- a/packages/client/src/client/client.ts +++ b/packages/client/src/client/client.ts @@ -616,7 +616,9 @@ export class Client< switch (method as ClientNotification['method']) { case 'notifications/roots/list_changed': if (!this._capabilities.roots?.listChanged) { - throw new UnsupportedCapabilityError(`Client does not support roots list changed notifications (required for ${method})`); + throw new UnsupportedCapabilityError( + `Client does not support roots list changed notifications (required for ${method})` + ); } break; diff --git a/packages/core/src/experimental/tasks/helpers.ts b/packages/core/src/experimental/tasks/helpers.ts index 69f9fc6ec..eee5b04d1 100644 --- a/packages/core/src/experimental/tasks/helpers.ts +++ b/packages/core/src/experimental/tasks/helpers.ts @@ -5,7 +5,7 @@ * @experimental */ -import { UnsupportedCapabilityError } from "../../types/types.js"; +import { UnsupportedCapabilityError } from '../../types/types.js'; /** * Type representing the task requests capability structure. @@ -40,7 +40,9 @@ export function assertToolsCallTaskCapability( switch (method) { case 'tools/call': if (!requests.tools?.call) { - throw new UnsupportedCapabilityError(`${entityName} does not support task creation for tools/call (required for ${method})`); + throw new UnsupportedCapabilityError( + `${entityName} does not support task creation for tools/call (required for ${method})` + ); } break; @@ -73,13 +75,17 @@ export function assertClientRequestTaskCapability( switch (method) { case 'sampling/createMessage': if (!requests.sampling?.createMessage) { - throw new UnsupportedCapabilityError(`${entityName} does not support task creation for sampling/createMessage (required for ${method})`); + throw new UnsupportedCapabilityError( + `${entityName} does not support task creation for sampling/createMessage (required for ${method})` + ); } break; case 'elicitation/create': if (!requests.elicitation?.create) { - throw new UnsupportedCapabilityError(`${entityName} does not support task creation for elicitation/create (required for ${method})`); + throw new UnsupportedCapabilityError( + `${entityName} does not support task creation for elicitation/create (required for ${method})` + ); } break; diff --git a/packages/core/src/types/types.ts b/packages/core/src/types/types.ts index 80e1df913..f3b8217c1 100644 --- a/packages/core/src/types/types.ts +++ b/packages/core/src/types/types.ts @@ -239,7 +239,7 @@ export enum ErrorCode { // MCP-specific error codes UrlElicitationRequired = -32042, - UnsupportedCapability = -32043 + UnsupportedCapability = -32043 } /**