@mastra/mcp 1.0.0-beta.2 → 1.0.0-beta.4
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 +106 -0
- package/dist/__fixtures__/tools.d.ts +1 -1
- package/dist/__fixtures__/tools.d.ts.map +1 -1
- package/dist/client/{elicitationActions.d.ts → actions/elicitation.d.ts} +2 -2
- package/dist/client/actions/elicitation.d.ts.map +1 -0
- package/dist/client/actions/progress.d.ts +23 -0
- package/dist/client/actions/progress.d.ts.map +1 -0
- package/dist/client/{promptActions.d.ts → actions/prompt.d.ts} +2 -2
- package/dist/client/actions/prompt.d.ts.map +1 -0
- package/dist/client/{resourceActions.d.ts → actions/resource.d.ts} +2 -2
- package/dist/client/actions/resource.d.ts.map +1 -0
- package/dist/client/client.d.ts +78 -138
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/configuration.d.ts +23 -1
- package/dist/client/configuration.d.ts.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/types.d.ts +188 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/index.cjs +333 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +314 -73
- package/dist/index.js.map +1 -1
- package/dist/server/server.d.ts +19 -4
- package/dist/server/server.d.ts.map +1 -1
- package/package.json +9 -7
- package/dist/client/elicitationActions.d.ts.map +0 -1
- package/dist/client/promptActions.d.ts.map +0 -1
- package/dist/client/resourceActions.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var $RefParser = require('@apidevtools/json-schema-ref-parser');
|
|
3
4
|
var base = require('@mastra/core/base');
|
|
4
5
|
var error = require('@mastra/core/error');
|
|
5
|
-
var protocol_js = require('@modelcontextprotocol/sdk/shared/protocol.js');
|
|
6
|
-
var equal = require('fast-deep-equal');
|
|
7
|
-
var uuid = require('uuid');
|
|
8
|
-
var $RefParser = require('@apidevtools/json-schema-ref-parser');
|
|
9
6
|
var tools = require('@mastra/core/tools');
|
|
10
7
|
var utils = require('@mastra/core/utils');
|
|
11
|
-
var index_js
|
|
12
|
-
var sse_js
|
|
13
|
-
var stdio_js
|
|
14
|
-
var streamableHttp_js
|
|
8
|
+
var index_js = require('@modelcontextprotocol/sdk/client/index.js');
|
|
9
|
+
var sse_js = require('@modelcontextprotocol/sdk/client/sse.js');
|
|
10
|
+
var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
11
|
+
var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
12
|
+
var protocol_js = require('@modelcontextprotocol/sdk/shared/protocol.js');
|
|
15
13
|
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
16
14
|
var exitHook = require('exit-hook');
|
|
17
15
|
var zod = require('zod');
|
|
18
16
|
var zodFromJsonSchema = require('zod-from-json-schema');
|
|
19
17
|
var zodFromJsonSchemaV3 = require('zod-from-json-schema-v3');
|
|
18
|
+
var equal = require('fast-deep-equal');
|
|
19
|
+
var uuid = require('uuid');
|
|
20
20
|
var crypto$1 = require('crypto');
|
|
21
21
|
var mcp = require('@mastra/core/mcp');
|
|
22
22
|
var requestContext = require('@mastra/core/request-context');
|
|
23
|
-
var index_js = require('@modelcontextprotocol/sdk/server/index.js');
|
|
24
|
-
var sse_js = require('@modelcontextprotocol/sdk/server/sse.js');
|
|
25
|
-
var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
26
|
-
var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
23
|
+
var index_js$1 = require('@modelcontextprotocol/sdk/server/index.js');
|
|
24
|
+
var sse_js$1 = require('@modelcontextprotocol/sdk/server/sse.js');
|
|
25
|
+
var stdio_js$1 = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
26
|
+
var streamableHttp_js$1 = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
27
27
|
|
|
28
28
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
29
29
|
|
|
30
|
-
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
31
30
|
var $RefParser__default = /*#__PURE__*/_interopDefault($RefParser);
|
|
31
|
+
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
32
32
|
|
|
33
|
-
// src/client/
|
|
33
|
+
// src/client/client.ts
|
|
34
34
|
|
|
35
|
-
// src/client/
|
|
35
|
+
// src/client/actions/elicitation.ts
|
|
36
36
|
var ElicitationClientActions = class {
|
|
37
37
|
client;
|
|
38
38
|
logger;
|
|
@@ -85,6 +85,23 @@ var ElicitationClientActions = class {
|
|
|
85
85
|
this.client.setElicitationRequestHandler(handler);
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
|
+
|
|
89
|
+
// src/client/actions/progress.ts
|
|
90
|
+
var ProgressClientActions = class {
|
|
91
|
+
client;
|
|
92
|
+
logger;
|
|
93
|
+
constructor({ client, logger }) {
|
|
94
|
+
this.client = client;
|
|
95
|
+
this.logger = logger;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Set a notification handler for progress updates.
|
|
99
|
+
* @param handler The callback function to handle progress notifications.
|
|
100
|
+
*/
|
|
101
|
+
onUpdate(handler) {
|
|
102
|
+
this.client.setProgressNotificationHandler(handler);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
88
105
|
var PromptClientActions = class {
|
|
89
106
|
client;
|
|
90
107
|
logger;
|
|
@@ -398,15 +415,21 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
398
415
|
timeout;
|
|
399
416
|
logHandler;
|
|
400
417
|
enableServerLogs;
|
|
418
|
+
enableProgressTracking;
|
|
401
419
|
serverConfig;
|
|
402
420
|
transport;
|
|
403
421
|
currentOperationContext = null;
|
|
422
|
+
exitHookUnsubscribe;
|
|
423
|
+
sigTermHandler;
|
|
424
|
+
_roots;
|
|
404
425
|
/** Provides access to resource operations (list, read, subscribe, etc.) */
|
|
405
426
|
resources;
|
|
406
427
|
/** Provides access to prompt operations (list, get, notifications) */
|
|
407
428
|
prompts;
|
|
408
429
|
/** Provides access to elicitation operations (request handling) */
|
|
409
430
|
elicitation;
|
|
431
|
+
/** Provides access to progress operations (notifications) */
|
|
432
|
+
progress;
|
|
410
433
|
/**
|
|
411
434
|
* @internal
|
|
412
435
|
*/
|
|
@@ -423,8 +446,16 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
423
446
|
this.logHandler = server.logger;
|
|
424
447
|
this.enableServerLogs = server.enableServerLogs ?? true;
|
|
425
448
|
this.serverConfig = server;
|
|
426
|
-
|
|
427
|
-
this.
|
|
449
|
+
this.enableProgressTracking = !!server.enableProgressTracking;
|
|
450
|
+
this._roots = server.roots ?? [];
|
|
451
|
+
const hasRoots = this._roots.length > 0 || !!capabilities.roots;
|
|
452
|
+
const clientCapabilities = {
|
|
453
|
+
...capabilities,
|
|
454
|
+
elicitation: {},
|
|
455
|
+
// Auto-enable roots capability if roots are provided
|
|
456
|
+
...hasRoots ? { roots: { listChanged: true, ...capabilities.roots ?? {} } } : {}
|
|
457
|
+
};
|
|
458
|
+
this.client = new index_js.Client(
|
|
428
459
|
{
|
|
429
460
|
name,
|
|
430
461
|
version
|
|
@@ -434,9 +465,13 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
434
465
|
}
|
|
435
466
|
);
|
|
436
467
|
this.setupLogging();
|
|
468
|
+
if (hasRoots) {
|
|
469
|
+
this.setupRootsHandler();
|
|
470
|
+
}
|
|
437
471
|
this.resources = new ResourceClientActions({ client: this, logger: this.logger });
|
|
438
472
|
this.prompts = new PromptClientActions({ client: this, logger: this.logger });
|
|
439
473
|
this.elicitation = new ElicitationClientActions({ client: this, logger: this.logger });
|
|
474
|
+
this.progress = new ProgressClientActions({ client: this, logger: this.logger });
|
|
440
475
|
}
|
|
441
476
|
/**
|
|
442
477
|
* Log a message at the specified level
|
|
@@ -475,13 +510,70 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
475
510
|
);
|
|
476
511
|
}
|
|
477
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Set up handler for roots/list requests from the server.
|
|
515
|
+
*
|
|
516
|
+
* Per MCP spec (https://modelcontextprotocol.io/specification/2025-11-25/client/roots):
|
|
517
|
+
* When a server sends a roots/list request, the client responds with the configured roots.
|
|
518
|
+
*/
|
|
519
|
+
setupRootsHandler() {
|
|
520
|
+
this.log("debug", "Setting up roots/list request handler");
|
|
521
|
+
this.client.setRequestHandler(types_js.ListRootsRequestSchema, async () => {
|
|
522
|
+
this.log("debug", `Responding to roots/list request with ${this._roots.length} roots`);
|
|
523
|
+
return { roots: this._roots };
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Get the currently configured roots.
|
|
528
|
+
*
|
|
529
|
+
* @returns Array of configured filesystem roots
|
|
530
|
+
*/
|
|
531
|
+
get roots() {
|
|
532
|
+
return [...this._roots];
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Update the list of filesystem roots and notify the server.
|
|
536
|
+
*
|
|
537
|
+
* Per MCP spec, when roots change, the client sends a `notifications/roots/list_changed`
|
|
538
|
+
* notification to inform the server that it should re-fetch the roots list.
|
|
539
|
+
*
|
|
540
|
+
* @param roots - New list of filesystem roots
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```typescript
|
|
544
|
+
* await client.setRoots([
|
|
545
|
+
* { uri: 'file:///home/user/projects', name: 'Projects' },
|
|
546
|
+
* { uri: 'file:///tmp', name: 'Temp' }
|
|
547
|
+
* ]);
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
async setRoots(roots) {
|
|
551
|
+
this.log("debug", `Updating roots to ${roots.length} entries`);
|
|
552
|
+
this._roots = [...roots];
|
|
553
|
+
await this.sendRootsListChanged();
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Send a roots/list_changed notification to the server.
|
|
557
|
+
*
|
|
558
|
+
* Per MCP spec, clients that support `listChanged` MUST send this notification
|
|
559
|
+
* when the list of roots changes. The server will then call roots/list to get
|
|
560
|
+
* the updated list.
|
|
561
|
+
*/
|
|
562
|
+
async sendRootsListChanged() {
|
|
563
|
+
if (!this.transport) {
|
|
564
|
+
this.log("debug", "Cannot send roots/list_changed: not connected");
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
this.log("debug", "Sending notifications/roots/list_changed");
|
|
568
|
+
await this.client.notification({ method: "notifications/roots/list_changed" });
|
|
569
|
+
}
|
|
478
570
|
async connectStdio(command) {
|
|
479
571
|
this.log("debug", `Using Stdio transport for command: ${command}`);
|
|
480
572
|
try {
|
|
481
|
-
this.transport = new stdio_js
|
|
573
|
+
this.transport = new stdio_js.StdioClientTransport({
|
|
482
574
|
command,
|
|
483
575
|
args: this.serverConfig.args,
|
|
484
|
-
env: { ...stdio_js
|
|
576
|
+
env: { ...stdio_js.getDefaultEnvironment(), ...this.serverConfig.env || {} }
|
|
485
577
|
});
|
|
486
578
|
await this.client.connect(this.transport, { timeout: this.serverConfig.timeout ?? this.timeout });
|
|
487
579
|
this.log("debug", `Successfully connected to MCP server via Stdio`);
|
|
@@ -497,7 +589,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
497
589
|
if (!shouldTrySSE) {
|
|
498
590
|
try {
|
|
499
591
|
this.log("debug", "Trying Streamable HTTP transport...");
|
|
500
|
-
const streamableTransport = new streamableHttp_js
|
|
592
|
+
const streamableTransport = new streamableHttp_js.StreamableHTTPClientTransport(url, {
|
|
501
593
|
requestInit,
|
|
502
594
|
reconnectionOptions: this.serverConfig.reconnectionOptions,
|
|
503
595
|
authProvider
|
|
@@ -515,7 +607,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
515
607
|
if (shouldTrySSE) {
|
|
516
608
|
this.log("debug", "Falling back to deprecated HTTP+SSE transport...");
|
|
517
609
|
try {
|
|
518
|
-
const sseTransport = new sse_js
|
|
610
|
+
const sseTransport = new sse_js.SSEClientTransport(url, { requestInit, eventSourceInit, authProvider });
|
|
519
611
|
await this.client.connect(sseTransport, { timeout: this.serverConfig.timeout ?? this.timeout });
|
|
520
612
|
this.transport = sseTransport;
|
|
521
613
|
this.log("debug", "Successfully connected using deprecated HTTP+SSE transport.");
|
|
@@ -568,14 +660,19 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
568
660
|
reject(e);
|
|
569
661
|
}
|
|
570
662
|
});
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
663
|
+
if (!this.exitHookUnsubscribe) {
|
|
664
|
+
this.exitHookUnsubscribe = exitHook.asyncExitHook(
|
|
665
|
+
async () => {
|
|
666
|
+
this.log("debug", `Disconnecting MCP server during exit`);
|
|
667
|
+
await this.disconnect();
|
|
668
|
+
},
|
|
669
|
+
{ wait: 5e3 }
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
if (!this.sigTermHandler) {
|
|
673
|
+
this.sigTermHandler = () => exitHook.gracefulExit();
|
|
674
|
+
process.on("SIGTERM", this.sigTermHandler);
|
|
675
|
+
}
|
|
579
676
|
this.log("debug", `Successfully connected to MCP server`);
|
|
580
677
|
return this.isConnected;
|
|
581
678
|
}
|
|
@@ -589,7 +686,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
589
686
|
* @internal
|
|
590
687
|
*/
|
|
591
688
|
get sessionId() {
|
|
592
|
-
if (this.transport instanceof streamableHttp_js
|
|
689
|
+
if (this.transport instanceof streamableHttp_js.StreamableHTTPClientTransport) {
|
|
593
690
|
return this.transport.sessionId;
|
|
594
691
|
}
|
|
595
692
|
return void 0;
|
|
@@ -610,8 +707,63 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
610
707
|
throw e;
|
|
611
708
|
} finally {
|
|
612
709
|
this.transport = void 0;
|
|
613
|
-
this.isConnected =
|
|
710
|
+
this.isConnected = null;
|
|
711
|
+
if (this.exitHookUnsubscribe) {
|
|
712
|
+
this.exitHookUnsubscribe();
|
|
713
|
+
this.exitHookUnsubscribe = void 0;
|
|
714
|
+
}
|
|
715
|
+
if (this.sigTermHandler) {
|
|
716
|
+
process.off("SIGTERM", this.sigTermHandler);
|
|
717
|
+
this.sigTermHandler = void 0;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Checks if an error indicates a session invalidation that requires reconnection.
|
|
723
|
+
*
|
|
724
|
+
* Common session-related errors include:
|
|
725
|
+
* - "No valid session ID provided" (HTTP 400)
|
|
726
|
+
* - "Server not initialized" (HTTP 400)
|
|
727
|
+
* - Connection refused errors
|
|
728
|
+
*
|
|
729
|
+
* @param error - The error to check
|
|
730
|
+
* @returns true if the error indicates a session problem requiring reconnection
|
|
731
|
+
*
|
|
732
|
+
* @internal
|
|
733
|
+
*/
|
|
734
|
+
isSessionError(error) {
|
|
735
|
+
if (!(error instanceof Error)) {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
const errorMessage = error.message.toLowerCase();
|
|
739
|
+
return errorMessage.includes("no valid session") || errorMessage.includes("session") || errorMessage.includes("server not initialized") || errorMessage.includes("http 400") || errorMessage.includes("http 401") || errorMessage.includes("http 403") || errorMessage.includes("econnrefused") || errorMessage.includes("fetch failed") || errorMessage.includes("connection refused");
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Forces a reconnection to the MCP server by disconnecting and reconnecting.
|
|
743
|
+
*
|
|
744
|
+
* This is useful when the session becomes invalid (e.g., after server restart)
|
|
745
|
+
* and the client needs to establish a fresh connection.
|
|
746
|
+
*
|
|
747
|
+
* @returns Promise resolving when reconnection is complete
|
|
748
|
+
* @throws {Error} If reconnection fails
|
|
749
|
+
*
|
|
750
|
+
* @internal
|
|
751
|
+
*/
|
|
752
|
+
async forceReconnect() {
|
|
753
|
+
this.log("debug", "Forcing reconnection to MCP server...");
|
|
754
|
+
try {
|
|
755
|
+
if (this.transport) {
|
|
756
|
+
await this.transport.close();
|
|
757
|
+
}
|
|
758
|
+
} catch (e) {
|
|
759
|
+
this.log("debug", "Error during force disconnect (ignored)", {
|
|
760
|
+
error: e instanceof Error ? e.message : String(e)
|
|
761
|
+
});
|
|
614
762
|
}
|
|
763
|
+
this.transport = void 0;
|
|
764
|
+
this.isConnected = null;
|
|
765
|
+
await this.connect();
|
|
766
|
+
this.log("debug", "Successfully reconnected to MCP server");
|
|
615
767
|
}
|
|
616
768
|
async listResources() {
|
|
617
769
|
this.log("debug", `Requesting resources from MCP server`);
|
|
@@ -699,6 +851,12 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
699
851
|
return handler(request.params);
|
|
700
852
|
});
|
|
701
853
|
}
|
|
854
|
+
setProgressNotificationHandler(handler) {
|
|
855
|
+
this.log("debug", "Setting progress notification handler");
|
|
856
|
+
this.client.setNotificationHandler(types_js.ProgressNotificationSchema, (notification) => {
|
|
857
|
+
handler(notification.params);
|
|
858
|
+
});
|
|
859
|
+
}
|
|
702
860
|
async convertInputSchema(inputSchema) {
|
|
703
861
|
if (utils.isZodType(inputSchema)) {
|
|
704
862
|
return inputSchema;
|
|
@@ -772,7 +930,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
772
930
|
}
|
|
773
931
|
async tools() {
|
|
774
932
|
this.log("debug", `Requesting tools from MCP server`);
|
|
775
|
-
const { tools: tools$1 } = await this.client.listTools({ timeout: this.timeout });
|
|
933
|
+
const { tools: tools$1 } = await this.client.listTools({}, { timeout: this.timeout });
|
|
776
934
|
const toolsRes = {};
|
|
777
935
|
for (const tool of tools$1) {
|
|
778
936
|
this.log("debug", `Processing tool: ${tool.name}`);
|
|
@@ -785,12 +943,14 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
785
943
|
execute: async (input, context) => {
|
|
786
944
|
const previousContext = this.currentOperationContext;
|
|
787
945
|
this.currentOperationContext = context?.requestContext || null;
|
|
788
|
-
|
|
789
|
-
this.log("debug", `Executing tool: ${tool.name}`, { toolArgs: input });
|
|
946
|
+
const executeToolCall = async () => {
|
|
947
|
+
this.log("debug", `Executing tool: ${tool.name}`, { toolArgs: input, runId: context?.runId });
|
|
790
948
|
const res = await this.client.callTool(
|
|
791
949
|
{
|
|
792
950
|
name: tool.name,
|
|
793
|
-
arguments: input
|
|
951
|
+
arguments: input,
|
|
952
|
+
// Use runId as progress token if available, otherwise generate a random UUID
|
|
953
|
+
...this.enableProgressTracking ? { _meta: { progressToken: context?.runId || crypto.randomUUID() } } : {}
|
|
794
954
|
},
|
|
795
955
|
types_js.CallToolResultSchema,
|
|
796
956
|
{
|
|
@@ -798,8 +958,31 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
798
958
|
}
|
|
799
959
|
);
|
|
800
960
|
this.log("debug", `Tool executed successfully: ${tool.name}`);
|
|
961
|
+
if (res.structuredContent !== void 0) {
|
|
962
|
+
return res.structuredContent;
|
|
963
|
+
}
|
|
801
964
|
return res;
|
|
965
|
+
};
|
|
966
|
+
try {
|
|
967
|
+
return await executeToolCall();
|
|
802
968
|
} catch (e) {
|
|
969
|
+
if (this.isSessionError(e)) {
|
|
970
|
+
this.log("debug", `Session error detected for tool ${tool.name}, attempting reconnection...`, {
|
|
971
|
+
error: e instanceof Error ? e.message : String(e)
|
|
972
|
+
});
|
|
973
|
+
try {
|
|
974
|
+
await this.forceReconnect();
|
|
975
|
+
this.log("debug", `Retrying tool ${tool.name} after reconnection...`);
|
|
976
|
+
return await executeToolCall();
|
|
977
|
+
} catch (reconnectError) {
|
|
978
|
+
this.log("error", `Reconnection or retry failed for tool ${tool.name}`, {
|
|
979
|
+
originalError: e instanceof Error ? e.message : String(e),
|
|
980
|
+
reconnectError: reconnectError instanceof Error ? reconnectError.stack : String(reconnectError),
|
|
981
|
+
toolArgs: input
|
|
982
|
+
});
|
|
983
|
+
throw e;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
803
986
|
this.log("error", `Error calling tool: ${tool.name}`, {
|
|
804
987
|
error: e instanceof Error ? e.stack : JSON.stringify(e, null, 2),
|
|
805
988
|
toolArgs: input
|
|
@@ -823,8 +1006,6 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
823
1006
|
return toolsRes;
|
|
824
1007
|
}
|
|
825
1008
|
};
|
|
826
|
-
|
|
827
|
-
// src/client/configuration.ts
|
|
828
1009
|
var mcpClientInstances = /* @__PURE__ */ new Map();
|
|
829
1010
|
var MCPClient = class extends base.MastraBase {
|
|
830
1011
|
serverConfigs = {};
|
|
@@ -897,6 +1078,48 @@ To fix this you have three different options:
|
|
|
897
1078
|
this.addToInstanceCache();
|
|
898
1079
|
return this;
|
|
899
1080
|
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Provides access to progress-related operations for tracking long-running operations.
|
|
1083
|
+
*
|
|
1084
|
+
* Progress tracking allows MCP servers to send updates about the status of ongoing operations,
|
|
1085
|
+
* providing real-time feedback to users about task completion and current state.
|
|
1086
|
+
*
|
|
1087
|
+
* @example
|
|
1088
|
+
* ```typescript
|
|
1089
|
+
* // Set up handler for progress updates from a server
|
|
1090
|
+
* await mcp.progress.onUpdate('serverName', (params) => {
|
|
1091
|
+
* console.log(`Progress: ${params.progress}%`);
|
|
1092
|
+
* console.log(`Status: ${params.message}`);
|
|
1093
|
+
*
|
|
1094
|
+
* if (params.total) {
|
|
1095
|
+
* console.log(`Completed ${params.progress} of ${params.total} items`);
|
|
1096
|
+
* }
|
|
1097
|
+
* });
|
|
1098
|
+
* ```
|
|
1099
|
+
*/
|
|
1100
|
+
get progress() {
|
|
1101
|
+
this.addToInstanceCache();
|
|
1102
|
+
return {
|
|
1103
|
+
onUpdate: async (serverName, handler) => {
|
|
1104
|
+
try {
|
|
1105
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
1106
|
+
return internalClient.progress.onUpdate(handler);
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
throw new error.MastraError(
|
|
1109
|
+
{
|
|
1110
|
+
id: "MCP_CLIENT_ON_UPDATE_PROGRESS_FAILED",
|
|
1111
|
+
domain: error.ErrorDomain.MCP,
|
|
1112
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1113
|
+
details: {
|
|
1114
|
+
serverName
|
|
1115
|
+
}
|
|
1116
|
+
},
|
|
1117
|
+
err
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
900
1123
|
/**
|
|
901
1124
|
* Provides access to elicitation-related operations for interactive user input collection.
|
|
902
1125
|
*
|
|
@@ -2142,7 +2365,7 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
2142
2365
|
if (opts.prompts) {
|
|
2143
2366
|
capabilities.prompts = { listChanged: true };
|
|
2144
2367
|
}
|
|
2145
|
-
this.server = new index_js.Server({ name: this.name, version: this.version }, { capabilities });
|
|
2368
|
+
this.server = new index_js$1.Server({ name: this.name, version: this.version }, { capabilities });
|
|
2146
2369
|
this.logger.info(
|
|
2147
2370
|
`Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")} and resources. Capabilities: ${JSON.stringify(capabilities)}`
|
|
2148
2371
|
);
|
|
@@ -2187,6 +2410,41 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
2187
2410
|
this.logger.debug(`Received elicitation response: ${JSON.stringify(response)}`);
|
|
2188
2411
|
return response;
|
|
2189
2412
|
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Reads and parses the JSON body from an HTTP request.
|
|
2415
|
+
* If the request body was already parsed by middleware (e.g., express.json()),
|
|
2416
|
+
* it uses the pre-parsed body from req.body. Otherwise, it reads from the stream.
|
|
2417
|
+
*
|
|
2418
|
+
* This allows the MCP server to work with Express apps that use express.json()
|
|
2419
|
+
* globally without requiring special route exclusions.
|
|
2420
|
+
*
|
|
2421
|
+
* @param req - The incoming HTTP request
|
|
2422
|
+
* @param options - Optional configuration
|
|
2423
|
+
* @param options.preParsedOnly - If true, only return pre-parsed body from middleware,
|
|
2424
|
+
* returning undefined if not available. This allows the caller to fall back to
|
|
2425
|
+
* their own body reading logic (e.g., SDK's getRawBody with size limits).
|
|
2426
|
+
*/
|
|
2427
|
+
async readJsonBody(req, options) {
|
|
2428
|
+
const reqWithBody = req;
|
|
2429
|
+
if (reqWithBody.body !== void 0) {
|
|
2430
|
+
return reqWithBody.body;
|
|
2431
|
+
}
|
|
2432
|
+
if (options?.preParsedOnly) {
|
|
2433
|
+
return void 0;
|
|
2434
|
+
}
|
|
2435
|
+
return new Promise((resolve, reject) => {
|
|
2436
|
+
let data = "";
|
|
2437
|
+
req.on("data", (chunk) => data += chunk);
|
|
2438
|
+
req.on("end", () => {
|
|
2439
|
+
try {
|
|
2440
|
+
resolve(JSON.parse(data));
|
|
2441
|
+
} catch (e) {
|
|
2442
|
+
reject(e);
|
|
2443
|
+
}
|
|
2444
|
+
});
|
|
2445
|
+
req.on("error", reject);
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2190
2448
|
/**
|
|
2191
2449
|
* Creates a new Server instance configured with all handlers for HTTP sessions.
|
|
2192
2450
|
* Each HTTP client connection gets its own Server instance to avoid routing conflicts.
|
|
@@ -2203,7 +2461,7 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
2203
2461
|
if (this.promptOptions) {
|
|
2204
2462
|
capabilities.prompts = { listChanged: true };
|
|
2205
2463
|
}
|
|
2206
|
-
const serverInstance = new index_js.Server({ name: this.name, version: this.version }, { capabilities });
|
|
2464
|
+
const serverInstance = new index_js$1.Server({ name: this.name, version: this.version }, { capabilities });
|
|
2207
2465
|
this.registerHandlersOnServer(serverInstance);
|
|
2208
2466
|
return serverInstance;
|
|
2209
2467
|
}
|
|
@@ -2575,7 +2833,9 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2575
2833
|
try {
|
|
2576
2834
|
const proxiedContext = context?.requestContext || new requestContext.RequestContext();
|
|
2577
2835
|
if (context?.mcp?.extra) {
|
|
2578
|
-
|
|
2836
|
+
Object.entries(context.mcp.extra).forEach(([key, value]) => {
|
|
2837
|
+
proxiedContext.set(key, value);
|
|
2838
|
+
});
|
|
2579
2839
|
}
|
|
2580
2840
|
const response = await agent.generate(inputData.message, {
|
|
2581
2841
|
...context ?? {},
|
|
@@ -2644,10 +2904,16 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2644
2904
|
inputData
|
|
2645
2905
|
);
|
|
2646
2906
|
try {
|
|
2647
|
-
const
|
|
2907
|
+
const proxiedContext = context?.requestContext || new requestContext.RequestContext();
|
|
2908
|
+
if (context?.mcp?.extra) {
|
|
2909
|
+
Object.entries(context.mcp.extra).forEach(([key, value]) => {
|
|
2910
|
+
proxiedContext.set(key, value);
|
|
2911
|
+
});
|
|
2912
|
+
}
|
|
2913
|
+
const run2 = await workflow.createRun({ runId: proxiedContext?.get("runId") });
|
|
2648
2914
|
const response = await run2.start({
|
|
2649
2915
|
inputData,
|
|
2650
|
-
requestContext:
|
|
2916
|
+
requestContext: proxiedContext,
|
|
2651
2917
|
tracingContext: context?.tracingContext
|
|
2652
2918
|
});
|
|
2653
2919
|
return response;
|
|
@@ -2766,7 +3032,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2766
3032
|
* ```
|
|
2767
3033
|
*/
|
|
2768
3034
|
async startStdio() {
|
|
2769
|
-
this.stdioTransport = new stdio_js.StdioServerTransport();
|
|
3035
|
+
this.stdioTransport = new stdio_js$1.StdioServerTransport();
|
|
2770
3036
|
try {
|
|
2771
3037
|
await this.server.connect(this.stdioTransport);
|
|
2772
3038
|
} catch (error$1) {
|
|
@@ -2803,7 +3069,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2803
3069
|
*
|
|
2804
3070
|
* @example
|
|
2805
3071
|
* ```typescript
|
|
2806
|
-
* import http from 'http';
|
|
3072
|
+
* import http from 'node:http';
|
|
2807
3073
|
*
|
|
2808
3074
|
* const httpServer = http.createServer(async (req, res) => {
|
|
2809
3075
|
* await server.startSSE({
|
|
@@ -2834,7 +3100,8 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2834
3100
|
res.end("SSE connection not established");
|
|
2835
3101
|
return;
|
|
2836
3102
|
}
|
|
2837
|
-
await this.
|
|
3103
|
+
const parsedBody = await this.readJsonBody(req, { preParsedOnly: true });
|
|
3104
|
+
await this.sseTransport.handlePostMessage(req, res, parsedBody);
|
|
2838
3105
|
} else {
|
|
2839
3106
|
this.logger.debug("Unknown path:", { path: url.pathname });
|
|
2840
3107
|
res.writeHead(404);
|
|
@@ -2961,8 +3228,8 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2961
3228
|
*
|
|
2962
3229
|
* @example
|
|
2963
3230
|
* ```typescript
|
|
2964
|
-
* import http from 'http';
|
|
2965
|
-
* import { randomUUID } from 'crypto';
|
|
3231
|
+
* import http from 'node:http';
|
|
3232
|
+
* import { randomUUID } from 'node:crypto';
|
|
2966
3233
|
*
|
|
2967
3234
|
* const httpServer = http.createServer(async (req, res) => {
|
|
2968
3235
|
* await server.startHTTP({
|
|
@@ -3006,7 +3273,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3006
3273
|
httpPath,
|
|
3007
3274
|
req,
|
|
3008
3275
|
res,
|
|
3009
|
-
options
|
|
3276
|
+
options
|
|
3010
3277
|
}) {
|
|
3011
3278
|
this.logger.debug(`startHTTP: Received ${req.method} request to ${url.pathname}`);
|
|
3012
3279
|
if (url.pathname !== httpPath) {
|
|
@@ -3015,11 +3282,18 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3015
3282
|
res.end();
|
|
3016
3283
|
return;
|
|
3017
3284
|
}
|
|
3018
|
-
|
|
3019
|
-
|
|
3285
|
+
const isStatelessMode = options?.serverless || options && "sessionIdGenerator" in options && options.sessionIdGenerator === void 0;
|
|
3286
|
+
if (isStatelessMode) {
|
|
3287
|
+
this.logger.debug("startHTTP: Running in stateless mode (serverless or sessionIdGenerator: undefined)");
|
|
3020
3288
|
await this.handleServerlessRequest(req, res);
|
|
3021
3289
|
return;
|
|
3022
3290
|
}
|
|
3291
|
+
const mergedOptions = {
|
|
3292
|
+
sessionIdGenerator: () => crypto$1.randomUUID(),
|
|
3293
|
+
// default: enabled
|
|
3294
|
+
...options
|
|
3295
|
+
// user-provided overrides default
|
|
3296
|
+
};
|
|
3023
3297
|
const sessionId = req.headers["mcp-session-id"];
|
|
3024
3298
|
let transport;
|
|
3025
3299
|
this.logger.debug(
|
|
@@ -3034,40 +3308,18 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3034
3308
|
`startHTTP: Handling GET request for existing session ${sessionId}. Calling transport.handleRequest.`
|
|
3035
3309
|
);
|
|
3036
3310
|
}
|
|
3037
|
-
const body = req.method === "POST" ? await
|
|
3038
|
-
let data = "";
|
|
3039
|
-
req.on("data", (chunk) => data += chunk);
|
|
3040
|
-
req.on("end", () => {
|
|
3041
|
-
try {
|
|
3042
|
-
resolve(JSON.parse(data));
|
|
3043
|
-
} catch (e) {
|
|
3044
|
-
reject(e);
|
|
3045
|
-
}
|
|
3046
|
-
});
|
|
3047
|
-
req.on("error", reject);
|
|
3048
|
-
}) : void 0;
|
|
3311
|
+
const body = req.method === "POST" ? await this.readJsonBody(req) : void 0;
|
|
3049
3312
|
await transport.handleRequest(req, res, body);
|
|
3050
3313
|
} else {
|
|
3051
3314
|
this.logger.debug(`startHTTP: No existing Streamable HTTP session ID found. ${req.method}`);
|
|
3052
3315
|
if (req.method === "POST") {
|
|
3053
|
-
const body = await
|
|
3054
|
-
let data = "";
|
|
3055
|
-
req.on("data", (chunk) => data += chunk);
|
|
3056
|
-
req.on("end", () => {
|
|
3057
|
-
try {
|
|
3058
|
-
resolve(JSON.parse(data));
|
|
3059
|
-
} catch (e) {
|
|
3060
|
-
reject(e);
|
|
3061
|
-
}
|
|
3062
|
-
});
|
|
3063
|
-
req.on("error", reject);
|
|
3064
|
-
});
|
|
3316
|
+
const body = await this.readJsonBody(req);
|
|
3065
3317
|
const { isInitializeRequest } = await import('@modelcontextprotocol/sdk/types.js');
|
|
3066
3318
|
if (isInitializeRequest(body)) {
|
|
3067
3319
|
this.logger.debug("startHTTP: Received Streamable HTTP initialize request, creating new transport.");
|
|
3068
|
-
transport = new streamableHttp_js.StreamableHTTPServerTransport({
|
|
3069
|
-
...
|
|
3070
|
-
sessionIdGenerator:
|
|
3320
|
+
transport = new streamableHttp_js$1.StreamableHTTPServerTransport({
|
|
3321
|
+
...mergedOptions,
|
|
3322
|
+
sessionIdGenerator: mergedOptions.sessionIdGenerator,
|
|
3071
3323
|
onsessioninitialized: (id) => {
|
|
3072
3324
|
this.streamableHTTPTransports.set(id, transport);
|
|
3073
3325
|
}
|
|
@@ -3171,24 +3423,13 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3171
3423
|
async handleServerlessRequest(req, res) {
|
|
3172
3424
|
try {
|
|
3173
3425
|
this.logger.debug(`handleServerlessRequest: Received ${req.method} request`);
|
|
3174
|
-
const body = req.method === "POST" ? await
|
|
3175
|
-
let data = "";
|
|
3176
|
-
req.on("data", (chunk) => data += chunk);
|
|
3177
|
-
req.on("end", () => {
|
|
3178
|
-
try {
|
|
3179
|
-
resolve(JSON.parse(data));
|
|
3180
|
-
} catch (e) {
|
|
3181
|
-
reject(new Error(`Invalid JSON in request body: ${e instanceof Error ? e.message : String(e)}`));
|
|
3182
|
-
}
|
|
3183
|
-
});
|
|
3184
|
-
req.on("error", reject);
|
|
3185
|
-
}) : void 0;
|
|
3426
|
+
const body = req.method === "POST" ? await this.readJsonBody(req) : void 0;
|
|
3186
3427
|
this.logger.debug(`handleServerlessRequest: Processing ${req.method} request`, {
|
|
3187
3428
|
method: body?.method,
|
|
3188
3429
|
id: body?.id
|
|
3189
3430
|
});
|
|
3190
3431
|
const transientServer = this.createServerInstance();
|
|
3191
|
-
const tempTransport = new streamableHttp_js.StreamableHTTPServerTransport({
|
|
3432
|
+
const tempTransport = new streamableHttp_js$1.StreamableHTTPServerTransport({
|
|
3192
3433
|
sessionIdGenerator: void 0,
|
|
3193
3434
|
enableJsonResponse: true
|
|
3194
3435
|
});
|
|
@@ -3250,7 +3491,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3250
3491
|
}) {
|
|
3251
3492
|
try {
|
|
3252
3493
|
this.logger.debug("Received SSE connection");
|
|
3253
|
-
this.sseTransport = new sse_js.SSEServerTransport(messagePath, res);
|
|
3494
|
+
this.sseTransport = new sse_js$1.SSEServerTransport(messagePath, res);
|
|
3254
3495
|
await this.server.connect(this.sseTransport);
|
|
3255
3496
|
this.server.onclose = async () => {
|
|
3256
3497
|
this.sseTransport = void 0;
|
|
@@ -3613,6 +3854,7 @@ Provided arguments: ${JSON.stringify(args, null, 2)}`,
|
|
|
3613
3854
|
}
|
|
3614
3855
|
};
|
|
3615
3856
|
|
|
3857
|
+
exports.InternalMastraMCPClient = InternalMastraMCPClient;
|
|
3616
3858
|
exports.MCPClient = MCPClient;
|
|
3617
3859
|
exports.MCPServer = MCPServer;
|
|
3618
3860
|
//# sourceMappingURL=index.cjs.map
|