@mastra/mcp 1.0.1 → 1.0.2-alpha.0
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 +11 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-mcp-overview.md +81 -78
- package/dist/docs/references/docs-mcp-publishing-mcp-server.md +15 -15
- package/dist/docs/references/reference-tools-mcp-client.md +223 -231
- package/dist/docs/references/reference-tools-mcp-server.md +280 -284
- package/dist/index.cjs +34 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +28 -12
- package/dist/index.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -60,7 +60,7 @@ Each server in the `servers` map is configured using the `MastraMCPServerDefinit
|
|
|
60
60
|
Retrieves all tools from all configured servers, with tool names namespaced by their server name (in the format `serverName_toolName`) to prevent conflicts. Intended to be passed onto an Agent definition.
|
|
61
61
|
|
|
62
62
|
```ts
|
|
63
|
-
new Agent({ id:
|
|
63
|
+
new Agent({ id: 'agent', tools: await mcp.listTools() })
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### listToolsets()
|
|
@@ -70,7 +70,7 @@ Returns an object mapping namespaced tool names (in the format `serverName.toolN
|
|
|
70
70
|
```typescript
|
|
71
71
|
const res = await agent.stream(prompt, {
|
|
72
72
|
toolsets: await mcp.listToolsets(),
|
|
73
|
-
})
|
|
73
|
+
})
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
### disconnect()
|
|
@@ -88,11 +88,11 @@ The `MCPClient` instance has a `resources` property that provides access to reso
|
|
|
88
88
|
```typescript
|
|
89
89
|
const mcpClient = new MCPClient({
|
|
90
90
|
/* ...servers configuration... */
|
|
91
|
-
})
|
|
91
|
+
})
|
|
92
92
|
|
|
93
93
|
// Access resource methods via mcpClient.resources
|
|
94
|
-
const allResourcesByServer = await mcpClient.resources.list()
|
|
95
|
-
const templatesByServer = await mcpClient.resources.templates()
|
|
94
|
+
const allResourcesByServer = await mcpClient.resources.list()
|
|
95
|
+
const templatesByServer = await mcpClient.resources.templates()
|
|
96
96
|
// ... and so on for other resource methods.
|
|
97
97
|
```
|
|
98
98
|
|
|
@@ -107,9 +107,9 @@ async list(): Promise<Record<string, Resource[]>>
|
|
|
107
107
|
Example:
|
|
108
108
|
|
|
109
109
|
```typescript
|
|
110
|
-
const resourcesByServer = await mcpClient.resources.list()
|
|
110
|
+
const resourcesByServer = await mcpClient.resources.list()
|
|
111
111
|
for (const serverName in resourcesByServer) {
|
|
112
|
-
console.log(`Resources from ${serverName}:`, resourcesByServer[serverName])
|
|
112
|
+
console.log(`Resources from ${serverName}:`, resourcesByServer[serverName])
|
|
113
113
|
}
|
|
114
114
|
```
|
|
115
115
|
|
|
@@ -124,9 +124,9 @@ async templates(): Promise<Record<string, ResourceTemplate[]>>
|
|
|
124
124
|
Example:
|
|
125
125
|
|
|
126
126
|
```typescript
|
|
127
|
-
const templatesByServer = await mcpClient.resources.templates()
|
|
127
|
+
const templatesByServer = await mcpClient.resources.templates()
|
|
128
128
|
for (const serverName in templatesByServer) {
|
|
129
|
-
console.log(`Templates from ${serverName}:`, templatesByServer[serverName])
|
|
129
|
+
console.log(`Templates from ${serverName}:`, templatesByServer[serverName])
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
@@ -144,11 +144,8 @@ async read(serverName: string, uri: string): Promise<ReadResourceResult>
|
|
|
144
144
|
Example:
|
|
145
145
|
|
|
146
146
|
```typescript
|
|
147
|
-
const content = await mcpClient.resources.read(
|
|
148
|
-
|
|
149
|
-
"weather://current",
|
|
150
|
-
);
|
|
151
|
-
console.log("Current weather:", content.contents[0].text);
|
|
147
|
+
const content = await mcpClient.resources.read('myWeatherServer', 'weather://current')
|
|
148
|
+
console.log('Current weather:', content.contents[0].text)
|
|
152
149
|
```
|
|
153
150
|
|
|
154
151
|
#### `resources.subscribe(serverName: string, uri: string)`
|
|
@@ -162,7 +159,7 @@ async subscribe(serverName: string, uri: string): Promise<object>
|
|
|
162
159
|
Example:
|
|
163
160
|
|
|
164
161
|
```typescript
|
|
165
|
-
await mcpClient.resources.subscribe(
|
|
162
|
+
await mcpClient.resources.subscribe('myWeatherServer', 'weather://current')
|
|
166
163
|
```
|
|
167
164
|
|
|
168
165
|
#### `resources.unsubscribe(serverName: string, uri: string)`
|
|
@@ -176,7 +173,7 @@ async unsubscribe(serverName: string, uri: string): Promise<object>
|
|
|
176
173
|
Example:
|
|
177
174
|
|
|
178
175
|
```typescript
|
|
179
|
-
await mcpClient.resources.unsubscribe(
|
|
176
|
+
await mcpClient.resources.unsubscribe('myWeatherServer', 'weather://current')
|
|
180
177
|
```
|
|
181
178
|
|
|
182
179
|
#### `resources.onUpdated(serverName: string, handler: (params: { uri: string }) => void)`
|
|
@@ -190,11 +187,11 @@ async onUpdated(serverName: string, handler: (params: { uri: string }) => void):
|
|
|
190
187
|
Example:
|
|
191
188
|
|
|
192
189
|
```typescript
|
|
193
|
-
mcpClient.resources.onUpdated(
|
|
194
|
-
console.log(`Resource updated on myWeatherServer: ${params.uri}`)
|
|
190
|
+
mcpClient.resources.onUpdated('myWeatherServer', params => {
|
|
191
|
+
console.log(`Resource updated on myWeatherServer: ${params.uri}`)
|
|
195
192
|
// You might want to re-fetch the resource content here
|
|
196
193
|
// await mcpClient.resources.read("myWeatherServer", params.uri);
|
|
197
|
-
})
|
|
194
|
+
})
|
|
198
195
|
```
|
|
199
196
|
|
|
200
197
|
#### `resources.onListChanged(serverName: string, handler: () => void)`
|
|
@@ -208,11 +205,11 @@ async onListChanged(serverName: string, handler: () => void): Promise<void>
|
|
|
208
205
|
Example:
|
|
209
206
|
|
|
210
207
|
```typescript
|
|
211
|
-
mcpClient.resources.onListChanged(
|
|
212
|
-
console.log(
|
|
208
|
+
mcpClient.resources.onListChanged('myWeatherServer', () => {
|
|
209
|
+
console.log('Resource list changed on myWeatherServer.')
|
|
213
210
|
// You should re-fetch the list of resources
|
|
214
211
|
// await mcpClient.resources.list();
|
|
215
|
-
})
|
|
212
|
+
})
|
|
216
213
|
```
|
|
217
214
|
|
|
218
215
|
### `elicitation` Property
|
|
@@ -222,27 +219,27 @@ The `MCPClient` instance has an `elicitation` property that provides access to e
|
|
|
222
219
|
```typescript
|
|
223
220
|
const mcpClient = new MCPClient({
|
|
224
221
|
/* ...servers configuration... */
|
|
225
|
-
})
|
|
222
|
+
})
|
|
226
223
|
|
|
227
224
|
// Set up elicitation handler
|
|
228
|
-
mcpClient.elicitation.onRequest(
|
|
225
|
+
mcpClient.elicitation.onRequest('serverName', async request => {
|
|
229
226
|
// Handle elicitation request from server
|
|
230
|
-
console.log(
|
|
231
|
-
console.log(
|
|
227
|
+
console.log('Server requests:', request.message)
|
|
228
|
+
console.log('Schema:', request.requestedSchema)
|
|
232
229
|
|
|
233
230
|
// Return user response
|
|
234
231
|
return {
|
|
235
|
-
action:
|
|
236
|
-
content: { name:
|
|
237
|
-
}
|
|
238
|
-
})
|
|
232
|
+
action: 'accept',
|
|
233
|
+
content: { name: 'John Doe', email: 'john@example.com' },
|
|
234
|
+
}
|
|
235
|
+
})
|
|
239
236
|
```
|
|
240
237
|
|
|
241
238
|
#### `elicitation.onRequest(serverName: string, handler: ElicitationHandler)`
|
|
242
239
|
|
|
243
240
|
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.
|
|
244
241
|
|
|
245
|
-
|
|
242
|
+
##### ElicitationHandler Function
|
|
246
243
|
|
|
247
244
|
The handler function receives a request object with:
|
|
248
245
|
|
|
@@ -257,105 +254,103 @@ The handler must return an `ElicitResult` with:
|
|
|
257
254
|
**Example:**
|
|
258
255
|
|
|
259
256
|
```typescript
|
|
260
|
-
mcpClient.elicitation.onRequest(
|
|
261
|
-
console.log(`Server requests: ${request.message}`)
|
|
257
|
+
mcpClient.elicitation.onRequest('serverName', async request => {
|
|
258
|
+
console.log(`Server requests: ${request.message}`)
|
|
262
259
|
|
|
263
260
|
// Example: Simple user input collection
|
|
264
261
|
if (request.requestedSchema.properties.name) {
|
|
265
262
|
// Simulate user accepting and providing data
|
|
266
263
|
return {
|
|
267
|
-
action:
|
|
264
|
+
action: 'accept',
|
|
268
265
|
content: {
|
|
269
|
-
name:
|
|
270
|
-
email:
|
|
266
|
+
name: 'Alice Smith',
|
|
267
|
+
email: 'alice@example.com',
|
|
271
268
|
},
|
|
272
|
-
}
|
|
269
|
+
}
|
|
273
270
|
}
|
|
274
271
|
|
|
275
272
|
// Simulate user declining the request
|
|
276
|
-
return { action:
|
|
277
|
-
})
|
|
273
|
+
return { action: 'decline' }
|
|
274
|
+
})
|
|
278
275
|
```
|
|
279
276
|
|
|
280
277
|
**Complete Interactive Example:**
|
|
281
278
|
|
|
282
279
|
```typescript
|
|
283
|
-
import { MCPClient } from
|
|
284
|
-
import { createInterface } from
|
|
280
|
+
import { MCPClient } from '@mastra/mcp'
|
|
281
|
+
import { createInterface } from 'readline'
|
|
285
282
|
|
|
286
283
|
const readline = createInterface({
|
|
287
284
|
input: process.stdin,
|
|
288
285
|
output: process.stdout,
|
|
289
|
-
})
|
|
286
|
+
})
|
|
290
287
|
|
|
291
288
|
function askQuestion(question: string): Promise<string> {
|
|
292
|
-
return new Promise(
|
|
293
|
-
readline.question(question,
|
|
294
|
-
})
|
|
289
|
+
return new Promise(resolve => {
|
|
290
|
+
readline.question(question, answer => resolve(answer.trim()))
|
|
291
|
+
})
|
|
295
292
|
}
|
|
296
293
|
|
|
297
294
|
const mcpClient = new MCPClient({
|
|
298
295
|
servers: {
|
|
299
296
|
interactiveServer: {
|
|
300
|
-
url: new URL(
|
|
297
|
+
url: new URL('http://localhost:3000/mcp'),
|
|
301
298
|
},
|
|
302
299
|
},
|
|
303
|
-
})
|
|
300
|
+
})
|
|
304
301
|
|
|
305
302
|
// Set up interactive elicitation handler
|
|
306
|
-
await mcpClient.elicitation.onRequest(
|
|
307
|
-
console.log(`\n📋 Server Request: ${request.message}`)
|
|
308
|
-
console.log(
|
|
303
|
+
await mcpClient.elicitation.onRequest('interactiveServer', async request => {
|
|
304
|
+
console.log(`\n📋 Server Request: ${request.message}`)
|
|
305
|
+
console.log('Required information:')
|
|
309
306
|
|
|
310
|
-
const schema = request.requestedSchema
|
|
311
|
-
const properties = schema.properties || {}
|
|
312
|
-
const required = schema.required || []
|
|
313
|
-
const content: Record<string, any> = {}
|
|
307
|
+
const schema = request.requestedSchema
|
|
308
|
+
const properties = schema.properties || {}
|
|
309
|
+
const required = schema.required || []
|
|
310
|
+
const content: Record<string, any> = {}
|
|
314
311
|
|
|
315
312
|
// Collect input for each field
|
|
316
313
|
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
317
|
-
const field = fieldSchema as any
|
|
318
|
-
const isRequired = required.includes(fieldName)
|
|
314
|
+
const field = fieldSchema as any
|
|
315
|
+
const isRequired = required.includes(fieldName)
|
|
319
316
|
|
|
320
|
-
let prompt = `${field.title || fieldName}
|
|
321
|
-
if (field.description) prompt += ` (${field.description})
|
|
322
|
-
if (isRequired) prompt +=
|
|
323
|
-
prompt +=
|
|
317
|
+
let prompt = `${field.title || fieldName}`
|
|
318
|
+
if (field.description) prompt += ` (${field.description})`
|
|
319
|
+
if (isRequired) prompt += ' *required*'
|
|
320
|
+
prompt += ': '
|
|
324
321
|
|
|
325
|
-
const answer = await askQuestion(prompt)
|
|
322
|
+
const answer = await askQuestion(prompt)
|
|
326
323
|
|
|
327
324
|
// Handle cancellation
|
|
328
|
-
if (answer.toLowerCase() ===
|
|
329
|
-
return { action:
|
|
325
|
+
if (answer.toLowerCase() === 'cancel') {
|
|
326
|
+
return { action: 'cancel' }
|
|
330
327
|
}
|
|
331
328
|
|
|
332
329
|
// Validate required fields
|
|
333
|
-
if (answer ===
|
|
334
|
-
console.log(`❌ ${fieldName} is required`)
|
|
335
|
-
return { action:
|
|
330
|
+
if (answer === '' && isRequired) {
|
|
331
|
+
console.log(`❌ ${fieldName} is required`)
|
|
332
|
+
return { action: 'decline' }
|
|
336
333
|
}
|
|
337
334
|
|
|
338
|
-
if (answer !==
|
|
339
|
-
content[fieldName] = answer
|
|
335
|
+
if (answer !== '') {
|
|
336
|
+
content[fieldName] = answer
|
|
340
337
|
}
|
|
341
338
|
}
|
|
342
339
|
|
|
343
340
|
// Confirm submission
|
|
344
|
-
console.log(
|
|
345
|
-
console.log(JSON.stringify(content, null, 2))
|
|
341
|
+
console.log('\n📝 You provided:')
|
|
342
|
+
console.log(JSON.stringify(content, null, 2))
|
|
346
343
|
|
|
347
|
-
const confirm = await askQuestion(
|
|
348
|
-
"\nSubmit this information? (yes/no/cancel): ",
|
|
349
|
-
);
|
|
344
|
+
const confirm = await askQuestion('\nSubmit this information? (yes/no/cancel): ')
|
|
350
345
|
|
|
351
|
-
if (confirm.toLowerCase() ===
|
|
352
|
-
return { action:
|
|
353
|
-
} else if (confirm.toLowerCase() ===
|
|
354
|
-
return { action:
|
|
346
|
+
if (confirm.toLowerCase() === 'yes' || confirm.toLowerCase() === 'y') {
|
|
347
|
+
return { action: 'accept', content }
|
|
348
|
+
} else if (confirm.toLowerCase() === 'cancel') {
|
|
349
|
+
return { action: 'cancel' }
|
|
355
350
|
} else {
|
|
356
|
-
return { action:
|
|
351
|
+
return { action: 'decline' }
|
|
357
352
|
}
|
|
358
|
-
})
|
|
353
|
+
})
|
|
359
354
|
```
|
|
360
355
|
|
|
361
356
|
### `prompts` Property
|
|
@@ -365,14 +360,14 @@ The `MCPClient` instance has a `prompts` property that provides access to prompt
|
|
|
365
360
|
```typescript
|
|
366
361
|
const mcpClient = new MCPClient({
|
|
367
362
|
/* ...servers configuration... */
|
|
368
|
-
})
|
|
363
|
+
})
|
|
369
364
|
|
|
370
365
|
// Access prompt methods via mcpClient.prompts
|
|
371
|
-
const allPromptsByServer = await mcpClient.prompts.list()
|
|
366
|
+
const allPromptsByServer = await mcpClient.prompts.list()
|
|
372
367
|
const { prompt, messages } = await mcpClient.prompts.get({
|
|
373
|
-
serverName:
|
|
374
|
-
name:
|
|
375
|
-
})
|
|
368
|
+
serverName: 'myWeatherServer',
|
|
369
|
+
name: 'current',
|
|
370
|
+
})
|
|
376
371
|
```
|
|
377
372
|
|
|
378
373
|
#### `prompts.list()`
|
|
@@ -386,9 +381,9 @@ async list(): Promise<Record<string, Prompt[]>>
|
|
|
386
381
|
Example:
|
|
387
382
|
|
|
388
383
|
```typescript
|
|
389
|
-
const promptsByServer = await mcpClient.prompts.list()
|
|
384
|
+
const promptsByServer = await mcpClient.prompts.list()
|
|
390
385
|
for (const serverName in promptsByServer) {
|
|
391
|
-
console.log(`Prompts from ${serverName}:`, promptsByServer[serverName])
|
|
386
|
+
console.log(`Prompts from ${serverName}:`, promptsByServer[serverName])
|
|
392
387
|
}
|
|
393
388
|
```
|
|
394
389
|
|
|
@@ -414,12 +409,12 @@ Example:
|
|
|
414
409
|
|
|
415
410
|
```typescript
|
|
416
411
|
const { prompt, messages } = await mcpClient.prompts.get({
|
|
417
|
-
serverName:
|
|
418
|
-
name:
|
|
419
|
-
args: { location:
|
|
420
|
-
})
|
|
421
|
-
console.log(prompt)
|
|
422
|
-
console.log(messages)
|
|
412
|
+
serverName: 'myWeatherServer',
|
|
413
|
+
name: 'current',
|
|
414
|
+
args: { location: 'London' },
|
|
415
|
+
})
|
|
416
|
+
console.log(prompt)
|
|
417
|
+
console.log(messages)
|
|
423
418
|
```
|
|
424
419
|
|
|
425
420
|
#### `prompts.onListChanged(serverName: string, handler: () => void)`
|
|
@@ -433,11 +428,11 @@ async onListChanged(serverName: string, handler: () => void): Promise<void>
|
|
|
433
428
|
Example:
|
|
434
429
|
|
|
435
430
|
```typescript
|
|
436
|
-
mcpClient.prompts.onListChanged(
|
|
437
|
-
console.log(
|
|
431
|
+
mcpClient.prompts.onListChanged('myWeatherServer', () => {
|
|
432
|
+
console.log('Prompt list changed on myWeatherServer.')
|
|
438
433
|
// You should re-fetch the list of prompts
|
|
439
434
|
// await mcpClient.prompts.list();
|
|
440
|
-
})
|
|
435
|
+
})
|
|
441
436
|
```
|
|
442
437
|
|
|
443
438
|
### `progress` Property
|
|
@@ -453,14 +448,14 @@ const mcpClient = new MCPClient({
|
|
|
453
448
|
enableProgressTracking: true,
|
|
454
449
|
},
|
|
455
450
|
},
|
|
456
|
-
})
|
|
451
|
+
})
|
|
457
452
|
|
|
458
453
|
// Subscribe to progress updates for a specific server
|
|
459
|
-
await mcpClient.progress.onUpdate('myServer',
|
|
460
|
-
console.log('📊 Progress:', params.progress, '/', params.total)
|
|
461
|
-
if (params.message) console.log('Message:', params.message)
|
|
462
|
-
if (params.progressToken) console.log('Token:', params.progressToken)
|
|
463
|
-
})
|
|
454
|
+
await mcpClient.progress.onUpdate('myServer', params => {
|
|
455
|
+
console.log('📊 Progress:', params.progress, '/', params.total)
|
|
456
|
+
if (params.message) console.log('Message:', params.message)
|
|
457
|
+
if (params.progressToken) console.log('Token:', params.progressToken)
|
|
458
|
+
})
|
|
464
459
|
```
|
|
465
460
|
|
|
466
461
|
#### `progress.onUpdate(serverName: string, handler)`
|
|
@@ -494,7 +489,7 @@ const mcpClient = new MCPClient({
|
|
|
494
489
|
enableProgressTracking: false,
|
|
495
490
|
},
|
|
496
491
|
},
|
|
497
|
-
})
|
|
492
|
+
})
|
|
498
493
|
```
|
|
499
494
|
|
|
500
495
|
## Elicitation
|
|
@@ -514,29 +509,29 @@ Elicitation is a feature that allows MCP servers to request structured informati
|
|
|
514
509
|
You must set up an elicitation handler before tools that use elicitation are called:
|
|
515
510
|
|
|
516
511
|
```typescript
|
|
517
|
-
import { MCPClient } from
|
|
512
|
+
import { MCPClient } from '@mastra/mcp'
|
|
518
513
|
|
|
519
514
|
const mcpClient = new MCPClient({
|
|
520
515
|
servers: {
|
|
521
516
|
interactiveServer: {
|
|
522
|
-
url: new URL(
|
|
517
|
+
url: new URL('http://localhost:3000/mcp'),
|
|
523
518
|
},
|
|
524
519
|
},
|
|
525
|
-
})
|
|
520
|
+
})
|
|
526
521
|
|
|
527
522
|
// Set up elicitation handler
|
|
528
|
-
mcpClient.elicitation.onRequest(
|
|
523
|
+
mcpClient.elicitation.onRequest('interactiveServer', async request => {
|
|
529
524
|
// Handle the server's request for user input
|
|
530
|
-
console.log(`Server needs: ${request.message}`)
|
|
525
|
+
console.log(`Server needs: ${request.message}`)
|
|
531
526
|
|
|
532
527
|
// Your logic to collect user input
|
|
533
|
-
const userData = await collectUserInput(request.requestedSchema)
|
|
528
|
+
const userData = await collectUserInput(request.requestedSchema)
|
|
534
529
|
|
|
535
530
|
return {
|
|
536
|
-
action:
|
|
531
|
+
action: 'accept',
|
|
537
532
|
content: userData,
|
|
538
|
-
}
|
|
539
|
-
})
|
|
533
|
+
}
|
|
534
|
+
})
|
|
540
535
|
```
|
|
541
536
|
|
|
542
537
|
### Response Types
|
|
@@ -547,21 +542,21 @@ Your elicitation handler must return one of three response types:
|
|
|
547
542
|
|
|
548
543
|
```typescript
|
|
549
544
|
return {
|
|
550
|
-
action:
|
|
551
|
-
content: { name:
|
|
552
|
-
}
|
|
545
|
+
action: 'accept',
|
|
546
|
+
content: { name: 'John Doe', email: 'john@example.com' },
|
|
547
|
+
}
|
|
553
548
|
```
|
|
554
549
|
|
|
555
550
|
- **Decline**: User explicitly declined to provide the information
|
|
556
551
|
|
|
557
552
|
```typescript
|
|
558
|
-
return { action:
|
|
553
|
+
return { action: 'decline' }
|
|
559
554
|
```
|
|
560
555
|
|
|
561
556
|
- **Cancel**: User dismissed or cancelled the request
|
|
562
557
|
|
|
563
558
|
```typescript
|
|
564
|
-
return { action:
|
|
559
|
+
return { action: 'cancel' }
|
|
565
560
|
```
|
|
566
561
|
|
|
567
562
|
### Schema-Based Input Collection
|
|
@@ -569,13 +564,13 @@ Your elicitation handler must return one of three response types:
|
|
|
569
564
|
The `requestedSchema` provides structure for the data the server needs:
|
|
570
565
|
|
|
571
566
|
```typescript
|
|
572
|
-
await mcpClient.elicitation.onRequest(
|
|
573
|
-
const { properties, required = [] } = request.requestedSchema
|
|
574
|
-
const content: Record<string, any> = {}
|
|
567
|
+
await mcpClient.elicitation.onRequest('interactiveServer', async request => {
|
|
568
|
+
const { properties, required = [] } = request.requestedSchema
|
|
569
|
+
const content: Record<string, any> = {}
|
|
575
570
|
|
|
576
571
|
for (const [fieldName, fieldSchema] of Object.entries(properties || {})) {
|
|
577
|
-
const field = fieldSchema as any
|
|
578
|
-
const isRequired = required.includes(fieldName)
|
|
572
|
+
const field = fieldSchema as any
|
|
573
|
+
const isRequired = required.includes(fieldName)
|
|
579
574
|
|
|
580
575
|
// Collect input based on field type and requirements
|
|
581
576
|
const value = await promptUser({
|
|
@@ -586,15 +581,15 @@ await mcpClient.elicitation.onRequest("interactiveServer", async (request) => {
|
|
|
586
581
|
required: isRequired,
|
|
587
582
|
format: field.format,
|
|
588
583
|
enum: field.enum,
|
|
589
|
-
})
|
|
584
|
+
})
|
|
590
585
|
|
|
591
586
|
if (value !== null) {
|
|
592
|
-
content[fieldName] = value
|
|
587
|
+
content[fieldName] = value
|
|
593
588
|
}
|
|
594
589
|
}
|
|
595
590
|
|
|
596
|
-
return { action:
|
|
597
|
-
})
|
|
591
|
+
return { action: 'accept', content }
|
|
592
|
+
})
|
|
598
593
|
```
|
|
599
594
|
|
|
600
595
|
### Best Practices
|
|
@@ -610,32 +605,32 @@ await mcpClient.elicitation.onRequest("interactiveServer", async (request) => {
|
|
|
610
605
|
For connecting to MCP servers that require OAuth authentication per the [MCP Auth Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization), use the `MCPOAuthClientProvider`:
|
|
611
606
|
|
|
612
607
|
```typescript
|
|
613
|
-
import { MCPClient, MCPOAuthClientProvider } from
|
|
608
|
+
import { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp'
|
|
614
609
|
|
|
615
610
|
// Create an OAuth provider
|
|
616
611
|
const oauthProvider = new MCPOAuthClientProvider({
|
|
617
|
-
redirectUrl:
|
|
612
|
+
redirectUrl: 'http://localhost:3000/oauth/callback',
|
|
618
613
|
clientMetadata: {
|
|
619
|
-
redirect_uris: [
|
|
620
|
-
client_name:
|
|
621
|
-
grant_types: [
|
|
622
|
-
response_types: [
|
|
614
|
+
redirect_uris: ['http://localhost:3000/oauth/callback'],
|
|
615
|
+
client_name: 'My MCP Client',
|
|
616
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
617
|
+
response_types: ['code'],
|
|
623
618
|
},
|
|
624
|
-
onRedirectToAuthorization:
|
|
619
|
+
onRedirectToAuthorization: url => {
|
|
625
620
|
// Handle authorization redirect (open browser, redirect response, etc.)
|
|
626
|
-
console.log(`Please visit: ${url}`)
|
|
621
|
+
console.log(`Please visit: ${url}`)
|
|
627
622
|
},
|
|
628
|
-
})
|
|
623
|
+
})
|
|
629
624
|
|
|
630
625
|
// Use the provider with MCPClient
|
|
631
626
|
const client = new MCPClient({
|
|
632
627
|
servers: {
|
|
633
628
|
protectedServer: {
|
|
634
|
-
url: new URL(
|
|
629
|
+
url: new URL('https://mcp.example.com/mcp'),
|
|
635
630
|
authProvider: oauthProvider,
|
|
636
631
|
},
|
|
637
632
|
},
|
|
638
|
-
})
|
|
633
|
+
})
|
|
639
634
|
```
|
|
640
635
|
|
|
641
636
|
### Quick Token Provider
|
|
@@ -643,24 +638,24 @@ const client = new MCPClient({
|
|
|
643
638
|
For testing or when you already have a valid access token:
|
|
644
639
|
|
|
645
640
|
```typescript
|
|
646
|
-
import { MCPClient, createSimpleTokenProvider } from
|
|
641
|
+
import { MCPClient, createSimpleTokenProvider } from '@mastra/mcp'
|
|
647
642
|
|
|
648
|
-
const provider = createSimpleTokenProvider(
|
|
649
|
-
redirectUrl:
|
|
643
|
+
const provider = createSimpleTokenProvider('your-access-token', {
|
|
644
|
+
redirectUrl: 'http://localhost:3000/callback',
|
|
650
645
|
clientMetadata: {
|
|
651
|
-
redirect_uris: [
|
|
652
|
-
client_name:
|
|
646
|
+
redirect_uris: ['http://localhost:3000/callback'],
|
|
647
|
+
client_name: 'Test Client',
|
|
653
648
|
},
|
|
654
|
-
})
|
|
649
|
+
})
|
|
655
650
|
|
|
656
651
|
const client = new MCPClient({
|
|
657
652
|
servers: {
|
|
658
653
|
testServer: {
|
|
659
|
-
url: new URL(
|
|
654
|
+
url: new URL('https://mcp.example.com/mcp'),
|
|
660
655
|
authProvider: provider,
|
|
661
656
|
},
|
|
662
657
|
},
|
|
663
|
-
})
|
|
658
|
+
})
|
|
664
659
|
```
|
|
665
660
|
|
|
666
661
|
### Custom Token Storage
|
|
@@ -668,39 +663,44 @@ const client = new MCPClient({
|
|
|
668
663
|
For persistent token storage across sessions, implement the `OAuthStorage` interface:
|
|
669
664
|
|
|
670
665
|
```typescript
|
|
671
|
-
import { MCPOAuthClientProvider, OAuthStorage } from
|
|
666
|
+
import { MCPOAuthClientProvider, OAuthStorage } from '@mastra/mcp'
|
|
672
667
|
|
|
673
668
|
class DatabaseOAuthStorage implements OAuthStorage {
|
|
674
|
-
constructor(
|
|
669
|
+
constructor(
|
|
670
|
+
private db: Database,
|
|
671
|
+
private userId: string,
|
|
672
|
+
) {}
|
|
675
673
|
|
|
676
674
|
async set(key: string, value: string): Promise<void> {
|
|
677
675
|
await this.db.query(
|
|
678
|
-
|
|
679
|
-
[this.userId, key, value, value]
|
|
680
|
-
)
|
|
676
|
+
'INSERT INTO oauth_tokens (user_id, key, value) VALUES (?, ?, ?) ON CONFLICT DO UPDATE SET value = ?',
|
|
677
|
+
[this.userId, key, value, value],
|
|
678
|
+
)
|
|
681
679
|
}
|
|
682
680
|
|
|
683
681
|
async get(key: string): Promise<string | undefined> {
|
|
684
682
|
const result = await this.db.query(
|
|
685
|
-
|
|
686
|
-
[this.userId, key]
|
|
687
|
-
)
|
|
688
|
-
return result?.[0]?.value
|
|
683
|
+
'SELECT value FROM oauth_tokens WHERE user_id = ? AND key = ?',
|
|
684
|
+
[this.userId, key],
|
|
685
|
+
)
|
|
686
|
+
return result?.[0]?.value
|
|
689
687
|
}
|
|
690
688
|
|
|
691
689
|
async delete(key: string): Promise<void> {
|
|
692
|
-
await this.db.query(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
)
|
|
690
|
+
await this.db.query('DELETE FROM oauth_tokens WHERE user_id = ? AND key = ?', [
|
|
691
|
+
this.userId,
|
|
692
|
+
key,
|
|
693
|
+
])
|
|
696
694
|
}
|
|
697
695
|
}
|
|
698
696
|
|
|
699
697
|
const provider = new MCPOAuthClientProvider({
|
|
700
|
-
redirectUrl:
|
|
701
|
-
clientMetadata: {
|
|
702
|
-
|
|
703
|
-
}
|
|
698
|
+
redirectUrl: 'http://localhost:3000/callback',
|
|
699
|
+
clientMetadata: {
|
|
700
|
+
/* ... */
|
|
701
|
+
},
|
|
702
|
+
storage: new DatabaseOAuthStorage(db, 'user-123'),
|
|
703
|
+
})
|
|
704
704
|
```
|
|
705
705
|
|
|
706
706
|
## Examples
|
|
@@ -710,74 +710,69 @@ const provider = new MCPOAuthClientProvider({
|
|
|
710
710
|
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:
|
|
711
711
|
|
|
712
712
|
```typescript
|
|
713
|
-
import { MCPClient } from
|
|
714
|
-
import { Agent } from
|
|
713
|
+
import { MCPClient } from '@mastra/mcp'
|
|
714
|
+
import { Agent } from '@mastra/core/agent'
|
|
715
715
|
|
|
716
716
|
const mcp = new MCPClient({
|
|
717
717
|
servers: {
|
|
718
718
|
stockPrice: {
|
|
719
|
-
command:
|
|
720
|
-
args: [
|
|
719
|
+
command: 'npx',
|
|
720
|
+
args: ['tsx', 'stock-price.ts'],
|
|
721
721
|
env: {
|
|
722
|
-
API_KEY:
|
|
722
|
+
API_KEY: 'your-api-key',
|
|
723
723
|
},
|
|
724
|
-
log:
|
|
725
|
-
console.log(`[${logMessage.level}] ${logMessage.message}`)
|
|
724
|
+
log: logMessage => {
|
|
725
|
+
console.log(`[${logMessage.level}] ${logMessage.message}`)
|
|
726
726
|
},
|
|
727
727
|
},
|
|
728
728
|
weather: {
|
|
729
|
-
url: new URL(
|
|
729
|
+
url: new URL('http://localhost:8080/sse'),
|
|
730
730
|
},
|
|
731
731
|
},
|
|
732
732
|
timeout: 30000, // Global 30s timeout
|
|
733
|
-
})
|
|
733
|
+
})
|
|
734
734
|
|
|
735
735
|
// Create an agent with access to all tools
|
|
736
736
|
const agent = new Agent({
|
|
737
|
-
id:
|
|
738
|
-
name:
|
|
739
|
-
instructions:
|
|
740
|
-
model:
|
|
737
|
+
id: 'multi-tool-agent',
|
|
738
|
+
name: 'Multi-tool Agent',
|
|
739
|
+
instructions: 'You have access to multiple tool servers.',
|
|
740
|
+
model: 'openai/gpt-5.1',
|
|
741
741
|
tools: await mcp.listTools(),
|
|
742
|
-
})
|
|
742
|
+
})
|
|
743
743
|
|
|
744
744
|
// Example of using resource methods
|
|
745
745
|
async function checkWeatherResource() {
|
|
746
746
|
try {
|
|
747
|
-
const weatherResources = await mcp.resources.list()
|
|
747
|
+
const weatherResources = await mcp.resources.list()
|
|
748
748
|
if (weatherResources.weather && weatherResources.weather.length > 0) {
|
|
749
|
-
const currentWeatherURI = weatherResources.weather[0].uri
|
|
750
|
-
const weatherData = await mcp.resources.read(
|
|
751
|
-
|
|
752
|
-
currentWeatherURI,
|
|
753
|
-
);
|
|
754
|
-
console.log("Weather data:", weatherData.contents[0].text);
|
|
749
|
+
const currentWeatherURI = weatherResources.weather[0].uri
|
|
750
|
+
const weatherData = await mcp.resources.read('weather', currentWeatherURI)
|
|
751
|
+
console.log('Weather data:', weatherData.contents[0].text)
|
|
755
752
|
}
|
|
756
753
|
} catch (error) {
|
|
757
|
-
console.error(
|
|
754
|
+
console.error('Error fetching weather resource:', error)
|
|
758
755
|
}
|
|
759
756
|
}
|
|
760
|
-
checkWeatherResource()
|
|
757
|
+
checkWeatherResource()
|
|
761
758
|
|
|
762
759
|
// Example of using prompt methods
|
|
763
760
|
async function checkWeatherPrompt() {
|
|
764
761
|
try {
|
|
765
|
-
const weatherPrompts = await mcp.prompts.list()
|
|
762
|
+
const weatherPrompts = await mcp.prompts.list()
|
|
766
763
|
if (weatherPrompts.weather && weatherPrompts.weather.length > 0) {
|
|
767
|
-
const currentWeatherPrompt = weatherPrompts.weather.find(
|
|
768
|
-
(p) => p.name === "current",
|
|
769
|
-
);
|
|
764
|
+
const currentWeatherPrompt = weatherPrompts.weather.find(p => p.name === 'current')
|
|
770
765
|
if (currentWeatherPrompt) {
|
|
771
|
-
console.log(
|
|
766
|
+
console.log('Weather prompt:', currentWeatherPrompt)
|
|
772
767
|
} else {
|
|
773
|
-
console.log(
|
|
768
|
+
console.log('Current weather prompt not found')
|
|
774
769
|
}
|
|
775
770
|
}
|
|
776
771
|
} catch (error) {
|
|
777
|
-
console.error(
|
|
772
|
+
console.error('Error fetching weather prompt:', error)
|
|
778
773
|
}
|
|
779
774
|
}
|
|
780
|
-
checkWeatherPrompt()
|
|
775
|
+
checkWeatherPrompt()
|
|
781
776
|
```
|
|
782
777
|
|
|
783
778
|
### Dynamic toolsets
|
|
@@ -785,30 +780,30 @@ checkWeatherPrompt();
|
|
|
785
780
|
When you need a new MCP connection for each user, use `listToolsets()` and add the tools when calling stream or generate:
|
|
786
781
|
|
|
787
782
|
```typescript
|
|
788
|
-
import { Agent } from
|
|
789
|
-
import { MCPClient } from
|
|
783
|
+
import { Agent } from '@mastra/core/agent'
|
|
784
|
+
import { MCPClient } from '@mastra/mcp'
|
|
790
785
|
|
|
791
786
|
// Create the agent first, without any tools
|
|
792
787
|
const agent = new Agent({
|
|
793
|
-
id:
|
|
794
|
-
name:
|
|
795
|
-
instructions:
|
|
796
|
-
model:
|
|
797
|
-
})
|
|
788
|
+
id: 'multi-tool-agent',
|
|
789
|
+
name: 'Multi-tool Agent',
|
|
790
|
+
instructions: 'You help users check stocks and weather.',
|
|
791
|
+
model: 'openai/gpt-5.1',
|
|
792
|
+
})
|
|
798
793
|
|
|
799
794
|
// Later, configure MCP with user-specific settings
|
|
800
795
|
const mcp = new MCPClient({
|
|
801
796
|
servers: {
|
|
802
797
|
stockPrice: {
|
|
803
|
-
command:
|
|
804
|
-
args: [
|
|
798
|
+
command: 'npx',
|
|
799
|
+
args: ['tsx', 'stock-price.ts'],
|
|
805
800
|
env: {
|
|
806
|
-
API_KEY:
|
|
801
|
+
API_KEY: 'user-123-api-key',
|
|
807
802
|
},
|
|
808
803
|
timeout: 20000, // Server-specific timeout
|
|
809
804
|
},
|
|
810
805
|
weather: {
|
|
811
|
-
url: new URL(
|
|
806
|
+
url: new URL('http://localhost:8080/sse'),
|
|
812
807
|
requestInit: {
|
|
813
808
|
headers: {
|
|
814
809
|
Authorization: `Bearer user-123-token`,
|
|
@@ -816,15 +811,12 @@ const mcp = new MCPClient({
|
|
|
816
811
|
},
|
|
817
812
|
},
|
|
818
813
|
},
|
|
819
|
-
})
|
|
814
|
+
})
|
|
820
815
|
|
|
821
816
|
// Pass all toolsets to stream() or generate()
|
|
822
|
-
const response = await agent.stream(
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
toolsets: await mcp.listToolsets(),
|
|
826
|
-
},
|
|
827
|
-
);
|
|
817
|
+
const response = await agent.stream('How is AAPL doing and what is the weather?', {
|
|
818
|
+
toolsets: await mcp.listToolsets(),
|
|
819
|
+
})
|
|
828
820
|
```
|
|
829
821
|
|
|
830
822
|
## Instance Management
|
|
@@ -844,31 +836,31 @@ const mcp1 = new MCPClient({
|
|
|
844
836
|
servers: {
|
|
845
837
|
/* ... */
|
|
846
838
|
},
|
|
847
|
-
})
|
|
839
|
+
})
|
|
848
840
|
|
|
849
841
|
// Second instance with same config - Will throw an error
|
|
850
842
|
const mcp2 = new MCPClient({
|
|
851
843
|
servers: {
|
|
852
844
|
/* ... */
|
|
853
845
|
},
|
|
854
|
-
})
|
|
846
|
+
})
|
|
855
847
|
|
|
856
848
|
// To fix, either:
|
|
857
849
|
// 1. Add unique IDs
|
|
858
850
|
const mcp3 = new MCPClient({
|
|
859
|
-
id:
|
|
851
|
+
id: 'instance-1',
|
|
860
852
|
servers: {
|
|
861
853
|
/* ... */
|
|
862
854
|
},
|
|
863
|
-
})
|
|
855
|
+
})
|
|
864
856
|
|
|
865
857
|
// 2. Or disconnect before recreating
|
|
866
|
-
await mcp1.disconnect()
|
|
858
|
+
await mcp1.disconnect()
|
|
867
859
|
const mcp4 = new MCPClient({
|
|
868
860
|
servers: {
|
|
869
861
|
/* ... */
|
|
870
862
|
},
|
|
871
|
-
})
|
|
863
|
+
})
|
|
872
864
|
```
|
|
873
865
|
|
|
874
866
|
## Server Lifecycle
|
|
@@ -889,10 +881,10 @@ When `fetch` is provided, `requestInit`, `eventSourceInit`, and `authProvider` b
|
|
|
889
881
|
const mcpClient = new MCPClient({
|
|
890
882
|
servers: {
|
|
891
883
|
apiServer: {
|
|
892
|
-
url: new URL(
|
|
884
|
+
url: new URL('https://api.example.com/mcp'),
|
|
893
885
|
fetch: async (url, init) => {
|
|
894
886
|
// Refresh token on each request
|
|
895
|
-
const token = await getAuthToken()
|
|
887
|
+
const token = await getAuthToken() // Your token refresh logic
|
|
896
888
|
|
|
897
889
|
return fetch(url, {
|
|
898
890
|
...init,
|
|
@@ -900,11 +892,11 @@ const mcpClient = new MCPClient({
|
|
|
900
892
|
...init?.headers,
|
|
901
893
|
Authorization: `Bearer ${token}`,
|
|
902
894
|
},
|
|
903
|
-
})
|
|
895
|
+
})
|
|
904
896
|
},
|
|
905
897
|
},
|
|
906
898
|
},
|
|
907
|
-
})
|
|
899
|
+
})
|
|
908
900
|
```
|
|
909
901
|
|
|
910
902
|
## Using SSE Request Headers
|
|
@@ -916,44 +908,44 @@ When using the legacy SSE MCP transport, you must configure both `requestInit` a
|
|
|
916
908
|
const sseClient = new MCPClient({
|
|
917
909
|
servers: {
|
|
918
910
|
exampleServer: {
|
|
919
|
-
url: new URL(
|
|
911
|
+
url: new URL('https://your-mcp-server.com/sse'),
|
|
920
912
|
// Note: requestInit alone isn't enough for SSE
|
|
921
913
|
requestInit: {
|
|
922
914
|
headers: {
|
|
923
|
-
Authorization:
|
|
915
|
+
Authorization: 'Bearer your-token',
|
|
924
916
|
},
|
|
925
917
|
},
|
|
926
918
|
// This is also required for SSE connections with custom headers
|
|
927
919
|
eventSourceInit: {
|
|
928
920
|
fetch(input: Request | URL | string, init?: RequestInit) {
|
|
929
|
-
const headers = new Headers(init?.headers || {})
|
|
930
|
-
headers.set(
|
|
921
|
+
const headers = new Headers(init?.headers || {})
|
|
922
|
+
headers.set('Authorization', 'Bearer your-token')
|
|
931
923
|
return fetch(input, {
|
|
932
924
|
...init,
|
|
933
925
|
headers,
|
|
934
|
-
})
|
|
926
|
+
})
|
|
935
927
|
},
|
|
936
928
|
},
|
|
937
929
|
},
|
|
938
930
|
},
|
|
939
|
-
})
|
|
931
|
+
})
|
|
940
932
|
|
|
941
933
|
// Option 2: Using custom fetch (simpler, works for both Streamable HTTP and SSE)
|
|
942
934
|
const sseClientWithFetch = new MCPClient({
|
|
943
935
|
servers: {
|
|
944
936
|
exampleServer: {
|
|
945
|
-
url: new URL(
|
|
937
|
+
url: new URL('https://your-mcp-server.com/sse'),
|
|
946
938
|
fetch: async (url, init) => {
|
|
947
|
-
const headers = new Headers(init?.headers || {})
|
|
948
|
-
headers.set(
|
|
939
|
+
const headers = new Headers(init?.headers || {})
|
|
940
|
+
headers.set('Authorization', 'Bearer your-token')
|
|
949
941
|
return fetch(url, {
|
|
950
942
|
...init,
|
|
951
943
|
headers,
|
|
952
|
-
})
|
|
944
|
+
})
|
|
953
945
|
},
|
|
954
946
|
},
|
|
955
947
|
},
|
|
956
|
-
})
|
|
948
|
+
})
|
|
957
949
|
```
|
|
958
950
|
|
|
959
951
|
## Related Information
|