diff --git a/packages/server/src/server/mcp.ts b/packages/server/src/server/mcp.ts index 316074e2d..4f9fd6d35 100644 --- a/packages/server/src/server/mcp.ts +++ b/packages/server/src/server/mcp.ts @@ -538,7 +538,8 @@ export class McpServer { name, title: prompt.title, description: prompt.description, - arguments: prompt.argsSchema ? promptArgumentsFromSchema(prompt.argsSchema) : undefined + arguments: prompt.argsSchema ? promptArgumentsFromSchema(prompt.argsSchema) : undefined, + _meta: prompt._meta }; }) }) @@ -707,6 +708,7 @@ export class McpServer { title: string | undefined, description: string | undefined, argsSchema: AnySchema | undefined, + _meta: Record | undefined, callback: PromptCallback ): RegisteredPrompt { // Track current schema and callback for handler regeneration @@ -717,6 +719,7 @@ export class McpServer { title, description, argsSchema, + _meta, handler: createPromptHandler(name, argsSchema, callback), enabled: true, disable: () => registeredPrompt.update({ enabled: false }), @@ -729,6 +732,7 @@ export class McpServer { } if (updates.title !== undefined) registeredPrompt.title = updates.title; if (updates.description !== undefined) registeredPrompt.description = updates.description; + if (updates._meta !== undefined) registeredPrompt._meta = updates._meta; // Track if we need to regenerate the handler let needsHandlerRegen = false; @@ -929,6 +933,7 @@ export class McpServer { title?: string; description?: string; argsSchema?: Args; + _meta?: Record; }, cb: PromptCallback ): RegisteredPrompt { @@ -936,13 +941,14 @@ export class McpServer { throw new Error(`Prompt ${name} is already registered`); } - const { title, description, argsSchema } = config; + const { title, description, argsSchema, _meta } = config; const registeredPrompt = this._createRegisteredPrompt( name, title, description, argsSchema, + _meta, cb as PromptCallback ); @@ -1231,6 +1237,7 @@ export type RegisteredPrompt = { title?: string; description?: string; argsSchema?: AnySchema; + _meta?: Record; /** @hidden */ handler: PromptHandler; enabled: boolean; @@ -1241,6 +1248,7 @@ export type RegisteredPrompt = { title?: string; description?: string; argsSchema?: Args; + _meta?: Record; callback?: PromptCallback; enabled?: boolean; }): void; diff --git a/test/integration/test/server/mcp.test.ts b/test/integration/test/server/mcp.test.ts index 4ea04beae..68b572108 100644 --- a/test/integration/test/server/mcp.test.ts +++ b/test/integration/test/server/mcp.test.ts @@ -4113,6 +4113,99 @@ describe('Zod v4', () => { } ]); }); + + /*** + * Test: Prompt Registration with _meta field + */ + test('should register prompt with _meta field and include it in list response', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + const metaData = { + author: 'test-author', + version: '1.2.3', + category: 'utility', + tags: ['test', 'example'] + }; + + mcpServer.registerPrompt( + 'test-with-meta', + { + description: 'A prompt with _meta field', + _meta: metaData + }, + async () => ({ + messages: [ + { + role: 'assistant', + content: { + type: 'text', + text: 'Test response' + } + } + ] + }) + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + const result = await client.request({ method: 'prompts/list' }); + + expect(result.prompts).toHaveLength(1); + expect(result.prompts[0]!.name).toBe('test-with-meta'); + expect(result.prompts[0]!.description).toBe('A prompt with _meta field'); + expect(result.prompts[0]!._meta).toEqual(metaData); + }); + + /*** + * Test: Prompt Registration without _meta field should have undefined _meta + */ + test('should register prompt without _meta field and have undefined _meta in response', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + mcpServer.registerPrompt( + 'test-without-meta', + { + description: 'A prompt without _meta field' + }, + async () => ({ + messages: [ + { + role: 'assistant', + content: { + type: 'text', + text: 'Test response' + } + } + ] + }) + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + const result = await client.request({ method: 'prompts/list' }); + + expect(result.prompts).toHaveLength(1); + expect(result.prompts[0]!.name).toBe('test-without-meta'); + expect(result.prompts[0]!._meta).toBeUndefined(); + }); }); describe('Tool title precedence', () => {