Skip to content

Commit d816a20

Browse files
committed
feat: add extensions field to client and server capabilities (backport #1630)
Backport of #1630 to v1.x branch. Adds the `extensions` field to `ClientCapabilities` and `ServerCapabilities` to allow servers and clients to advertise extension support in the capability object, per SEP-2133. Adapted from the main branch implementation to use v1.x types: - Uses `object` type in spec.types.ts (matching existing `experimental` field) - Uses `AssertObjectSchema` in Zod schemas instead of `JSONObjectSchema` :house: Remote-Dev: homespace
1 parent 7213816 commit d816a20

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

src/spec.types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,10 @@ export interface ClientCapabilities {
383383
};
384384
};
385385
};
386+
/**
387+
* Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
388+
*/
389+
extensions?: { [key: string]: object };
386390
}
387391

388392
/**
@@ -461,6 +465,10 @@ export interface ServerCapabilities {
461465
};
462466
};
463467
};
468+
/**
469+
* Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
470+
*/
471+
extensions?: { [key: string]: object };
464472
}
465473

466474
/**

src/types.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,11 @@ export const ClientCapabilitiesSchema = z.object({
511511
/**
512512
* Present if the client supports task creation.
513513
*/
514-
tasks: ClientTasksCapabilitySchema.optional()
514+
tasks: ClientTasksCapabilitySchema.optional(),
515+
/**
516+
* Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
517+
*/
518+
extensions: z.record(z.string(), AssertObjectSchema).optional()
515519
});
516520

517521
export const InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
@@ -589,7 +593,11 @@ export const ServerCapabilitiesSchema = z.object({
589593
/**
590594
* Present if the server supports task creation.
591595
*/
592-
tasks: ServerTasksCapabilitySchema.optional()
596+
tasks: ServerTasksCapabilitySchema.optional(),
597+
/**
598+
* Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
599+
*/
600+
extensions: z.record(z.string(), AssertObjectSchema).optional()
593601
});
594602

595603
/**

test/server/mcp.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,60 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
198198
message: 'Completed step 3 of 3'
199199
});
200200
});
201+
202+
/***
203+
* Test: Extensions capability registration
204+
*/
205+
test('should register and advertise server extensions capability', async () => {
206+
const mcpServer = new McpServer({
207+
name: 'test server',
208+
version: '1.0'
209+
});
210+
const client = new Client({
211+
name: 'test client',
212+
version: '1.0'
213+
});
214+
215+
mcpServer.server.registerCapabilities({
216+
extensions: {
217+
'io.modelcontextprotocol/test-extension': { listChanged: true }
218+
}
219+
});
220+
221+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
222+
await Promise.all([client.connect(clientTransport), mcpServer.connect(serverTransport)]);
223+
224+
const capabilities = client.getServerCapabilities();
225+
expect(capabilities?.extensions).toBeDefined();
226+
expect(capabilities?.extensions?.['io.modelcontextprotocol/test-extension']).toEqual({ listChanged: true });
227+
});
228+
229+
test('should advertise client extensions capability to server', async () => {
230+
const mcpServer = new McpServer({
231+
name: 'test server',
232+
version: '1.0'
233+
});
234+
const client = new Client(
235+
{
236+
name: 'test client',
237+
version: '1.0'
238+
},
239+
{
240+
capabilities: {
241+
extensions: {
242+
'io.modelcontextprotocol/test-extension': { streaming: true }
243+
}
244+
}
245+
}
246+
);
247+
248+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
249+
await Promise.all([client.connect(clientTransport), mcpServer.connect(serverTransport)]);
250+
251+
const capabilities = mcpServer.server.getClientCapabilities();
252+
expect(capabilities?.extensions).toBeDefined();
253+
expect(capabilities?.extensions?.['io.modelcontextprotocol/test-extension']).toEqual({ streaming: true });
254+
});
201255
});
202256

203257
describe('ResourceTemplate', () => {

0 commit comments

Comments
 (0)