@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.
@@ -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
- let client = null;
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 validation = validateMultipleServerConfigs(serverConfigs);
1100
- if (!validation.success) {
1101
- dbg(
1102
- "Server config validation failed",
1103
- validation.errors || validation.error
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: validation.error.message,
1109
- details: validation.errors
913
+ error: "Failed to connect to any servers",
914
+ details: serverErrors
1110
915
  },
1111
- validation.error.status
916
+ 400
1112
917
  );
1113
918
  }
1114
- client = createMCPClientWithMultipleConnections(validation.validConfigs);
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 toolsets = await client.getToolsets();
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
- toolsetServers: Object.keys(toolsets),
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(toolsets).forEach((serverTools) => {
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
- if (client?.elicitation?.onRequest) {
1157
- for (const serverName of Object.keys(serverConfigs)) {
1158
- const normalizedName = normalizeServerConfigName(serverName);
1159
- const elicitationHandler = createElicitationHandler(streamingContext);
1160
- client.elicitation.onRequest(normalizedName, elicitationHandler);
1161
- }
1162
- }
1163
- try {
1164
- if (client) {
1165
- await createStreamingResponse(
1166
- streamingAgent,
1167
- formattedMessages,
1168
- toolsets,
1169
- streamingContext,
1170
- provider
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
- await safeDisconnect(client);
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
- await safeDisconnect(client);
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 normalizeServerId(serverId) {
1528
- return serverId.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
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 id = normalizeServerId(serverId);
1543
- if (this.mcpClients.has(id)) return;
1544
- const validation = validateServerConfig(serverConfig);
1545
- if (!validation.success) {
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
- this.configs.set(id, validation.config);
1550
- this.statuses.set(id, "connecting");
1551
- const client = new MCPClient2({
1552
- id: `mcpjam-${id}`,
1553
- servers: { [id]: validation.config }
1554
- });
1555
- try {
1556
- await client.getTools();
1557
- this.mcpClients.set(id, client);
1558
- this.statuses.set(id, "connected");
1559
- if (client.elicitation?.onRequest) {
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
- await this.discoverServerResources(id);
1569
- } catch (err) {
1570
- this.statuses.set(id, "error");
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.disconnect();
1573
- } catch {
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
- this.mcpClients.delete(id);
1576
- throw err;
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 = normalizeServerId(serverId);
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 = normalizeServerId(serverId);
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 [serverId, status] of this.statuses.entries()) {
1610
- servers2[serverId] = {
1611
- status,
1612
- config: this.configs.get(serverId)
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 id = normalizeServerId(serverId);
1623
- const client = this.mcpClients.get(id);
1676
+ const client = this.mcpClients.get(serverId);
1624
1677
  if (!client) return;
1625
- const tools2 = await client.getTools();
1626
- for (const [name, tool] of Object.entries(tools2)) {
1627
- this.toolRegistry.set(`${id}:${name}`, {
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: id
1689
+ serverId
1633
1690
  });
1634
1691
  }
1635
1692
  try {
1636
1693
  const res = await client.resources.list();
1637
- for (const [group, list] of Object.entries(res)) {
1694
+ for (const [, list] of Object.entries(res)) {
1638
1695
  for (const r of list) {
1639
- this.resourceRegistry.set(`${id}:${r.uri}`, {
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: id
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 [group, list] of Object.entries(prompts2)) {
1709
+ for (const [, list] of Object.entries(prompts2)) {
1653
1710
  for (const p of list) {
1654
- this.promptRegistry.set(`${id}:${p.name}`, {
1711
+ this.promptRegistry.set(`${serverId}:${p.name}`, {
1655
1712
  name: p.name,
1656
1713
  description: p.description,
1657
1714
  arguments: p.arguments,
1658
- serverId: id
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 = normalizeServerId(serverId);
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 = normalizeServerId(serverId);
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 = normalizeServerId(serverId);
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
- serverId = normalizeServerId(sid);
1766
+ const mappedId = this.getServerUniqueId(sid);
1767
+ serverId = mappedId || (this.mcpClients.has(sid) ? sid : "");
1705
1768
  name = n;
1706
1769
  } else {
1707
- for (const [key, tool2] of this.toolRegistry.entries()) {
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 result = await tool.execute({ context: parameters || {} });
1747
- return { result };
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 client = this.mcpClients.get(serverId);
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(serverId, uri);
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 client = this.mcpClients.get(serverId);
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: serverId,
1850
+ serverName: resolvedServerId,
1761
1851
  name: promptName,
1762
1852
  args: args || {}
1763
1853
  });