@mcpjam/inspector 1.0.1 → 1.0.2

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.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/mcp_jam.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>MCPJam Inspector</title>
8
- <script type="module" crossorigin src="/assets/index-Dn41SYnl.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BZlC0Ubf.css">
8
+ <script type="module" crossorigin src="/assets/index-DfX8IFHm.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-D5Niv-PI.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -286,7 +286,9 @@ tools.post("/execute", async (c) => {
286
286
  return c.json({ error: `Server '${serverId}' is not connected` }, 400);
287
287
  }
288
288
  const executionId = `exec_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
289
- const execPromise = Promise.resolve().then(() => mcp2.executeToolDirect(toolName, parameters || {})).catch((error) => {
289
+ const execPromise = Promise.resolve().then(
290
+ () => mcp2.executeToolDirect(`${serverId}:${toolName}`, parameters || {})
291
+ ).catch((error) => {
290
292
  if (state) state.error = error;
291
293
  throw error;
292
294
  });
@@ -345,10 +347,7 @@ tools.post("/execute", async (c) => {
345
347
  state.result = race.res;
346
348
  activeExecution = null;
347
349
  mcp2.clearElicitationCallback();
348
- return c.json(
349
- { status: "completed", toolName, result: race.res.result },
350
- 200
351
- );
350
+ return c.json({ status: "completed", toolName, result: race.res }, 200);
352
351
  }
353
352
  return c.json(
354
353
  {
@@ -400,7 +399,7 @@ tools.post("/respond", async (c) => {
400
399
  {
401
400
  status: "completed",
402
401
  toolName: state.toolName,
403
- result: race.res.result
402
+ result: race.res
404
403
  },
405
404
  200
406
405
  );
@@ -502,6 +501,269 @@ resources.post("/read", async (c) => {
502
501
  );
503
502
  }
504
503
  });
504
+ resources.get("/widget/:sessionId", async (c) => {
505
+ const sessionId = c.req.param("sessionId");
506
+ const widgetData = c.req.query("data");
507
+ if (!widgetData) {
508
+ return c.html("<html><body>Error: Missing widget data</body></html>", 400);
509
+ }
510
+ return c.html(`
511
+ <!DOCTYPE html>
512
+ <html>
513
+ <head>
514
+ <meta charset="utf-8">
515
+ <title>Loading Widget...</title>
516
+ </head>
517
+ <body>
518
+ <script>
519
+ (async function() {
520
+ // Change URL to "/" BEFORE loading widget (for React Router)
521
+ history.replaceState(null, '', '/');
522
+
523
+ // Fetch the actual widget HTML
524
+ const response = await fetch('/api/mcp/resources/widget-content?data=${encodeURIComponent(widgetData)}');
525
+ const html = await response.text();
526
+
527
+ // Replace entire document with widget HTML
528
+ document.open();
529
+ document.write(html);
530
+ document.close();
531
+ })();
532
+ </script>
533
+ </body>
534
+ </html>
535
+ `);
536
+ });
537
+ resources.get("/widget-content", async (c) => {
538
+ try {
539
+ const widgetData = c.req.query("data");
540
+ if (!widgetData) {
541
+ return c.html(
542
+ "<html><body>Error: Missing widget data</body></html>",
543
+ 400
544
+ );
545
+ }
546
+ const { serverId, uri, toolInput, toolOutput, toolId } = JSON.parse(
547
+ Buffer.from(widgetData, "base64").toString()
548
+ );
549
+ const mcpClientManager = c.mcpJamClientManager;
550
+ const connectedServers = mcpClientManager.getConnectedServers();
551
+ let actualServerId = serverId;
552
+ if (!connectedServers[serverId]) {
553
+ const serverNames = Object.keys(connectedServers);
554
+ const match = serverNames.find(
555
+ (name) => name.toLowerCase() === serverId.toLowerCase()
556
+ );
557
+ if (match) {
558
+ actualServerId = match;
559
+ } else {
560
+ return c.html(
561
+ `<html><body>
562
+ <h3>Error: Server not connected</h3>
563
+ <p>Requested server: ${serverId}</p>
564
+ <p>Available servers: ${serverNames.join(", ")}</p>
565
+ </body></html>`,
566
+ 404
567
+ );
568
+ }
569
+ }
570
+ const content = await mcpClientManager.getResource(uri, actualServerId);
571
+ let htmlContent = "";
572
+ if (Array.isArray(content)) {
573
+ htmlContent = content[0]?.text || content[0]?.blob || "";
574
+ } else if (content && typeof content === "object") {
575
+ htmlContent = content.text || content.blob || "";
576
+ if (!htmlContent && Array.isArray(content.contents)) {
577
+ htmlContent = content.contents[0]?.text || content.contents[0]?.blob || "";
578
+ }
579
+ }
580
+ if (!htmlContent) {
581
+ return c.html(
582
+ "<html><body>Error: No HTML content found</body></html>",
583
+ 404
584
+ );
585
+ }
586
+ const widgetStateKey = `openai-widget-state:${toolId}`;
587
+ const apiScript = `
588
+ <script>
589
+ (function() {
590
+ 'use strict';
591
+
592
+ const openaiAPI = {
593
+ toolInput: ${JSON.stringify(toolInput)},
594
+ toolOutput: ${JSON.stringify(toolOutput)},
595
+ displayMode: 'inline',
596
+ maxHeight: 600,
597
+ theme: 'dark',
598
+ locale: 'en-US',
599
+ safeArea: { insets: { top: 0, bottom: 0, left: 0, right: 0 } },
600
+ userAgent: {},
601
+ widgetState: null,
602
+
603
+ async setWidgetState(state) {
604
+ this.widgetState = state;
605
+ try {
606
+ localStorage.setItem(${JSON.stringify(widgetStateKey)}, JSON.stringify(state));
607
+ } catch (err) {
608
+ console.error('[OpenAI Widget] Failed to save widget state:', err);
609
+ }
610
+ window.parent.postMessage({
611
+ type: 'openai:setWidgetState',
612
+ toolId: ${JSON.stringify(toolId)},
613
+ state
614
+ }, '*');
615
+ },
616
+
617
+ async callTool(toolName, params = {}) {
618
+ return new Promise((resolve, reject) => {
619
+ const requestId = \`tool_\${Date.now()}_\${Math.random()}\`;
620
+ const handler = (event) => {
621
+ if (event.data.type === 'openai:callTool:response' &&
622
+ event.data.requestId === requestId) {
623
+ window.removeEventListener('message', handler);
624
+ if (event.data.error) {
625
+ reject(new Error(event.data.error));
626
+ } else {
627
+ resolve(event.data.result);
628
+ }
629
+ }
630
+ };
631
+ window.addEventListener('message', handler);
632
+ window.parent.postMessage({
633
+ type: 'openai:callTool',
634
+ requestId,
635
+ toolName,
636
+ params
637
+ }, '*');
638
+ setTimeout(() => {
639
+ window.removeEventListener('message', handler);
640
+ reject(new Error('Tool call timeout'));
641
+ }, 30000);
642
+ });
643
+ },
644
+
645
+ async sendFollowupTurn(message) {
646
+ const payload = typeof message === 'string'
647
+ ? { prompt: message }
648
+ : message;
649
+ window.parent.postMessage({
650
+ type: 'openai:sendFollowup',
651
+ message: payload.prompt || payload
652
+ }, '*');
653
+ },
654
+
655
+ async requestDisplayMode(options = {}) {
656
+ const mode = options.mode || 'inline';
657
+ this.displayMode = mode;
658
+ window.parent.postMessage({
659
+ type: 'openai:requestDisplayMode',
660
+ mode
661
+ }, '*');
662
+ return { mode };
663
+ },
664
+
665
+ async sendFollowUpMessage(args) {
666
+ const prompt = typeof args === 'string' ? args : (args?.prompt || '');
667
+ return this.sendFollowupTurn(prompt);
668
+ }
669
+ };
670
+
671
+ Object.defineProperty(window, 'openai', {
672
+ value: openaiAPI,
673
+ writable: false,
674
+ configurable: false,
675
+ enumerable: true
676
+ });
677
+
678
+ Object.defineProperty(window, 'webplus', {
679
+ value: openaiAPI,
680
+ writable: false,
681
+ configurable: false,
682
+ enumerable: true
683
+ });
684
+
685
+ setTimeout(() => {
686
+ try {
687
+ const globalsEvent = new CustomEvent('webplus:set_globals', {
688
+ detail: {
689
+ globals: {
690
+ displayMode: openaiAPI.displayMode,
691
+ maxHeight: openaiAPI.maxHeight,
692
+ theme: openaiAPI.theme,
693
+ locale: openaiAPI.locale,
694
+ safeArea: openaiAPI.safeArea,
695
+ userAgent: openaiAPI.userAgent
696
+ }
697
+ }
698
+ });
699
+ window.dispatchEvent(globalsEvent);
700
+ } catch (err) {}
701
+ }, 0);
702
+
703
+ setTimeout(() => {
704
+ try {
705
+ const stored = localStorage.getItem(${JSON.stringify(widgetStateKey)});
706
+ if (stored && window.openai) {
707
+ window.openai.widgetState = JSON.parse(stored);
708
+ }
709
+ } catch (err) {}
710
+ }, 0);
711
+ })();
712
+ </script>
713
+ `;
714
+ let modifiedHtml;
715
+ if (htmlContent.includes("<html>") && htmlContent.includes("<head>")) {
716
+ modifiedHtml = htmlContent.replace(
717
+ "<head>",
718
+ `<head><base href="/">${apiScript}`
719
+ );
720
+ } else {
721
+ modifiedHtml = `<!DOCTYPE html>
722
+ <html>
723
+ <head>
724
+ <base href="/">
725
+ <meta charset="UTF-8">
726
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
727
+ ${apiScript}
728
+ </head>
729
+ <body>
730
+ ${htmlContent}
731
+ </body>
732
+ </html>`;
733
+ }
734
+ const trustedCdns = [
735
+ "https://persistent.oaistatic.com",
736
+ "https://*.oaistatic.com",
737
+ "https://unpkg.com",
738
+ "https://cdn.jsdelivr.net",
739
+ "https://cdnjs.cloudflare.com",
740
+ "https://cdn.skypack.dev"
741
+ ].join(" ");
742
+ c.header(
743
+ "Content-Security-Policy",
744
+ [
745
+ "default-src 'self'",
746
+ `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${trustedCdns}`,
747
+ "worker-src 'self' blob:",
748
+ "child-src 'self' blob:",
749
+ `style-src 'self' 'unsafe-inline' ${trustedCdns}`,
750
+ "img-src 'self' data: https: blob:",
751
+ "media-src 'self' data: https: blob:",
752
+ `font-src 'self' data: ${trustedCdns}`,
753
+ "connect-src 'self' https: wss: ws:",
754
+ "frame-ancestors 'self'"
755
+ ].join("; ")
756
+ );
757
+ c.header("X-Frame-Options", "SAMEORIGIN");
758
+ c.header("X-Content-Type-Options", "nosniff");
759
+ return c.html(modifiedHtml);
760
+ } catch (error) {
761
+ return c.html(
762
+ `<html><body>Error: ${error instanceof Error ? error.message : "Unknown error"}</body></html>`,
763
+ 500
764
+ );
765
+ }
766
+ });
505
767
  var resources_default = resources;
506
768
 
507
769
  // routes/mcp/prompts.ts
@@ -776,11 +1038,16 @@ function convertMastraToolsToVercelTools(mastraTools) {
776
1038
  }
777
1039
 
778
1040
  // ../shared/http-tool-calls.ts
779
- function flattenToolsets(toolsets) {
1041
+ function flattenToolsetsWithServerId(toolsets) {
780
1042
  const flattened = {};
781
- for (const serverTools of Object.values(toolsets || {})) {
1043
+ for (const [serverId, serverTools] of Object.entries(toolsets || {})) {
782
1044
  if (serverTools && typeof serverTools === "object") {
783
- Object.assign(flattened, serverTools);
1045
+ for (const [toolName, tool2] of Object.entries(serverTools)) {
1046
+ flattened[toolName] = {
1047
+ ...tool2,
1048
+ _serverId: serverId
1049
+ };
1050
+ }
784
1051
  }
785
1052
  }
786
1053
  return flattened;
@@ -818,13 +1085,18 @@ async function executeToolCallsFromMessages(messages, options) {
818
1085
  let tools2 = {};
819
1086
  if (options.client) {
820
1087
  const toolsets = await options.client.getToolsets();
821
- tools2 = flattenToolsets(toolsets);
1088
+ tools2 = flattenToolsetsWithServerId(toolsets);
822
1089
  } else if (options.toolsets) {
823
- tools2 = flattenToolsets(options.toolsets);
1090
+ const toolsets = options.toolsets;
1091
+ tools2 = flattenToolsetsWithServerId(toolsets);
824
1092
  } else {
825
1093
  tools2 = options.tools;
826
1094
  }
827
1095
  const index = buildIndexWithAliases(tools2);
1096
+ const extractServerId = (toolName) => {
1097
+ const tool2 = index[toolName];
1098
+ return tool2?._serverId;
1099
+ };
828
1100
  const existingToolResultIds = /* @__PURE__ */ new Set();
829
1101
  for (const msg of messages) {
830
1102
  if (!msg || msg.role !== "tool" || !Array.isArray(msg.content))
@@ -861,6 +1133,7 @@ async function executeToolCallsFromMessages(messages, options) {
861
1133
  } else {
862
1134
  output = { type: "text", value: String(result) };
863
1135
  }
1136
+ const serverId = extractServerId(toolName);
864
1137
  const toolResultMessage = {
865
1138
  role: "tool",
866
1139
  content: [
@@ -868,7 +1141,11 @@ async function executeToolCallsFromMessages(messages, options) {
868
1141
  type: "tool-result",
869
1142
  toolCallId: content.toolCallId,
870
1143
  toolName,
871
- output
1144
+ output,
1145
+ // Preserve full result including _meta for OpenAI Apps SDK
1146
+ result,
1147
+ // Add serverId for OpenAI component resolution
1148
+ serverId
872
1149
  }
873
1150
  ]
874
1151
  };
@@ -951,11 +1228,15 @@ var runBackendConversation = async (options) => {
951
1228
  if (msg.role === "tool" && Array.isArray(content)) {
952
1229
  for (const item of content) {
953
1230
  if (item?.type === "tool-result") {
1231
+ const fullResult = item.result;
954
1232
  const rawOutput = item.output ?? item.result ?? item.value ?? item.data ?? item.content;
955
1233
  const resultEvent = {
956
1234
  toolName: item.toolName ?? item.name,
957
- result: extractToolResultValue(rawOutput),
958
- error: item.error
1235
+ // Use full result if available, otherwise extract value from output
1236
+ result: fullResult ?? extractToolResultValue(rawOutput),
1237
+ error: item.error,
1238
+ // Preserve serverId if present
1239
+ serverId: item.serverId
959
1240
  };
960
1241
  iterationToolResults.push(resultEvent);
961
1242
  handlers?.onToolResult?.(resultEvent);
@@ -1066,7 +1347,8 @@ var handleAgentStepFinish = (streamingContext, text, toolCalls, toolResults, emi
1066
1347
  toolResult: {
1067
1348
  id: currentToolCallId,
1068
1349
  toolCallId: currentToolCallId,
1069
- result: result.result,
1350
+ // Preserve full result which may include _meta for OpenAI Apps SDK
1351
+ result: result.result || result,
1070
1352
  error: result.error,
1071
1353
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1072
1354
  }
@@ -1243,14 +1525,17 @@ var sendMessagesToBackend = async (messages, streamingContext, mcpClientManager,
1243
1525
  return { role: "user", content: m.content };
1244
1526
  }
1245
1527
  });
1246
- const flatTools = await mcpClientManager.getFlattenedToolsetsForEnabledServers(
1247
- selectedServers
1248
- );
1249
- const toolDefs = Object.entries(flatTools).map(([name, tool2]) => ({
1250
- name,
1251
- description: tool2?.description,
1252
- inputSchema: zodToJsonSchema3(tool2?.inputSchema)
1253
- }));
1528
+ const toolsets = await mcpClientManager.getToolsetsWithServerIds(selectedServers);
1529
+ const toolDefs = [];
1530
+ for (const serverTools of Object.values(toolsets)) {
1531
+ for (const [name, tool2] of Object.entries(serverTools)) {
1532
+ toolDefs.push({
1533
+ name,
1534
+ description: tool2?.description,
1535
+ inputSchema: zodToJsonSchema3(tool2?.inputSchema)
1536
+ });
1537
+ }
1538
+ }
1254
1539
  if (!baseUrl) {
1255
1540
  throw new Error("CONVEX_HTTP_URL is not set");
1256
1541
  }
@@ -1277,7 +1562,9 @@ var sendMessagesToBackend = async (messages, streamingContext, mcpClientManager,
1277
1562
  toolCallId: currentToolCallId,
1278
1563
  result: result.result,
1279
1564
  error: result.error,
1280
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1565
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1566
+ serverId: result.serverId
1567
+ // Propagate serverId
1281
1568
  }
1282
1569
  });
1283
1570
  };
@@ -1301,7 +1588,7 @@ var sendMessagesToBackend = async (messages, streamingContext, mcpClientManager,
1301
1588
  },
1302
1589
  executeToolCalls: async (messages2) => {
1303
1590
  await executeToolCallsFromMessages(messages2, {
1304
- tools: flatTools
1591
+ toolsets
1305
1592
  });
1306
1593
  },
1307
1594
  handlers: {
@@ -1323,7 +1610,7 @@ var sendMessagesToBackend = async (messages, streamingContext, mcpClientManager,
1323
1610
  text,
1324
1611
  toolCalls,
1325
1612
  toolResults.map((result) => ({
1326
- result: result.result,
1613
+ result: result.result || result,
1327
1614
  error: result.error
1328
1615
  })),
1329
1616
  false
@@ -9445,6 +9732,15 @@ var MCPJamClientManager = class {
9445
9732
  getServerIdForName(serverName) {
9446
9733
  return this.serverIdMapping.get(serverName);
9447
9734
  }
9735
+ // Reverse lookup: get server name from internal server ID
9736
+ getServerNameForId(serverId) {
9737
+ for (const [name, id] of this.serverIdMapping.entries()) {
9738
+ if (id === serverId) {
9739
+ return name;
9740
+ }
9741
+ }
9742
+ return void 0;
9743
+ }
9448
9744
  flattenToolsets(toolsets) {
9449
9745
  const flattenedTools = {};
9450
9746
  Object.values(toolsets).forEach((serverTools) => {
@@ -9452,6 +9748,26 @@ var MCPJamClientManager = class {
9452
9748
  });
9453
9749
  return flattenedTools;
9454
9750
  }
9751
+ async getToolsetsWithServerIds(serverNameFilter) {
9752
+ const toolsetsByServer = {};
9753
+ const allServerIdsFromFilter = serverNameFilter?.map(
9754
+ (serverName) => this.getServerIdForName(serverName)
9755
+ );
9756
+ for (const [serverId, client] of this.mcpClients.entries()) {
9757
+ if (serverNameFilter && !allServerIdsFromFilter?.includes(serverId))
9758
+ continue;
9759
+ if (this.getConnectionStatus(serverId) !== "connected") continue;
9760
+ try {
9761
+ const toolsets = await client.getToolsets();
9762
+ const flattenedTools = this.flattenToolsets(toolsets);
9763
+ const serverName = this.getServerNameForId(serverId) || serverId;
9764
+ toolsetsByServer[serverName] = flattenedTools;
9765
+ } catch (error) {
9766
+ console.warn(`Failed to get tools from server ${serverId}:`, error);
9767
+ }
9768
+ }
9769
+ return toolsetsByServer;
9770
+ }
9455
9771
  async getFlattenedToolsetsForEnabledServers(serverNameFilter) {
9456
9772
  const allFlattenedTools = {};
9457
9773
  const allServerIdsFromFilter = serverNameFilter?.map(
@@ -9687,30 +10003,12 @@ var MCPJamClientManager = class {
9687
10003
  const tool2 = flattenedTools[name];
9688
10004
  if (!tool2)
9689
10005
  throw new Error(`Tool '${name}' not found in server '${serverId}'`);
9690
- const schema = tool2.inputSchema;
9691
- const hasContextProperty = schema && typeof schema === "object" && schema.properties && Object.prototype.hasOwnProperty.call(
9692
- schema.properties,
9693
- "context"
9694
- );
9695
- const requiresContext = hasContextProperty || schema && Array.isArray(schema.required) && schema.required.includes("context");
9696
- const contextWrapped = { context: parameters || {} };
9697
- const direct = parameters || {};
9698
- const attempts = requiresContext ? [contextWrapped, direct] : [direct, contextWrapped];
9699
- let lastError = void 0;
9700
- for (const args of attempts) {
9701
- try {
9702
- console.log("args", args);
9703
- const result = await tool2.execute(args);
9704
- if (result && result.isError) {
9705
- const errorText = result.content && result.content[0] && result.content[0].text ? result.content[0].text : "Unknown error";
9706
- throw new Error(errorText);
9707
- }
9708
- return { result };
9709
- } catch (err) {
9710
- lastError = err;
9711
- }
10006
+ const result = await tool2.execute({ context: parameters || {} });
10007
+ if (result && result.isError) {
10008
+ const errorText = result.content && result.content[0] && result.content[0].text ? result.content[0].text : "Unknown error";
10009
+ throw new Error(errorText);
9712
10010
  }
9713
- throw lastError;
10011
+ return { result };
9714
10012
  }
9715
10013
  async getResource(resourceUri, serverId) {
9716
10014
  let uri = resourceUri;