Skip to content

Commit 1cba565

Browse files
Backport: feat(packages/mcp): add support for MCP server prompts exposed (#9994)
This is an automated backport of #9957 to the release-v5.0 branch. Co-authored-by: Aayush Kapoor <[email protected]>
1 parent 78069cd commit 1cba565

File tree

11 files changed

+468
-33
lines changed

11 files changed

+468
-33
lines changed

.changeset/fast-bobcats-relate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ai-sdk/mcp': patch
3+
---
4+
5+
feat(packages/mcp): add support for MCP server prompts exposed

content/docs/03-ai-sdk-core/16-mcp-tools.mdx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ description: Learn how to connect to Model Context Protocol (MCP) servers and us
99
The MCP tools feature is experimental and may change in the future.
1010
</Note>
1111

12-
The AI SDK supports connecting to [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers to access their tools.
13-
This enables your AI applications to discover and use tools across various services through a standardized interface.
12+
The AI SDK supports connecting to [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers to access their tools, resources, and prompts.
13+
This enables your AI applications to discover and use capabilities across various services through a standardized interface.
1414

1515
## Initializing an MCP Client
1616

@@ -226,6 +226,27 @@ Resource templates are dynamic URI patterns that allow flexible queries. List al
226226
const templates = await mcpClient.listResourceTemplates();
227227
```
228228

229+
## Using MCP Prompts
230+
231+
According to the MCP specification, prompts are user-controlled templates that servers expose for clients to list and retrieve with optional arguments.
232+
233+
### Listing Prompts
234+
235+
```typescript
236+
const prompts = await mcpClient.listPrompts();
237+
```
238+
239+
### Getting a Prompt
240+
241+
Retrieve prompt messages, optionally passing arguments defined by the server:
242+
243+
```typescript
244+
const prompt = await mcpClient.getPrompt({
245+
name: 'code_review',
246+
arguments: { code: 'function add(a, b) { return a + b; }' },
247+
});
248+
```
249+
229250
## Examples
230251

231252
You can see MCP tools in action in the following example:

content/docs/07-reference/01-ai-sdk-core/23-create-mcp-client.mdx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Creates a lightweight Model Context Protocol (MCP) client that connects to an MC
99

1010
- **Tools**: Automatic conversion between MCP tools and AI SDK tools
1111
- **Resources**: Methods to list, read, and discover resource templates from MCP servers
12+
- **Prompts**: Methods to list available prompts and retrieve prompt messages
1213

1314
It currently does not support accepting notifications from an MCP server, and custom configuration of the client.
1415

@@ -235,6 +236,68 @@ Returns a Promise that resolves to an `MCPClient` with the following methods:
235236
},
236237
],
237238
},
239+
{
240+
name: 'listPrompts',
241+
type: `async (options?: {
242+
params?: PaginatedRequest['params'];
243+
options?: RequestOptions;
244+
}) => Promise<ListPromptsResult>`,
245+
description: 'Lists available prompts from the MCP server.',
246+
properties: [
247+
{
248+
type: 'options',
249+
parameters: [
250+
{
251+
name: 'params',
252+
type: "PaginatedRequest['params']",
253+
isOptional: true,
254+
description: 'Optional pagination parameters including cursor.',
255+
},
256+
{
257+
name: 'options',
258+
type: 'RequestOptions',
259+
isOptional: true,
260+
description:
261+
'Optional request options including signal and timeout.',
262+
},
263+
],
264+
},
265+
],
266+
},
267+
{
268+
name: 'getPrompt',
269+
type: `async (args: {
270+
name: string;
271+
arguments?: Record<string, unknown>;
272+
options?: RequestOptions;
273+
}) => Promise<GetPromptResult>`,
274+
description: 'Retrieves a prompt by name, optionally passing arguments.',
275+
properties: [
276+
{
277+
type: 'args',
278+
parameters: [
279+
{
280+
name: 'name',
281+
type: 'string',
282+
description: 'Prompt name to retrieve.',
283+
},
284+
{
285+
name: 'arguments',
286+
type: 'Record<string, unknown>',
287+
isOptional: true,
288+
description: 'Optional arguments to fill into the prompt.',
289+
},
290+
{
291+
name: 'options',
292+
type: 'RequestOptions',
293+
isOptional: true,
294+
description:
295+
'Optional request options including signal and timeout.',
296+
},
297+
],
298+
},
299+
],
300+
},
238301
{
239302
name: 'close',
240303
type: 'async () => void',

examples/mcp/README.md

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,38 @@ pnpm install
1818
pnpm build
1919
```
2020

21-
## Streamable HTTP Transport (Stateful)
21+
## Running Examples
2222

23-
Start server
23+
Start the server for a specific example
2424

2525
```sh
26-
pnpm http:server
26+
pnpm server:<folder-name>
2727
```
2828

29-
Run example:
29+
Run the client for a specific example
3030

3131
```sh
32-
pnpm http:client
32+
pnpm client:<folder-name>
3333
```
3434

35-
## Stdio Transport
35+
Available examples/folders:
3636

37-
Build
37+
- `sse` - SSE Transport (Legacy)
38+
- `http` - Streamable HTTP Transport (Stateful)
39+
- `mcp-with-auth` - MCP with authentication
40+
- `mcp-prompts` - MCP prompts example
41+
- `mcp-resources` - MCP resources example
42+
- `stdio` - Stdio Transport (requires `pnpm stdio:build` first)
3843

39-
```sh
40-
pnpm stdio:build
41-
```
42-
43-
Run example:
44-
45-
```sh
46-
pnpm stdio:client
47-
```
48-
49-
## SSE Transport (Legacy)
50-
51-
Start server
44+
Example usage:
5245

5346
```sh
54-
pnpm sse:server
47+
# Start the HTTP server
48+
pnpm server:http
5549
```
5650

57-
Run example:
51+
In another terminal, run the HTTP client:
5852

5953
```sh
60-
pnpm sse:client
54+
pnpm client:http
6155
```

examples/mcp/package.json

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
"version": "0.0.0",
44
"private": true,
55
"scripts": {
6-
"sse:server": "tsx src/sse/server.ts",
7-
"sse:client": "tsx src/sse/client.ts",
8-
"sse-auth:server": "tsx src/mcp-with-auth/server.ts",
9-
"sse-auth:client": "tsx src/mcp-with-auth/client.ts",
6+
"server:sse": "tsx src/sse/server.ts",
7+
"client:sse": "tsx src/sse/client.ts",
8+
"server:http": "tsx src/http/server.ts",
9+
"client:http": "tsx src/http/client.ts",
10+
"server:mcp-with-auth": "tsx src/mcp-with-auth/server.ts",
11+
"client:mcp-with-auth": "tsx src/mcp-with-auth/client.ts",
12+
"server:mcp-prompts": "tsx src/mcp-prompts/server.ts",
13+
"client:mcp-prompts": "tsx src/mcp-prompts/client.ts",
14+
"server:mcp-resources": "tsx src/mcp-resources/server.ts",
15+
"client:mcp-resources": "tsx src/mcp-resources/client.ts",
1016
"stdio:build": "tsc src/stdio/server.ts --outDir src/stdio/dist --target es2023 --module nodenext",
11-
"stdio:client": "tsx src/stdio/client.ts",
12-
"http:server": "tsx src/http/server.ts",
13-
"http:client": "tsx src/http/client.ts",
17+
"client:stdio": "tsx src/stdio/client.ts",
1418
"custom-transport:build": "tsc src/custom-transport/server.ts --outDir src/custom-transport/dist --target es2023 --module nodenext",
1519
"custom-transport:client": "tsx src/custom-transport/client.ts",
1620
"type-check": "tsc --build"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { experimental_createMCPClient } from '@ai-sdk/mcp';
2+
3+
async function main() {
4+
const mcpClient = await experimental_createMCPClient({
5+
transport: {
6+
type: 'sse',
7+
url: 'http://localhost:8083/sse',
8+
},
9+
});
10+
11+
try {
12+
const prompts = await mcpClient.listPrompts();
13+
console.log('PROMPTS:', JSON.stringify(prompts, null, 2));
14+
15+
const prompt = await mcpClient.getPrompt({
16+
name: 'code_review',
17+
arguments: {
18+
code: 'function add(a, b) { return a + b; }\n',
19+
},
20+
});
21+
console.log('GET PROMPT:', JSON.stringify(prompt, null, 2));
22+
} finally {
23+
await mcpClient.close();
24+
}
25+
}
26+
27+
main().catch(err => {
28+
console.error(err);
29+
process.exit(1);
30+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
3+
import express from 'express';
4+
import { z } from 'zod';
5+
6+
const app = express();
7+
8+
const server = new McpServer({
9+
name: 'mcp-prompts-example',
10+
version: '1.0.0',
11+
});
12+
13+
server.prompt(
14+
'code_review',
15+
'Asks the LLM to analyze code quality and suggest improvements',
16+
{ code: z.string() },
17+
async ({ code }) => {
18+
return {
19+
description: 'Code review prompt',
20+
messages: [
21+
{
22+
role: 'user',
23+
content: {
24+
type: 'text',
25+
text: `Please review this code and suggest improvements:\n${code}`,
26+
},
27+
},
28+
],
29+
};
30+
},
31+
);
32+
33+
let transport: SSEServerTransport;
34+
35+
app.get('/sse', async (_req, res) => {
36+
transport = new SSEServerTransport('/messages', res);
37+
await server.connect(transport);
38+
});
39+
40+
app.post('/messages', async (req, res) => {
41+
await transport.handlePostMessage(req, res);
42+
});
43+
44+
app.listen(8083, () => {
45+
console.log('MCP prompts example server listening on http://localhost:8083');
46+
});

packages/mcp/src/tool/mcp-client.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
ListResourceTemplatesResult,
88
ListResourcesResult,
99
ReadResourceResult,
10+
ListPromptsResult,
11+
GetPromptResult,
1012
} from './types';
1113
import {
1214
beforeEach,
@@ -148,6 +150,80 @@ describe('MCPClient', () => {
148150
`);
149151
});
150152

153+
it('should list prompts from the server', async () => {
154+
client = await createMCPClient({
155+
transport: { type: 'sse', url: 'https://example.com/sse' },
156+
});
157+
158+
const prompts = await client.listPrompts();
159+
160+
expectTypeOf(prompts).toEqualTypeOf<ListPromptsResult>();
161+
162+
expect(prompts.prompts).toMatchInlineSnapshot(`
163+
[
164+
{
165+
"arguments": [
166+
{
167+
"description": "The code to review",
168+
"name": "code",
169+
"required": true,
170+
},
171+
],
172+
"description": "Asks the LLM to analyze code quality and suggest improvements",
173+
"name": "code_review",
174+
"title": "Request Code Review",
175+
},
176+
]
177+
`);
178+
});
179+
180+
it('should get a prompt by name', async () => {
181+
client = await createMCPClient({
182+
transport: { type: 'sse', url: 'https://example.com/sse' },
183+
});
184+
185+
const prompt = await client.getPrompt({
186+
name: 'code_review',
187+
arguments: { code: 'print(42)' },
188+
});
189+
190+
expectTypeOf(prompt).toEqualTypeOf<GetPromptResult>();
191+
192+
expect(prompt).toMatchInlineSnapshot(`
193+
{
194+
"description": "Code review prompt",
195+
"messages": [
196+
{
197+
"content": {
198+
"text": "Please review this code:\nfunction add(a, b) { return a + b; }",
199+
"type": "text",
200+
},
201+
"role": "user",
202+
},
203+
],
204+
}
205+
`);
206+
});
207+
208+
it('should throw if the server does not support prompts', async () => {
209+
createMockTransport.mockImplementation(
210+
() =>
211+
new MockMCPTransport({
212+
resources: [],
213+
prompts: [],
214+
}),
215+
);
216+
217+
client = await createMCPClient({
218+
transport: { type: 'sse', url: 'https://example.com/sse' },
219+
});
220+
221+
await expect(client.listPrompts()).rejects.toThrow(MCPClientError);
222+
await expect(client.getPrompt({ name: 'code_review' })).rejects.toThrow(
223+
MCPClientError,
224+
);
225+
});
226+
151227
it('should return typed AI SDK compatible tool set when schemas are provided', async () => {
152228
const mockTransport = new MockMCPTransport({
153229
overrideTools: [

0 commit comments

Comments
 (0)