@mastra/mcp 1.0.0-beta.6 → 1.0.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/dist/docs/README.md +33 -0
- package/dist/docs/SKILL.md +34 -0
- package/dist/docs/SOURCE_MAP.json +6 -0
- package/dist/docs/mcp/01-overview.md +362 -0
- package/dist/docs/mcp/02-publishing-mcp-server.md +111 -0
- package/dist/docs/tools/01-reference.md +2005 -0
- package/dist/docs/tools-mcp/01-mcp-overview.md +376 -0
- package/dist/index.cjs +34 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +34 -6
- package/dist/index.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1,2005 @@
|
|
|
1
|
+
# Tools API Reference
|
|
2
|
+
|
|
3
|
+
> API reference for tools - 3 entries
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Reference: MCPClient
|
|
9
|
+
|
|
10
|
+
> API Reference for MCPClient - A class for managing multiple Model Context Protocol servers and their tools.
|
|
11
|
+
|
|
12
|
+
The `MCPClient` class provides a way to manage multiple MCP server connections and their tools in a Mastra application. It handles connection lifecycle, tool namespacing, and provides access to tools across all configured servers.
|
|
13
|
+
|
|
14
|
+
This class replaces the deprecated [`MastraMCPClient`](https://mastra.ai/reference/v1/tools/client).
|
|
15
|
+
|
|
16
|
+
## Constructor
|
|
17
|
+
|
|
18
|
+
Creates a new instance of the MCPClient class.
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
constructor({
|
|
22
|
+
id?: string;
|
|
23
|
+
servers: Record<string, MastraMCPServerDefinition>;
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}: MCPClientOptions)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### MCPClientOptions
|
|
29
|
+
|
|
30
|
+
<br />
|
|
31
|
+
|
|
32
|
+
### MastraMCPServerDefinition
|
|
33
|
+
|
|
34
|
+
Each server in the `servers` map is configured using the `MastraMCPServerDefinition` type. The transport type is detected based on the provided parameters:
|
|
35
|
+
|
|
36
|
+
- If `command` is provided, it uses the Stdio transport.
|
|
37
|
+
- If `url` is provided, it first attempts to use the Streamable HTTP transport and falls back to the legacy SSE transport if the initial connection fails.
|
|
38
|
+
|
|
39
|
+
<br />
|
|
40
|
+
|
|
41
|
+
## Methods
|
|
42
|
+
|
|
43
|
+
### listTools()
|
|
44
|
+
|
|
45
|
+
Retrieves all tools from all configured servers, with tool names namespaced by their server name (in the format `serverName_toolName`) to prevent conflicts.
|
|
46
|
+
Intended to be passed onto an Agent definition.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
new Agent({ tools: await mcp.listTools() });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### listToolsets()
|
|
53
|
+
|
|
54
|
+
Returns an object mapping namespaced tool names (in the format `serverName.toolName`) to their tool implementations.
|
|
55
|
+
Intended to be passed dynamically into the generate or stream method.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
const res = await agent.stream(prompt, {
|
|
59
|
+
toolsets: await mcp.listToolsets(),
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### disconnect()
|
|
64
|
+
|
|
65
|
+
Disconnects from all MCP servers and cleans up resources.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
async disconnect(): Promise<void>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `resources` Property
|
|
72
|
+
|
|
73
|
+
The `MCPClient` instance has a `resources` property that provides access to resource-related operations.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const mcpClient = new MCPClient({
|
|
77
|
+
/* ...servers configuration... */
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Access resource methods via mcpClient.resources
|
|
81
|
+
const allResourcesByServer = await mcpClient.resources.list();
|
|
82
|
+
const templatesByServer = await mcpClient.resources.templates();
|
|
83
|
+
// ... and so on for other resource methods.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### `resources.list()`
|
|
87
|
+
|
|
88
|
+
Retrieves all available resources from all connected MCP servers, grouped by server name.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
async list(): Promise<Record<string, Resource[]>>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const resourcesByServer = await mcpClient.resources.list();
|
|
98
|
+
for (const serverName in resourcesByServer) {
|
|
99
|
+
console.log(`Resources from ${serverName}:`, resourcesByServer[serverName]);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### `resources.templates()`
|
|
104
|
+
|
|
105
|
+
Retrieves all available resource templates from all connected MCP servers, grouped by server name.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
async templates(): Promise<Record<string, ResourceTemplate[]>>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Example:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const templatesByServer = await mcpClient.resources.templates();
|
|
115
|
+
for (const serverName in templatesByServer) {
|
|
116
|
+
console.log(`Templates from ${serverName}:`, templatesByServer[serverName]);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### `resources.read(serverName: string, uri: string)`
|
|
121
|
+
|
|
122
|
+
Reads the content of a specific resource from a named server.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
async read(serverName: string, uri: string): Promise<ReadResourceResult>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- `serverName`: The identifier of the server (key used in the `servers` constructor option).
|
|
129
|
+
- `uri`: The URI of the resource to read.
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const content = await mcpClient.resources.read(
|
|
135
|
+
"myWeatherServer",
|
|
136
|
+
"weather://current",
|
|
137
|
+
);
|
|
138
|
+
console.log("Current weather:", content.contents[0].text);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `resources.subscribe(serverName: string, uri: string)`
|
|
142
|
+
|
|
143
|
+
Subscribes to updates for a specific resource on a named server.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
async subscribe(serverName: string, uri: string): Promise<object>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Example:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
await mcpClient.resources.subscribe("myWeatherServer", "weather://current");
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### `resources.unsubscribe(serverName: string, uri: string)`
|
|
156
|
+
|
|
157
|
+
Unsubscribes from updates for a specific resource on a named server.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
async unsubscribe(serverName: string, uri: string): Promise<object>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Example:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
await mcpClient.resources.unsubscribe("myWeatherServer", "weather://current");
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### `resources.onUpdated(serverName: string, handler: (params: { uri: string }) => void)`
|
|
170
|
+
|
|
171
|
+
Sets a notification handler that will be called when a subscribed resource on a specific server is updated.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
async onUpdated(serverName: string, handler: (params: { uri: string }) => void): Promise<void>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Example:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
mcpClient.resources.onUpdated("myWeatherServer", (params) => {
|
|
181
|
+
console.log(`Resource updated on myWeatherServer: ${params.uri}`);
|
|
182
|
+
// You might want to re-fetch the resource content here
|
|
183
|
+
// await mcpClient.resources.read("myWeatherServer", params.uri);
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### `resources.onListChanged(serverName: string, handler: () => void)`
|
|
188
|
+
|
|
189
|
+
Sets a notification handler that will be called when the overall list of available resources changes on a specific server.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
async onListChanged(serverName: string, handler: () => void): Promise<void>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Example:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
mcpClient.resources.onListChanged("myWeatherServer", () => {
|
|
199
|
+
console.log("Resource list changed on myWeatherServer.");
|
|
200
|
+
// You should re-fetch the list of resources
|
|
201
|
+
// await mcpClient.resources.list();
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `elicitation` Property
|
|
206
|
+
|
|
207
|
+
The `MCPClient` instance has an `elicitation` property that provides access to elicitation-related operations. Elicitation allows MCP servers to request structured information from users.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const mcpClient = new MCPClient({
|
|
211
|
+
/* ...servers configuration... */
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Set up elicitation handler
|
|
215
|
+
mcpClient.elicitation.onRequest("serverName", async (request) => {
|
|
216
|
+
// Handle elicitation request from server
|
|
217
|
+
console.log("Server requests:", request.message);
|
|
218
|
+
console.log("Schema:", request.requestedSchema);
|
|
219
|
+
|
|
220
|
+
// Return user response
|
|
221
|
+
return {
|
|
222
|
+
action: "accept",
|
|
223
|
+
content: { name: "John Doe", email: "john@example.com" },
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### `elicitation.onRequest(serverName: string, handler: ElicitationHandler)`
|
|
229
|
+
|
|
230
|
+
Sets up a handler function that will be called when any connected MCP server sends an elicitation request. The handler receives the request and must return a response.
|
|
231
|
+
|
|
232
|
+
**ElicitationHandler Function:**
|
|
233
|
+
|
|
234
|
+
The handler function receives a request object with:
|
|
235
|
+
|
|
236
|
+
- `message`: A human-readable message describing what information is needed
|
|
237
|
+
- `requestedSchema`: A JSON schema defining the structure of the expected response
|
|
238
|
+
|
|
239
|
+
The handler must return an `ElicitResult` with:
|
|
240
|
+
|
|
241
|
+
- `action`: One of `'accept'`, `'decline'`, or `'cancel'`
|
|
242
|
+
- `content`: The user's data (only when action is `'accept'`)
|
|
243
|
+
|
|
244
|
+
**Example:**
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
mcpClient.elicitation.onRequest("serverName", async (request) => {
|
|
248
|
+
console.log(`Server requests: ${request.message}`);
|
|
249
|
+
|
|
250
|
+
// Example: Simple user input collection
|
|
251
|
+
if (request.requestedSchema.properties.name) {
|
|
252
|
+
// Simulate user accepting and providing data
|
|
253
|
+
return {
|
|
254
|
+
action: "accept",
|
|
255
|
+
content: {
|
|
256
|
+
name: "Alice Smith",
|
|
257
|
+
email: "alice@example.com",
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Simulate user declining the request
|
|
263
|
+
return { action: "decline" };
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Complete Interactive Example:**
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import { MCPClient } from "@mastra/mcp";
|
|
271
|
+
import { createInterface } from "readline";
|
|
272
|
+
|
|
273
|
+
const readline = createInterface({
|
|
274
|
+
input: process.stdin,
|
|
275
|
+
output: process.stdout,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
function askQuestion(question: string): Promise<string> {
|
|
279
|
+
return new Promise((resolve) => {
|
|
280
|
+
readline.question(question, (answer) => resolve(answer.trim()));
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const mcpClient = new MCPClient({
|
|
285
|
+
servers: {
|
|
286
|
+
interactiveServer: {
|
|
287
|
+
url: new URL("http://localhost:3000/mcp"),
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Set up interactive elicitation handler
|
|
293
|
+
await mcpClient.elicitation.onRequest("interactiveServer", async (request) => {
|
|
294
|
+
console.log(`\n📋 Server Request: ${request.message}`);
|
|
295
|
+
console.log("Required information:");
|
|
296
|
+
|
|
297
|
+
const schema = request.requestedSchema;
|
|
298
|
+
const properties = schema.properties || {};
|
|
299
|
+
const required = schema.required || [];
|
|
300
|
+
const content: Record<string, any> = {};
|
|
301
|
+
|
|
302
|
+
// Collect input for each field
|
|
303
|
+
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
304
|
+
const field = fieldSchema as any;
|
|
305
|
+
const isRequired = required.includes(fieldName);
|
|
306
|
+
|
|
307
|
+
let prompt = `${field.title || fieldName}`;
|
|
308
|
+
if (field.description) prompt += ` (${field.description})`;
|
|
309
|
+
if (isRequired) prompt += " *required*";
|
|
310
|
+
prompt += ": ";
|
|
311
|
+
|
|
312
|
+
const answer = await askQuestion(prompt);
|
|
313
|
+
|
|
314
|
+
// Handle cancellation
|
|
315
|
+
if (answer.toLowerCase() === "cancel") {
|
|
316
|
+
return { action: "cancel" };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Validate required fields
|
|
320
|
+
if (answer === "" && isRequired) {
|
|
321
|
+
console.log(`❌ ${fieldName} is required`);
|
|
322
|
+
return { action: "decline" };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (answer !== "") {
|
|
326
|
+
content[fieldName] = answer;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Confirm submission
|
|
331
|
+
console.log("\n📝 You provided:");
|
|
332
|
+
console.log(JSON.stringify(content, null, 2));
|
|
333
|
+
|
|
334
|
+
const confirm = await askQuestion(
|
|
335
|
+
"\nSubmit this information? (yes/no/cancel): ",
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
if (confirm.toLowerCase() === "yes" || confirm.toLowerCase() === "y") {
|
|
339
|
+
return { action: "accept", content };
|
|
340
|
+
} else if (confirm.toLowerCase() === "cancel") {
|
|
341
|
+
return { action: "cancel" };
|
|
342
|
+
} else {
|
|
343
|
+
return { action: "decline" };
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### `prompts` Property
|
|
349
|
+
|
|
350
|
+
The `MCPClient` instance has a `prompts` property that provides access to prompt-related operations.
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
const mcpClient = new MCPClient({
|
|
354
|
+
/* ...servers configuration... */
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Access prompt methods via mcpClient.prompts
|
|
358
|
+
const allPromptsByServer = await mcpClient.prompts.list();
|
|
359
|
+
const { prompt, messages } = await mcpClient.prompts.get({
|
|
360
|
+
serverName: "myWeatherServer",
|
|
361
|
+
name: "current",
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### `prompts.list()`
|
|
366
|
+
|
|
367
|
+
Retrieves all available prompts from all connected MCP servers, grouped by server name.
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
async list(): Promise<Record<string, Prompt[]>>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Example:
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
const promptsByServer = await mcpClient.prompts.list();
|
|
377
|
+
for (const serverName in promptsByServer) {
|
|
378
|
+
console.log(`Prompts from ${serverName}:`, promptsByServer[serverName]);
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
#### `prompts.get({ serverName, name, args?, version? })`
|
|
383
|
+
|
|
384
|
+
Retrieves a specific prompt and its messages from a server.
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
async get({
|
|
388
|
+
serverName,
|
|
389
|
+
name,
|
|
390
|
+
args?,
|
|
391
|
+
version?,
|
|
392
|
+
}: {
|
|
393
|
+
serverName: string;
|
|
394
|
+
name: string;
|
|
395
|
+
args?: Record<string, any>;
|
|
396
|
+
version?: string;
|
|
397
|
+
}): Promise<{ prompt: Prompt; messages: PromptMessage[] }>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Example:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const { prompt, messages } = await mcpClient.prompts.get({
|
|
404
|
+
serverName: "myWeatherServer",
|
|
405
|
+
name: "current",
|
|
406
|
+
args: { location: "London" },
|
|
407
|
+
});
|
|
408
|
+
console.log(prompt);
|
|
409
|
+
console.log(messages);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### `prompts.onListChanged(serverName: string, handler: () => void)`
|
|
413
|
+
|
|
414
|
+
Sets a notification handler that will be called when the list of available prompts changes on a specific server.
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
async onListChanged(serverName: string, handler: () => void): Promise<void>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Example:
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
mcpClient.prompts.onListChanged("myWeatherServer", () => {
|
|
424
|
+
console.log("Prompt list changed on myWeatherServer.");
|
|
425
|
+
// You should re-fetch the list of prompts
|
|
426
|
+
// await mcpClient.prompts.list();
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### `progress` Property
|
|
431
|
+
|
|
432
|
+
The `MCPClient` instance has a `progress` property for subscribing to progress notifications emitted by MCP servers while tools execute.
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
const mcpClient = new MCPClient({
|
|
436
|
+
servers: {
|
|
437
|
+
myServer: {
|
|
438
|
+
url: new URL('http://localhost:4111/api/mcp/myServer/mcp'),
|
|
439
|
+
// Enabled by default; set to false to disable
|
|
440
|
+
enableProgressTracking: true,
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Subscribe to progress updates for a specific server
|
|
446
|
+
await mcpClient.progress.onUpdate('myServer', (params) => {
|
|
447
|
+
console.log('📊 Progress:', params.progress, '/', params.total);
|
|
448
|
+
if (params.message) console.log('Message:', params.message);
|
|
449
|
+
if (params.progressToken) console.log('Token:', params.progressToken);
|
|
450
|
+
});
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### `progress.onUpdate(serverName: string, handler)`
|
|
454
|
+
|
|
455
|
+
Registers a handler function to receive progress updates from the specified server.
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
async onUpdate(
|
|
459
|
+
serverName: string,
|
|
460
|
+
handler: (params: {
|
|
461
|
+
progressToken: string;
|
|
462
|
+
progress: number;
|
|
463
|
+
total?: number;
|
|
464
|
+
message?: string;
|
|
465
|
+
}) => void,
|
|
466
|
+
): Promise<void>
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Notes:
|
|
470
|
+
|
|
471
|
+
- When `enableProgressTracking` is true (default), tool calls include a `progressToken` so you can correlate updates to a specific run.
|
|
472
|
+
- If you pass a `runId` when executing a tool, it will be used as the `progressToken`.
|
|
473
|
+
|
|
474
|
+
To disable progress tracking for a server:
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
const mcpClient = new MCPClient({
|
|
478
|
+
servers: {
|
|
479
|
+
myServer: {
|
|
480
|
+
url: new URL('http://localhost:4111/api/mcp/myServer/mcp'),
|
|
481
|
+
enableProgressTracking: false,
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Elicitation
|
|
488
|
+
|
|
489
|
+
Elicitation is a feature that allows MCP servers to request structured information from users. When a server needs additional data, it can send an elicitation request that the client handles by prompting the user. A common example is during a tool call.
|
|
490
|
+
|
|
491
|
+
### How Elicitation Works
|
|
492
|
+
|
|
493
|
+
1. **Server Request**: An MCP server tool calls `server.elicitation.sendRequest()` with a message and schema
|
|
494
|
+
2. **Client Handler**: Your elicitation handler function is called with the request
|
|
495
|
+
3. **User Interaction**: Your handler collects user input (via UI, CLI, etc.)
|
|
496
|
+
4. **Response**: Your handler returns the user's response (accept/decline/cancel)
|
|
497
|
+
5. **Tool Continuation**: The server tool receives the response and continues execution
|
|
498
|
+
|
|
499
|
+
### Setting Up Elicitation
|
|
500
|
+
|
|
501
|
+
You must set up an elicitation handler before tools that use elicitation are called:
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
import { MCPClient } from "@mastra/mcp";
|
|
505
|
+
|
|
506
|
+
const mcpClient = new MCPClient({
|
|
507
|
+
servers: {
|
|
508
|
+
interactiveServer: {
|
|
509
|
+
url: new URL("http://localhost:3000/mcp"),
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// Set up elicitation handler
|
|
515
|
+
mcpClient.elicitation.onRequest("interactiveServer", async (request) => {
|
|
516
|
+
// Handle the server's request for user input
|
|
517
|
+
console.log(`Server needs: ${request.message}`);
|
|
518
|
+
|
|
519
|
+
// Your logic to collect user input
|
|
520
|
+
const userData = await collectUserInput(request.requestedSchema);
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
action: "accept",
|
|
524
|
+
content: userData,
|
|
525
|
+
};
|
|
526
|
+
});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Response Types
|
|
530
|
+
|
|
531
|
+
Your elicitation handler must return one of three response types:
|
|
532
|
+
|
|
533
|
+
- **Accept**: User provided data and confirmed submission
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
return {
|
|
537
|
+
action: "accept",
|
|
538
|
+
content: { name: "John Doe", email: "john@example.com" },
|
|
539
|
+
};
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
- **Decline**: User explicitly declined to provide the information
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
return { action: "decline" };
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
- **Cancel**: User dismissed or cancelled the request
|
|
549
|
+
```typescript
|
|
550
|
+
return { action: "cancel" };
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### Schema-Based Input Collection
|
|
554
|
+
|
|
555
|
+
The `requestedSchema` provides structure for the data the server needs:
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
await mcpClient.elicitation.onRequest("interactiveServer", async (request) => {
|
|
559
|
+
const { properties, required = [] } = request.requestedSchema;
|
|
560
|
+
const content: Record<string, any> = {};
|
|
561
|
+
|
|
562
|
+
for (const [fieldName, fieldSchema] of Object.entries(properties || {})) {
|
|
563
|
+
const field = fieldSchema as any;
|
|
564
|
+
const isRequired = required.includes(fieldName);
|
|
565
|
+
|
|
566
|
+
// Collect input based on field type and requirements
|
|
567
|
+
const value = await promptUser({
|
|
568
|
+
name: fieldName,
|
|
569
|
+
title: field.title,
|
|
570
|
+
description: field.description,
|
|
571
|
+
type: field.type,
|
|
572
|
+
required: isRequired,
|
|
573
|
+
format: field.format,
|
|
574
|
+
enum: field.enum,
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
if (value !== null) {
|
|
578
|
+
content[fieldName] = value;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return { action: "accept", content };
|
|
583
|
+
});
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Best Practices
|
|
587
|
+
|
|
588
|
+
- **Always handle elicitation**: Set up your handler before calling tools that might use elicitation
|
|
589
|
+
- **Validate input**: Check that required fields are provided
|
|
590
|
+
- **Respect user choice**: Handle decline and cancel responses gracefully
|
|
591
|
+
- **Clear UI**: Make it obvious what information is being requested and why
|
|
592
|
+
- **Security**: Never auto-accept requests for sensitive information
|
|
593
|
+
|
|
594
|
+
## Examples
|
|
595
|
+
|
|
596
|
+
### Static Tool Configuration
|
|
597
|
+
|
|
598
|
+
For tools where you have a single connection to the MCP server for you entire app, use `listTools()` and pass the tools to your agent:
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
import { MCPClient } from "@mastra/mcp";
|
|
602
|
+
import { Agent } from "@mastra/core/agent";
|
|
603
|
+
|
|
604
|
+
const mcp = new MCPClient({
|
|
605
|
+
servers: {
|
|
606
|
+
stockPrice: {
|
|
607
|
+
command: "npx",
|
|
608
|
+
args: ["tsx", "stock-price.ts"],
|
|
609
|
+
env: {
|
|
610
|
+
API_KEY: "your-api-key",
|
|
611
|
+
},
|
|
612
|
+
log: (logMessage) => {
|
|
613
|
+
console.log(`[${logMessage.level}] ${logMessage.message}`);
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
weather: {
|
|
617
|
+
url: new URL("http://localhost:8080/sse"),
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
timeout: 30000, // Global 30s timeout
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
// Create an agent with access to all tools
|
|
624
|
+
const agent = new Agent({
|
|
625
|
+
name: "Multi-tool Agent",
|
|
626
|
+
instructions: "You have access to multiple tool servers.",
|
|
627
|
+
model: "openai/gpt-5.1",
|
|
628
|
+
tools: await mcp.listTools(),
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Example of using resource methods
|
|
632
|
+
async function checkWeatherResource() {
|
|
633
|
+
try {
|
|
634
|
+
const weatherResources = await mcp.resources.list();
|
|
635
|
+
if (weatherResources.weather && weatherResources.weather.length > 0) {
|
|
636
|
+
const currentWeatherURI = weatherResources.weather[0].uri;
|
|
637
|
+
const weatherData = await mcp.resources.read(
|
|
638
|
+
"weather",
|
|
639
|
+
currentWeatherURI,
|
|
640
|
+
);
|
|
641
|
+
console.log("Weather data:", weatherData.contents[0].text);
|
|
642
|
+
}
|
|
643
|
+
} catch (error) {
|
|
644
|
+
console.error("Error fetching weather resource:", error);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
checkWeatherResource();
|
|
648
|
+
|
|
649
|
+
// Example of using prompt methods
|
|
650
|
+
async function checkWeatherPrompt() {
|
|
651
|
+
try {
|
|
652
|
+
const weatherPrompts = await mcp.prompts.list();
|
|
653
|
+
if (weatherPrompts.weather && weatherPrompts.weather.length > 0) {
|
|
654
|
+
const currentWeatherPrompt = weatherPrompts.weather.find(
|
|
655
|
+
(p) => p.name === "current",
|
|
656
|
+
);
|
|
657
|
+
if (currentWeatherPrompt) {
|
|
658
|
+
console.log("Weather prompt:", currentWeatherPrompt);
|
|
659
|
+
} else {
|
|
660
|
+
console.log("Current weather prompt not found");
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
} catch (error) {
|
|
664
|
+
console.error("Error fetching weather prompt:", error);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
checkWeatherPrompt();
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### Dynamic toolsets
|
|
671
|
+
|
|
672
|
+
When you need a new MCP connection for each user, use `listToolsets()` and add the tools when calling stream or generate:
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
import { Agent } from "@mastra/core/agent";
|
|
676
|
+
import { MCPClient } from "@mastra/mcp";
|
|
677
|
+
|
|
678
|
+
// Create the agent first, without any tools
|
|
679
|
+
const agent = new Agent({
|
|
680
|
+
name: "Multi-tool Agent",
|
|
681
|
+
instructions: "You help users check stocks and weather.",
|
|
682
|
+
model: "openai/gpt-5.1",
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
// Later, configure MCP with user-specific settings
|
|
686
|
+
const mcp = new MCPClient({
|
|
687
|
+
servers: {
|
|
688
|
+
stockPrice: {
|
|
689
|
+
command: "npx",
|
|
690
|
+
args: ["tsx", "stock-price.ts"],
|
|
691
|
+
env: {
|
|
692
|
+
API_KEY: "user-123-api-key",
|
|
693
|
+
},
|
|
694
|
+
timeout: 20000, // Server-specific timeout
|
|
695
|
+
},
|
|
696
|
+
weather: {
|
|
697
|
+
url: new URL("http://localhost:8080/sse"),
|
|
698
|
+
requestInit: {
|
|
699
|
+
headers: {
|
|
700
|
+
Authorization: `Bearer user-123-token`,
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
// Pass all toolsets to stream() or generate()
|
|
708
|
+
const response = await agent.stream(
|
|
709
|
+
"How is AAPL doing and what is the weather?",
|
|
710
|
+
{
|
|
711
|
+
toolsets: await mcp.listToolsets(),
|
|
712
|
+
},
|
|
713
|
+
);
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
## Instance Management
|
|
717
|
+
|
|
718
|
+
The `MCPClient` class includes built-in memory leak prevention for managing multiple instances:
|
|
719
|
+
|
|
720
|
+
1. Creating multiple instances with identical configurations without an `id` will throw an error to prevent memory leaks
|
|
721
|
+
2. If you need multiple instances with identical configurations, provide a unique `id` for each instance
|
|
722
|
+
3. Call `await configuration.disconnect()` before recreating an instance with the same configuration
|
|
723
|
+
4. If you only need one instance, consider moving the configuration to a higher scope to avoid recreation
|
|
724
|
+
|
|
725
|
+
For example, if you try to create multiple instances with the same configuration without an `id`:
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
// First instance - OK
|
|
729
|
+
const mcp1 = new MCPClient({
|
|
730
|
+
servers: {
|
|
731
|
+
/* ... */
|
|
732
|
+
},
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
// Second instance with same config - Will throw an error
|
|
736
|
+
const mcp2 = new MCPClient({
|
|
737
|
+
servers: {
|
|
738
|
+
/* ... */
|
|
739
|
+
},
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
// To fix, either:
|
|
743
|
+
// 1. Add unique IDs
|
|
744
|
+
const mcp3 = new MCPClient({
|
|
745
|
+
id: "instance-1",
|
|
746
|
+
servers: {
|
|
747
|
+
/* ... */
|
|
748
|
+
},
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
// 2. Or disconnect before recreating
|
|
752
|
+
await mcp1.disconnect();
|
|
753
|
+
const mcp4 = new MCPClient({
|
|
754
|
+
servers: {
|
|
755
|
+
/* ... */
|
|
756
|
+
},
|
|
757
|
+
});
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
## Server Lifecycle
|
|
761
|
+
|
|
762
|
+
MCPClient handles server connections gracefully:
|
|
763
|
+
|
|
764
|
+
1. Automatic connection management for multiple servers
|
|
765
|
+
2. Graceful server shutdown to prevent error messages during development
|
|
766
|
+
3. Proper cleanup of resources when disconnecting
|
|
767
|
+
|
|
768
|
+
## Using Custom Fetch for Dynamic Authentication
|
|
769
|
+
|
|
770
|
+
For HTTP servers, you can provide a custom `fetch` function to handle dynamic authentication, request interception, or other custom behavior. This is particularly useful when you need to refresh tokens on each request or customize request behavior.
|
|
771
|
+
|
|
772
|
+
When `fetch` is provided, `requestInit`, `eventSourceInit`, and `authProvider` become optional, as you can handle these concerns within your custom fetch function.
|
|
773
|
+
|
|
774
|
+
```typescript
|
|
775
|
+
const mcpClient = new MCPClient({
|
|
776
|
+
servers: {
|
|
777
|
+
apiServer: {
|
|
778
|
+
url: new URL("https://api.example.com/mcp"),
|
|
779
|
+
fetch: async (url, init) => {
|
|
780
|
+
// Refresh token on each request
|
|
781
|
+
const token = await getAuthToken(); // Your token refresh logic
|
|
782
|
+
|
|
783
|
+
return fetch(url, {
|
|
784
|
+
...init,
|
|
785
|
+
headers: {
|
|
786
|
+
...init?.headers,
|
|
787
|
+
Authorization: `Bearer ${token}`,
|
|
788
|
+
},
|
|
789
|
+
});
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
},
|
|
793
|
+
});
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
## Using SSE Request Headers
|
|
797
|
+
|
|
798
|
+
When using the legacy SSE MCP transport, you must configure both `requestInit` and `eventSourceInit` due to a bug in the MCP SDK. Alternatively, you can use a custom `fetch` function which will be automatically used for both POST requests and SSE connections:
|
|
799
|
+
|
|
800
|
+
```ts
|
|
801
|
+
// Option 1: Using requestInit and eventSourceInit (required for SSE)
|
|
802
|
+
const sseClient = new MCPClient({
|
|
803
|
+
servers: {
|
|
804
|
+
exampleServer: {
|
|
805
|
+
url: new URL("https://your-mcp-server.com/sse"),
|
|
806
|
+
// Note: requestInit alone isn't enough for SSE
|
|
807
|
+
requestInit: {
|
|
808
|
+
headers: {
|
|
809
|
+
Authorization: "Bearer your-token",
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
// This is also required for SSE connections with custom headers
|
|
813
|
+
eventSourceInit: {
|
|
814
|
+
fetch(input: Request | URL | string, init?: RequestInit) {
|
|
815
|
+
const headers = new Headers(init?.headers || {});
|
|
816
|
+
headers.set("Authorization", "Bearer your-token");
|
|
817
|
+
return fetch(input, {
|
|
818
|
+
...init,
|
|
819
|
+
headers,
|
|
820
|
+
});
|
|
821
|
+
},
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
// Option 2: Using custom fetch (simpler, works for both Streamable HTTP and SSE)
|
|
828
|
+
const sseClientWithFetch = new MCPClient({
|
|
829
|
+
servers: {
|
|
830
|
+
exampleServer: {
|
|
831
|
+
url: new URL("https://your-mcp-server.com/sse"),
|
|
832
|
+
fetch: async (url, init) => {
|
|
833
|
+
const headers = new Headers(init?.headers || {});
|
|
834
|
+
headers.set("Authorization", "Bearer your-token");
|
|
835
|
+
return fetch(url, {
|
|
836
|
+
...init,
|
|
837
|
+
headers,
|
|
838
|
+
});
|
|
839
|
+
},
|
|
840
|
+
},
|
|
841
|
+
},
|
|
842
|
+
});
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
## Related Information
|
|
846
|
+
|
|
847
|
+
- For creating MCP servers, see the [MCPServer documentation](./mcp-server).
|
|
848
|
+
- For more about the Model Context Protocol, see the [@modelcontextprotocol/sdk documentation](https://github.com/modelcontextprotocol/typescript-sdk).
|
|
849
|
+
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## Reference: MCPServer
|
|
853
|
+
|
|
854
|
+
> API Reference for MCPServer - A class for exposing Mastra tools and capabilities as a Model Context Protocol server.
|
|
855
|
+
|
|
856
|
+
The `MCPServer` class provides the functionality to expose your existing Mastra tools and Agents as a Model Context Protocol (MCP) server. This allows any MCP client (like Cursor, Windsurf, or Claude Desktop) to connect to these capabilities and make them available to an agent.
|
|
857
|
+
|
|
858
|
+
Note that if you only need to use your tools or agents directly within your Mastra application, you don't necessarily need to create an MCP server. This API is specifically for exposing your Mastra tools and agents to _external_ MCP clients.
|
|
859
|
+
|
|
860
|
+
It supports both [stdio (subprocess) and SSE (HTTP) MCP transports](https://modelcontextprotocol.io/docs/concepts/transports).
|
|
861
|
+
|
|
862
|
+
## Constructor
|
|
863
|
+
|
|
864
|
+
To create a new `MCPServer`, you need to provide some basic information about your server, the tools it will offer, and optionally, any agents you want to expose as tools.
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
import { Agent } from "@mastra/core/agent";
|
|
868
|
+
import { createTool } from "@mastra/core/tools";
|
|
869
|
+
import { MCPServer } from "@mastra/mcp";
|
|
870
|
+
import { z } from "zod";
|
|
871
|
+
import { dataProcessingWorkflow } from "../workflows/dataProcessingWorkflow";
|
|
872
|
+
|
|
873
|
+
const myAgent = new Agent({
|
|
874
|
+
id: "my-example-agent",
|
|
875
|
+
name: "MyExampleAgent",
|
|
876
|
+
description: "A generalist to help with basic questions."
|
|
877
|
+
instructions: "You are a helpful assistant.",
|
|
878
|
+
model: "openai/gpt-5.1",
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
const weatherTool = createTool({
|
|
882
|
+
id: "getWeather",
|
|
883
|
+
description: "Gets the current weather for a location.",
|
|
884
|
+
inputSchema: z.object({ location: z.string() }),
|
|
885
|
+
execute: async (inputData) => `Weather in ${inputData.location} is sunny.`,
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
const server = new MCPServer({
|
|
889
|
+
id: "my-custom-server",
|
|
890
|
+
name: "My Custom Server",
|
|
891
|
+
version: "1.0.0",
|
|
892
|
+
description: "A server that provides weather data and agent capabilities",
|
|
893
|
+
instructions: "Use the available tools to help users with weather information and data processing tasks.",
|
|
894
|
+
tools: { weatherTool },
|
|
895
|
+
agents: { myAgent }, // this agent will become tool "ask_myAgent"
|
|
896
|
+
workflows: {
|
|
897
|
+
dataProcessingWorkflow, // this workflow will become tool "run_dataProcessingWorkflow"
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### Configuration Properties
|
|
903
|
+
|
|
904
|
+
The constructor accepts an `MCPServerConfig` object with the following properties:
|
|
905
|
+
|
|
906
|
+
## Exposing Agents as Tools
|
|
907
|
+
|
|
908
|
+
A powerful feature of `MCPServer` is its ability to automatically expose your Mastra Agents as callable tools. When you provide agents in the `agents` property of the configuration:
|
|
909
|
+
|
|
910
|
+
- **Tool Naming**: Each agent is converted into a tool named `ask_<agentKey>`, where `<agentKey>` is the key you used for that agent in the `agents` object. For instance, if you configure `agents: { myAgentKey: myAgentInstance }`, a tool named `ask_myAgentKey` will be created.
|
|
911
|
+
|
|
912
|
+
- **Tool Functionality**:
|
|
913
|
+
- **Description**: The generated tool's description will be in the format: "Ask agent `<AgentName>` a question. Original agent instructions: `<agent description>`".
|
|
914
|
+
- **Input**: The tool expects a single object argument with a `message` property (string): `{ message: "Your question for the agent" }`.
|
|
915
|
+
- **Execution**: When this tool is called, it invokes the `generate()` method of the corresponding agent, passing the provided `query`.
|
|
916
|
+
- **Output**: The direct result from the agent's `generate()` method is returned as the output of the tool.
|
|
917
|
+
|
|
918
|
+
- **Name Collisions**: If an explicit tool defined in the `tools` configuration has the same name as an agent-derived tool (e.g., you have a tool named `ask_myAgentKey` and also an agent with the key `myAgentKey`), the _explicitly defined tool will take precedence_. The agent will not be converted into a tool in this conflicting case, and a warning will be logged.
|
|
919
|
+
|
|
920
|
+
This makes it straightforward to allow MCP clients to interact with your agents using natural language queries, just like any other tool.
|
|
921
|
+
|
|
922
|
+
### Agent-to-Tool Conversion
|
|
923
|
+
|
|
924
|
+
When you provide agents in the `agents` configuration property, `MCPServer` will automatically create a corresponding tool for each agent. The tool will be named `ask_<agentIdentifier>`, where `<agentIdentifier>` is the key you used in the `agents` object.
|
|
925
|
+
|
|
926
|
+
The description for this generated tool will be: "Ask agent `<agent.name>` a question. Agent description: `<agent.description>`".
|
|
927
|
+
|
|
928
|
+
**Important**: For an agent to be converted into a tool, it **must** have a non-empty `description` string property set in its configuration when it was instantiated (e.g., `new Agent({ name: 'myAgent', description: 'This agent does X.', ... })`). If an agent is passed to `MCPServer` with a missing or empty `description`, an error will be thrown when the `MCPServer` is instantiated, and server setup will fail.
|
|
929
|
+
|
|
930
|
+
This allows you to quickly expose the generative capabilities of your agents through the MCP, enabling clients to "ask" your agents questions directly.
|
|
931
|
+
|
|
932
|
+
### Accessing MCP Context in Tools
|
|
933
|
+
|
|
934
|
+
Tools exposed through `MCPServer` can access MCP request context (authentication, session IDs, etc.) via two different properties depending on how the tool is invoked:
|
|
935
|
+
|
|
936
|
+
| Call Pattern | Access Method |
|
|
937
|
+
|-------------|---------------|
|
|
938
|
+
| Direct tool call | `context?.mcp?.extra` |
|
|
939
|
+
| Agent tool call | `context?.requestContext?.get("mcp.extra")` |
|
|
940
|
+
|
|
941
|
+
**Universal pattern** (works in both contexts):
|
|
942
|
+
```typescript
|
|
943
|
+
const mcpExtra = context?.mcp?.extra ?? context?.requestContext?.get("mcp.extra");
|
|
944
|
+
const authInfo = mcpExtra?.authInfo;
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
#### Example: Tool that works in both contexts
|
|
948
|
+
|
|
949
|
+
```typescript
|
|
950
|
+
import { createTool } from "@mastra/core/tools";
|
|
951
|
+
import { z } from "zod";
|
|
952
|
+
|
|
953
|
+
const fetchUserData = createTool({
|
|
954
|
+
id: "fetchUserData",
|
|
955
|
+
description: "Fetches user data using authentication from MCP context",
|
|
956
|
+
inputSchema: z.object({
|
|
957
|
+
userId: z.string().describe("The ID of the user to fetch"),
|
|
958
|
+
}),
|
|
959
|
+
execute: async (inputData, context) => {
|
|
960
|
+
// Access MCP authentication context
|
|
961
|
+
// When called directly via MCP: context.mcp.extra
|
|
962
|
+
// When called via agent: context.requestContext.get('mcp.extra')
|
|
963
|
+
const mcpExtra = context?.mcp?.extra || context?.requestContext?.get("mcp.extra");
|
|
964
|
+
const authInfo = mcpExtra?.authInfo;
|
|
965
|
+
|
|
966
|
+
if (!authInfo?.token) {
|
|
967
|
+
throw new Error("Authentication required");
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const response = await fetch(`https://api.example.com/users/${inputData.userId}`, {
|
|
971
|
+
headers: {
|
|
972
|
+
Authorization: `Bearer ${authInfo.token}`,
|
|
973
|
+
},
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
return response.json();
|
|
977
|
+
},
|
|
978
|
+
});
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
## Methods
|
|
982
|
+
|
|
983
|
+
These are the functions you can call on an `MCPServer` instance to control its behavior and get information.
|
|
984
|
+
|
|
985
|
+
### startStdio()
|
|
986
|
+
|
|
987
|
+
Use this method to start the server so it communicates using standard input and output (stdio). This is typical when running the server as a command-line program.
|
|
988
|
+
|
|
989
|
+
```typescript
|
|
990
|
+
async startStdio(): Promise<void>
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
Here's how you would start the server using stdio:
|
|
994
|
+
|
|
995
|
+
```typescript
|
|
996
|
+
const server = new MCPServer({
|
|
997
|
+
id: "my-server",
|
|
998
|
+
name: "My Server",
|
|
999
|
+
version: "1.0.0",
|
|
1000
|
+
tools: { /* ... */ },
|
|
1001
|
+
});
|
|
1002
|
+
await server.startStdio();
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### startSSE()
|
|
1006
|
+
|
|
1007
|
+
This method helps you integrate the MCP server with an existing web server to use Server-Sent Events (SSE) for communication. You'll call this from your web server's code when it receives a request for the SSE or message paths.
|
|
1008
|
+
|
|
1009
|
+
```typescript
|
|
1010
|
+
async startSSE({
|
|
1011
|
+
url,
|
|
1012
|
+
ssePath,
|
|
1013
|
+
messagePath,
|
|
1014
|
+
req,
|
|
1015
|
+
res,
|
|
1016
|
+
}: {
|
|
1017
|
+
url: URL;
|
|
1018
|
+
ssePath: string;
|
|
1019
|
+
messagePath: string;
|
|
1020
|
+
req: any;
|
|
1021
|
+
res: any;
|
|
1022
|
+
}): Promise<void>
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
Here's an example of how you might use `startSSE` within an HTTP server request handler. In this example an MCP client could connect to your MCP server at `http://localhost:1234/sse`:
|
|
1026
|
+
|
|
1027
|
+
```typescript
|
|
1028
|
+
import http from "http";
|
|
1029
|
+
|
|
1030
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
1031
|
+
await server.startSSE({
|
|
1032
|
+
url: new URL(req.url || "", `http://localhost:1234`),
|
|
1033
|
+
ssePath: "/sse",
|
|
1034
|
+
messagePath: "/message",
|
|
1035
|
+
req,
|
|
1036
|
+
res,
|
|
1037
|
+
});
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
httpServer.listen(PORT, () => {
|
|
1041
|
+
console.log(`HTTP server listening on port ${PORT}`);
|
|
1042
|
+
});
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
Here are the details for the values needed by the `startSSE` method:
|
|
1046
|
+
|
|
1047
|
+
### startHonoSSE()
|
|
1048
|
+
|
|
1049
|
+
This method helps you integrate the MCP server with an existing web server to use Server-Sent Events (SSE) for communication. You'll call this from your web server's code when it receives a request for the SSE or message paths.
|
|
1050
|
+
|
|
1051
|
+
```typescript
|
|
1052
|
+
async startHonoSSE({
|
|
1053
|
+
url,
|
|
1054
|
+
ssePath,
|
|
1055
|
+
messagePath,
|
|
1056
|
+
req,
|
|
1057
|
+
res,
|
|
1058
|
+
}: {
|
|
1059
|
+
url: URL;
|
|
1060
|
+
ssePath: string;
|
|
1061
|
+
messagePath: string;
|
|
1062
|
+
req: any;
|
|
1063
|
+
res: any;
|
|
1064
|
+
}): Promise<void>
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
Here's an example of how you might use `startHonoSSE` within an HTTP server request handler. In this example an MCP client could connect to your MCP server at `http://localhost:1234/hono-sse`:
|
|
1068
|
+
|
|
1069
|
+
```typescript
|
|
1070
|
+
import http from "http";
|
|
1071
|
+
|
|
1072
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
1073
|
+
await server.startHonoSSE({
|
|
1074
|
+
url: new URL(req.url || "", `http://localhost:1234`),
|
|
1075
|
+
ssePath: "/hono-sse",
|
|
1076
|
+
messagePath: "/message",
|
|
1077
|
+
req,
|
|
1078
|
+
res,
|
|
1079
|
+
});
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
httpServer.listen(PORT, () => {
|
|
1083
|
+
console.log(`HTTP server listening on port ${PORT}`);
|
|
1084
|
+
});
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
Here are the details for the values needed by the `startHonoSSE` method:
|
|
1088
|
+
|
|
1089
|
+
### startHTTP()
|
|
1090
|
+
|
|
1091
|
+
This method helps you integrate the MCP server with an existing web server to use streamable HTTP for communication. You'll call this from your web server's code when it receives HTTP requests.
|
|
1092
|
+
|
|
1093
|
+
```typescript
|
|
1094
|
+
async startHTTP({
|
|
1095
|
+
url,
|
|
1096
|
+
httpPath,
|
|
1097
|
+
req,
|
|
1098
|
+
res,
|
|
1099
|
+
options = { sessionIdGenerator: () => randomUUID() },
|
|
1100
|
+
}: {
|
|
1101
|
+
url: URL;
|
|
1102
|
+
httpPath: string;
|
|
1103
|
+
req: http.IncomingMessage;
|
|
1104
|
+
res: http.ServerResponse<http.IncomingMessage>;
|
|
1105
|
+
options?: StreamableHTTPServerTransportOptions;
|
|
1106
|
+
}): Promise<void>
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
Here's an example of how you might use `startHTTP` within an HTTP server request handler. In this example an MCP client could connect to your MCP server at `http://localhost:1234/http`:
|
|
1110
|
+
|
|
1111
|
+
```typescript
|
|
1112
|
+
import http from "http";
|
|
1113
|
+
|
|
1114
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
1115
|
+
await server.startHTTP({
|
|
1116
|
+
url: new URL(req.url || "", "http://localhost:1234"),
|
|
1117
|
+
httpPath: `/mcp`,
|
|
1118
|
+
req,
|
|
1119
|
+
res,
|
|
1120
|
+
options: {
|
|
1121
|
+
sessionIdGenerator: () => randomUUID(),
|
|
1122
|
+
},
|
|
1123
|
+
});
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
httpServer.listen(PORT, () => {
|
|
1127
|
+
console.log(`HTTP server listening on port ${PORT}`);
|
|
1128
|
+
});
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
For **serverless environments** (Supabase Edge Functions, Cloudflare Workers, Vercel Edge, etc.), use `serverless: true` to enable stateless operation:
|
|
1132
|
+
|
|
1133
|
+
```typescript
|
|
1134
|
+
// Supabase Edge Function example
|
|
1135
|
+
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
|
|
1136
|
+
import { MCPServer } from "@mastra/mcp";
|
|
1137
|
+
// Note: You will need to convert req/res format from Deno to Node
|
|
1138
|
+
import { toReqRes, toFetchResponse } from "fetch-to-node";
|
|
1139
|
+
|
|
1140
|
+
const server = new MCPServer({
|
|
1141
|
+
id: "my-serverless-mcp",
|
|
1142
|
+
name: "My Serverless MCP",
|
|
1143
|
+
version: "1.0.0",
|
|
1144
|
+
tools: { /* your tools */ },
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
serve(async (req) => {
|
|
1148
|
+
const url = new URL(req.url);
|
|
1149
|
+
|
|
1150
|
+
if (url.pathname === "/mcp") {
|
|
1151
|
+
// Convert Deno Request to Node.js-compatible format
|
|
1152
|
+
const { req: nodeReq, res: nodeRes } = toReqRes(req);
|
|
1153
|
+
|
|
1154
|
+
await server.startHTTP({
|
|
1155
|
+
url,
|
|
1156
|
+
httpPath: "/mcp",
|
|
1157
|
+
req: nodeReq,
|
|
1158
|
+
res: nodeRes,
|
|
1159
|
+
options: {
|
|
1160
|
+
serverless: true, // ← Enable stateless mode for serverless
|
|
1161
|
+
},
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
return toFetchResponse(nodeRes);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
return new Response("Not found", { status: 404 });
|
|
1168
|
+
});
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
> **Note:**
|
|
1172
|
+
|
|
1173
|
+
**When to use `serverless: true`**
|
|
1174
|
+
|
|
1175
|
+
Use `serverless: true` when deploying to environments where each request runs in a fresh, stateless execution context:
|
|
1176
|
+
- Supabase Edge Functions
|
|
1177
|
+
- Cloudflare Workers
|
|
1178
|
+
- Vercel Edge Functions
|
|
1179
|
+
- Netlify Edge Functions
|
|
1180
|
+
- AWS Lambda
|
|
1181
|
+
- Deno Deploy
|
|
1182
|
+
|
|
1183
|
+
Use the default session-based mode (without `serverless: true`) for:
|
|
1184
|
+
- Long-lived Node.js servers
|
|
1185
|
+
- Docker containers
|
|
1186
|
+
- Traditional hosting (VPS, dedicated servers)
|
|
1187
|
+
|
|
1188
|
+
The serverless mode disables session management and creates fresh server instances per request, which is necessary for stateless environments where memory doesn't persist between invocations.
|
|
1189
|
+
|
|
1190
|
+
**Note:** The following MCP features require session state or persistent connections and will **not work** in serverless mode:
|
|
1191
|
+
- **Elicitation** - Interactive user input requests during tool execution require session management to route responses back to the correct client
|
|
1192
|
+
- **Resource subscriptions** - `resources/subscribe` and `resources/unsubscribe` need persistent connections to maintain subscription state
|
|
1193
|
+
- **Resource update notifications** - `resources.notifyUpdated()` requires active subscriptions and persistent connections to notify clients
|
|
1194
|
+
- **Prompt list change notifications** - `prompts.notifyListChanged()` requires persistent connections to push updates to clients
|
|
1195
|
+
|
|
1196
|
+
These features work normally in long-lived server environments (Node.js servers, Docker containers, etc.).
|
|
1197
|
+
|
|
1198
|
+
Here are the details for the values needed by the `startHTTP` method:
|
|
1199
|
+
|
|
1200
|
+
The `StreamableHTTPServerTransportOptions` object allows you to customize the behavior of the HTTP transport. Here are the available options:
|
|
1201
|
+
|
|
1202
|
+
### close()
|
|
1203
|
+
|
|
1204
|
+
This method closes the server and releases all resources.
|
|
1205
|
+
|
|
1206
|
+
```typescript
|
|
1207
|
+
async close(): Promise<void>
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
### getServerInfo()
|
|
1211
|
+
|
|
1212
|
+
This method gives you a look at the server's basic information.
|
|
1213
|
+
|
|
1214
|
+
```typescript
|
|
1215
|
+
getServerInfo(): ServerInfo
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
### getServerDetail()
|
|
1219
|
+
|
|
1220
|
+
This method gives you a detailed look at the server's information.
|
|
1221
|
+
|
|
1222
|
+
```typescript
|
|
1223
|
+
getServerDetail(): ServerDetail
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
### getToolListInfo()
|
|
1227
|
+
|
|
1228
|
+
This method gives you a look at the tools that were set up when you created the server. It's a read-only list, useful for debugging purposes.
|
|
1229
|
+
|
|
1230
|
+
```typescript
|
|
1231
|
+
getToolListInfo(): ToolListInfo
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
### getToolInfo()
|
|
1235
|
+
|
|
1236
|
+
This method gives you detailed information about a specific tool.
|
|
1237
|
+
|
|
1238
|
+
```typescript
|
|
1239
|
+
getToolInfo(toolName: string): ToolInfo
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
### executeTool()
|
|
1243
|
+
|
|
1244
|
+
This method executes a specific tool and returns the result.
|
|
1245
|
+
|
|
1246
|
+
```typescript
|
|
1247
|
+
executeTool(toolName: string, input: any): Promise<any>
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
### getStdioTransport()
|
|
1251
|
+
|
|
1252
|
+
If you started the server with `startStdio()`, you can use this to get the object that manages the stdio communication. This is mostly for checking things internally or for testing.
|
|
1253
|
+
|
|
1254
|
+
```typescript
|
|
1255
|
+
getStdioTransport(): StdioServerTransport | undefined
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
### getSseTransport()
|
|
1259
|
+
|
|
1260
|
+
If you started the server with `startSSE()`, you can use this to get the object that manages the SSE communication. Like `getStdioTransport`, this is mainly for internal checks or testing.
|
|
1261
|
+
|
|
1262
|
+
```typescript
|
|
1263
|
+
getSseTransport(): SSEServerTransport | undefined
|
|
1264
|
+
```
|
|
1265
|
+
|
|
1266
|
+
### getSseHonoTransport()
|
|
1267
|
+
|
|
1268
|
+
If you started the server with `startHonoSSE()`, you can use this to get the object that manages the SSE communication. Like `getSseTransport`, this is mainly for internal checks or testing.
|
|
1269
|
+
|
|
1270
|
+
```typescript
|
|
1271
|
+
getSseHonoTransport(): SSETransport | undefined
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
### getStreamableHTTPTransport()
|
|
1275
|
+
|
|
1276
|
+
If you started the server with `startHTTP()`, you can use this to get the object that manages the HTTP communication. Like `getSseTransport`, this is mainly for internal checks or testing.
|
|
1277
|
+
|
|
1278
|
+
```typescript
|
|
1279
|
+
getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined
|
|
1280
|
+
```
|
|
1281
|
+
|
|
1282
|
+
### tools()
|
|
1283
|
+
|
|
1284
|
+
Executes a specific tool provided by this MCP server.
|
|
1285
|
+
|
|
1286
|
+
```typescript
|
|
1287
|
+
async executeTool(
|
|
1288
|
+
toolId: string,
|
|
1289
|
+
args: any,
|
|
1290
|
+
executionContext?: { messages?: any[]; toolCallId?: string },
|
|
1291
|
+
): Promise<any>
|
|
1292
|
+
```
|
|
1293
|
+
|
|
1294
|
+
## Resource Handling
|
|
1295
|
+
|
|
1296
|
+
### What are MCP Resources?
|
|
1297
|
+
|
|
1298
|
+
Resources are a core primitive in the Model Context Protocol (MCP) that allow servers to expose data and content that can be read by clients and used as context for LLM interactions. They represent any kind of data that an MCP server wants to make available, such as:
|
|
1299
|
+
|
|
1300
|
+
- File contents
|
|
1301
|
+
- Database records
|
|
1302
|
+
- API responses
|
|
1303
|
+
- Live system data
|
|
1304
|
+
- Screenshots and images
|
|
1305
|
+
- Log files
|
|
1306
|
+
|
|
1307
|
+
Resources are identified by unique URIs (e.g., `file:///home/user/documents/report.pdf`, `postgres://database/customers/schema`) and can contain either text (UTF-8 encoded) or binary data (base64 encoded).
|
|
1308
|
+
|
|
1309
|
+
Clients can discover resources through:
|
|
1310
|
+
|
|
1311
|
+
1. **Direct resources**: Servers expose a list of concrete resources via a `resources/list` endpoint.
|
|
1312
|
+
2. **Resource templates**: For dynamic resources, servers can expose URI templates (RFC 6570) that clients use to construct resource URIs.
|
|
1313
|
+
|
|
1314
|
+
To read a resource, clients make a `resources/read` request with the URI. Servers can also notify clients about changes to the resource list (`notifications/resources/list_changed`) or updates to specific resource content (`notifications/resources/updated`) if a client has subscribed to that resource.
|
|
1315
|
+
|
|
1316
|
+
For more detailed information, refer to the [official MCP documentation on Resources](https://modelcontextprotocol.io/docs/concepts/resources).
|
|
1317
|
+
|
|
1318
|
+
### `MCPServerResources` Type
|
|
1319
|
+
|
|
1320
|
+
The `resources` option takes an object of type `MCPServerResources`. This type defines the callbacks your server will use to handle resource requests:
|
|
1321
|
+
|
|
1322
|
+
```typescript
|
|
1323
|
+
export type MCPServerResources = {
|
|
1324
|
+
// Callback to list available resources
|
|
1325
|
+
listResources: () => Promise<Resource[]>;
|
|
1326
|
+
|
|
1327
|
+
// Callback to get the content of a specific resource
|
|
1328
|
+
getResourceContent: ({
|
|
1329
|
+
uri,
|
|
1330
|
+
}: {
|
|
1331
|
+
uri: string;
|
|
1332
|
+
}) => Promise<MCPServerResourceContent | MCPServerResourceContent[]>;
|
|
1333
|
+
|
|
1334
|
+
// Optional callback to list available resource templates
|
|
1335
|
+
resourceTemplates?: () => Promise<ResourceTemplate[]>;
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
export type MCPServerResourceContent = { text?: string } | { blob?: string };
|
|
1339
|
+
```
|
|
1340
|
+
|
|
1341
|
+
Example:
|
|
1342
|
+
|
|
1343
|
+
```typescript
|
|
1344
|
+
import { MCPServer } from "@mastra/mcp";
|
|
1345
|
+
import type {
|
|
1346
|
+
MCPServerResourceContent,
|
|
1347
|
+
Resource,
|
|
1348
|
+
ResourceTemplate,
|
|
1349
|
+
} from "@mastra/mcp";
|
|
1350
|
+
|
|
1351
|
+
// Resources/resource templates will generally be dynamically fetched.
|
|
1352
|
+
const myResources: Resource[] = [
|
|
1353
|
+
{ uri: "file://data/123.txt", name: "Data File", mimeType: "text/plain" },
|
|
1354
|
+
];
|
|
1355
|
+
|
|
1356
|
+
const myResourceContents: Record<string, MCPServerResourceContent> = {
|
|
1357
|
+
"file://data.txt/123": { text: "This is the content of the data file." },
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
const myResourceTemplates: ResourceTemplate[] = [
|
|
1361
|
+
{
|
|
1362
|
+
uriTemplate: "file://data/{id}",
|
|
1363
|
+
name: "Data File",
|
|
1364
|
+
description: "A file containing data.",
|
|
1365
|
+
mimeType: "text/plain",
|
|
1366
|
+
},
|
|
1367
|
+
];
|
|
1368
|
+
|
|
1369
|
+
const myResourceHandlers: MCPServerResources = {
|
|
1370
|
+
listResources: async () => myResources,
|
|
1371
|
+
getResourceContent: async ({ uri }) => {
|
|
1372
|
+
if (myResourceContents[uri]) {
|
|
1373
|
+
return myResourceContents[uri];
|
|
1374
|
+
}
|
|
1375
|
+
throw new Error(`Resource content not found for ${uri}`);
|
|
1376
|
+
},
|
|
1377
|
+
resourceTemplates: async () => myResourceTemplates,
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
const serverWithResources = new MCPServer({
|
|
1381
|
+
id: "resourceful-server",
|
|
1382
|
+
name: "Resourceful Server",
|
|
1383
|
+
version: "1.0.0",
|
|
1384
|
+
tools: {
|
|
1385
|
+
/* ... your tools ... */
|
|
1386
|
+
},
|
|
1387
|
+
resources: myResourceHandlers,
|
|
1388
|
+
});
|
|
1389
|
+
```
|
|
1390
|
+
|
|
1391
|
+
### Notifying Clients of Resource Changes
|
|
1392
|
+
|
|
1393
|
+
If the available resources or their content change, your server can notify connected clients that are subscribed to the specific resource.
|
|
1394
|
+
|
|
1395
|
+
#### `server.resources.notifyUpdated({ uri: string })`
|
|
1396
|
+
|
|
1397
|
+
Call this method when the content of a specific resource (identified by its `uri`) has been updated. If any clients are subscribed to this URI, they will receive a `notifications/resources/updated` message.
|
|
1398
|
+
|
|
1399
|
+
```typescript
|
|
1400
|
+
async server.resources.notifyUpdated({ uri: string }): Promise<void>
|
|
1401
|
+
```
|
|
1402
|
+
|
|
1403
|
+
Example:
|
|
1404
|
+
|
|
1405
|
+
```typescript
|
|
1406
|
+
// After updating the content of 'file://data.txt'
|
|
1407
|
+
await serverWithResources.resources.notifyUpdated({ uri: "file://data.txt" });
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
#### `server.resources.notifyListChanged()`
|
|
1411
|
+
|
|
1412
|
+
Call this method when the overall list of available resources has changed (e.g., a resource was added or removed). This will send a `notifications/resources/list_changed` message to clients, prompting them to re-fetch the list of resources.
|
|
1413
|
+
|
|
1414
|
+
```typescript
|
|
1415
|
+
async server.resources.notifyListChanged(): Promise<void>
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
Example:
|
|
1419
|
+
|
|
1420
|
+
```typescript
|
|
1421
|
+
// After adding a new resource to the list managed by 'myResourceHandlers.listResources'
|
|
1422
|
+
await serverWithResources.resources.notifyListChanged();
|
|
1423
|
+
```
|
|
1424
|
+
|
|
1425
|
+
## Prompt Handling
|
|
1426
|
+
|
|
1427
|
+
### What are MCP Prompts?
|
|
1428
|
+
|
|
1429
|
+
Prompts are reusable templates or workflows that MCP servers expose to clients. They can accept arguments, include resource context, support versioning, and be used to standardize LLM interactions.
|
|
1430
|
+
|
|
1431
|
+
Prompts are identified by a unique name (and optional version) and can be dynamic or static.
|
|
1432
|
+
|
|
1433
|
+
### `MCPServerPrompts` Type
|
|
1434
|
+
|
|
1435
|
+
The `prompts` option takes an object of type `MCPServerPrompts`. This type defines the callbacks your server will use to handle prompt requests:
|
|
1436
|
+
|
|
1437
|
+
```typescript
|
|
1438
|
+
export type MCPServerPrompts = {
|
|
1439
|
+
// Callback to list available prompts
|
|
1440
|
+
listPrompts: () => Promise<Prompt[]>;
|
|
1441
|
+
|
|
1442
|
+
// Callback to get the messages/content for a specific prompt
|
|
1443
|
+
getPromptMessages?: ({
|
|
1444
|
+
name,
|
|
1445
|
+
version,
|
|
1446
|
+
args,
|
|
1447
|
+
}: {
|
|
1448
|
+
name: string;
|
|
1449
|
+
version?: string;
|
|
1450
|
+
args?: any;
|
|
1451
|
+
}) => Promise<{ prompt: Prompt; messages: PromptMessage[] }>;
|
|
1452
|
+
};
|
|
1453
|
+
```
|
|
1454
|
+
|
|
1455
|
+
Example:
|
|
1456
|
+
|
|
1457
|
+
```typescript
|
|
1458
|
+
import { MCPServer } from "@mastra/mcp";
|
|
1459
|
+
import type { Prompt, PromptMessage, MCPServerPrompts } from "@mastra/mcp";
|
|
1460
|
+
|
|
1461
|
+
const prompts: Prompt[] = [
|
|
1462
|
+
{
|
|
1463
|
+
name: "analyze-code",
|
|
1464
|
+
description: "Analyze code for improvements",
|
|
1465
|
+
version: "v1",
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
name: "analyze-code",
|
|
1469
|
+
description: "Analyze code for improvements (new logic)",
|
|
1470
|
+
version: "v2",
|
|
1471
|
+
},
|
|
1472
|
+
];
|
|
1473
|
+
|
|
1474
|
+
const myPromptHandlers: MCPServerPrompts = {
|
|
1475
|
+
listPrompts: async () => prompts,
|
|
1476
|
+
getPromptMessages: async ({ name, version, args }) => {
|
|
1477
|
+
if (name === "analyze-code") {
|
|
1478
|
+
if (version === "v2") {
|
|
1479
|
+
const prompt = prompts.find(
|
|
1480
|
+
(p) => p.name === name && p.version === "v2",
|
|
1481
|
+
);
|
|
1482
|
+
if (!prompt) throw new Error("Prompt version not found");
|
|
1483
|
+
return {
|
|
1484
|
+
prompt,
|
|
1485
|
+
messages: [
|
|
1486
|
+
{
|
|
1487
|
+
role: "user",
|
|
1488
|
+
content: {
|
|
1489
|
+
type: "text",
|
|
1490
|
+
text: `Analyze this code with the new logic: ${args.code}`,
|
|
1491
|
+
},
|
|
1492
|
+
},
|
|
1493
|
+
],
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
// Default or v1
|
|
1497
|
+
const prompt = prompts.find((p) => p.name === name && p.version === "v1");
|
|
1498
|
+
if (!prompt) throw new Error("Prompt version not found");
|
|
1499
|
+
return {
|
|
1500
|
+
prompt,
|
|
1501
|
+
messages: [
|
|
1502
|
+
{
|
|
1503
|
+
role: "user",
|
|
1504
|
+
content: { type: "text", text: `Analyze this code: ${args.code}` },
|
|
1505
|
+
},
|
|
1506
|
+
],
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
throw new Error("Prompt not found");
|
|
1510
|
+
},
|
|
1511
|
+
};
|
|
1512
|
+
|
|
1513
|
+
const serverWithPrompts = new MCPServer({
|
|
1514
|
+
id: "promptful-server",
|
|
1515
|
+
name: "Promptful Server",
|
|
1516
|
+
version: "1.0.0",
|
|
1517
|
+
tools: {
|
|
1518
|
+
/* ... */
|
|
1519
|
+
},
|
|
1520
|
+
prompts: myPromptHandlers,
|
|
1521
|
+
});
|
|
1522
|
+
```
|
|
1523
|
+
|
|
1524
|
+
### Notifying Clients of Prompt Changes
|
|
1525
|
+
|
|
1526
|
+
If the available prompts change, your server can notify connected clients:
|
|
1527
|
+
|
|
1528
|
+
#### `server.prompts.notifyListChanged()`
|
|
1529
|
+
|
|
1530
|
+
Call this method when the overall list of available prompts has changed (e.g., a prompt was added or removed). This will send a `notifications/prompts/list_changed` message to clients, prompting them to re-fetch the list of prompts.
|
|
1531
|
+
|
|
1532
|
+
```typescript
|
|
1533
|
+
await serverWithPrompts.prompts.notifyListChanged();
|
|
1534
|
+
```
|
|
1535
|
+
|
|
1536
|
+
### Best Practices for Prompt Handling
|
|
1537
|
+
|
|
1538
|
+
- Use clear, descriptive prompt names and descriptions.
|
|
1539
|
+
- Validate all required arguments in `getPromptMessages`.
|
|
1540
|
+
- Include a `version` field if you expect to make breaking changes.
|
|
1541
|
+
- Use the `version` parameter to select the correct prompt logic.
|
|
1542
|
+
- Notify clients when prompt lists change.
|
|
1543
|
+
- Handle errors with informative messages.
|
|
1544
|
+
- Document argument expectations and available versions.
|
|
1545
|
+
|
|
1546
|
+
---
|
|
1547
|
+
|
|
1548
|
+
## Examples
|
|
1549
|
+
|
|
1550
|
+
For practical examples of setting up and deploying an MCPServer, see the [Publishing an MCP Server guide](https://mastra.ai/docs/v1/mcp/publishing-mcp-server).
|
|
1551
|
+
|
|
1552
|
+
The example at the beginning of this page also demonstrates how to instantiate `MCPServer` with both tools and agents.
|
|
1553
|
+
|
|
1554
|
+
## Elicitation
|
|
1555
|
+
|
|
1556
|
+
### What is Elicitation?
|
|
1557
|
+
|
|
1558
|
+
Elicitation is a feature in the Model Context Protocol (MCP) that allows servers to request structured information from users. This enables interactive workflows where servers can collect additional data dynamically.
|
|
1559
|
+
|
|
1560
|
+
The `MCPServer` class automatically includes elicitation capabilities. Tools receive a `context.mcp` object in their `execute` function that includes an `elicitation.sendRequest()` method for requesting user input.
|
|
1561
|
+
|
|
1562
|
+
### Tool Execution Signature
|
|
1563
|
+
|
|
1564
|
+
When tools are executed within an MCP server context, they receive MCP-specific capabilities via the `context.mcp` object:
|
|
1565
|
+
|
|
1566
|
+
```typescript
|
|
1567
|
+
execute: async (inputData, context) => {
|
|
1568
|
+
// input contains the tool's inputData parameters
|
|
1569
|
+
// context.mcp contains server capabilities like elicitation and authentication info
|
|
1570
|
+
|
|
1571
|
+
// Access authentication information (when available)
|
|
1572
|
+
if (context.mcp?.extra?.authInfo) {
|
|
1573
|
+
console.log("Authenticated request from:", context.mcp.extra.authInfo.clientId);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// Use elicitation capabilities
|
|
1577
|
+
const result = await context.mcp.elicitation.sendRequest({
|
|
1578
|
+
message: "Please provide information",
|
|
1579
|
+
requestedSchema: {
|
|
1580
|
+
/* schema */
|
|
1581
|
+
},
|
|
1582
|
+
});
|
|
1583
|
+
|
|
1584
|
+
return result;
|
|
1585
|
+
};
|
|
1586
|
+
```
|
|
1587
|
+
|
|
1588
|
+
### How Elicitation Works
|
|
1589
|
+
|
|
1590
|
+
A common use case is during tool execution. When a tool needs user input, it can use the elicitation functionality provided through the context parameter:
|
|
1591
|
+
|
|
1592
|
+
1. The tool calls `context.mcp.elicitation.sendRequest()` with a message and schema
|
|
1593
|
+
2. The request is sent to the connected MCP client
|
|
1594
|
+
3. The client presents the request to the user (via UI, command line, etc.)
|
|
1595
|
+
4. The user provides input, declines, or cancels the request
|
|
1596
|
+
5. The client sends the response back to the server
|
|
1597
|
+
6. The tool receives the response and continues execution
|
|
1598
|
+
|
|
1599
|
+
### Using Elicitation in Tools
|
|
1600
|
+
|
|
1601
|
+
Here's an example of a tool that uses elicitation to collect user contact information:
|
|
1602
|
+
|
|
1603
|
+
```typescript
|
|
1604
|
+
import { MCPServer } from "@mastra/mcp";
|
|
1605
|
+
import { createTool } from "@mastra/core/tools";
|
|
1606
|
+
import { z } from "zod";
|
|
1607
|
+
|
|
1608
|
+
const server = new MCPServer({
|
|
1609
|
+
id: "interactive-server",
|
|
1610
|
+
name: "Interactive Server",
|
|
1611
|
+
version: "1.0.0",
|
|
1612
|
+
tools: {
|
|
1613
|
+
collectContactInfo: createTool({
|
|
1614
|
+
id: "collectContactInfo",
|
|
1615
|
+
description: "Collects user contact information through elicitation",
|
|
1616
|
+
inputSchema: z.object({
|
|
1617
|
+
reason: z
|
|
1618
|
+
.string()
|
|
1619
|
+
.optional()
|
|
1620
|
+
.describe("Reason for collecting contact info"),
|
|
1621
|
+
}),
|
|
1622
|
+
execute: async (inputData, context) => {
|
|
1623
|
+
const { reason } = inputData;
|
|
1624
|
+
|
|
1625
|
+
// Log session info if available
|
|
1626
|
+
console.log("Request from session:", context.mcp?.extra?.sessionId);
|
|
1627
|
+
|
|
1628
|
+
try {
|
|
1629
|
+
// Request user input via elicitation
|
|
1630
|
+
const result = await context.mcp.elicitation.sendRequest({
|
|
1631
|
+
message: reason
|
|
1632
|
+
? `Please provide your contact information. ${reason}`
|
|
1633
|
+
: "Please provide your contact information",
|
|
1634
|
+
requestedSchema: {
|
|
1635
|
+
type: "object",
|
|
1636
|
+
properties: {
|
|
1637
|
+
name: {
|
|
1638
|
+
type: "string",
|
|
1639
|
+
title: "Full Name",
|
|
1640
|
+
description: "Your full name",
|
|
1641
|
+
},
|
|
1642
|
+
email: {
|
|
1643
|
+
type: "string",
|
|
1644
|
+
title: "Email Address",
|
|
1645
|
+
description: "Your email address",
|
|
1646
|
+
format: "email",
|
|
1647
|
+
},
|
|
1648
|
+
phone: {
|
|
1649
|
+
type: "string",
|
|
1650
|
+
title: "Phone Number",
|
|
1651
|
+
description: "Your phone number (optional)",
|
|
1652
|
+
},
|
|
1653
|
+
},
|
|
1654
|
+
required: ["name", "email"],
|
|
1655
|
+
},
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
// Handle the user's response
|
|
1659
|
+
if (result.action === "accept") {
|
|
1660
|
+
return `Contact information collected: ${JSON.stringify(result.content, null, 2)}`;
|
|
1661
|
+
} else if (result.action === "decline") {
|
|
1662
|
+
return "Contact information collection was declined by the user.";
|
|
1663
|
+
} else {
|
|
1664
|
+
return "Contact information collection was cancelled by the user.";
|
|
1665
|
+
}
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
return `Error collecting contact information: ${error}`;
|
|
1668
|
+
}
|
|
1669
|
+
},
|
|
1670
|
+
}),
|
|
1671
|
+
},
|
|
1672
|
+
});
|
|
1673
|
+
```
|
|
1674
|
+
|
|
1675
|
+
### Elicitation Request Schema
|
|
1676
|
+
|
|
1677
|
+
The `requestedSchema` must be a flat object with primitive properties only. Supported types include:
|
|
1678
|
+
|
|
1679
|
+
- **String**: `{ type: 'string', title: 'Display Name', description: 'Help text' }`
|
|
1680
|
+
- **Number**: `{ type: 'number', minimum: 0, maximum: 100 }`
|
|
1681
|
+
- **Boolean**: `{ type: 'boolean', default: false }`
|
|
1682
|
+
- **Enum**: `{ type: 'string', enum: ['option1', 'option2'] }`
|
|
1683
|
+
|
|
1684
|
+
Example schema:
|
|
1685
|
+
|
|
1686
|
+
```typescript
|
|
1687
|
+
{
|
|
1688
|
+
type: 'object',
|
|
1689
|
+
properties: {
|
|
1690
|
+
name: {
|
|
1691
|
+
type: 'string',
|
|
1692
|
+
title: 'Full Name',
|
|
1693
|
+
description: 'Your complete name',
|
|
1694
|
+
},
|
|
1695
|
+
age: {
|
|
1696
|
+
type: 'number',
|
|
1697
|
+
title: 'Age',
|
|
1698
|
+
minimum: 18,
|
|
1699
|
+
maximum: 120,
|
|
1700
|
+
},
|
|
1701
|
+
newsletter: {
|
|
1702
|
+
type: 'boolean',
|
|
1703
|
+
title: 'Subscribe to Newsletter',
|
|
1704
|
+
default: false,
|
|
1705
|
+
},
|
|
1706
|
+
},
|
|
1707
|
+
required: ['name'],
|
|
1708
|
+
}
|
|
1709
|
+
```
|
|
1710
|
+
|
|
1711
|
+
### Response Actions
|
|
1712
|
+
|
|
1713
|
+
Users can respond to elicitation requests in three ways:
|
|
1714
|
+
|
|
1715
|
+
1. **Accept** (`action: 'accept'`): User provided data and confirmed submission
|
|
1716
|
+
- Contains `content` field with the submitted data
|
|
1717
|
+
2. **Decline** (`action: 'decline'`): User explicitly declined to provide information
|
|
1718
|
+
- No content field
|
|
1719
|
+
3. **Cancel** (`action: 'cancel'`): User dismissed the request without deciding
|
|
1720
|
+
- No content field
|
|
1721
|
+
|
|
1722
|
+
Tools should handle all three response types appropriately.
|
|
1723
|
+
|
|
1724
|
+
### Security Considerations
|
|
1725
|
+
|
|
1726
|
+
- **Never request sensitive information** like passwords, SSNs, or credit card numbers
|
|
1727
|
+
- Validate all user input against the provided schema
|
|
1728
|
+
- Handle declining and cancellation gracefully
|
|
1729
|
+
- Provide clear reasons for data collection
|
|
1730
|
+
- Respect user privacy and preferences
|
|
1731
|
+
|
|
1732
|
+
### Tool Execution API
|
|
1733
|
+
|
|
1734
|
+
The elicitation functionality is available through the `options` parameter in tool execution:
|
|
1735
|
+
|
|
1736
|
+
```typescript
|
|
1737
|
+
// Within a tool's execute function
|
|
1738
|
+
execute: async (inputData, context) => {
|
|
1739
|
+
// Use elicitation for user input
|
|
1740
|
+
const result = await context.mcp.elicitation.sendRequest({
|
|
1741
|
+
message: string, // Message to display to user
|
|
1742
|
+
requestedSchema: object // JSON schema defining expected response structure
|
|
1743
|
+
}): Promise<ElicitResult>
|
|
1744
|
+
|
|
1745
|
+
// Access authentication info if needed
|
|
1746
|
+
if (context.mcp?.extra?.authInfo) {
|
|
1747
|
+
// Use context.mcp.extra.authInfo.token, etc.
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
```
|
|
1751
|
+
|
|
1752
|
+
Note that elicitation is **session-aware** when using HTTP-based transports (SSE or HTTP). This means that when multiple clients are connected to the same server, elicitation requests are routed to the correct client session that initiated the tool execution.
|
|
1753
|
+
|
|
1754
|
+
The `ElicitResult` type:
|
|
1755
|
+
|
|
1756
|
+
```typescript
|
|
1757
|
+
type ElicitResult = {
|
|
1758
|
+
action: "accept" | "decline" | "cancel";
|
|
1759
|
+
content?: any; // Only present when action is 'accept'
|
|
1760
|
+
};
|
|
1761
|
+
```
|
|
1762
|
+
|
|
1763
|
+
## Authentication Context
|
|
1764
|
+
|
|
1765
|
+
Tools can access request metadata via `context.mcp.extra` when using HTTP-based transports:
|
|
1766
|
+
|
|
1767
|
+
```typescript
|
|
1768
|
+
execute: async (inputData, context) => {
|
|
1769
|
+
if (!context.mcp?.extra?.authInfo?.token) {
|
|
1770
|
+
return "Authentication required";
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
// Use the auth token
|
|
1774
|
+
const response = await fetch("/api/data", {
|
|
1775
|
+
headers: { Authorization: `Bearer ${context.mcp.extra.authInfo.token}` },
|
|
1776
|
+
signal: context.mcp.extra.signal,
|
|
1777
|
+
});
|
|
1778
|
+
|
|
1779
|
+
return response.json();
|
|
1780
|
+
};
|
|
1781
|
+
```
|
|
1782
|
+
|
|
1783
|
+
The `extra` object contains:
|
|
1784
|
+
|
|
1785
|
+
- `authInfo`: Authentication info (when provided by server middleware)
|
|
1786
|
+
- `sessionId`: Session identifier
|
|
1787
|
+
- `signal`: AbortSignal for cancellation
|
|
1788
|
+
- `sendNotification`/`sendRequest`: MCP protocol functions
|
|
1789
|
+
|
|
1790
|
+
> Note: To enable authentication, your HTTP server needs middleware that populates `req.auth` before calling `server.startHTTP()`. For example:
|
|
1791
|
+
>
|
|
1792
|
+
> ```typescript
|
|
1793
|
+
> httpServer.createServer((req, res) => {
|
|
1794
|
+
> // Add auth middleware
|
|
1795
|
+
> req.auth = validateAuthToken(req.headers.authorization);
|
|
1796
|
+
>
|
|
1797
|
+
> // Then pass to MCP server
|
|
1798
|
+
> await server.startHTTP({ url, httpPath, req, res });
|
|
1799
|
+
> });
|
|
1800
|
+
> ```
|
|
1801
|
+
|
|
1802
|
+
## Related Information
|
|
1803
|
+
|
|
1804
|
+
- For connecting to MCP servers in Mastra, see the [MCPClient documentation](./mcp-client).
|
|
1805
|
+
- For more about the Model Context Protocol, see the [@modelcontextprotocol/sdk documentation](https://github.com/modelcontextprotocol/typescript-sdk).
|
|
1806
|
+
|
|
1807
|
+
---
|
|
1808
|
+
|
|
1809
|
+
## Reference: MastraMCPClient (Deprecated)
|
|
1810
|
+
|
|
1811
|
+
> API Reference for MastraMCPClient - A client implementation for the Model Context Protocol.
|
|
1812
|
+
|
|
1813
|
+
The `MastraMCPClient` class provides a client implementation for interacting with Model Context Protocol (MCP) servers. It handles connection management, resource discovery, and tool execution through the MCP protocol.
|
|
1814
|
+
|
|
1815
|
+
## Deprecation notice
|
|
1816
|
+
|
|
1817
|
+
`MastraMCPClient` is being deprecated in favour of [`MCPClient`](./mcp-client). Rather than having two different interfaces for managing a single MCP server vs multiple MCP servers, we opted to recommend using the interface to manage multiple even when using a single MCP server.
|
|
1818
|
+
|
|
1819
|
+
## Constructor
|
|
1820
|
+
|
|
1821
|
+
Creates a new instance of the MastraMCPClient.
|
|
1822
|
+
|
|
1823
|
+
```typescript
|
|
1824
|
+
constructor({
|
|
1825
|
+
name,
|
|
1826
|
+
version = '1.0.0',
|
|
1827
|
+
server,
|
|
1828
|
+
capabilities = {},
|
|
1829
|
+
timeout = 60000,
|
|
1830
|
+
}: {
|
|
1831
|
+
name: string;
|
|
1832
|
+
server: MastraMCPServerDefinition;
|
|
1833
|
+
capabilities?: ClientCapabilities;
|
|
1834
|
+
version?: string;
|
|
1835
|
+
timeout?: number;
|
|
1836
|
+
})
|
|
1837
|
+
```
|
|
1838
|
+
|
|
1839
|
+
### Parameters
|
|
1840
|
+
|
|
1841
|
+
<br />
|
|
1842
|
+
|
|
1843
|
+
### MastraMCPServerDefinition
|
|
1844
|
+
|
|
1845
|
+
MCP servers can be configured using this definition. The client automatically detects the transport type based on the provided parameters:
|
|
1846
|
+
|
|
1847
|
+
- If `command` is provided, it uses the Stdio transport.
|
|
1848
|
+
- If `url` is provided, it first attempts to use the Streamable HTTP transport and falls back to the legacy SSE transport if the initial connection fails.
|
|
1849
|
+
|
|
1850
|
+
<br />
|
|
1851
|
+
|
|
1852
|
+
### LogHandler
|
|
1853
|
+
|
|
1854
|
+
The `LogHandler` function takes a `LogMessage` object as its parameter and returns void. The `LogMessage` object has the following properties. The `LoggingLevel` type is a string enum with values: `debug`, `info`, `warn`, and `error`.
|
|
1855
|
+
|
|
1856
|
+
<br />
|
|
1857
|
+
|
|
1858
|
+
## Methods
|
|
1859
|
+
|
|
1860
|
+
### connect()
|
|
1861
|
+
|
|
1862
|
+
Establishes a connection with the MCP server.
|
|
1863
|
+
|
|
1864
|
+
```typescript
|
|
1865
|
+
async connect(): Promise<void>
|
|
1866
|
+
```
|
|
1867
|
+
|
|
1868
|
+
### disconnect()
|
|
1869
|
+
|
|
1870
|
+
Closes the connection with the MCP server.
|
|
1871
|
+
|
|
1872
|
+
```typescript
|
|
1873
|
+
async disconnect(): Promise<void>
|
|
1874
|
+
```
|
|
1875
|
+
|
|
1876
|
+
### resources()
|
|
1877
|
+
|
|
1878
|
+
Retrieves the list of available resources from the server.
|
|
1879
|
+
|
|
1880
|
+
```typescript
|
|
1881
|
+
async resources(): Promise<ListResourcesResult>
|
|
1882
|
+
```
|
|
1883
|
+
|
|
1884
|
+
### tools()
|
|
1885
|
+
|
|
1886
|
+
Fetches and initializes available tools from the server, converting them into Mastra-compatible tool formats.
|
|
1887
|
+
|
|
1888
|
+
```typescript
|
|
1889
|
+
async tools(): Promise<Record<string, Tool>>
|
|
1890
|
+
```
|
|
1891
|
+
|
|
1892
|
+
Returns an object mapping tool names to their corresponding Mastra tool implementations.
|
|
1893
|
+
|
|
1894
|
+
## Examples
|
|
1895
|
+
|
|
1896
|
+
### Using with Mastra Agent
|
|
1897
|
+
|
|
1898
|
+
#### Example with Stdio Server
|
|
1899
|
+
|
|
1900
|
+
```typescript
|
|
1901
|
+
import { Agent } from "@mastra/core/agent";
|
|
1902
|
+
import { MastraMCPClient } from "@mastra/mcp";
|
|
1903
|
+
|
|
1904
|
+
// Initialize the MCP client using mcp/fetch as an example https://hub.docker.com/r/mcp/fetch
|
|
1905
|
+
// Visit https://github.com/docker/mcp-servers for other reference docker mcp servers
|
|
1906
|
+
const fetchClient = new MastraMCPClient({
|
|
1907
|
+
name: "fetch",
|
|
1908
|
+
server: {
|
|
1909
|
+
command: "docker",
|
|
1910
|
+
args: ["run", "-i", "--rm", "mcp/fetch"],
|
|
1911
|
+
logger: (logMessage) => {
|
|
1912
|
+
console.log(`[${logMessage.level}] ${logMessage.message}`);
|
|
1913
|
+
},
|
|
1914
|
+
},
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
// Create a Mastra Agent
|
|
1918
|
+
const agent = new Agent({
|
|
1919
|
+
name: "Fetch agent",
|
|
1920
|
+
instructions:
|
|
1921
|
+
"You are able to fetch data from URLs on demand and discuss the response data with the user.",
|
|
1922
|
+
model: "openai/gpt-5.1",
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1925
|
+
try {
|
|
1926
|
+
// Connect to the MCP server
|
|
1927
|
+
await fetchClient.connect();
|
|
1928
|
+
|
|
1929
|
+
// Gracefully handle process exits so the docker subprocess is cleaned up
|
|
1930
|
+
process.on("exit", () => {
|
|
1931
|
+
fetchClient.disconnect();
|
|
1932
|
+
});
|
|
1933
|
+
|
|
1934
|
+
// Get available tools
|
|
1935
|
+
const tools = await fetchClient.tools();
|
|
1936
|
+
|
|
1937
|
+
// Use the agent with the MCP tools
|
|
1938
|
+
const response = await agent.generate(
|
|
1939
|
+
"Tell me about mastra.ai/docs. Tell me generally what this page is and the content it includes.",
|
|
1940
|
+
{
|
|
1941
|
+
toolsets: {
|
|
1942
|
+
fetch: tools,
|
|
1943
|
+
},
|
|
1944
|
+
},
|
|
1945
|
+
);
|
|
1946
|
+
|
|
1947
|
+
console.log("\n\n" + response.text);
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
console.error("Error:", error);
|
|
1950
|
+
} finally {
|
|
1951
|
+
// Always disconnect when done
|
|
1952
|
+
await fetchClient.disconnect();
|
|
1953
|
+
}
|
|
1954
|
+
```
|
|
1955
|
+
|
|
1956
|
+
### Example with SSE Server
|
|
1957
|
+
|
|
1958
|
+
```typescript
|
|
1959
|
+
// Initialize the MCP client using an SSE server
|
|
1960
|
+
const sseClient = new MastraMCPClient({
|
|
1961
|
+
name: "sse-client",
|
|
1962
|
+
server: {
|
|
1963
|
+
url: new URL("https://your-mcp-server.com/sse"),
|
|
1964
|
+
// Optional fetch request configuration - Note: requestInit alone isn't enough for SSE
|
|
1965
|
+
requestInit: {
|
|
1966
|
+
headers: {
|
|
1967
|
+
Authorization: "Bearer your-token",
|
|
1968
|
+
},
|
|
1969
|
+
},
|
|
1970
|
+
// Required for SSE connections with custom headers
|
|
1971
|
+
eventSourceInit: {
|
|
1972
|
+
fetch(input: Request | URL | string, init?: RequestInit) {
|
|
1973
|
+
const headers = new Headers(init?.headers || {});
|
|
1974
|
+
headers.set("Authorization", "Bearer your-token");
|
|
1975
|
+
return fetch(input, {
|
|
1976
|
+
...init,
|
|
1977
|
+
headers,
|
|
1978
|
+
});
|
|
1979
|
+
},
|
|
1980
|
+
},
|
|
1981
|
+
// Optional additional logging configuration
|
|
1982
|
+
logger: (logMessage) => {
|
|
1983
|
+
console.log(
|
|
1984
|
+
`[${logMessage.level}] ${logMessage.serverName}: ${logMessage.message}`,
|
|
1985
|
+
);
|
|
1986
|
+
},
|
|
1987
|
+
// Disable server logs
|
|
1988
|
+
enableServerLogs: false,
|
|
1989
|
+
},
|
|
1990
|
+
});
|
|
1991
|
+
|
|
1992
|
+
// The rest of the usage is identical to the stdio example
|
|
1993
|
+
```
|
|
1994
|
+
|
|
1995
|
+
### Important Note About SSE Authentication
|
|
1996
|
+
|
|
1997
|
+
When using SSE connections with authentication or custom headers, you need to configure both `requestInit` and `eventSourceInit`. This is because SSE connections use the browser's EventSource API, which doesn't support custom headers directly.
|
|
1998
|
+
|
|
1999
|
+
The `eventSourceInit` configuration allows you to customize the underlying fetch request used for the SSE connection, ensuring your authentication headers are properly included.
|
|
2000
|
+
Without `eventSourceInit`, authentication headers specified in `requestInit` won't be included in the connection request, leading to 401 Unauthorized errors.
|
|
2001
|
+
|
|
2002
|
+
## Related Information
|
|
2003
|
+
|
|
2004
|
+
- For managing multiple MCP servers in your application, see the [MCPClient documentation](./mcp-client)
|
|
2005
|
+
- For more details about the Model Context Protocol, see the [@modelcontextprotocol/sdk documentation](https://github.com/modelcontextprotocol/typescript-sdk).
|