@mcpjam/inspector 0.9.5 → 0.9.6
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/dist/client/assets/index-CxSz4W4P.css +1 -0
- package/dist/client/assets/index-Uo2CGMHd.js +1738 -0
- package/dist/client/assets/index-Uo2CGMHd.js.map +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +391 -301
- package/dist/server/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/client/assets/index-GNDQHxBB.css +0 -1
- package/dist/client/assets/index-bdptyNo6.js +0 -1735
- package/dist/client/assets/index-bdptyNo6.js.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -537,171 +537,6 @@ var prompts_default = prompts;
|
|
|
537
537
|
|
|
538
538
|
// routes/mcp/chat.ts
|
|
539
539
|
import { Hono as Hono6 } from "hono";
|
|
540
|
-
|
|
541
|
-
// utils/mcp-utils.ts
|
|
542
|
-
import { MCPClient } from "@mastra/mcp";
|
|
543
|
-
function validateServerConfig(serverConfig) {
|
|
544
|
-
if (!serverConfig) {
|
|
545
|
-
return {
|
|
546
|
-
success: false,
|
|
547
|
-
error: {
|
|
548
|
-
message: "Server configuration is required",
|
|
549
|
-
status: 400
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
const config = { ...serverConfig };
|
|
554
|
-
if (config.url) {
|
|
555
|
-
try {
|
|
556
|
-
if (typeof config.url === "string") {
|
|
557
|
-
const parsed = new URL(config.url);
|
|
558
|
-
parsed.search = "";
|
|
559
|
-
parsed.hash = "";
|
|
560
|
-
config.url = parsed;
|
|
561
|
-
} else if (typeof config.url === "object" && !config.url.href) {
|
|
562
|
-
return {
|
|
563
|
-
success: false,
|
|
564
|
-
error: {
|
|
565
|
-
message: "Invalid URL configuration",
|
|
566
|
-
status: 400
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
if (config.oauth?.access_token) {
|
|
571
|
-
const authHeaders = {
|
|
572
|
-
Authorization: `Bearer ${config.oauth.access_token}`,
|
|
573
|
-
...config.requestInit?.headers || {}
|
|
574
|
-
};
|
|
575
|
-
config.requestInit = {
|
|
576
|
-
...config.requestInit,
|
|
577
|
-
headers: authHeaders
|
|
578
|
-
};
|
|
579
|
-
config.eventSourceInit = {
|
|
580
|
-
fetch(input, init) {
|
|
581
|
-
const headers = new Headers(init?.headers || {});
|
|
582
|
-
headers.set(
|
|
583
|
-
"Authorization",
|
|
584
|
-
`Bearer ${config.oauth.access_token}`
|
|
585
|
-
);
|
|
586
|
-
if (config.requestInit?.headers) {
|
|
587
|
-
const requestHeaders = new Headers(config.requestInit.headers);
|
|
588
|
-
requestHeaders.forEach((value, key) => {
|
|
589
|
-
if (key.toLowerCase() !== "authorization") {
|
|
590
|
-
headers.set(key, value);
|
|
591
|
-
}
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
return fetch(input, {
|
|
595
|
-
...init,
|
|
596
|
-
headers
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
};
|
|
600
|
-
} else if (config.requestInit?.headers) {
|
|
601
|
-
config.eventSourceInit = {
|
|
602
|
-
fetch(input, init) {
|
|
603
|
-
const headers = new Headers(init?.headers || {});
|
|
604
|
-
const requestHeaders = new Headers(config.requestInit.headers);
|
|
605
|
-
requestHeaders.forEach((value, key) => {
|
|
606
|
-
headers.set(key, value);
|
|
607
|
-
});
|
|
608
|
-
return fetch(input, {
|
|
609
|
-
...init,
|
|
610
|
-
headers
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
} catch (error) {
|
|
616
|
-
return {
|
|
617
|
-
success: false,
|
|
618
|
-
error: {
|
|
619
|
-
message: `Invalid URL format: ${error}`,
|
|
620
|
-
status: 400
|
|
621
|
-
}
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
return {
|
|
626
|
-
success: true,
|
|
627
|
-
config
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
var validateMultipleServerConfigs = (serverConfigs) => {
|
|
631
|
-
if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
|
|
632
|
-
return {
|
|
633
|
-
success: false,
|
|
634
|
-
error: {
|
|
635
|
-
message: "At least one server configuration is required",
|
|
636
|
-
status: 400
|
|
637
|
-
}
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
const validConfigs = {};
|
|
641
|
-
const errors = {};
|
|
642
|
-
let hasErrors = false;
|
|
643
|
-
for (const [serverName, serverConfig] of Object.entries(serverConfigs)) {
|
|
644
|
-
const validationResult = validateServerConfig(serverConfig);
|
|
645
|
-
if (validationResult.success && validationResult.config) {
|
|
646
|
-
validConfigs[serverName] = validationResult.config;
|
|
647
|
-
} else {
|
|
648
|
-
hasErrors = true;
|
|
649
|
-
let errorMessage = "Configuration validation failed";
|
|
650
|
-
if (validationResult.error) {
|
|
651
|
-
errorMessage = validationResult.error.message;
|
|
652
|
-
}
|
|
653
|
-
errors[serverName] = errorMessage;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
if (!hasErrors) {
|
|
657
|
-
return {
|
|
658
|
-
success: true,
|
|
659
|
-
validConfigs
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
if (Object.keys(validConfigs).length > 0) {
|
|
663
|
-
return {
|
|
664
|
-
success: false,
|
|
665
|
-
validConfigs,
|
|
666
|
-
errors
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
return {
|
|
670
|
-
success: false,
|
|
671
|
-
errors,
|
|
672
|
-
error: {
|
|
673
|
-
message: "All server configurations failed validation",
|
|
674
|
-
status: 400
|
|
675
|
-
}
|
|
676
|
-
};
|
|
677
|
-
};
|
|
678
|
-
function createMCPClientWithMultipleConnections(serverConfigs) {
|
|
679
|
-
const originalMCPClient = new MCPClient({
|
|
680
|
-
id: `chat-${Date.now()}`,
|
|
681
|
-
servers: serverConfigs
|
|
682
|
-
});
|
|
683
|
-
const originalGetTools = originalMCPClient.getTools.bind(originalMCPClient);
|
|
684
|
-
originalMCPClient.getTools = async () => {
|
|
685
|
-
const tools2 = await originalGetTools();
|
|
686
|
-
const fixedTools = {};
|
|
687
|
-
for (const [toolName, toolConfig] of Object.entries(tools2)) {
|
|
688
|
-
const parts = toolName.split("_");
|
|
689
|
-
if (parts.length >= 3 && parts[0] === parts[1]) {
|
|
690
|
-
const fixedName = parts.slice(1).join("_");
|
|
691
|
-
fixedTools[fixedName] = toolConfig;
|
|
692
|
-
} else {
|
|
693
|
-
fixedTools[toolName] = toolConfig;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
return fixedTools;
|
|
697
|
-
};
|
|
698
|
-
return originalMCPClient;
|
|
699
|
-
}
|
|
700
|
-
function normalizeServerConfigName(serverName) {
|
|
701
|
-
return serverName.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// routes/mcp/chat.ts
|
|
705
540
|
import { Agent } from "@mastra/core/agent";
|
|
706
541
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
707
542
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
@@ -763,35 +598,6 @@ var createLlmModel = (modelDefinition, apiKey, ollamaBaseUrl) => {
|
|
|
763
598
|
);
|
|
764
599
|
}
|
|
765
600
|
};
|
|
766
|
-
var createElicitationHandler = (streamingContext) => {
|
|
767
|
-
return async (elicitationRequest) => {
|
|
768
|
-
const requestId = `elicit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
769
|
-
if (streamingContext.controller && streamingContext.encoder) {
|
|
770
|
-
streamingContext.controller.enqueue(
|
|
771
|
-
streamingContext.encoder.encode(
|
|
772
|
-
`data: ${JSON.stringify({
|
|
773
|
-
type: "elicitation_request",
|
|
774
|
-
requestId,
|
|
775
|
-
message: elicitationRequest.message,
|
|
776
|
-
schema: elicitationRequest.requestedSchema,
|
|
777
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
778
|
-
})}
|
|
779
|
-
|
|
780
|
-
`
|
|
781
|
-
)
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
return new Promise((resolve, reject) => {
|
|
785
|
-
pendingElicitations2.set(requestId, { resolve, reject });
|
|
786
|
-
setTimeout(() => {
|
|
787
|
-
if (pendingElicitations2.has(requestId)) {
|
|
788
|
-
pendingElicitations2.delete(requestId);
|
|
789
|
-
reject(new Error("Elicitation timeout"));
|
|
790
|
-
}
|
|
791
|
-
}, ELICITATION_TIMEOUT);
|
|
792
|
-
});
|
|
793
|
-
};
|
|
794
|
-
};
|
|
795
601
|
var wrapToolsWithStreaming = (tools2, streamingContext) => {
|
|
796
602
|
const wrappedTools = {};
|
|
797
603
|
for (const [name, tool] of Object.entries(tools2)) {
|
|
@@ -1000,15 +806,6 @@ var fallbackToCompletion = async (agent, messages, streamingContext, provider) =
|
|
|
1000
806
|
);
|
|
1001
807
|
}
|
|
1002
808
|
};
|
|
1003
|
-
var safeDisconnect = async (client) => {
|
|
1004
|
-
if (client) {
|
|
1005
|
-
try {
|
|
1006
|
-
await client.disconnect();
|
|
1007
|
-
} catch (cleanupError) {
|
|
1008
|
-
console.warn("[mcp/chat] Error cleaning up MCP client:", cleanupError);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
809
|
var createStreamingResponse = async (agent, messages, toolsets, streamingContext, provider) => {
|
|
1013
810
|
const stream = await agent.stream(messages, {
|
|
1014
811
|
maxSteps: MAX_AGENT_STEPS,
|
|
@@ -1039,7 +836,7 @@ var createStreamingResponse = async (agent, messages, toolsets, streamingContext
|
|
|
1039
836
|
);
|
|
1040
837
|
};
|
|
1041
838
|
chat.post("/", async (c) => {
|
|
1042
|
-
|
|
839
|
+
const mcpClientManager = c.mcpJamClientManager;
|
|
1043
840
|
try {
|
|
1044
841
|
const requestData = await c.req.json();
|
|
1045
842
|
const {
|
|
@@ -1096,22 +893,36 @@ chat.post("/", async (c) => {
|
|
|
1096
893
|
400
|
|
1097
894
|
);
|
|
1098
895
|
}
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
896
|
+
const serverErrors = {};
|
|
897
|
+
const connectedServers = [];
|
|
898
|
+
for (const [serverName, serverConfig] of Object.entries(serverConfigs)) {
|
|
899
|
+
try {
|
|
900
|
+
await mcpClientManager.connectToServer(serverName, serverConfig);
|
|
901
|
+
connectedServers.push(serverName);
|
|
902
|
+
dbg("Connected to server", { serverName });
|
|
903
|
+
} catch (error) {
|
|
904
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
905
|
+
serverErrors[serverName] = errorMessage;
|
|
906
|
+
dbg("Failed to connect to server", { serverName, error: errorMessage });
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (connectedServers.length === 0) {
|
|
1105
910
|
return c.json(
|
|
1106
911
|
{
|
|
1107
912
|
success: false,
|
|
1108
|
-
error:
|
|
1109
|
-
details:
|
|
913
|
+
error: "Failed to connect to any servers",
|
|
914
|
+
details: serverErrors
|
|
1110
915
|
},
|
|
1111
|
-
|
|
916
|
+
400
|
|
1112
917
|
);
|
|
1113
918
|
}
|
|
1114
|
-
|
|
919
|
+
if (Object.keys(serverErrors).length > 0) {
|
|
920
|
+
dbg("Some servers failed to connect", {
|
|
921
|
+
connectedServers,
|
|
922
|
+
failedServers: Object.keys(serverErrors),
|
|
923
|
+
errors: serverErrors
|
|
924
|
+
});
|
|
925
|
+
}
|
|
1115
926
|
const llmModel = createLlmModel(model, apiKey, ollamaBaseUrl);
|
|
1116
927
|
const agent = new Agent({
|
|
1117
928
|
name: "MCP Chat Agent",
|
|
@@ -1124,9 +935,26 @@ chat.post("/", async (c) => {
|
|
|
1124
935
|
role: msg.role,
|
|
1125
936
|
content: msg.content
|
|
1126
937
|
}));
|
|
1127
|
-
const
|
|
938
|
+
const allTools = mcpClientManager.getAvailableTools();
|
|
939
|
+
const toolsByServer = {};
|
|
940
|
+
for (const tool of allTools) {
|
|
941
|
+
if (!toolsByServer[tool.serverId]) {
|
|
942
|
+
toolsByServer[tool.serverId] = {};
|
|
943
|
+
}
|
|
944
|
+
toolsByServer[tool.serverId][tool.name] = {
|
|
945
|
+
description: tool.description,
|
|
946
|
+
inputSchema: tool.inputSchema,
|
|
947
|
+
execute: async (params) => {
|
|
948
|
+
return await mcpClientManager.executeToolDirect(
|
|
949
|
+
`${tool.serverId}:${tool.name}`,
|
|
950
|
+
params
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
}
|
|
1128
955
|
dbg("Streaming start", {
|
|
1129
|
-
|
|
956
|
+
connectedServers,
|
|
957
|
+
toolCount: allTools.length,
|
|
1130
958
|
messageCount: formattedMessages.length
|
|
1131
959
|
});
|
|
1132
960
|
const encoder = new TextEncoder3();
|
|
@@ -1140,7 +968,7 @@ chat.post("/", async (c) => {
|
|
|
1140
968
|
stepIndex: 0
|
|
1141
969
|
};
|
|
1142
970
|
const flattenedTools = {};
|
|
1143
|
-
Object.values(
|
|
971
|
+
Object.values(toolsByServer).forEach((serverTools) => {
|
|
1144
972
|
Object.assign(flattenedTools, serverTools);
|
|
1145
973
|
});
|
|
1146
974
|
const streamingWrappedTools = wrapToolsWithStreaming(
|
|
@@ -1153,25 +981,44 @@ chat.post("/", async (c) => {
|
|
|
1153
981
|
model: agent.model,
|
|
1154
982
|
tools: Object.keys(streamingWrappedTools).length > 0 ? streamingWrappedTools : void 0
|
|
1155
983
|
});
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
984
|
+
mcpClientManager.setElicitationCallback(async (request) => {
|
|
985
|
+
const elicitationRequest = {
|
|
986
|
+
message: request.message,
|
|
987
|
+
requestedSchema: request.schema
|
|
988
|
+
};
|
|
989
|
+
if (streamingContext.controller && streamingContext.encoder) {
|
|
990
|
+
streamingContext.controller.enqueue(
|
|
991
|
+
streamingContext.encoder.encode(
|
|
992
|
+
`data: ${JSON.stringify({
|
|
993
|
+
type: "elicitation_request",
|
|
994
|
+
requestId: request.requestId,
|
|
995
|
+
message: elicitationRequest.message,
|
|
996
|
+
schema: elicitationRequest.requestedSchema,
|
|
997
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
998
|
+
})}
|
|
999
|
+
|
|
1000
|
+
`
|
|
1001
|
+
)
|
|
1171
1002
|
);
|
|
1172
|
-
} else {
|
|
1173
|
-
throw new Error("MCP client is null");
|
|
1174
1003
|
}
|
|
1004
|
+
return new Promise((resolve, reject) => {
|
|
1005
|
+
pendingElicitations2.set(request.requestId, { resolve, reject });
|
|
1006
|
+
setTimeout(() => {
|
|
1007
|
+
if (pendingElicitations2.has(request.requestId)) {
|
|
1008
|
+
pendingElicitations2.delete(request.requestId);
|
|
1009
|
+
reject(new Error("Elicitation timeout"));
|
|
1010
|
+
}
|
|
1011
|
+
}, ELICITATION_TIMEOUT);
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
try {
|
|
1015
|
+
await createStreamingResponse(
|
|
1016
|
+
streamingAgent,
|
|
1017
|
+
formattedMessages,
|
|
1018
|
+
toolsByServer,
|
|
1019
|
+
streamingContext,
|
|
1020
|
+
provider
|
|
1021
|
+
);
|
|
1175
1022
|
} catch (error) {
|
|
1176
1023
|
controller.enqueue(
|
|
1177
1024
|
encoder.encode(
|
|
@@ -1184,7 +1031,7 @@ chat.post("/", async (c) => {
|
|
|
1184
1031
|
)
|
|
1185
1032
|
);
|
|
1186
1033
|
} finally {
|
|
1187
|
-
|
|
1034
|
+
mcpClientManager.clearElicitationCallback();
|
|
1188
1035
|
controller.close();
|
|
1189
1036
|
}
|
|
1190
1037
|
}
|
|
@@ -1198,7 +1045,7 @@ chat.post("/", async (c) => {
|
|
|
1198
1045
|
});
|
|
1199
1046
|
} catch (error) {
|
|
1200
1047
|
console.error("[mcp/chat] Error in chat API:", error);
|
|
1201
|
-
|
|
1048
|
+
mcpClientManager.clearElicitationCallback();
|
|
1202
1049
|
return c.json(
|
|
1203
1050
|
{
|
|
1204
1051
|
success: false,
|
|
@@ -1218,6 +1065,182 @@ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
|
|
|
1218
1065
|
import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
1219
1066
|
import { createOllama as createOllama2 } from "ollama-ai-provider";
|
|
1220
1067
|
import { Agent as Agent2 } from "@mastra/core/agent";
|
|
1068
|
+
|
|
1069
|
+
// utils/mcp-utils.ts
|
|
1070
|
+
import { MCPClient } from "@mastra/mcp";
|
|
1071
|
+
function validateServerConfig(serverConfig) {
|
|
1072
|
+
if (!serverConfig) {
|
|
1073
|
+
return {
|
|
1074
|
+
success: false,
|
|
1075
|
+
error: {
|
|
1076
|
+
message: "Server configuration is required",
|
|
1077
|
+
status: 400
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
const config = { ...serverConfig };
|
|
1082
|
+
if (config.url) {
|
|
1083
|
+
try {
|
|
1084
|
+
if (typeof config.url === "string") {
|
|
1085
|
+
const parsed = new URL(config.url);
|
|
1086
|
+
parsed.search = "";
|
|
1087
|
+
parsed.hash = "";
|
|
1088
|
+
config.url = parsed;
|
|
1089
|
+
} else if (typeof config.url === "object" && !config.url.href) {
|
|
1090
|
+
return {
|
|
1091
|
+
success: false,
|
|
1092
|
+
error: {
|
|
1093
|
+
message: "Invalid URL configuration",
|
|
1094
|
+
status: 400
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
if (config.oauth?.access_token) {
|
|
1099
|
+
const authHeaders = {
|
|
1100
|
+
Authorization: `Bearer ${config.oauth.access_token}`,
|
|
1101
|
+
...config.requestInit?.headers || {}
|
|
1102
|
+
};
|
|
1103
|
+
config.requestInit = {
|
|
1104
|
+
...config.requestInit,
|
|
1105
|
+
headers: authHeaders
|
|
1106
|
+
};
|
|
1107
|
+
config.eventSourceInit = {
|
|
1108
|
+
fetch(input, init) {
|
|
1109
|
+
const headers = new Headers(init?.headers || {});
|
|
1110
|
+
headers.set(
|
|
1111
|
+
"Authorization",
|
|
1112
|
+
`Bearer ${config.oauth.access_token}`
|
|
1113
|
+
);
|
|
1114
|
+
if (config.requestInit?.headers) {
|
|
1115
|
+
const requestHeaders = new Headers(config.requestInit.headers);
|
|
1116
|
+
requestHeaders.forEach((value, key) => {
|
|
1117
|
+
if (key.toLowerCase() !== "authorization") {
|
|
1118
|
+
headers.set(key, value);
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
return fetch(input, {
|
|
1123
|
+
...init,
|
|
1124
|
+
headers
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
} else if (config.requestInit?.headers) {
|
|
1129
|
+
config.eventSourceInit = {
|
|
1130
|
+
fetch(input, init) {
|
|
1131
|
+
const headers = new Headers(init?.headers || {});
|
|
1132
|
+
const requestHeaders = new Headers(config.requestInit.headers);
|
|
1133
|
+
requestHeaders.forEach((value, key) => {
|
|
1134
|
+
headers.set(key, value);
|
|
1135
|
+
});
|
|
1136
|
+
return fetch(input, {
|
|
1137
|
+
...init,
|
|
1138
|
+
headers
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
return {
|
|
1145
|
+
success: false,
|
|
1146
|
+
error: {
|
|
1147
|
+
message: `Invalid URL format: ${error}`,
|
|
1148
|
+
status: 400
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return {
|
|
1154
|
+
success: true,
|
|
1155
|
+
config
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function generateUniqueServerID(serverName) {
|
|
1159
|
+
const normalizedBase = normalizeServerConfigName(serverName);
|
|
1160
|
+
const timestamp = Date.now().toString(36);
|
|
1161
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
1162
|
+
return `${normalizedBase}_${timestamp}_${random}`;
|
|
1163
|
+
}
|
|
1164
|
+
var validateMultipleServerConfigs = (serverConfigs) => {
|
|
1165
|
+
if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
|
|
1166
|
+
return {
|
|
1167
|
+
success: false,
|
|
1168
|
+
error: {
|
|
1169
|
+
message: "At least one server configuration is required",
|
|
1170
|
+
status: 400
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
const validConfigs = {};
|
|
1175
|
+
const serverNameMapping = {};
|
|
1176
|
+
const errors = {};
|
|
1177
|
+
let hasErrors = false;
|
|
1178
|
+
for (const [serverName, serverConfig] of Object.entries(serverConfigs)) {
|
|
1179
|
+
const validationResult = validateServerConfig(serverConfig);
|
|
1180
|
+
if (validationResult.success && validationResult.config) {
|
|
1181
|
+
const serverID = generateUniqueServerID(serverName);
|
|
1182
|
+
validConfigs[serverID] = validationResult.config;
|
|
1183
|
+
serverNameMapping[serverID] = serverName;
|
|
1184
|
+
} else {
|
|
1185
|
+
hasErrors = true;
|
|
1186
|
+
let errorMessage = "Configuration validation failed";
|
|
1187
|
+
if (validationResult.error) {
|
|
1188
|
+
errorMessage = validationResult.error.message;
|
|
1189
|
+
}
|
|
1190
|
+
errors[serverName] = errorMessage;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (!hasErrors) {
|
|
1194
|
+
return {
|
|
1195
|
+
success: true,
|
|
1196
|
+
validConfigs,
|
|
1197
|
+
serverNameMapping
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
if (Object.keys(validConfigs).length > 0) {
|
|
1201
|
+
return {
|
|
1202
|
+
success: false,
|
|
1203
|
+
validConfigs,
|
|
1204
|
+
serverNameMapping,
|
|
1205
|
+
errors
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
return {
|
|
1209
|
+
success: false,
|
|
1210
|
+
errors,
|
|
1211
|
+
error: {
|
|
1212
|
+
message: "All server configurations failed validation",
|
|
1213
|
+
status: 400
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
};
|
|
1217
|
+
function createMCPClientWithMultipleConnections(serverConfigs) {
|
|
1218
|
+
const originalMCPClient = new MCPClient({
|
|
1219
|
+
id: `chat-${Date.now()}`,
|
|
1220
|
+
servers: serverConfigs
|
|
1221
|
+
});
|
|
1222
|
+
const originalGetTools = originalMCPClient.getTools.bind(originalMCPClient);
|
|
1223
|
+
originalMCPClient.getTools = async () => {
|
|
1224
|
+
const tools2 = await originalGetTools();
|
|
1225
|
+
const fixedTools = {};
|
|
1226
|
+
for (const [toolName, toolConfig] of Object.entries(tools2)) {
|
|
1227
|
+
const parts = toolName.split("_");
|
|
1228
|
+
if (parts.length >= 3 && parts[0] === parts[1]) {
|
|
1229
|
+
const fixedName = parts.slice(1).join("_");
|
|
1230
|
+
fixedTools[fixedName] = toolConfig;
|
|
1231
|
+
} else {
|
|
1232
|
+
fixedTools[toolName] = toolConfig;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return fixedTools;
|
|
1236
|
+
};
|
|
1237
|
+
return originalMCPClient;
|
|
1238
|
+
}
|
|
1239
|
+
function normalizeServerConfigName(serverName) {
|
|
1240
|
+
return serverName.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// routes/mcp/tests.ts
|
|
1221
1244
|
var tests = new Hono7();
|
|
1222
1245
|
tests.post("/generate", async (c) => {
|
|
1223
1246
|
try {
|
|
@@ -1524,13 +1547,20 @@ var mcp_default = mcp;
|
|
|
1524
1547
|
|
|
1525
1548
|
// services/mcpjam-client-manager.ts
|
|
1526
1549
|
import { MCPClient as MCPClient2 } from "@mastra/mcp";
|
|
1527
|
-
function
|
|
1528
|
-
|
|
1550
|
+
function generateUniqueServerId(serverId) {
|
|
1551
|
+
const normalizedBase = serverId.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
1552
|
+
const timestamp = Date.now().toString(36);
|
|
1553
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
1554
|
+
return `${normalizedBase}_${timestamp}_${random}`;
|
|
1529
1555
|
}
|
|
1530
1556
|
var MCPJamClientManager = class {
|
|
1531
1557
|
mcpClients = /* @__PURE__ */ new Map();
|
|
1532
1558
|
statuses = /* @__PURE__ */ new Map();
|
|
1533
1559
|
configs = /* @__PURE__ */ new Map();
|
|
1560
|
+
// Map original server names to unique IDs
|
|
1561
|
+
serverIdMapping = /* @__PURE__ */ new Map();
|
|
1562
|
+
// Track in-flight connections to avoid duplicate concurrent connects
|
|
1563
|
+
pendingConnections = /* @__PURE__ */ new Map();
|
|
1534
1564
|
toolRegistry = /* @__PURE__ */ new Map();
|
|
1535
1565
|
resourceRegistry = /* @__PURE__ */ new Map();
|
|
1536
1566
|
promptRegistry = /* @__PURE__ */ new Map();
|
|
@@ -1538,46 +1568,69 @@ var MCPJamClientManager = class {
|
|
|
1538
1568
|
pendingElicitations = /* @__PURE__ */ new Map();
|
|
1539
1569
|
// Optional callback for handling elicitation requests
|
|
1540
1570
|
elicitationCallback;
|
|
1571
|
+
// Helper method to get unique ID for a server name
|
|
1572
|
+
getServerUniqueId(serverName) {
|
|
1573
|
+
return this.serverIdMapping.get(serverName);
|
|
1574
|
+
}
|
|
1575
|
+
// Public method to get server ID for external use (like frontend)
|
|
1576
|
+
getServerIdForName(serverName) {
|
|
1577
|
+
return this.serverIdMapping.get(serverName);
|
|
1578
|
+
}
|
|
1541
1579
|
async connectToServer(serverId, serverConfig) {
|
|
1542
|
-
const
|
|
1543
|
-
if (
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
this.statuses.set(id, "error");
|
|
1547
|
-
throw new Error(validation.error.message);
|
|
1580
|
+
const pending = this.pendingConnections.get(serverId);
|
|
1581
|
+
if (pending) {
|
|
1582
|
+
await pending;
|
|
1583
|
+
return;
|
|
1548
1584
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
const normalizedName = normalizeServerConfigName(serverId);
|
|
1561
|
-
client.elicitation.onRequest(
|
|
1562
|
-
normalizedName,
|
|
1563
|
-
async (elicitationRequest) => {
|
|
1564
|
-
return await this.handleElicitationRequest(elicitationRequest);
|
|
1565
|
-
}
|
|
1566
|
-
);
|
|
1585
|
+
const connectPromise = (async () => {
|
|
1586
|
+
let id = this.serverIdMapping.get(serverId);
|
|
1587
|
+
if (!id) {
|
|
1588
|
+
id = generateUniqueServerId(serverId);
|
|
1589
|
+
this.serverIdMapping.set(serverId, id);
|
|
1590
|
+
}
|
|
1591
|
+
if (this.mcpClients.has(id)) return;
|
|
1592
|
+
const validation = validateServerConfig(serverConfig);
|
|
1593
|
+
if (!validation.success) {
|
|
1594
|
+
this.statuses.set(id, "error");
|
|
1595
|
+
throw new Error(validation.error.message);
|
|
1567
1596
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1597
|
+
this.configs.set(id, validation.config);
|
|
1598
|
+
this.statuses.set(id, "connecting");
|
|
1599
|
+
const client = new MCPClient2({
|
|
1600
|
+
id: `mcpjam-${id}`,
|
|
1601
|
+
servers: { [id]: validation.config }
|
|
1602
|
+
});
|
|
1571
1603
|
try {
|
|
1572
|
-
await client.
|
|
1573
|
-
|
|
1604
|
+
await client.getTools();
|
|
1605
|
+
this.mcpClients.set(id, client);
|
|
1606
|
+
this.statuses.set(id, "connected");
|
|
1607
|
+
if (client.elicitation?.onRequest) {
|
|
1608
|
+
client.elicitation.onRequest(
|
|
1609
|
+
id,
|
|
1610
|
+
async (elicitationRequest) => {
|
|
1611
|
+
return await this.handleElicitationRequest(elicitationRequest);
|
|
1612
|
+
}
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
await this.discoverServerResources(id);
|
|
1616
|
+
} catch (err) {
|
|
1617
|
+
this.statuses.set(id, "error");
|
|
1618
|
+
try {
|
|
1619
|
+
await client.disconnect();
|
|
1620
|
+
} catch {
|
|
1621
|
+
}
|
|
1622
|
+
this.mcpClients.delete(id);
|
|
1623
|
+
throw err;
|
|
1574
1624
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
}
|
|
1625
|
+
})().finally(() => {
|
|
1626
|
+
this.pendingConnections.delete(serverId);
|
|
1627
|
+
});
|
|
1628
|
+
this.pendingConnections.set(serverId, connectPromise);
|
|
1629
|
+
await connectPromise;
|
|
1578
1630
|
}
|
|
1579
1631
|
async disconnectFromServer(serverId) {
|
|
1580
|
-
const id =
|
|
1632
|
+
const id = this.getServerUniqueId(serverId);
|
|
1633
|
+
if (!id) return;
|
|
1581
1634
|
const client = this.mcpClients.get(id);
|
|
1582
1635
|
if (client) {
|
|
1583
1636
|
try {
|
|
@@ -1587,6 +1640,7 @@ var MCPJamClientManager = class {
|
|
|
1587
1640
|
}
|
|
1588
1641
|
this.mcpClients.delete(id);
|
|
1589
1642
|
this.statuses.set(id, "disconnected");
|
|
1643
|
+
this.serverIdMapping.delete(serverId);
|
|
1590
1644
|
for (const key of Array.from(this.toolRegistry.keys())) {
|
|
1591
1645
|
const item = this.toolRegistry.get(key);
|
|
1592
1646
|
if (item.serverId === id) this.toolRegistry.delete(key);
|
|
@@ -1601,15 +1655,15 @@ var MCPJamClientManager = class {
|
|
|
1601
1655
|
}
|
|
1602
1656
|
}
|
|
1603
1657
|
getConnectionStatus(serverId) {
|
|
1604
|
-
const id =
|
|
1605
|
-
return this.statuses.get(id) || "disconnected";
|
|
1658
|
+
const id = this.getServerUniqueId(serverId);
|
|
1659
|
+
return id ? this.statuses.get(id) || "disconnected" : "disconnected";
|
|
1606
1660
|
}
|
|
1607
1661
|
getConnectedServers() {
|
|
1608
1662
|
const servers2 = {};
|
|
1609
|
-
for (const [
|
|
1610
|
-
servers2[
|
|
1611
|
-
status,
|
|
1612
|
-
config: this.configs.get(
|
|
1663
|
+
for (const [originalName, uniqueId] of this.serverIdMapping.entries()) {
|
|
1664
|
+
servers2[originalName] = {
|
|
1665
|
+
status: this.statuses.get(uniqueId) || "disconnected",
|
|
1666
|
+
config: this.configs.get(uniqueId)
|
|
1613
1667
|
};
|
|
1614
1668
|
}
|
|
1615
1669
|
return servers2;
|
|
@@ -1619,29 +1673,32 @@ var MCPJamClientManager = class {
|
|
|
1619
1673
|
await Promise.all(serverIds.map((id) => this.discoverServerResources(id)));
|
|
1620
1674
|
}
|
|
1621
1675
|
async discoverServerResources(serverId) {
|
|
1622
|
-
const
|
|
1623
|
-
const client = this.mcpClients.get(id);
|
|
1676
|
+
const client = this.mcpClients.get(serverId);
|
|
1624
1677
|
if (!client) return;
|
|
1625
|
-
const
|
|
1626
|
-
|
|
1627
|
-
|
|
1678
|
+
const toolsets = await client.getToolsets();
|
|
1679
|
+
const flattenedTools = {};
|
|
1680
|
+
Object.values(toolsets).forEach((serverTools) => {
|
|
1681
|
+
Object.assign(flattenedTools, serverTools);
|
|
1682
|
+
});
|
|
1683
|
+
for (const [name, tool] of Object.entries(flattenedTools)) {
|
|
1684
|
+
this.toolRegistry.set(`${serverId}:${name}`, {
|
|
1628
1685
|
name,
|
|
1629
1686
|
description: tool.description,
|
|
1630
1687
|
inputSchema: tool.inputSchema,
|
|
1631
1688
|
outputSchema: tool.outputSchema,
|
|
1632
|
-
serverId
|
|
1689
|
+
serverId
|
|
1633
1690
|
});
|
|
1634
1691
|
}
|
|
1635
1692
|
try {
|
|
1636
1693
|
const res = await client.resources.list();
|
|
1637
|
-
for (const [
|
|
1694
|
+
for (const [, list] of Object.entries(res)) {
|
|
1638
1695
|
for (const r of list) {
|
|
1639
|
-
this.resourceRegistry.set(`${
|
|
1696
|
+
this.resourceRegistry.set(`${serverId}:${r.uri}`, {
|
|
1640
1697
|
uri: r.uri,
|
|
1641
1698
|
name: r.name,
|
|
1642
1699
|
description: r.description,
|
|
1643
1700
|
mimeType: r.mimeType,
|
|
1644
|
-
serverId
|
|
1701
|
+
serverId
|
|
1645
1702
|
});
|
|
1646
1703
|
}
|
|
1647
1704
|
}
|
|
@@ -1649,13 +1706,13 @@ var MCPJamClientManager = class {
|
|
|
1649
1706
|
}
|
|
1650
1707
|
try {
|
|
1651
1708
|
const prompts2 = await client.prompts.list();
|
|
1652
|
-
for (const [
|
|
1709
|
+
for (const [, list] of Object.entries(prompts2)) {
|
|
1653
1710
|
for (const p of list) {
|
|
1654
|
-
this.promptRegistry.set(`${
|
|
1711
|
+
this.promptRegistry.set(`${serverId}:${p.name}`, {
|
|
1655
1712
|
name: p.name,
|
|
1656
1713
|
description: p.description,
|
|
1657
1714
|
arguments: p.arguments,
|
|
1658
|
-
serverId
|
|
1715
|
+
serverId
|
|
1659
1716
|
});
|
|
1660
1717
|
}
|
|
1661
1718
|
}
|
|
@@ -1666,7 +1723,10 @@ var MCPJamClientManager = class {
|
|
|
1666
1723
|
return Array.from(this.toolRegistry.values());
|
|
1667
1724
|
}
|
|
1668
1725
|
async getToolsetsForServer(serverId) {
|
|
1669
|
-
const id =
|
|
1726
|
+
const id = this.getServerUniqueId(serverId);
|
|
1727
|
+
if (!id) {
|
|
1728
|
+
throw new Error(`No MCP client available for server: ${serverId}`);
|
|
1729
|
+
}
|
|
1670
1730
|
const client = this.mcpClients.get(id);
|
|
1671
1731
|
if (!client) {
|
|
1672
1732
|
throw new Error(`No MCP client available for server: ${serverId}`);
|
|
@@ -1682,7 +1742,8 @@ var MCPJamClientManager = class {
|
|
|
1682
1742
|
return Array.from(this.resourceRegistry.values());
|
|
1683
1743
|
}
|
|
1684
1744
|
getResourcesForServer(serverId) {
|
|
1685
|
-
const id =
|
|
1745
|
+
const id = this.getServerUniqueId(serverId);
|
|
1746
|
+
if (!id) return [];
|
|
1686
1747
|
return Array.from(this.resourceRegistry.values()).filter(
|
|
1687
1748
|
(r) => r.serverId === id
|
|
1688
1749
|
);
|
|
@@ -1691,20 +1752,22 @@ var MCPJamClientManager = class {
|
|
|
1691
1752
|
return Array.from(this.promptRegistry.values());
|
|
1692
1753
|
}
|
|
1693
1754
|
getPromptsForServer(serverId) {
|
|
1694
|
-
const id =
|
|
1755
|
+
const id = this.getServerUniqueId(serverId);
|
|
1756
|
+
if (!id) return [];
|
|
1695
1757
|
return Array.from(this.promptRegistry.values()).filter(
|
|
1696
1758
|
(p) => p.serverId === id
|
|
1697
1759
|
);
|
|
1698
1760
|
}
|
|
1699
|
-
async executeToolDirect(toolName, parameters) {
|
|
1761
|
+
async executeToolDirect(toolName, parameters = {}) {
|
|
1700
1762
|
let serverId = "";
|
|
1701
1763
|
let name = toolName;
|
|
1702
1764
|
if (toolName.includes(":")) {
|
|
1703
1765
|
const [sid, n] = toolName.split(":", 2);
|
|
1704
|
-
|
|
1766
|
+
const mappedId = this.getServerUniqueId(sid);
|
|
1767
|
+
serverId = mappedId || (this.mcpClients.has(sid) ? sid : "");
|
|
1705
1768
|
name = n;
|
|
1706
1769
|
} else {
|
|
1707
|
-
for (const
|
|
1770
|
+
for (const tool2 of this.toolRegistry.values()) {
|
|
1708
1771
|
if (tool2.name === toolName) {
|
|
1709
1772
|
serverId = tool2.serverId;
|
|
1710
1773
|
name = toolName;
|
|
@@ -1743,21 +1806,48 @@ var MCPJamClientManager = class {
|
|
|
1743
1806
|
const tool = flattenedTools[name];
|
|
1744
1807
|
if (!tool)
|
|
1745
1808
|
throw new Error(`Tool '${name}' not found in server '${serverId}'`);
|
|
1746
|
-
const
|
|
1747
|
-
|
|
1809
|
+
const schema = tool.inputSchema;
|
|
1810
|
+
const hasContextProperty = schema && typeof schema === "object" && schema.properties && Object.prototype.hasOwnProperty.call(
|
|
1811
|
+
schema.properties,
|
|
1812
|
+
"context"
|
|
1813
|
+
);
|
|
1814
|
+
const requiresContext = hasContextProperty || schema && Array.isArray(schema.required) && schema.required.includes("context");
|
|
1815
|
+
const contextWrapped = { context: parameters || {} };
|
|
1816
|
+
const direct = parameters || {};
|
|
1817
|
+
const attempts = requiresContext ? [contextWrapped, direct] : [direct, contextWrapped];
|
|
1818
|
+
let lastError = void 0;
|
|
1819
|
+
for (const args of attempts) {
|
|
1820
|
+
try {
|
|
1821
|
+
const result = await tool.execute(args);
|
|
1822
|
+
return { result };
|
|
1823
|
+
} catch (err) {
|
|
1824
|
+
lastError = err;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
throw lastError;
|
|
1748
1828
|
}
|
|
1749
1829
|
async getResource(resourceUri, serverId) {
|
|
1750
1830
|
let uri = resourceUri;
|
|
1751
|
-
const
|
|
1831
|
+
const mappedId = this.getServerUniqueId(serverId);
|
|
1832
|
+
const resolvedServerId = mappedId || (this.mcpClients.has(serverId) ? serverId : void 0);
|
|
1833
|
+
if (!resolvedServerId) {
|
|
1834
|
+
throw new Error(`No MCP client available for server: ${serverId}`);
|
|
1835
|
+
}
|
|
1836
|
+
const client = this.mcpClients.get(resolvedServerId);
|
|
1752
1837
|
if (!client) throw new Error("No MCP client available");
|
|
1753
|
-
const content = await client.resources.read(
|
|
1838
|
+
const content = await client.resources.read(resolvedServerId, uri);
|
|
1754
1839
|
return { contents: content?.contents || [] };
|
|
1755
1840
|
}
|
|
1756
1841
|
async getPrompt(promptName, serverId, args) {
|
|
1757
|
-
const
|
|
1842
|
+
const mappedId = this.getServerUniqueId(serverId);
|
|
1843
|
+
const resolvedServerId = mappedId || (this.mcpClients.has(serverId) ? serverId : void 0);
|
|
1844
|
+
if (!resolvedServerId) {
|
|
1845
|
+
throw new Error(`No MCP client available for server: ${serverId}`);
|
|
1846
|
+
}
|
|
1847
|
+
const client = this.mcpClients.get(resolvedServerId);
|
|
1758
1848
|
if (!client) throw new Error("No MCP client available");
|
|
1759
1849
|
const content = await client.prompts.get({
|
|
1760
|
-
serverName:
|
|
1850
|
+
serverName: resolvedServerId,
|
|
1761
1851
|
name: promptName,
|
|
1762
1852
|
args: args || {}
|
|
1763
1853
|
});
|