Skip to content

Commit 40c4d6d

Browse files
chughtapanclaude
andcommitted
Use extensions capability, add prompt grouping
- Switch capability registration from experimental to extensions - Add prompt registrations with group membership to example server - Add prompt grouping to mixed-group test - Update README capability section Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5119772 commit 40c4d6d

4 files changed

Lines changed: 139 additions & 11 deletions

File tree

sdk/typescript/README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Organize MCP tools, resources, and other primitives into named groups. This package implements the [Grouping Extension specification](../../specification/draft/grouping.mdx) as an add-on for the
44
[`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk).
55

6-
> **Status:** Experimental. The capability is registered under `experimental["io.modelcontextprotocol/grouping"]` until the SDK ships the `extensions` field on `ServerCapabilities`.
6+
> **Status:** Experimental. This is an Interest Group exploration, not an official extension.
77
88
## Install
99

@@ -197,16 +197,14 @@ The extension registers its capability at:
197197

198198
```json
199199
{
200-
"experimental": {
200+
"extensions": {
201201
"io.modelcontextprotocol/grouping": {
202202
"listChanged": true
203203
}
204204
}
205205
}
206206
```
207207

208-
When the MCP SDK adds the `extensions` field to `ServerCapabilities`, this will move to `capabilities.extensions["io.modelcontextprotocol/grouping"]`.
209-
210208
## Running the examples
211209

212210
```bash

sdk/typescript/examples/server.ts

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,117 @@ mcpServer.registerResource(
353353
}
354354
);
355355

356-
// TODO: add prompts once SDK registerPrompt supports _meta passthrough
356+
// ===== Prompts =====
357+
358+
mcpServer.registerPrompt(
359+
'email_thank_contributor',
360+
{
361+
description: 'Compose an email thanking someone for their recent contributions.',
362+
argsSchema: {
363+
name: z.string().describe('Recipient name')
364+
},
365+
...metaForGroup('email')
366+
},
367+
async ({ name }) => ({
368+
messages: [
369+
{
370+
role: 'user' as const,
371+
content: {
372+
type: 'text' as const,
373+
text: `Compose an email thanking ${name} for their recent contributions. Keep it warm, specific, and concise.`
374+
}
375+
}
376+
]
377+
})
378+
);
379+
380+
mcpServer.registerPrompt(
381+
'calendar_meeting_agenda',
382+
{
383+
description: 'Draft a short agenda for an upcoming meeting.',
384+
argsSchema: {
385+
topic: z.string().describe('Meeting topic')
386+
},
387+
...metaForGroup('calendar')
388+
},
389+
async ({ topic }) => ({
390+
messages: [
391+
{
392+
role: 'user' as const,
393+
content: {
394+
type: 'text' as const,
395+
text: `Draft a short meeting agenda for a meeting about: ${topic}. Include goals, timeboxes, and expected outcomes.`
396+
}
397+
}
398+
]
399+
})
400+
);
401+
402+
mcpServer.registerPrompt(
403+
'spreadsheets_quick_analysis',
404+
{
405+
description: 'Suggest a simple spreadsheet layout for tracking a metric.',
406+
argsSchema: {
407+
metric: z.string().describe('The metric to track')
408+
},
409+
...metaForGroup('spreadsheets')
410+
},
411+
async ({ metric }) => ({
412+
messages: [
413+
{
414+
role: 'user' as const,
415+
content: {
416+
type: 'text' as const,
417+
text: `Suggest a simple spreadsheet layout for tracking: ${metric}. Include column headers and a brief note on how to use it.`
418+
}
419+
}
420+
]
421+
})
422+
);
423+
424+
mcpServer.registerPrompt(
425+
'documents_write_outline',
426+
{
427+
description: 'Create an outline for a document on a topic.',
428+
argsSchema: {
429+
topic: z.string().describe('Document topic')
430+
},
431+
...metaForGroup('documents')
432+
},
433+
async ({ topic }) => ({
434+
messages: [
435+
{
436+
role: 'user' as const,
437+
content: {
438+
type: 'text' as const,
439+
text: `Create a clear outline for a document about: ${topic}. Use headings and a short description under each heading.`
440+
}
441+
}
442+
]
443+
})
444+
);
445+
446+
mcpServer.registerPrompt(
447+
'todos_plan_day',
448+
{
449+
description: 'Turn a list of tasks into a simple day plan.',
450+
argsSchema: {
451+
tasks: z.array(z.string()).describe('Tasks to plan')
452+
},
453+
...metaForGroup('todos')
454+
},
455+
async ({ tasks }) => ({
456+
messages: [
457+
{
458+
role: 'user' as const,
459+
content: {
460+
type: 'text' as const,
461+
text: `Create a simple plan for the day from these tasks:\n- ${tasks.join('\n- ')}\n\nPrioritize and group similar tasks.`
462+
}
463+
}
464+
]
465+
})
466+
);
357467

358468
// --- Start server ---
359469
async function main() {

sdk/typescript/src/server.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ export interface RegisteredGroup {
3939
/**
4040
* Extension that adds group support to an McpServer.
4141
*
42-
* Registers `groups/list` request handler and capability via the `experimental`
43-
* path (until the SDK ships the `extensions` field).
42+
* Registers `groups/list` request handler and capability under `extensions`.
4443
*/
4544
export class GroupingExtension {
4645
private _groups = new Map<string, RegisteredGroup>();
@@ -56,7 +55,7 @@ export class GroupingExtension {
5655
if (this._handlersInitialized) return;
5756

5857
this._mcpServer.server.registerCapabilities({
59-
experimental: {
58+
extensions: {
6059
[GROUPING_EXTENSION_ID]: { listChanged: true }
6160
}
6261
});

sdk/typescript/test/groups.test.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,21 @@ describe('Server Groups', () => {
113113
content: [{ type: 'text', text: 'hi' }]
114114
}));
115115

116-
// TODO: add prompt grouping once SDK registerPrompt supports _meta passthrough
116+
// Add a prompt to the same group
117+
mcpServer.registerPrompt(
118+
'prompt1',
119+
{
120+
description: 'Test prompt 1',
121+
_meta: {
122+
[GROUPS_META_KEY]: ['mixed-group']
123+
}
124+
},
125+
async () => ({ messages: [] })
126+
);
127+
128+
mcpServer.registerPrompt('prompt-no-group', { description: 'Prompt with no group' }, async () => ({
129+
messages: []
130+
}));
117131

118132
// Add a resource to the same group
119133
mcpServer.registerResource(
@@ -137,8 +151,6 @@ describe('Server Groups', () => {
137151
})
138152
);
139153

140-
// TODO: add task-tool grouping once SDK experimental.tasks is available
141-
142154
await Promise.all([client.connect(clientTransport), mcpServer.connect(serverTransport)]);
143155

144156
// Verify tools
@@ -161,6 +173,15 @@ describe('Server Groups', () => {
161173
if (resourceNoGroup?._meta) {
162174
expect(resourceNoGroup._meta).not.toHaveProperty(GROUPS_META_KEY);
163175
}
176+
177+
// Verify prompts
178+
const promptsResult = await client.listPrompts();
179+
const prompt1 = promptsResult.prompts.find(p => p.name === 'prompt1');
180+
const promptNoGroup = promptsResult.prompts.find(p => p.name === 'prompt-no-group');
181+
expect(prompt1?._meta?.[GROUPS_META_KEY]).toEqual(['mixed-group']);
182+
if (promptNoGroup?._meta) {
183+
expect(promptNoGroup._meta).not.toHaveProperty(GROUPS_META_KEY);
184+
}
164185
});
165186

166187
test('should add a group to another group', async () => {

0 commit comments

Comments
 (0)