@mastra/mcp 0.10.4 → 0.10.5-alpha.1
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 +28 -0
- package/dist/_tsup-dts-rollup.d.cts +88 -80
- package/dist/_tsup-dts-rollup.d.ts +88 -80
- package/dist/index.cjs +714 -168
- package/dist/index.js +699 -153
- package/integration-tests/node_modules/.bin/vitest +2 -2
- package/integration-tests/package.json +2 -2
- package/package.json +7 -7
- package/src/__fixtures__/tools.ts +0 -9
- package/src/client/client.ts +45 -1
- package/src/client/configuration.ts +166 -35
- package/src/server/promptActions.ts +13 -2
- package/src/server/resourceActions.ts +32 -4
- package/src/server/server.test.ts +84 -0
- package/src/server/server.ts +302 -114
- package/integration-tests/node_modules/.bin/mastra +0 -21
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MastraBase } from '@mastra/core/base';
|
|
2
|
+
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
3
|
import { createTool as createTool$1 } from '@mastra/core/tools';
|
|
3
4
|
import { isZodType } from '@mastra/core/utils';
|
|
4
5
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
@@ -20,7 +21,6 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
20
21
|
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
21
22
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
22
23
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
23
|
-
import { streamSSE } from 'hono/streaming';
|
|
24
24
|
|
|
25
25
|
// src/client/client.ts
|
|
26
26
|
var PromptClientActions = class {
|
|
@@ -492,7 +492,42 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
492
492
|
error: errorDetails,
|
|
493
493
|
originalJsonSchema: inputSchema
|
|
494
494
|
});
|
|
495
|
-
throw new
|
|
495
|
+
throw new MastraError({
|
|
496
|
+
id: "MCP_TOOL_INPUT_SCHEMA_CONVERSION_FAILED",
|
|
497
|
+
domain: ErrorDomain.MCP,
|
|
498
|
+
category: ErrorCategory.USER,
|
|
499
|
+
details: { error: errorDetails ?? "Unknown error" }
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
convertOutputSchema(outputSchema) {
|
|
504
|
+
if (!outputSchema) return;
|
|
505
|
+
if (isZodType(outputSchema)) {
|
|
506
|
+
return outputSchema;
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
return convertJsonSchemaToZod(outputSchema);
|
|
510
|
+
} catch (error) {
|
|
511
|
+
let errorDetails;
|
|
512
|
+
if (error instanceof Error) {
|
|
513
|
+
errorDetails = error.stack;
|
|
514
|
+
} else {
|
|
515
|
+
try {
|
|
516
|
+
errorDetails = JSON.stringify(error);
|
|
517
|
+
} catch {
|
|
518
|
+
errorDetails = String(error);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
this.log("error", "Failed to convert JSON schema to Zod schema using zodFromJsonSchema", {
|
|
522
|
+
error: errorDetails,
|
|
523
|
+
originalJsonSchema: outputSchema
|
|
524
|
+
});
|
|
525
|
+
throw new MastraError({
|
|
526
|
+
id: "MCP_TOOL_OUTPUT_SCHEMA_CONVERSION_FAILED",
|
|
527
|
+
domain: ErrorDomain.MCP,
|
|
528
|
+
category: ErrorCategory.USER,
|
|
529
|
+
details: { error: errorDetails ?? "Unknown error" }
|
|
530
|
+
});
|
|
496
531
|
}
|
|
497
532
|
}
|
|
498
533
|
async tools() {
|
|
@@ -506,6 +541,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
506
541
|
id: `${this.name}_${tool.name}`,
|
|
507
542
|
description: tool.description || "",
|
|
508
543
|
inputSchema: this.convertInputSchema(tool.inputSchema),
|
|
544
|
+
outputSchema: this.convertOutputSchema(tool.outputSchema),
|
|
509
545
|
execute: async ({ context, runtimeContext }) => {
|
|
510
546
|
const previousContext = this.currentOperationContext;
|
|
511
547
|
this.currentOperationContext = runtimeContext || null;
|
|
@@ -609,7 +645,16 @@ To fix this you have three different options:
|
|
|
609
645
|
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
610
646
|
allResources[serverName] = await internalClient.resources.list();
|
|
611
647
|
} catch (error) {
|
|
612
|
-
|
|
648
|
+
const mastraError = new MastraError({
|
|
649
|
+
id: "MCP_CLIENT_LIST_RESOURCES_FAILED",
|
|
650
|
+
domain: ErrorDomain.MCP,
|
|
651
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
652
|
+
details: {
|
|
653
|
+
serverName
|
|
654
|
+
}
|
|
655
|
+
}, error);
|
|
656
|
+
this.logger.trackException(mastraError);
|
|
657
|
+
this.logger.error("Failed to list resources from server:", { error: mastraError.toString() });
|
|
613
658
|
}
|
|
614
659
|
}
|
|
615
660
|
return allResources;
|
|
@@ -621,30 +666,97 @@ To fix this you have three different options:
|
|
|
621
666
|
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
622
667
|
allTemplates[serverName] = await internalClient.resources.templates();
|
|
623
668
|
} catch (error) {
|
|
624
|
-
|
|
669
|
+
const mastraError = new MastraError({
|
|
670
|
+
id: "MCP_CLIENT_LIST_RESOURCE_TEMPLATES_FAILED",
|
|
671
|
+
domain: ErrorDomain.MCP,
|
|
672
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
673
|
+
details: {
|
|
674
|
+
serverName
|
|
675
|
+
}
|
|
676
|
+
}, error);
|
|
677
|
+
this.logger.trackException(mastraError);
|
|
678
|
+
this.logger.error("Failed to list resource templates from server:", { error: mastraError.toString() });
|
|
625
679
|
}
|
|
626
680
|
}
|
|
627
681
|
return allTemplates;
|
|
628
682
|
},
|
|
629
683
|
read: async (serverName, uri) => {
|
|
630
|
-
|
|
631
|
-
|
|
684
|
+
try {
|
|
685
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
686
|
+
return internalClient.resources.read(uri);
|
|
687
|
+
} catch (error) {
|
|
688
|
+
throw new MastraError({
|
|
689
|
+
id: "MCP_CLIENT_READ_RESOURCE_FAILED",
|
|
690
|
+
domain: ErrorDomain.MCP,
|
|
691
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
692
|
+
details: {
|
|
693
|
+
serverName,
|
|
694
|
+
uri
|
|
695
|
+
}
|
|
696
|
+
}, error);
|
|
697
|
+
}
|
|
632
698
|
},
|
|
633
699
|
subscribe: async (serverName, uri) => {
|
|
634
|
-
|
|
635
|
-
|
|
700
|
+
try {
|
|
701
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
702
|
+
return internalClient.resources.subscribe(uri);
|
|
703
|
+
} catch (error) {
|
|
704
|
+
throw new MastraError({
|
|
705
|
+
id: "MCP_CLIENT_SUBSCRIBE_RESOURCE_FAILED",
|
|
706
|
+
domain: ErrorDomain.MCP,
|
|
707
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
708
|
+
details: {
|
|
709
|
+
serverName,
|
|
710
|
+
uri
|
|
711
|
+
}
|
|
712
|
+
}, error);
|
|
713
|
+
}
|
|
636
714
|
},
|
|
637
715
|
unsubscribe: async (serverName, uri) => {
|
|
638
|
-
|
|
639
|
-
|
|
716
|
+
try {
|
|
717
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
718
|
+
return internalClient.resources.unsubscribe(uri);
|
|
719
|
+
} catch (err) {
|
|
720
|
+
throw new MastraError({
|
|
721
|
+
id: "MCP_CLIENT_UNSUBSCRIBE_RESOURCE_FAILED",
|
|
722
|
+
domain: ErrorDomain.MCP,
|
|
723
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
724
|
+
details: {
|
|
725
|
+
serverName,
|
|
726
|
+
uri
|
|
727
|
+
}
|
|
728
|
+
}, err);
|
|
729
|
+
}
|
|
640
730
|
},
|
|
641
731
|
onUpdated: async (serverName, handler) => {
|
|
642
|
-
|
|
643
|
-
|
|
732
|
+
try {
|
|
733
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
734
|
+
return internalClient.resources.onUpdated(handler);
|
|
735
|
+
} catch (err) {
|
|
736
|
+
throw new MastraError({
|
|
737
|
+
id: "MCP_CLIENT_ON_UPDATED_RESOURCE_FAILED",
|
|
738
|
+
domain: ErrorDomain.MCP,
|
|
739
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
740
|
+
details: {
|
|
741
|
+
serverName
|
|
742
|
+
}
|
|
743
|
+
}, err);
|
|
744
|
+
}
|
|
644
745
|
},
|
|
645
746
|
onListChanged: async (serverName, handler) => {
|
|
646
|
-
|
|
647
|
-
|
|
747
|
+
try {
|
|
748
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
749
|
+
return internalClient.resources.onListChanged(handler);
|
|
750
|
+
} catch (err) {
|
|
751
|
+
throw new MastraError({
|
|
752
|
+
id: "MCP_CLIENT_ON_LIST_CHANGED_RESOURCE_FAILED",
|
|
753
|
+
domain: ErrorDomain.MCP,
|
|
754
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
755
|
+
details: {
|
|
756
|
+
serverName
|
|
757
|
+
}
|
|
758
|
+
}, err);
|
|
759
|
+
}
|
|
648
760
|
}
|
|
649
761
|
};
|
|
650
762
|
}
|
|
@@ -658,18 +770,50 @@ To fix this you have three different options:
|
|
|
658
770
|
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
659
771
|
allPrompts[serverName] = await internalClient.prompts.list();
|
|
660
772
|
} catch (error) {
|
|
661
|
-
|
|
773
|
+
const mastraError = new MastraError({
|
|
774
|
+
id: "MCP_CLIENT_LIST_PROMPTS_FAILED",
|
|
775
|
+
domain: ErrorDomain.MCP,
|
|
776
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
777
|
+
details: {
|
|
778
|
+
serverName
|
|
779
|
+
}
|
|
780
|
+
}, error);
|
|
781
|
+
this.logger.trackException(mastraError);
|
|
782
|
+
this.logger.error("Failed to list prompts from server:", { error: mastraError.toString() });
|
|
662
783
|
}
|
|
663
784
|
}
|
|
664
785
|
return allPrompts;
|
|
665
786
|
},
|
|
666
787
|
get: async ({ serverName, name, args, version }) => {
|
|
667
|
-
|
|
668
|
-
|
|
788
|
+
try {
|
|
789
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
790
|
+
return internalClient.prompts.get({ name, args, version });
|
|
791
|
+
} catch (error) {
|
|
792
|
+
throw new MastraError({
|
|
793
|
+
id: "MCP_CLIENT_GET_PROMPT_FAILED",
|
|
794
|
+
domain: ErrorDomain.MCP,
|
|
795
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
796
|
+
details: {
|
|
797
|
+
serverName,
|
|
798
|
+
name
|
|
799
|
+
}
|
|
800
|
+
}, error);
|
|
801
|
+
}
|
|
669
802
|
},
|
|
670
803
|
onListChanged: async (serverName, handler) => {
|
|
671
|
-
|
|
672
|
-
|
|
804
|
+
try {
|
|
805
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
806
|
+
return internalClient.prompts.onListChanged(handler);
|
|
807
|
+
} catch (error) {
|
|
808
|
+
throw new MastraError({
|
|
809
|
+
id: "MCP_CLIENT_ON_LIST_CHANGED_PROMPT_FAILED",
|
|
810
|
+
domain: ErrorDomain.MCP,
|
|
811
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
812
|
+
details: {
|
|
813
|
+
serverName
|
|
814
|
+
}
|
|
815
|
+
}, error);
|
|
816
|
+
}
|
|
673
817
|
}
|
|
674
818
|
};
|
|
675
819
|
}
|
|
@@ -701,21 +845,37 @@ To fix this you have three different options:
|
|
|
701
845
|
async getTools() {
|
|
702
846
|
this.addToInstanceCache();
|
|
703
847
|
const connectedTools = {};
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
848
|
+
try {
|
|
849
|
+
await this.eachClientTools(async ({ serverName, tools }) => {
|
|
850
|
+
for (const [toolName, toolConfig] of Object.entries(tools)) {
|
|
851
|
+
connectedTools[`${serverName}_${toolName}`] = toolConfig;
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
} catch (error) {
|
|
855
|
+
throw new MastraError({
|
|
856
|
+
id: "MCP_CLIENT_GET_TOOLS_FAILED",
|
|
857
|
+
domain: ErrorDomain.MCP,
|
|
858
|
+
category: ErrorCategory.THIRD_PARTY
|
|
859
|
+
}, error);
|
|
860
|
+
}
|
|
709
861
|
return connectedTools;
|
|
710
862
|
}
|
|
711
863
|
async getToolsets() {
|
|
712
864
|
this.addToInstanceCache();
|
|
713
865
|
const connectedToolsets = {};
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
866
|
+
try {
|
|
867
|
+
await this.eachClientTools(async ({ serverName, tools }) => {
|
|
868
|
+
if (tools) {
|
|
869
|
+
connectedToolsets[serverName] = tools;
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
} catch (error) {
|
|
873
|
+
throw new MastraError({
|
|
874
|
+
id: "MCP_CLIENT_GET_TOOLSETS_FAILED",
|
|
875
|
+
domain: ErrorDomain.MCP,
|
|
876
|
+
category: ErrorCategory.THIRD_PARTY
|
|
877
|
+
}, error);
|
|
878
|
+
}
|
|
719
879
|
return connectedToolsets;
|
|
720
880
|
}
|
|
721
881
|
/**
|
|
@@ -762,13 +922,19 @@ To fix this you have three different options:
|
|
|
762
922
|
try {
|
|
763
923
|
await mcpClient.connect();
|
|
764
924
|
} catch (e) {
|
|
925
|
+
const mastraError = new MastraError({
|
|
926
|
+
id: "MCP_CLIENT_CONNECT_FAILED",
|
|
927
|
+
domain: ErrorDomain.MCP,
|
|
928
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
929
|
+
text: `Failed to connect to MCP server ${name}: ${e instanceof Error ? e.stack || e.message : String(e)}`,
|
|
930
|
+
details: {
|
|
931
|
+
name
|
|
932
|
+
}
|
|
933
|
+
}, e);
|
|
934
|
+
this.logger.trackException(mastraError);
|
|
935
|
+
this.logger.error("MCPClient errored connecting to MCP server:", { error: mastraError.toString() });
|
|
765
936
|
this.mcpClientsById.delete(name);
|
|
766
|
-
|
|
767
|
-
error: e instanceof Error ? e.message : String(e)
|
|
768
|
-
});
|
|
769
|
-
throw new Error(
|
|
770
|
-
`Failed to connect to MCP server ${name}: ${e instanceof Error ? e.stack || e.message : String(e)}`
|
|
771
|
-
);
|
|
937
|
+
throw mastraError;
|
|
772
938
|
}
|
|
773
939
|
this.logger.debug(`Connected to ${name} MCP server`);
|
|
774
940
|
return mcpClient;
|
|
@@ -798,6 +964,165 @@ var MCPConfiguration = class extends MCPClient {
|
|
|
798
964
|
);
|
|
799
965
|
}
|
|
800
966
|
};
|
|
967
|
+
|
|
968
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/utils/stream.js
|
|
969
|
+
var StreamingApi = class {
|
|
970
|
+
writer;
|
|
971
|
+
encoder;
|
|
972
|
+
writable;
|
|
973
|
+
abortSubscribers = [];
|
|
974
|
+
responseReadable;
|
|
975
|
+
aborted = false;
|
|
976
|
+
closed = false;
|
|
977
|
+
constructor(writable, _readable) {
|
|
978
|
+
this.writable = writable;
|
|
979
|
+
this.writer = writable.getWriter();
|
|
980
|
+
this.encoder = new TextEncoder();
|
|
981
|
+
const reader = _readable.getReader();
|
|
982
|
+
this.abortSubscribers.push(async () => {
|
|
983
|
+
await reader.cancel();
|
|
984
|
+
});
|
|
985
|
+
this.responseReadable = new ReadableStream({
|
|
986
|
+
async pull(controller) {
|
|
987
|
+
const { done, value } = await reader.read();
|
|
988
|
+
done ? controller.close() : controller.enqueue(value);
|
|
989
|
+
},
|
|
990
|
+
cancel: () => {
|
|
991
|
+
this.abort();
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
async write(input) {
|
|
996
|
+
try {
|
|
997
|
+
if (typeof input === "string") {
|
|
998
|
+
input = this.encoder.encode(input);
|
|
999
|
+
}
|
|
1000
|
+
await this.writer.write(input);
|
|
1001
|
+
} catch {
|
|
1002
|
+
}
|
|
1003
|
+
return this;
|
|
1004
|
+
}
|
|
1005
|
+
async writeln(input) {
|
|
1006
|
+
await this.write(input + "\n");
|
|
1007
|
+
return this;
|
|
1008
|
+
}
|
|
1009
|
+
sleep(ms) {
|
|
1010
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
1011
|
+
}
|
|
1012
|
+
async close() {
|
|
1013
|
+
try {
|
|
1014
|
+
await this.writer.close();
|
|
1015
|
+
} catch {
|
|
1016
|
+
}
|
|
1017
|
+
this.closed = true;
|
|
1018
|
+
}
|
|
1019
|
+
async pipe(body) {
|
|
1020
|
+
this.writer.releaseLock();
|
|
1021
|
+
await body.pipeTo(this.writable, { preventClose: true });
|
|
1022
|
+
this.writer = this.writable.getWriter();
|
|
1023
|
+
}
|
|
1024
|
+
onAbort(listener) {
|
|
1025
|
+
this.abortSubscribers.push(listener);
|
|
1026
|
+
}
|
|
1027
|
+
abort() {
|
|
1028
|
+
if (!this.aborted) {
|
|
1029
|
+
this.aborted = true;
|
|
1030
|
+
this.abortSubscribers.forEach((subscriber) => subscriber());
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/helper/streaming/utils.js
|
|
1036
|
+
var isOldBunVersion = () => {
|
|
1037
|
+
const version = typeof Bun !== "undefined" ? Bun.version : void 0;
|
|
1038
|
+
if (version === void 0) {
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
const result = version.startsWith("1.1") || version.startsWith("1.0") || version.startsWith("0.");
|
|
1042
|
+
isOldBunVersion = () => result;
|
|
1043
|
+
return result;
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/utils/html.js
|
|
1047
|
+
var HtmlEscapedCallbackPhase = {
|
|
1048
|
+
Stringify: 1};
|
|
1049
|
+
var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
|
|
1050
|
+
if (typeof str === "object" && !(str instanceof String)) {
|
|
1051
|
+
if (!(str instanceof Promise)) {
|
|
1052
|
+
str = str.toString();
|
|
1053
|
+
}
|
|
1054
|
+
if (str instanceof Promise) {
|
|
1055
|
+
str = await str;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
const callbacks = str.callbacks;
|
|
1059
|
+
if (!callbacks?.length) {
|
|
1060
|
+
return Promise.resolve(str);
|
|
1061
|
+
}
|
|
1062
|
+
if (buffer) {
|
|
1063
|
+
buffer[0] += str;
|
|
1064
|
+
} else {
|
|
1065
|
+
buffer = [str];
|
|
1066
|
+
}
|
|
1067
|
+
const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then(
|
|
1068
|
+
(res) => Promise.all(
|
|
1069
|
+
res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))
|
|
1070
|
+
).then(() => buffer[0])
|
|
1071
|
+
);
|
|
1072
|
+
{
|
|
1073
|
+
return resStr;
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/helper/streaming/sse.js
|
|
1078
|
+
var SSEStreamingApi = class extends StreamingApi {
|
|
1079
|
+
constructor(writable, readable) {
|
|
1080
|
+
super(writable, readable);
|
|
1081
|
+
}
|
|
1082
|
+
async writeSSE(message) {
|
|
1083
|
+
const data = await resolveCallback(message.data, HtmlEscapedCallbackPhase.Stringify, false, {});
|
|
1084
|
+
const dataLines = data.split("\n").map((line) => {
|
|
1085
|
+
return `data: ${line}`;
|
|
1086
|
+
}).join("\n");
|
|
1087
|
+
const sseData = [
|
|
1088
|
+
message.event && `event: ${message.event}`,
|
|
1089
|
+
dataLines,
|
|
1090
|
+
message.id && `id: ${message.id}`,
|
|
1091
|
+
message.retry && `retry: ${message.retry}`
|
|
1092
|
+
].filter(Boolean).join("\n") + "\n\n";
|
|
1093
|
+
await this.write(sseData);
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
var run = async (stream2, cb, onError) => {
|
|
1097
|
+
try {
|
|
1098
|
+
await cb(stream2);
|
|
1099
|
+
} catch (e) {
|
|
1100
|
+
{
|
|
1101
|
+
console.error(e);
|
|
1102
|
+
}
|
|
1103
|
+
} finally {
|
|
1104
|
+
stream2.close();
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
var contextStash = /* @__PURE__ */ new WeakMap();
|
|
1108
|
+
var streamSSE = (c, cb, onError) => {
|
|
1109
|
+
const { readable, writable } = new TransformStream();
|
|
1110
|
+
const stream2 = new SSEStreamingApi(writable, readable);
|
|
1111
|
+
if (isOldBunVersion()) {
|
|
1112
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1113
|
+
if (!stream2.closed) {
|
|
1114
|
+
stream2.abort();
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
contextStash.set(stream2.responseReadable, c);
|
|
1119
|
+
c.header("Transfer-Encoding", "chunked");
|
|
1120
|
+
c.header("Content-Type", "text/event-stream");
|
|
1121
|
+
c.header("Cache-Control", "no-cache");
|
|
1122
|
+
c.header("Connection", "keep-alive");
|
|
1123
|
+
run(stream2, cb);
|
|
1124
|
+
return c.newResponse(stream2.responseReadable);
|
|
1125
|
+
};
|
|
801
1126
|
var MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
|
|
802
1127
|
var SSETransport = class {
|
|
803
1128
|
messageUrl;
|
|
@@ -809,9 +1134,9 @@ var SSETransport = class {
|
|
|
809
1134
|
/**
|
|
810
1135
|
* Creates a new SSETransport, which will direct the MPC client to POST messages to messageUrl
|
|
811
1136
|
*/
|
|
812
|
-
constructor(messageUrl,
|
|
1137
|
+
constructor(messageUrl, stream2) {
|
|
813
1138
|
this.messageUrl = messageUrl;
|
|
814
|
-
this.stream =
|
|
1139
|
+
this.stream = stream2;
|
|
815
1140
|
this._sessionId = crypto.randomUUID();
|
|
816
1141
|
this.stream.onAbort(() => {
|
|
817
1142
|
void this.close();
|
|
@@ -828,6 +1153,10 @@ var SSETransport = class {
|
|
|
828
1153
|
if (this.stream.closed) {
|
|
829
1154
|
throw new Error("SSE transport already closed!");
|
|
830
1155
|
}
|
|
1156
|
+
await this.stream.writeSSE({
|
|
1157
|
+
event: "ping",
|
|
1158
|
+
data: ""
|
|
1159
|
+
});
|
|
831
1160
|
await this.stream.writeSSE({
|
|
832
1161
|
event: "endpoint",
|
|
833
1162
|
data: `${this.messageUrl}?sessionId=${this.sessionId}`
|
|
@@ -882,8 +1211,6 @@ var SSETransport = class {
|
|
|
882
1211
|
});
|
|
883
1212
|
}
|
|
884
1213
|
};
|
|
885
|
-
|
|
886
|
-
// src/server/promptActions.ts
|
|
887
1214
|
var ServerPromptActions = class {
|
|
888
1215
|
getLogger;
|
|
889
1216
|
getSdkServer;
|
|
@@ -903,15 +1230,23 @@ var ServerPromptActions = class {
|
|
|
903
1230
|
try {
|
|
904
1231
|
await this.getSdkServer().sendPromptListChanged();
|
|
905
1232
|
} catch (error) {
|
|
1233
|
+
const mastraError = new MastraError(
|
|
1234
|
+
{
|
|
1235
|
+
id: "MCP_SERVER_PROMPT_LIST_CHANGED_NOTIFICATION_FAILED",
|
|
1236
|
+
domain: ErrorDomain.MCP,
|
|
1237
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1238
|
+
text: "Failed to send prompt list changed notification"
|
|
1239
|
+
},
|
|
1240
|
+
error
|
|
1241
|
+
);
|
|
906
1242
|
this.getLogger().error("Failed to send prompt list changed notification:", {
|
|
907
|
-
error:
|
|
1243
|
+
error: mastraError.toString()
|
|
908
1244
|
});
|
|
909
|
-
|
|
1245
|
+
this.getLogger().trackException(mastraError);
|
|
1246
|
+
throw mastraError;
|
|
910
1247
|
}
|
|
911
1248
|
}
|
|
912
1249
|
};
|
|
913
|
-
|
|
914
|
-
// src/server/resourceActions.ts
|
|
915
1250
|
var ServerResourceActions = class {
|
|
916
1251
|
getSubscriptions;
|
|
917
1252
|
getLogger;
|
|
@@ -935,8 +1270,23 @@ var ServerResourceActions = class {
|
|
|
935
1270
|
try {
|
|
936
1271
|
await this.getSdkServer().sendResourceUpdated({ uri });
|
|
937
1272
|
} catch (error) {
|
|
938
|
-
|
|
939
|
-
|
|
1273
|
+
const mastraError = new MastraError(
|
|
1274
|
+
{
|
|
1275
|
+
id: "MCP_SERVER_RESOURCE_UPDATED_NOTIFICATION_FAILED",
|
|
1276
|
+
domain: ErrorDomain.MCP,
|
|
1277
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1278
|
+
text: "Failed to send resource updated notification",
|
|
1279
|
+
details: {
|
|
1280
|
+
uri
|
|
1281
|
+
}
|
|
1282
|
+
},
|
|
1283
|
+
error
|
|
1284
|
+
);
|
|
1285
|
+
this.getLogger().trackException(mastraError);
|
|
1286
|
+
this.getLogger().error("Failed to send resource updated notification:", {
|
|
1287
|
+
error: mastraError.toString()
|
|
1288
|
+
});
|
|
1289
|
+
throw mastraError;
|
|
940
1290
|
}
|
|
941
1291
|
} else {
|
|
942
1292
|
this.getLogger().debug(`Resource ${uri} was updated, but no active subscriptions for it.`);
|
|
@@ -955,8 +1305,20 @@ var ServerResourceActions = class {
|
|
|
955
1305
|
try {
|
|
956
1306
|
await this.getSdkServer().sendResourceListChanged();
|
|
957
1307
|
} catch (error) {
|
|
958
|
-
|
|
959
|
-
|
|
1308
|
+
const mastraError = new MastraError(
|
|
1309
|
+
{
|
|
1310
|
+
id: "MCP_SERVER_RESOURCE_LIST_CHANGED_NOTIFICATION_FAILED",
|
|
1311
|
+
domain: ErrorDomain.MCP,
|
|
1312
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1313
|
+
text: "Failed to send resource list changed notification"
|
|
1314
|
+
},
|
|
1315
|
+
error
|
|
1316
|
+
);
|
|
1317
|
+
this.getLogger().trackException(mastraError);
|
|
1318
|
+
this.getLogger().error("Failed to send resource list changed notification:", {
|
|
1319
|
+
error: mastraError.toString()
|
|
1320
|
+
});
|
|
1321
|
+
throw mastraError;
|
|
960
1322
|
}
|
|
961
1323
|
}
|
|
962
1324
|
};
|
|
@@ -1166,8 +1528,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1166
1528
|
context
|
|
1167
1529
|
);
|
|
1168
1530
|
try {
|
|
1169
|
-
const
|
|
1170
|
-
const response = await
|
|
1531
|
+
const run2 = workflow.createRun({ runId: runtimeContext?.get("runId") });
|
|
1532
|
+
const response = await run2.start({ inputData: context, runtimeContext });
|
|
1171
1533
|
return response;
|
|
1172
1534
|
} catch (error) {
|
|
1173
1535
|
this.logger.error(
|
|
@@ -1190,6 +1552,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1190
1552
|
name: workflowToolName,
|
|
1191
1553
|
description: coreTool.description,
|
|
1192
1554
|
parameters: coreTool.parameters,
|
|
1555
|
+
outputSchema: coreTool.outputSchema,
|
|
1193
1556
|
execute: coreTool.execute,
|
|
1194
1557
|
toolType: "workflow"
|
|
1195
1558
|
};
|
|
@@ -1229,13 +1592,32 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1229
1592
|
name: toolName,
|
|
1230
1593
|
description: coreTool.description,
|
|
1231
1594
|
parameters: coreTool.parameters,
|
|
1595
|
+
outputSchema: coreTool.outputSchema,
|
|
1232
1596
|
execute: coreTool.execute
|
|
1233
1597
|
};
|
|
1234
1598
|
this.logger.info(`Registered explicit tool: '${toolName}'`);
|
|
1235
1599
|
}
|
|
1236
1600
|
this.logger.info(`Total defined tools registered: ${Object.keys(definedConvertedTools).length}`);
|
|
1237
|
-
|
|
1238
|
-
|
|
1601
|
+
let agentDerivedTools = {};
|
|
1602
|
+
let workflowDerivedTools = {};
|
|
1603
|
+
try {
|
|
1604
|
+
agentDerivedTools = this.convertAgentsToTools(agentsConfig, definedConvertedTools);
|
|
1605
|
+
workflowDerivedTools = this.convertWorkflowsToTools(workflowsConfig, definedConvertedTools);
|
|
1606
|
+
} catch (e) {
|
|
1607
|
+
const mastraError = new MastraError(
|
|
1608
|
+
{
|
|
1609
|
+
id: "MCP_SERVER_AGENT_OR_WORKFLOW_TOOL_CONVERSION_FAILED",
|
|
1610
|
+
domain: ErrorDomain.MCP,
|
|
1611
|
+
category: ErrorCategory.USER
|
|
1612
|
+
},
|
|
1613
|
+
e
|
|
1614
|
+
);
|
|
1615
|
+
this.logger.trackException(mastraError);
|
|
1616
|
+
this.logger.error("Failed to convert tools:", {
|
|
1617
|
+
error: mastraError.toString()
|
|
1618
|
+
});
|
|
1619
|
+
throw mastraError;
|
|
1620
|
+
}
|
|
1239
1621
|
const allConvertedTools = { ...definedConvertedTools, ...agentDerivedTools, ...workflowDerivedTools };
|
|
1240
1622
|
const finalToolCount = Object.keys(allConvertedTools).length;
|
|
1241
1623
|
const definedCount = Object.keys(definedConvertedTools).length;
|
|
@@ -1257,11 +1639,17 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1257
1639
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1258
1640
|
this.logger.debug("Handling ListTools request");
|
|
1259
1641
|
return {
|
|
1260
|
-
tools: Object.values(this.convertedTools).map((tool) =>
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1642
|
+
tools: Object.values(this.convertedTools).map((tool) => {
|
|
1643
|
+
const toolSpec = {
|
|
1644
|
+
name: tool.name,
|
|
1645
|
+
description: tool.description,
|
|
1646
|
+
inputSchema: tool.parameters.jsonSchema
|
|
1647
|
+
};
|
|
1648
|
+
if (tool.outputSchema) {
|
|
1649
|
+
toolSpec.outputSchema = tool.outputSchema.jsonSchema;
|
|
1650
|
+
}
|
|
1651
|
+
return toolSpec;
|
|
1652
|
+
})
|
|
1265
1653
|
};
|
|
1266
1654
|
});
|
|
1267
1655
|
}
|
|
@@ -1284,7 +1672,6 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1284
1672
|
isError: true
|
|
1285
1673
|
};
|
|
1286
1674
|
}
|
|
1287
|
-
this.logger.debug(`CallTool: Invoking '${request.params.name}' with arguments:`, request.params.arguments);
|
|
1288
1675
|
const validation = tool.parameters.validate?.(request.params.arguments ?? {});
|
|
1289
1676
|
if (validation && !validation.success) {
|
|
1290
1677
|
this.logger.warn(`CallTool: Invalid tool arguments for '${request.params.name}'`, {
|
|
@@ -1303,17 +1690,38 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1303
1690
|
};
|
|
1304
1691
|
}
|
|
1305
1692
|
const result = await tool.execute(validation?.value, { messages: [], toolCallId: "" });
|
|
1693
|
+
this.logger.debug(`CallTool: Tool '${request.params.name}' executed successfully with result:`, result);
|
|
1306
1694
|
const duration = Date.now() - startTime;
|
|
1307
1695
|
this.logger.info(`Tool '${request.params.name}' executed successfully in ${duration}ms.`);
|
|
1308
|
-
|
|
1309
|
-
|
|
1696
|
+
const response = { isError: false, content: [] };
|
|
1697
|
+
if (tool.outputSchema) {
|
|
1698
|
+
if (!result.structuredContent) {
|
|
1699
|
+
throw new Error(`Tool ${request.params.name} has an output schema but no structured content was provided.`);
|
|
1700
|
+
}
|
|
1701
|
+
const outputValidation = tool.outputSchema.validate?.(result.structuredContent ?? {});
|
|
1702
|
+
if (outputValidation && !outputValidation.success) {
|
|
1703
|
+
this.logger.warn(`CallTool: Invalid structured content for '${request.params.name}'`, {
|
|
1704
|
+
errors: outputValidation.error
|
|
1705
|
+
});
|
|
1706
|
+
throw new Error(
|
|
1707
|
+
`Invalid structured content for tool ${request.params.name}: ${JSON.stringify(outputValidation.error)}`
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
response.structuredContent = result.structuredContent;
|
|
1711
|
+
}
|
|
1712
|
+
if (result.content) {
|
|
1713
|
+
response.content = result.content;
|
|
1714
|
+
} else if (response.structuredContent) {
|
|
1715
|
+
response.content = [{ type: "text", text: JSON.stringify(response.structuredContent) }];
|
|
1716
|
+
} else {
|
|
1717
|
+
response.content = [
|
|
1310
1718
|
{
|
|
1311
1719
|
type: "text",
|
|
1312
1720
|
text: typeof result === "string" ? result : JSON.stringify(result)
|
|
1313
1721
|
}
|
|
1314
|
-
]
|
|
1315
|
-
|
|
1316
|
-
|
|
1722
|
+
];
|
|
1723
|
+
}
|
|
1724
|
+
return response;
|
|
1317
1725
|
} catch (error) {
|
|
1318
1726
|
const duration = Date.now() - startTime;
|
|
1319
1727
|
if (error instanceof z.ZodError) {
|
|
@@ -1587,7 +1995,23 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1587
1995
|
*/
|
|
1588
1996
|
async startStdio() {
|
|
1589
1997
|
this.stdioTransport = new StdioServerTransport();
|
|
1590
|
-
|
|
1998
|
+
try {
|
|
1999
|
+
await this.server.connect(this.stdioTransport);
|
|
2000
|
+
} catch (error) {
|
|
2001
|
+
const mastraError = new MastraError(
|
|
2002
|
+
{
|
|
2003
|
+
id: "MCP_SERVER_STDIO_CONNECTION_FAILED",
|
|
2004
|
+
domain: ErrorDomain.MCP,
|
|
2005
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2006
|
+
},
|
|
2007
|
+
error
|
|
2008
|
+
);
|
|
2009
|
+
this.logger.trackException(mastraError);
|
|
2010
|
+
this.logger.error("Failed to connect MCP server using stdio transport:", {
|
|
2011
|
+
error: mastraError.toString()
|
|
2012
|
+
});
|
|
2013
|
+
throw mastraError;
|
|
2014
|
+
}
|
|
1591
2015
|
this.logger.info("Started MCP Server (stdio)");
|
|
1592
2016
|
}
|
|
1593
2017
|
/**
|
|
@@ -1601,23 +2025,42 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1601
2025
|
* @param res HTTP response (must support .write/.end)
|
|
1602
2026
|
*/
|
|
1603
2027
|
async startSSE({ url, ssePath, messagePath, req, res }) {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
2028
|
+
try {
|
|
2029
|
+
if (url.pathname === ssePath) {
|
|
2030
|
+
await this.connectSSE({
|
|
2031
|
+
messagePath,
|
|
2032
|
+
res
|
|
2033
|
+
});
|
|
2034
|
+
} else if (url.pathname === messagePath) {
|
|
2035
|
+
this.logger.debug("Received message");
|
|
2036
|
+
if (!this.sseTransport) {
|
|
2037
|
+
res.writeHead(503);
|
|
2038
|
+
res.end("SSE connection not established");
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
await this.sseTransport.handlePostMessage(req, res);
|
|
2042
|
+
} else {
|
|
2043
|
+
this.logger.debug("Unknown path:", { path: url.pathname });
|
|
2044
|
+
res.writeHead(404);
|
|
2045
|
+
res.end();
|
|
1615
2046
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
2047
|
+
} catch (e) {
|
|
2048
|
+
const mastraError = new MastraError(
|
|
2049
|
+
{
|
|
2050
|
+
id: "MCP_SERVER_SSE_START_FAILED",
|
|
2051
|
+
domain: ErrorDomain.MCP,
|
|
2052
|
+
category: ErrorCategory.USER,
|
|
2053
|
+
details: {
|
|
2054
|
+
url: url.toString(),
|
|
2055
|
+
ssePath,
|
|
2056
|
+
messagePath
|
|
2057
|
+
}
|
|
2058
|
+
},
|
|
2059
|
+
e
|
|
2060
|
+
);
|
|
2061
|
+
this.logger.trackException(mastraError);
|
|
2062
|
+
this.logger.error("Failed to start MCP Server (SSE):", { error: mastraError.toString() });
|
|
2063
|
+
throw mastraError;
|
|
1621
2064
|
}
|
|
1622
2065
|
}
|
|
1623
2066
|
/**
|
|
@@ -1630,31 +2073,50 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1630
2073
|
* @param context Incoming Hono context
|
|
1631
2074
|
*/
|
|
1632
2075
|
async startHonoSSE({ url, ssePath, messagePath, context }) {
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
2076
|
+
try {
|
|
2077
|
+
if (url.pathname === ssePath) {
|
|
2078
|
+
return streamSSE(context, async (stream2) => {
|
|
2079
|
+
await this.connectHonoSSE({
|
|
2080
|
+
messagePath,
|
|
2081
|
+
stream: stream2
|
|
2082
|
+
});
|
|
1638
2083
|
});
|
|
1639
|
-
})
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2084
|
+
} else if (url.pathname === messagePath) {
|
|
2085
|
+
this.logger.debug("Received message");
|
|
2086
|
+
const sessionId = context.req.query("sessionId");
|
|
2087
|
+
this.logger.debug("Received message for sessionId", { sessionId });
|
|
2088
|
+
if (!sessionId) {
|
|
2089
|
+
return context.text("No sessionId provided", 400);
|
|
2090
|
+
}
|
|
2091
|
+
if (!this.sseHonoTransports.has(sessionId)) {
|
|
2092
|
+
return context.text(`No transport found for sessionId ${sessionId}`, 400);
|
|
2093
|
+
}
|
|
2094
|
+
const message = await this.sseHonoTransports.get(sessionId)?.handlePostMessage(context);
|
|
2095
|
+
if (!message) {
|
|
2096
|
+
return context.text("Transport not found", 400);
|
|
2097
|
+
}
|
|
2098
|
+
return message;
|
|
2099
|
+
} else {
|
|
2100
|
+
this.logger.debug("Unknown path:", { path: url.pathname });
|
|
2101
|
+
return context.text("Unknown path", 404);
|
|
1653
2102
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
2103
|
+
} catch (e) {
|
|
2104
|
+
const mastraError = new MastraError(
|
|
2105
|
+
{
|
|
2106
|
+
id: "MCP_SERVER_HONO_SSE_START_FAILED",
|
|
2107
|
+
domain: ErrorDomain.MCP,
|
|
2108
|
+
category: ErrorCategory.USER,
|
|
2109
|
+
details: {
|
|
2110
|
+
url: url.toString(),
|
|
2111
|
+
ssePath,
|
|
2112
|
+
messagePath
|
|
2113
|
+
}
|
|
2114
|
+
},
|
|
2115
|
+
e
|
|
2116
|
+
);
|
|
2117
|
+
this.logger.trackException(mastraError);
|
|
2118
|
+
this.logger.error("Failed to start MCP Server (Hono SSE):", { error: mastraError.toString() });
|
|
2119
|
+
throw mastraError;
|
|
1658
2120
|
}
|
|
1659
2121
|
}
|
|
1660
2122
|
/**
|
|
@@ -1783,7 +2245,17 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1783
2245
|
}
|
|
1784
2246
|
}
|
|
1785
2247
|
} catch (error) {
|
|
1786
|
-
|
|
2248
|
+
const mastraError = new MastraError(
|
|
2249
|
+
{
|
|
2250
|
+
id: "MCP_SERVER_HTTP_CONNECTION_FAILED",
|
|
2251
|
+
domain: ErrorDomain.MCP,
|
|
2252
|
+
category: ErrorCategory.USER,
|
|
2253
|
+
text: "Failed to connect MCP server using HTTP transport"
|
|
2254
|
+
},
|
|
2255
|
+
error
|
|
2256
|
+
);
|
|
2257
|
+
this.logger.trackException(mastraError);
|
|
2258
|
+
this.logger.error("startHTTP: Error handling Streamable HTTP request:", { error: mastraError });
|
|
1787
2259
|
if (!res.headersSent) {
|
|
1788
2260
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1789
2261
|
res.end(
|
|
@@ -1797,8 +2269,6 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1797
2269
|
// Cannot determine original request ID in catch
|
|
1798
2270
|
})
|
|
1799
2271
|
);
|
|
1800
|
-
} else {
|
|
1801
|
-
this.logger.error("startHTTP: Error after headers sent:", error);
|
|
1802
2272
|
}
|
|
1803
2273
|
}
|
|
1804
2274
|
}
|
|
@@ -1806,38 +2276,72 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1806
2276
|
messagePath,
|
|
1807
2277
|
res
|
|
1808
2278
|
}) {
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
this.
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2279
|
+
try {
|
|
2280
|
+
this.logger.debug("Received SSE connection");
|
|
2281
|
+
this.sseTransport = new SSEServerTransport(messagePath, res);
|
|
2282
|
+
await this.server.connect(this.sseTransport);
|
|
2283
|
+
this.server.onclose = async () => {
|
|
2284
|
+
this.sseTransport = void 0;
|
|
2285
|
+
await this.server.close();
|
|
2286
|
+
};
|
|
2287
|
+
res.on("close", () => {
|
|
2288
|
+
this.sseTransport = void 0;
|
|
2289
|
+
});
|
|
2290
|
+
} catch (e) {
|
|
2291
|
+
const mastraError = new MastraError(
|
|
2292
|
+
{
|
|
2293
|
+
id: "MCP_SERVER_SSE_CONNECT_FAILED",
|
|
2294
|
+
domain: ErrorDomain.MCP,
|
|
2295
|
+
category: ErrorCategory.USER,
|
|
2296
|
+
details: {
|
|
2297
|
+
messagePath
|
|
2298
|
+
}
|
|
2299
|
+
},
|
|
2300
|
+
e
|
|
2301
|
+
);
|
|
2302
|
+
this.logger.trackException(mastraError);
|
|
2303
|
+
this.logger.error("Failed to connect to MCP Server (SSE):", { error: mastraError });
|
|
2304
|
+
throw mastraError;
|
|
2305
|
+
}
|
|
1819
2306
|
}
|
|
1820
|
-
async connectHonoSSE({ messagePath, stream }) {
|
|
2307
|
+
async connectHonoSSE({ messagePath, stream: stream2 }) {
|
|
1821
2308
|
this.logger.debug("Received SSE connection");
|
|
1822
|
-
const sseTransport = new SSETransport(messagePath,
|
|
2309
|
+
const sseTransport = new SSETransport(messagePath, stream2);
|
|
1823
2310
|
const sessionId = sseTransport.sessionId;
|
|
1824
2311
|
this.logger.debug("SSE Transport created with sessionId:", { sessionId });
|
|
1825
2312
|
this.sseHonoTransports.set(sessionId, sseTransport);
|
|
1826
|
-
|
|
2313
|
+
stream2.onAbort(() => {
|
|
1827
2314
|
this.logger.debug("SSE Transport aborted with sessionId:", { sessionId });
|
|
1828
2315
|
this.sseHonoTransports.delete(sessionId);
|
|
1829
2316
|
});
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
this.
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
2317
|
+
try {
|
|
2318
|
+
await this.server.connect(sseTransport);
|
|
2319
|
+
this.server.onclose = async () => {
|
|
2320
|
+
this.logger.debug("SSE Transport closed with sessionId:", { sessionId });
|
|
2321
|
+
this.sseHonoTransports.delete(sessionId);
|
|
2322
|
+
await this.server.close();
|
|
2323
|
+
};
|
|
2324
|
+
while (true) {
|
|
2325
|
+
const sessionIds = Array.from(this.sseHonoTransports.keys() || []);
|
|
2326
|
+
this.logger.debug("Active Hono SSE sessions:", { sessionIds });
|
|
2327
|
+
await stream2.write(":keep-alive\n\n");
|
|
2328
|
+
await stream2.sleep(6e4);
|
|
2329
|
+
}
|
|
2330
|
+
} catch (e) {
|
|
2331
|
+
const mastraError = new MastraError(
|
|
2332
|
+
{
|
|
2333
|
+
id: "MCP_SERVER_HONO_SSE_CONNECT_FAILED",
|
|
2334
|
+
domain: ErrorDomain.MCP,
|
|
2335
|
+
category: ErrorCategory.USER,
|
|
2336
|
+
details: {
|
|
2337
|
+
messagePath
|
|
2338
|
+
}
|
|
2339
|
+
},
|
|
2340
|
+
e
|
|
2341
|
+
);
|
|
2342
|
+
this.logger.trackException(mastraError);
|
|
2343
|
+
this.logger.error("Failed to connect to MCP Server (Hono SSE):", { error: mastraError });
|
|
2344
|
+
throw mastraError;
|
|
1841
2345
|
}
|
|
1842
2346
|
}
|
|
1843
2347
|
/**
|
|
@@ -1875,7 +2379,17 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1875
2379
|
await this.server.close();
|
|
1876
2380
|
this.logger.info("MCP server closed.");
|
|
1877
2381
|
} catch (error) {
|
|
1878
|
-
|
|
2382
|
+
const mastraError = new MastraError(
|
|
2383
|
+
{
|
|
2384
|
+
id: "MCP_SERVER_CLOSE_FAILED",
|
|
2385
|
+
domain: ErrorDomain.MCP,
|
|
2386
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2387
|
+
},
|
|
2388
|
+
error
|
|
2389
|
+
);
|
|
2390
|
+
this.logger.trackException(mastraError);
|
|
2391
|
+
this.logger.error("Error closing MCP server:", { error: mastraError });
|
|
2392
|
+
throw mastraError;
|
|
1879
2393
|
}
|
|
1880
2394
|
}
|
|
1881
2395
|
/**
|
|
@@ -1920,6 +2434,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1920
2434
|
name: tool.name,
|
|
1921
2435
|
description: tool.description,
|
|
1922
2436
|
inputSchema: tool.parameters?.jsonSchema || tool.parameters,
|
|
2437
|
+
outputSchema: tool.outputSchema?.jsonSchema || tool.outputSchema,
|
|
1923
2438
|
toolType: tool.toolType
|
|
1924
2439
|
}))
|
|
1925
2440
|
};
|
|
@@ -1940,6 +2455,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1940
2455
|
name: tool.name,
|
|
1941
2456
|
description: tool.description,
|
|
1942
2457
|
inputSchema: tool.parameters?.jsonSchema || tool.parameters,
|
|
2458
|
+
outputSchema: tool.outputSchema?.jsonSchema || tool.outputSchema,
|
|
1943
2459
|
toolType: tool.toolType
|
|
1944
2460
|
};
|
|
1945
2461
|
}
|
|
@@ -1953,30 +2469,47 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1953
2469
|
*/
|
|
1954
2470
|
async executeTool(toolId, args, executionContext) {
|
|
1955
2471
|
const tool = this.convertedTools[toolId];
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
let validatedArgs = args;
|
|
1962
|
-
if (tool.parameters instanceof z.ZodType && typeof tool.parameters.safeParse === "function") {
|
|
1963
|
-
const validation = tool.parameters.safeParse(args ?? {});
|
|
1964
|
-
if (!validation.success) {
|
|
1965
|
-
const errorMessages = validation.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
1966
|
-
this.logger.warn(`ExecuteTool: Invalid tool arguments for '${toolId}': ${errorMessages}`, {
|
|
1967
|
-
errors: validation.error.format()
|
|
1968
|
-
});
|
|
1969
|
-
throw new z.ZodError(validation.error.issues);
|
|
2472
|
+
let validatedArgs;
|
|
2473
|
+
try {
|
|
2474
|
+
if (!tool) {
|
|
2475
|
+
this.logger.warn(`ExecuteTool: Unknown tool '${toolId}' requested on MCPServer '${this.name}'.`);
|
|
2476
|
+
throw new Error(`Unknown tool: ${toolId}`);
|
|
1970
2477
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2478
|
+
this.logger.debug(`ExecuteTool: Invoking '${toolId}' with arguments:`, args);
|
|
2479
|
+
if (tool.parameters instanceof z.ZodType && typeof tool.parameters.safeParse === "function") {
|
|
2480
|
+
const validation = tool.parameters.safeParse(args ?? {});
|
|
2481
|
+
if (!validation.success) {
|
|
2482
|
+
const errorMessages = validation.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
2483
|
+
this.logger.warn(`ExecuteTool: Invalid tool arguments for '${toolId}': ${errorMessages}`, {
|
|
2484
|
+
errors: validation.error.format()
|
|
2485
|
+
});
|
|
2486
|
+
throw new z.ZodError(validation.error.issues);
|
|
2487
|
+
}
|
|
2488
|
+
validatedArgs = validation.data;
|
|
2489
|
+
} else {
|
|
2490
|
+
this.logger.debug(
|
|
2491
|
+
`ExecuteTool: Tool '${toolId}' parameters is not a Zod schema with safeParse or is undefined. Skipping validation.`
|
|
2492
|
+
);
|
|
2493
|
+
}
|
|
2494
|
+
if (!tool.execute) {
|
|
2495
|
+
this.logger.error(`ExecuteTool: Tool '${toolId}' does not have an execute function.`);
|
|
2496
|
+
throw new Error(`Tool '${toolId}' cannot be executed.`);
|
|
2497
|
+
}
|
|
2498
|
+
} catch (error) {
|
|
2499
|
+
const mastraError = new MastraError(
|
|
2500
|
+
{
|
|
2501
|
+
id: "MCP_SERVER_TOOL_EXECUTE_PREPARATION_FAILED",
|
|
2502
|
+
domain: ErrorDomain.MCP,
|
|
2503
|
+
category: ErrorCategory.USER,
|
|
2504
|
+
details: {
|
|
2505
|
+
toolId,
|
|
2506
|
+
args
|
|
2507
|
+
}
|
|
2508
|
+
},
|
|
2509
|
+
error
|
|
1975
2510
|
);
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
this.logger.error(`ExecuteTool: Tool '${toolId}' does not have an execute function.`);
|
|
1979
|
-
throw new Error(`Tool '${toolId}' cannot be executed.`);
|
|
2511
|
+
this.logger.trackException(mastraError);
|
|
2512
|
+
throw mastraError;
|
|
1980
2513
|
}
|
|
1981
2514
|
try {
|
|
1982
2515
|
const finalExecutionContext = {
|
|
@@ -1987,8 +2520,21 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1987
2520
|
this.logger.info(`ExecuteTool: Tool '${toolId}' executed successfully.`);
|
|
1988
2521
|
return result;
|
|
1989
2522
|
} catch (error) {
|
|
2523
|
+
const mastraError = new MastraError(
|
|
2524
|
+
{
|
|
2525
|
+
id: "MCP_SERVER_TOOL_EXECUTE_FAILED",
|
|
2526
|
+
domain: ErrorDomain.MCP,
|
|
2527
|
+
category: ErrorCategory.USER,
|
|
2528
|
+
details: {
|
|
2529
|
+
toolId,
|
|
2530
|
+
validatedArgs
|
|
2531
|
+
}
|
|
2532
|
+
},
|
|
2533
|
+
error
|
|
2534
|
+
);
|
|
2535
|
+
this.logger.trackException(mastraError);
|
|
1990
2536
|
this.logger.error(`ExecuteTool: Tool execution failed for '${toolId}':`, { error });
|
|
1991
|
-
throw
|
|
2537
|
+
throw mastraError;
|
|
1992
2538
|
}
|
|
1993
2539
|
}
|
|
1994
2540
|
};
|