@mastra/mcp 0.10.5-alpha.1 → 0.10.5
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +38 -0
- package/dist/_tsup-dts-rollup.d.cts +84 -51
- package/dist/_tsup-dts-rollup.d.ts +84 -51
- package/dist/index.cjs +403 -398
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +404 -399
- package/integration-tests/node_modules/.bin/mastra +21 -0
- package/package.json +3 -3
- package/src/client/client.test.ts +310 -1
- package/src/client/client.ts +21 -2
- package/src/client/configuration.ts +22 -2
- package/src/client/elicitationActions.ts +26 -0
- package/src/client/index.ts +1 -1
- package/src/server/server.test.ts +349 -0
- package/src/server/server.ts +424 -457
- package/src/server/types.ts +25 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*|*MINGW*|*MSYS*)
|
|
6
|
+
if command -v cygpath > /dev/null 2>&1; then
|
|
7
|
+
basedir=`cygpath -w "$basedir"`
|
|
8
|
+
fi
|
|
9
|
+
;;
|
|
10
|
+
esac
|
|
11
|
+
|
|
12
|
+
if [ -z "$NODE_PATH" ]; then
|
|
13
|
+
export NODE_PATH="/home/runner/work/mastra/mastra/packages/cli/dist/node_modules:/home/runner/work/mastra/mastra/packages/cli/node_modules:/home/runner/work/mastra/mastra/packages/node_modules:/home/runner/work/mastra/mastra/node_modules:/home/runner/work/mastra/node_modules:/home/runner/work/node_modules:/home/runner/node_modules:/home/node_modules:/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/node_modules"
|
|
14
|
+
else
|
|
15
|
+
export NODE_PATH="/home/runner/work/mastra/mastra/packages/cli/dist/node_modules:/home/runner/work/mastra/mastra/packages/cli/node_modules:/home/runner/work/mastra/mastra/packages/node_modules:/home/runner/work/mastra/mastra/node_modules:/home/runner/work/mastra/node_modules:/home/runner/work/node_modules:/home/runner/node_modules:/home/node_modules:/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
|
+
fi
|
|
17
|
+
if [ -x "$basedir/node" ]; then
|
|
18
|
+
exec "$basedir/node" "$basedir/../mastra/dist/index.js" "$@"
|
|
19
|
+
else
|
|
20
|
+
exec node "$basedir/../mastra/dist/index.js" "$@"
|
|
21
|
+
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/mcp",
|
|
3
|
-
"version": "0.10.5
|
|
3
|
+
"version": "0.10.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"vitest": "^3.2.3",
|
|
51
51
|
"zod": "^3.25.67",
|
|
52
52
|
"zod-to-json-schema": "^3.24.5",
|
|
53
|
-
"@internal/lint": "0.0.
|
|
54
|
-
"@mastra/core": "0.10.7
|
|
53
|
+
"@internal/lint": "0.0.14",
|
|
54
|
+
"@mastra/core": "0.10.7"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|
|
@@ -5,7 +5,7 @@ import type { AddressInfo } from 'node:net';
|
|
|
5
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
6
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
7
7
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
|
|
11
11
|
import { InternalMastraMCPClient } from './client.js';
|
|
@@ -208,3 +208,312 @@ describe('MastraMCPClient with Streamable HTTP', () => {
|
|
|
208
208
|
});
|
|
209
209
|
});
|
|
210
210
|
});
|
|
211
|
+
|
|
212
|
+
describe('MastraMCPClient - Elicitation Tests', () => {
|
|
213
|
+
let testServer: {
|
|
214
|
+
httpServer: HttpServer;
|
|
215
|
+
mcpServer: McpServer;
|
|
216
|
+
serverTransport: StreamableHTTPServerTransport;
|
|
217
|
+
baseUrl: URL;
|
|
218
|
+
};
|
|
219
|
+
let client: InternalMastraMCPClient;
|
|
220
|
+
|
|
221
|
+
beforeEach(async () => {
|
|
222
|
+
testServer = await setupTestServer(false);
|
|
223
|
+
|
|
224
|
+
// Add elicitation-enabled tools to the test server
|
|
225
|
+
testServer.mcpServer.tool(
|
|
226
|
+
'collectUserInfo',
|
|
227
|
+
'Collects user information through elicitation',
|
|
228
|
+
{
|
|
229
|
+
message: z.string().describe('Message to show to user').default('Please provide your information'),
|
|
230
|
+
},
|
|
231
|
+
async ({ message }): Promise<CallToolResult> => {
|
|
232
|
+
const result = await testServer.mcpServer.server.elicitInput({
|
|
233
|
+
message: message,
|
|
234
|
+
requestedSchema: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
properties: {
|
|
237
|
+
name: { type: 'string', title: 'Name' },
|
|
238
|
+
email: { type: 'string', title: 'Email', format: 'email' },
|
|
239
|
+
},
|
|
240
|
+
required: ['name'],
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
246
|
+
};
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
testServer.mcpServer.tool(
|
|
251
|
+
'collectSensitiveInfo',
|
|
252
|
+
'Collects sensitive information that might be rejected',
|
|
253
|
+
{
|
|
254
|
+
message: z.string().describe('Message to show to user').default('Please provide sensitive information'),
|
|
255
|
+
},
|
|
256
|
+
async ({ message }): Promise<CallToolResult> => {
|
|
257
|
+
const result = await testServer.mcpServer.server.elicitInput({
|
|
258
|
+
message: message,
|
|
259
|
+
requestedSchema: {
|
|
260
|
+
type: 'object',
|
|
261
|
+
properties: {
|
|
262
|
+
ssn: { type: 'string', title: 'Social Security Number' },
|
|
263
|
+
creditCard: { type: 'string', title: 'Credit Card Number' },
|
|
264
|
+
},
|
|
265
|
+
required: ['ssn'],
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
testServer.mcpServer.tool(
|
|
276
|
+
'collectOptionalInfo',
|
|
277
|
+
'Collects optional information that might be cancelled',
|
|
278
|
+
{
|
|
279
|
+
message: z.string().describe('Message to show to user').default('Optional information request'),
|
|
280
|
+
},
|
|
281
|
+
async ({ message }): Promise<CallToolResult> => {
|
|
282
|
+
const result = await testServer.mcpServer.server.elicitInput({
|
|
283
|
+
message: message,
|
|
284
|
+
requestedSchema: {
|
|
285
|
+
type: 'object',
|
|
286
|
+
properties: {
|
|
287
|
+
feedback: { type: 'string', title: 'Feedback' },
|
|
288
|
+
rating: { type: 'number', title: 'Rating', minimum: 1, maximum: 5 },
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
295
|
+
};
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
afterEach(async () => {
|
|
301
|
+
await client?.disconnect().catch(() => {});
|
|
302
|
+
await testServer?.mcpServer.close().catch(() => {});
|
|
303
|
+
await testServer?.serverTransport.close().catch(() => {});
|
|
304
|
+
testServer?.httpServer.close();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should handle elicitation request with accept response', async () => {
|
|
308
|
+
const mockHandler = vi.fn(async (request) => {
|
|
309
|
+
expect(request.message).toBe('Please provide your information');
|
|
310
|
+
expect(request.requestedSchema).toBeDefined();
|
|
311
|
+
expect(request.requestedSchema.properties.name).toBeDefined();
|
|
312
|
+
expect(request.requestedSchema.properties.email).toBeDefined();
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
action: 'accept' as const,
|
|
316
|
+
content: {
|
|
317
|
+
name: 'John Doe',
|
|
318
|
+
email: 'john@example.com',
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
client = new InternalMastraMCPClient({
|
|
324
|
+
name: 'elicitation-accept-client',
|
|
325
|
+
server: {
|
|
326
|
+
url: testServer.baseUrl,
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
client.elicitation.onRequest(mockHandler);
|
|
330
|
+
await client.connect();
|
|
331
|
+
|
|
332
|
+
// Get the tools and call the elicitation tool
|
|
333
|
+
const tools = await client.tools();
|
|
334
|
+
const collectUserInfoTool = tools['collectUserInfo'];
|
|
335
|
+
expect(collectUserInfoTool).toBeDefined();
|
|
336
|
+
|
|
337
|
+
// Call the tool which will trigger elicitation
|
|
338
|
+
const result = await collectUserInfoTool.execute({
|
|
339
|
+
context: { message: 'Please provide your information' }
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
console.log('result', result);
|
|
343
|
+
|
|
344
|
+
expect(mockHandler).toHaveBeenCalledTimes(1);
|
|
345
|
+
expect(result.content).toBeDefined();
|
|
346
|
+
expect(result.content[0].type).toBe('text');
|
|
347
|
+
|
|
348
|
+
const elicitationResult = JSON.parse(result.content[0].text);
|
|
349
|
+
expect(elicitationResult.action).toBe('accept');
|
|
350
|
+
expect(elicitationResult.content).toEqual({
|
|
351
|
+
name: 'John Doe',
|
|
352
|
+
email: 'john@example.com',
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should handle elicitation request with reject response', async () => {
|
|
357
|
+
const mockHandler = vi.fn(async (request) => {
|
|
358
|
+
expect(request.message).toBe('Please provide sensitive information');
|
|
359
|
+
return { action: 'reject' as const };
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
client = new InternalMastraMCPClient({
|
|
363
|
+
name: 'elicitation-reject-client',
|
|
364
|
+
server: {
|
|
365
|
+
url: testServer.baseUrl,
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
client.elicitation.onRequest(mockHandler);
|
|
369
|
+
await client.connect();
|
|
370
|
+
|
|
371
|
+
// Get the tools and call the sensitive info tool
|
|
372
|
+
const tools = await client.tools();
|
|
373
|
+
const collectSensitiveInfoTool = tools['collectSensitiveInfo'];
|
|
374
|
+
expect(collectSensitiveInfoTool).toBeDefined();
|
|
375
|
+
|
|
376
|
+
// Call the tool which will trigger elicitation
|
|
377
|
+
const result = await collectSensitiveInfoTool.execute({
|
|
378
|
+
context: { message: 'Please provide sensitive information' }
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
expect(mockHandler).toHaveBeenCalledTimes(1);
|
|
382
|
+
expect(result.content).toBeDefined();
|
|
383
|
+
expect(result.content[0].type).toBe('text');
|
|
384
|
+
|
|
385
|
+
const elicitationResult = JSON.parse(result.content[0].text);
|
|
386
|
+
expect(elicitationResult.action).toBe('reject');
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should handle elicitation request with cancel response', async () => {
|
|
390
|
+
const mockHandler = vi.fn(async (_request) => {
|
|
391
|
+
return { action: 'cancel' as const };
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
client = new InternalMastraMCPClient({
|
|
395
|
+
name: 'elicitation-cancel-client',
|
|
396
|
+
server: {
|
|
397
|
+
url: testServer.baseUrl,
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
client.elicitation.onRequest(mockHandler);
|
|
401
|
+
await client.connect();
|
|
402
|
+
|
|
403
|
+
// Get the tools and call the optional info tool
|
|
404
|
+
const tools = await client.tools();
|
|
405
|
+
const collectOptionalInfoTool = tools['collectOptionalInfo'];
|
|
406
|
+
expect(collectOptionalInfoTool).toBeDefined();
|
|
407
|
+
|
|
408
|
+
// Call the tool which will trigger elicitation
|
|
409
|
+
const result = await collectOptionalInfoTool.execute({
|
|
410
|
+
context: { message: 'Optional information request' }
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
expect(mockHandler).toHaveBeenCalledTimes(1);
|
|
414
|
+
expect(result.content).toBeDefined();
|
|
415
|
+
expect(result.content[0].type).toBe('text');
|
|
416
|
+
|
|
417
|
+
const elicitationResult = JSON.parse(result.content[0].text);
|
|
418
|
+
expect(elicitationResult.action).toBe('cancel');
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should return an error when elicitation handler throws error', async () => {
|
|
422
|
+
const mockHandler = vi.fn(async (_request) => {
|
|
423
|
+
throw new Error('Handler failed');
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
client = new InternalMastraMCPClient({
|
|
427
|
+
name: 'elicitation-error-client',
|
|
428
|
+
server: {
|
|
429
|
+
url: testServer.baseUrl,
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
client.elicitation.onRequest(mockHandler);
|
|
433
|
+
await client.connect();
|
|
434
|
+
|
|
435
|
+
// Get the tools and call a tool that will trigger elicitation
|
|
436
|
+
const tools = await client.tools();
|
|
437
|
+
const collectUserInfoTool = tools['collectUserInfo'];
|
|
438
|
+
expect(collectUserInfoTool).toBeDefined();
|
|
439
|
+
|
|
440
|
+
// Call the tool which will trigger elicitation, handler will throw error
|
|
441
|
+
const result = await collectUserInfoTool.execute({
|
|
442
|
+
context: { message: 'This will cause handler to throw' }
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
expect(mockHandler).toHaveBeenCalledTimes(1);
|
|
446
|
+
expect(result.content).toBeDefined();
|
|
447
|
+
|
|
448
|
+
expect(result.isError).toBe(true);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should return an error when client has no elicitation handler', async () => {
|
|
452
|
+
client = new InternalMastraMCPClient({
|
|
453
|
+
name: 'no-elicitation-client',
|
|
454
|
+
server: {
|
|
455
|
+
url: testServer.baseUrl,
|
|
456
|
+
// No elicitationHandler provided
|
|
457
|
+
},
|
|
458
|
+
});
|
|
459
|
+
await client.connect();
|
|
460
|
+
|
|
461
|
+
// Get the tools and call a tool that will trigger elicitation
|
|
462
|
+
const tools = await client.tools();
|
|
463
|
+
const collectUserInfoTool = tools['collectUserInfo'];
|
|
464
|
+
expect(collectUserInfoTool).toBeDefined();
|
|
465
|
+
|
|
466
|
+
// Call the tool which will trigger elicitation, should fail gracefully
|
|
467
|
+
const result = await collectUserInfoTool.execute({
|
|
468
|
+
context: { message: 'This should fail gracefully' }
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
expect(result.content).toBeDefined();
|
|
472
|
+
expect(result.isError).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should validate elicitation request schema structure', async () => {
|
|
476
|
+
const mockHandler = vi.fn(async (request) => {
|
|
477
|
+
// Verify the request has the expected structure
|
|
478
|
+
expect(request).toHaveProperty('message');
|
|
479
|
+
expect(request).toHaveProperty('requestedSchema');
|
|
480
|
+
expect(typeof request.message).toBe('string');
|
|
481
|
+
expect(typeof request.requestedSchema).toBe('object');
|
|
482
|
+
expect(request.requestedSchema).toHaveProperty('type', 'object');
|
|
483
|
+
expect(request.requestedSchema).toHaveProperty('properties');
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
action: 'accept' as const,
|
|
487
|
+
content: { validated: true },
|
|
488
|
+
};
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
client = new InternalMastraMCPClient({
|
|
492
|
+
name: 'schema-validation-client',
|
|
493
|
+
server: {
|
|
494
|
+
url: testServer.baseUrl,
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
client.elicitation.onRequest(mockHandler);
|
|
498
|
+
await client.connect();
|
|
499
|
+
|
|
500
|
+
// Get the tools and call a tool that will trigger elicitation
|
|
501
|
+
const tools = await client.tools();
|
|
502
|
+
const collectUserInfoTool = tools['collectUserInfo'];
|
|
503
|
+
expect(collectUserInfoTool).toBeDefined();
|
|
504
|
+
|
|
505
|
+
// Call the tool which will trigger elicitation with schema validation
|
|
506
|
+
const result = await collectUserInfoTool.execute({
|
|
507
|
+
context: { message: 'Schema validation test' }
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
console.log('result', result);
|
|
511
|
+
|
|
512
|
+
expect(mockHandler).toHaveBeenCalledTimes(1);
|
|
513
|
+
expect(result.content).toBeDefined();
|
|
514
|
+
expect(result.content[0].type).toBe('text');
|
|
515
|
+
|
|
516
|
+
const elicitationResultText = result.content[0].text;
|
|
517
|
+
expect(elicitationResultText).toContain('Elicitation response content does not match requested schema');
|
|
518
|
+
});
|
|
519
|
+
});
|
package/src/client/client.ts
CHANGED
|
@@ -14,6 +14,8 @@ import { DEFAULT_REQUEST_TIMEOUT_MSEC } from '@modelcontextprotocol/sdk/shared/p
|
|
|
14
14
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
15
15
|
import type {
|
|
16
16
|
ClientCapabilities,
|
|
17
|
+
ElicitRequest,
|
|
18
|
+
ElicitResult,
|
|
17
19
|
GetPromptResult,
|
|
18
20
|
ListPromptsResult,
|
|
19
21
|
LoggingLevel,
|
|
@@ -28,12 +30,14 @@ import {
|
|
|
28
30
|
ListPromptsResultSchema,
|
|
29
31
|
GetPromptResultSchema,
|
|
30
32
|
PromptListChangedNotificationSchema,
|
|
33
|
+
ElicitRequestSchema,
|
|
31
34
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
32
35
|
|
|
33
36
|
import { asyncExitHook, gracefulExit } from 'exit-hook';
|
|
34
37
|
import { z } from 'zod';
|
|
35
38
|
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
|
|
36
39
|
import type { JSONSchema } from 'zod-from-json-schema';
|
|
40
|
+
import { ElicitationClientActions } from './elicitationActions';
|
|
37
41
|
import { PromptClientActions } from './promptActions';
|
|
38
42
|
import { ResourceClientActions } from './resourceActions';
|
|
39
43
|
|
|
@@ -51,6 +55,9 @@ export interface LogMessage {
|
|
|
51
55
|
|
|
52
56
|
export type LogHandler = (logMessage: LogMessage) => void;
|
|
53
57
|
|
|
58
|
+
// Elicitation handler type
|
|
59
|
+
export type ElicitationHandler = (request: ElicitRequest['params']) => Promise<ElicitResult>;
|
|
60
|
+
|
|
54
61
|
// Base options common to all server definitions
|
|
55
62
|
type BaseServerOptions = {
|
|
56
63
|
logger?: LogHandler;
|
|
@@ -130,7 +137,7 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
130
137
|
private currentOperationContext: RuntimeContext | null = null;
|
|
131
138
|
public readonly resources: ResourceClientActions;
|
|
132
139
|
public readonly prompts: PromptClientActions;
|
|
133
|
-
|
|
140
|
+
public readonly elicitation: ElicitationClientActions;
|
|
134
141
|
constructor({
|
|
135
142
|
name,
|
|
136
143
|
version = '1.0.0',
|
|
@@ -145,21 +152,25 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
145
152
|
this.enableServerLogs = server.enableServerLogs ?? true;
|
|
146
153
|
this.serverConfig = server;
|
|
147
154
|
|
|
155
|
+
const clientCapabilities = { ...capabilities, elicitation: {} };
|
|
156
|
+
|
|
148
157
|
this.client = new Client(
|
|
149
158
|
{
|
|
150
159
|
name,
|
|
151
160
|
version,
|
|
152
161
|
},
|
|
153
162
|
{
|
|
154
|
-
capabilities,
|
|
163
|
+
capabilities: clientCapabilities,
|
|
155
164
|
},
|
|
156
165
|
);
|
|
157
166
|
|
|
158
167
|
// Set up log message capturing
|
|
159
168
|
this.setupLogging();
|
|
160
169
|
|
|
170
|
+
|
|
161
171
|
this.resources = new ResourceClientActions({ client: this, logger: this.logger });
|
|
162
172
|
this.prompts = new PromptClientActions({ client: this, logger: this.logger });
|
|
173
|
+
this.elicitation = new ElicitationClientActions({ client: this, logger: this.logger });
|
|
163
174
|
}
|
|
164
175
|
|
|
165
176
|
/**
|
|
@@ -450,6 +461,14 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
450
461
|
});
|
|
451
462
|
}
|
|
452
463
|
|
|
464
|
+
setElicitationRequestHandler(handler: ElicitationHandler): void {
|
|
465
|
+
this.log('debug', 'Setting elicitation request handler');
|
|
466
|
+
this.client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
467
|
+
this.log('debug', `Received elicitation request: ${request.params.message}`);
|
|
468
|
+
return handler(request.params);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
453
472
|
private convertInputSchema(
|
|
454
473
|
inputSchema: Awaited<ReturnType<Client['listTools']>>['tools'][0]['inputSchema'] | JSONSchema,
|
|
455
474
|
): z.ZodType {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MastraBase } from '@mastra/core/base';
|
|
2
2
|
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
3
3
|
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
4
|
-
import type { Prompt, Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import type { ElicitRequest, ElicitResult, Prompt, Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import equal from 'fast-deep-equal';
|
|
6
6
|
import { v5 as uuidv5 } from 'uuid';
|
|
7
7
|
import { InternalMastraMCPClient } from './client';
|
|
@@ -64,6 +64,26 @@ To fix this you have three different options:
|
|
|
64
64
|
this.addToInstanceCache();
|
|
65
65
|
return this;
|
|
66
66
|
}
|
|
67
|
+
public get elicitation() {
|
|
68
|
+
this.addToInstanceCache();
|
|
69
|
+
return {
|
|
70
|
+
onRequest: async (serverName: string, handler: (request: ElicitRequest['params']) => Promise<ElicitResult>) => {
|
|
71
|
+
try {
|
|
72
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
73
|
+
return internalClient.elicitation.onRequest(handler);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
throw new MastraError({
|
|
76
|
+
id: 'MCP_CLIENT_ON_REQUEST_ELICITATION_FAILED',
|
|
77
|
+
domain: ErrorDomain.MCP,
|
|
78
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
79
|
+
details: {
|
|
80
|
+
serverName,
|
|
81
|
+
}
|
|
82
|
+
}, err);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
67
87
|
|
|
68
88
|
public get resources() {
|
|
69
89
|
this.addToInstanceCache();
|
|
@@ -356,7 +376,7 @@ To fix this you have three different options:
|
|
|
356
376
|
const existingClient = this.mcpClientsById.get(name);
|
|
357
377
|
|
|
358
378
|
this.logger.debug(`getConnectedClient ${name} exists: ${exists}`);
|
|
359
|
-
|
|
379
|
+
|
|
360
380
|
if (exists) {
|
|
361
381
|
// This is just to satisfy Typescript since technically you could have this.mcpClientsById.set('someKey', undefined);
|
|
362
382
|
// Should never reach this point basically we always create a new MastraMCPClient instance when we add to the Map.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IMastraLogger } from "@mastra/core/logger";
|
|
2
|
+
import type { ElicitRequest, ElicitResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import type { InternalMastraMCPClient } from "./client";
|
|
4
|
+
|
|
5
|
+
interface ElicitationClientActionsConfig {
|
|
6
|
+
client: InternalMastraMCPClient;
|
|
7
|
+
logger: IMastraLogger;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ElicitationClientActions {
|
|
11
|
+
private readonly client: InternalMastraMCPClient;
|
|
12
|
+
private readonly logger: IMastraLogger;
|
|
13
|
+
|
|
14
|
+
constructor({ client, logger }: ElicitationClientActionsConfig) {
|
|
15
|
+
this.client = client;
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Set a handler for elicitation requests.
|
|
21
|
+
* @param handler The callback function to handle the elicitation request.
|
|
22
|
+
*/
|
|
23
|
+
public onRequest(handler: (request: ElicitRequest['params']) => Promise<ElicitResult>): void {
|
|
24
|
+
this.client.setElicitationRequestHandler(handler);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { LoggingLevel, LogMessage, LogHandler, MastraMCPServerDefinition } from './client';
|
|
1
|
+
export type { LoggingLevel, LogMessage, LogHandler, MastraMCPServerDefinition, ElicitationHandler } from './client';
|
|
2
2
|
export { MastraMCPClient } from './client';
|
|
3
3
|
export * from './configuration';
|