From d384e41a399db82ca77212a253547046138f4123 Mon Sep 17 00:00:00 2001 From: Tapan Chugh Date: Wed, 4 Mar 2026 22:33:50 -0800 Subject: [PATCH 1/5] Add extensions field to ServerCapabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extensions currently must use the experimental field as a workaround. This adds a dedicated extensions field (record of URI → object) to ServerCapabilitiesSchema, enabling capabilities like extensions["io.modelcontextprotocol/grouping"]: { listChanged: true }. Co-Authored-By: Claude Opus 4.6 --- packages/core/src/types/types.ts | 6 +++++- test/integration/test/server/mcp.test.ts | 27 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/core/src/types/types.ts b/packages/core/src/types/types.ts index 6985154db..739f89855 100644 --- a/packages/core/src/types/types.ts +++ b/packages/core/src/types/types.ts @@ -604,7 +604,11 @@ export const ServerCapabilitiesSchema = z.object({ /** * Present if the server supports task creation. */ - tasks: ServerTasksCapabilitySchema.optional() + tasks: ServerTasksCapabilitySchema.optional(), + /** + * Extensions that the server supports. Keys are extension URIs. + */ + extensions: z.record(z.string(), AssertObjectSchema).optional() }); /** diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index 4ea04beae..bfc4383f8 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -338,6 +338,33 @@ describe('Zod v4', () => { message: 'Completed step 3 of 3' }); }); + + /*** + * Test: Extensions capability registration + */ + test('should register and advertise extensions capability', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + mcpServer.server.registerCapabilities({ + extensions: { + 'io.modelcontextprotocol/grouping': { listChanged: true } + } + }); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + await Promise.all([client.connect(clientTransport), mcpServer.connect(serverTransport)]); + + const capabilities = client.getServerCapabilities(); + expect(capabilities?.extensions).toBeDefined(); + expect(capabilities?.extensions?.['io.modelcontextprotocol/grouping']).toEqual({ listChanged: true }); + }); }); describe('ResourceTemplate', () => { From cef2b2fbee5075924e55e67519273d109c0ed651 Mon Sep 17 00:00:00 2001 From: Tapan Chugh Date: Wed, 4 Mar 2026 22:49:18 -0800 Subject: [PATCH 2/5] Fix extensions field comment to use correct SEP-2133 terminology Use "extension identifiers" (vendor-prefix/extension-name) instead of "URIs" per the SEP-2133 Extensions specification. Co-Authored-By: Claude Opus 4.6 --- packages/core/src/types/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/types/types.ts b/packages/core/src/types/types.ts index 739f89855..f0cb214a5 100644 --- a/packages/core/src/types/types.ts +++ b/packages/core/src/types/types.ts @@ -606,7 +606,7 @@ export const ServerCapabilitiesSchema = z.object({ */ tasks: ServerTasksCapabilitySchema.optional(), /** - * Extensions that the server supports. Keys are extension URIs. + * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name). */ extensions: z.record(z.string(), AssertObjectSchema).optional() }); From bc8c2a0b4be8ec10aa5121ccfe1a5f7a66c44165 Mon Sep 17 00:00:00 2001 From: Tapan Chugh Date: Wed, 4 Mar 2026 22:50:37 -0800 Subject: [PATCH 3/5] Use generic test-extension identifier in extensions capability test Co-Authored-By: Claude Opus 4.6 --- test/integration/test/server/mcp.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index bfc4383f8..01d1fe7a4 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -354,7 +354,7 @@ describe('Zod v4', () => { mcpServer.server.registerCapabilities({ extensions: { - 'io.modelcontextprotocol/grouping': { listChanged: true } + 'io.modelcontextprotocol/test-extension': { listChanged: true } } }); @@ -363,7 +363,7 @@ describe('Zod v4', () => { const capabilities = client.getServerCapabilities(); expect(capabilities?.extensions).toBeDefined(); - expect(capabilities?.extensions?.['io.modelcontextprotocol/grouping']).toEqual({ listChanged: true }); + expect(capabilities?.extensions?.['io.modelcontextprotocol/test-extension']).toEqual({ listChanged: true }); }); }); From 3dab53b2fb988c51a3767b534f26c1f894f2ca13 Mon Sep 17 00:00:00 2001 From: Tapan Chugh Date: Thu, 5 Mar 2026 09:03:01 -0800 Subject: [PATCH 4/5] Add extensions field to ClientCapabilities Addresses PR feedback: extensions need to be on both client and server capability objects per SEP-2133. Co-Authored-By: Claude Opus 4.6 --- packages/core/src/types/types.ts | 6 ++++- test/integration/test/server/mcp.test.ts | 29 +++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/core/src/types/types.ts b/packages/core/src/types/types.ts index f0cb214a5..dc0fe26a3 100644 --- a/packages/core/src/types/types.ts +++ b/packages/core/src/types/types.ts @@ -526,7 +526,11 @@ export const ClientCapabilitiesSchema = z.object({ /** * Present if the client supports task creation. */ - tasks: ClientTasksCapabilitySchema.optional() + tasks: ClientTasksCapabilitySchema.optional(), + /** + * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name). + */ + extensions: z.record(z.string(), AssertObjectSchema).optional() }); export const InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({ diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index 01d1fe7a4..b58149716 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -342,7 +342,7 @@ describe('Zod v4', () => { /*** * Test: Extensions capability registration */ - test('should register and advertise extensions capability', async () => { + test('should register and advertise server extensions capability', async () => { const mcpServer = new McpServer({ name: 'test server', version: '1.0' @@ -365,6 +365,33 @@ describe('Zod v4', () => { expect(capabilities?.extensions).toBeDefined(); expect(capabilities?.extensions?.['io.modelcontextprotocol/test-extension']).toEqual({ listChanged: true }); }); + + test('should advertise client extensions capability to server', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + const client = new Client( + { + name: 'test client', + version: '1.0' + }, + { + capabilities: { + extensions: { + 'io.modelcontextprotocol/test-extension': { streaming: true } + } + } + } + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + await Promise.all([client.connect(clientTransport), mcpServer.connect(serverTransport)]); + + const capabilities = mcpServer.server.getClientCapabilities(); + expect(capabilities?.extensions).toBeDefined(); + expect(capabilities?.extensions?.['io.modelcontextprotocol/test-extension']).toEqual({ streaming: true }); + }); }); describe('ResourceTemplate', () => { From 49fd0e7830a46696f38db542e3817a8ac20c8f2d Mon Sep 17 00:00:00 2001 From: Tapan Chugh Date: Fri, 6 Mar 2026 17:41:00 -0800 Subject: [PATCH 5/5] Fix extensions field to use JSONObjectSchema instead of removed AssertObjectSchema The merge with main brought in #1612 which replaced AssertObjectSchema with JSONObjectSchema, but the extensions fields were not updated. Also fixes spec.types.ts to use JSONObject (matching experimental) for bidirectional type compatibility. Co-Authored-By: Claude Opus 4.6 --- packages/core/src/types/spec.types.ts | 4 ++-- packages/core/src/types/types.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/types/spec.types.ts b/packages/core/src/types/spec.types.ts index f36434bef..ba849c546 100644 --- a/packages/core/src/types/spec.types.ts +++ b/packages/core/src/types/spec.types.ts @@ -586,7 +586,7 @@ export interface ClientCapabilities { * @example Extensions — UI extension with MIME type support * {@includeCode ./examples/ClientCapabilities/extensions-ui-mime-types.json} */ - extensions?: { [key: string]: object }; + extensions?: { [key: string]: JSONObject }; } /** @@ -703,7 +703,7 @@ export interface ServerCapabilities { * @example Extensions — UI extension support * {@includeCode ./examples/ServerCapabilities/extensions-ui.json} */ - extensions?: { [key: string]: object }; + extensions?: { [key: string]: JSONObject }; } /** diff --git a/packages/core/src/types/types.ts b/packages/core/src/types/types.ts index 1e44b96e5..967092d3f 100644 --- a/packages/core/src/types/types.ts +++ b/packages/core/src/types/types.ts @@ -560,7 +560,7 @@ export const ClientCapabilitiesSchema = z.object({ /** * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name). */ - extensions: z.record(z.string(), AssertObjectSchema).optional() + extensions: z.record(z.string(), JSONObjectSchema).optional() }); export const InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({ @@ -642,7 +642,7 @@ export const ServerCapabilitiesSchema = z.object({ /** * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name). */ - extensions: z.record(z.string(), AssertObjectSchema).optional() + extensions: z.record(z.string(), JSONObjectSchema).optional() }); /**