Skip to content
16 changes: 12 additions & 4 deletions packages/server/src/server/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,8 @@ export class McpServer {
name,
title: prompt.title,
description: prompt.description,
arguments: prompt.argsSchema ? promptArgumentsFromStandardSchema(prompt.argsSchema) : undefined
arguments: prompt.argsSchema ? promptArgumentsFromStandardSchema(prompt.argsSchema) : undefined,
_meta: prompt._meta
};
})
})
Expand Down Expand Up @@ -699,7 +700,8 @@ export class McpServer {
title: string | undefined,
description: string | undefined,
argsSchema: StandardSchemaWithJSON | undefined,
callback: PromptCallback<StandardSchemaWithJSON | undefined>
callback: PromptCallback<StandardSchemaWithJSON | undefined>,
_meta: Record<string, unknown> | undefined
): RegisteredPrompt {
// Track current schema and callback for handler regeneration
let currentArgsSchema = argsSchema;
Expand All @@ -709,6 +711,7 @@ export class McpServer {
title,
description,
argsSchema,
_meta,
handler: createPromptHandler(name, argsSchema, callback),
enabled: true,
disable: () => registeredPrompt.update({ enabled: false }),
Expand All @@ -721,6 +724,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;
Expand Down Expand Up @@ -921,21 +925,23 @@ export class McpServer {
title?: string;
description?: string;
argsSchema?: Args;
_meta?: Record<string, unknown>;
},
cb: PromptCallback<Args>
): RegisteredPrompt {
if (this._registeredPrompts[name]) {
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,
cb as PromptCallback<StandardSchemaWithJSON | undefined>
cb as PromptCallback<StandardSchemaWithJSON | undefined>,
_meta
);

this.setPromptRequestHandlers();
Expand Down Expand Up @@ -1234,6 +1240,7 @@ export type RegisteredPrompt = {
title?: string;
description?: string;
argsSchema?: StandardSchemaWithJSON;
_meta?: Record<string, unknown>;
/** @hidden */
handler: PromptHandler;
enabled: boolean;
Expand All @@ -1244,6 +1251,7 @@ export type RegisteredPrompt = {
title?: string;
description?: string;
argsSchema?: Args;
_meta?: Record<string, unknown>;
callback?: PromptCallback<Args>;
enabled?: boolean;
}): void;
Expand Down
93 changes: 93 additions & 0 deletions test/integration/test/server/mcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4251,6 +4251,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', () => {
Expand Down
Loading