@mcpjam/inspector 0.9.2 → 0.9.3

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-C2tpp_KC.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BxtnlkQW.css">
8
+ <script type="module" crossorigin src="/assets/index-DzF9hvwr.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-Bv0p7Vhi.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -29,7 +29,10 @@ function validateServerConfig(serverConfig) {
29
29
  if (config.url) {
30
30
  try {
31
31
  if (typeof config.url === "string") {
32
- config.url = new URL(config.url);
32
+ const parsed = new URL(config.url);
33
+ parsed.search = "";
34
+ parsed.hash = "";
35
+ config.url = parsed;
33
36
  } else if (typeof config.url === "object" && !config.url.href) {
34
37
  return {
35
38
  success: false,
@@ -231,6 +234,7 @@ var connect_default = connect;
231
234
  // routes/mcp/tools.ts
232
235
  import { Hono as Hono2 } from "hono";
233
236
  import { zodToJsonSchema } from "zod-to-json-schema";
237
+ import { TextEncoder } from "util";
234
238
  var tools = new Hono2();
235
239
  var pendingElicitations = /* @__PURE__ */ new Map();
236
240
  tools.post("/", async (c) => {
@@ -644,7 +648,16 @@ import { createAnthropic } from "@ai-sdk/anthropic";
644
648
  import { createOpenAI } from "@ai-sdk/openai";
645
649
  import { createOllama } from "ollama-ai-provider";
646
650
  import { MCPClient as MCPClient2 } from "@mastra/mcp";
651
+ import { TextEncoder as TextEncoder2 } from "util";
647
652
  var chat = new Hono5();
653
+ var DEBUG_ENABLED = process.env.MCP_DEBUG !== "false";
654
+ var dbg = (...args) => {
655
+ if (DEBUG_ENABLED) console.log("[mcp/chat]", ...args);
656
+ };
657
+ try {
658
+ process.setMaxListeners?.(50);
659
+ } catch {
660
+ }
648
661
  var pendingElicitations2 = /* @__PURE__ */ new Map();
649
662
  chat.post("/", async (c) => {
650
663
  let client = null;
@@ -694,9 +707,16 @@ chat.post("/", async (c) => {
694
707
  400
695
708
  );
696
709
  }
710
+ dbg("Incoming chat request", {
711
+ provider: model?.provider,
712
+ modelId: model?.id,
713
+ messageCount: messages?.length,
714
+ serverCount: serverConfigs ? Object.keys(serverConfigs).length : 0
715
+ });
697
716
  if (serverConfigs && Object.keys(serverConfigs).length > 0) {
698
717
  const validation = validateMultipleServerConfigs(serverConfigs);
699
718
  if (!validation.success) {
719
+ dbg("Server config validation failed", validation.errors || validation.error);
700
720
  return c.json(
701
721
  {
702
722
  success: false,
@@ -707,17 +727,20 @@ chat.post("/", async (c) => {
707
727
  );
708
728
  }
709
729
  client = createMCPClientWithMultipleConnections(validation.validConfigs);
730
+ dbg("Created MCP client with servers", Object.keys(validation.validConfigs));
710
731
  } else {
711
732
  client = new MCPClient2({
712
733
  id: `chat-${Date.now()}`,
713
734
  servers: {}
714
735
  });
736
+ dbg("Created MCP client without servers");
715
737
  }
716
- const tools2 = await client.getTools();
717
738
  const llmModel = getLlmModel(model, apiKey, ollamaBaseUrl);
739
+ dbg("LLM model initialized", { provider: model.provider, id: model.id });
718
740
  let toolCallId = 0;
719
741
  let streamController = null;
720
742
  let encoder = null;
743
+ let lastEmittedToolCallId = null;
721
744
  const elicitationHandler = async (elicitationRequest) => {
722
745
  const requestId2 = `elicit_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
723
746
  if (streamController && encoder) {
@@ -735,6 +758,7 @@ chat.post("/", async (c) => {
735
758
  )
736
759
  );
737
760
  }
761
+ dbg("Elicitation requested", { requestId: requestId2 });
738
762
  return new Promise((resolve, reject) => {
739
763
  pendingElicitations2.set(requestId2, { resolve, reject });
740
764
  setTimeout(() => {
@@ -749,8 +773,10 @@ chat.post("/", async (c) => {
749
773
  for (const serverName of Object.keys(serverConfigs)) {
750
774
  const normalizedName = serverName.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
751
775
  client.elicitation.onRequest(normalizedName, elicitationHandler);
776
+ dbg("Registered elicitation handler", { serverName, normalizedName });
752
777
  }
753
778
  }
779
+ const tools2 = await client.getTools();
754
780
  const originalTools = tools2 && Object.keys(tools2).length > 0 ? tools2 : {};
755
781
  const wrappedTools = {};
756
782
  for (const [name, tool] of Object.entries(originalTools)) {
@@ -758,6 +784,7 @@ chat.post("/", async (c) => {
758
784
  ...tool,
759
785
  execute: async (params) => {
760
786
  const currentToolCallId = ++toolCallId;
787
+ const startedAt = Date.now();
761
788
  if (streamController && encoder) {
762
789
  streamController.enqueue(
763
790
  encoder.encode(
@@ -776,8 +803,10 @@ chat.post("/", async (c) => {
776
803
  )
777
804
  );
778
805
  }
806
+ dbg("Tool executing", { name, currentToolCallId, params });
779
807
  try {
780
808
  const result = await tool.execute(params);
809
+ dbg("Tool result", { name, currentToolCallId, ms: Date.now() - startedAt });
781
810
  if (streamController && encoder) {
782
811
  streamController.enqueue(
783
812
  encoder.encode(
@@ -797,6 +826,7 @@ chat.post("/", async (c) => {
797
826
  }
798
827
  return result;
799
828
  } catch (error) {
829
+ dbg("Tool error", { name, currentToolCallId, error: error instanceof Error ? error.message : String(error) });
800
830
  if (streamController && encoder) {
801
831
  streamController.enqueue(
802
832
  encoder.encode(
@@ -829,19 +859,110 @@ chat.post("/", async (c) => {
829
859
  role: msg.role,
830
860
  content: msg.content
831
861
  }));
862
+ const toolsets = serverConfigs ? await client.getToolsets() : void 0;
863
+ dbg("Streaming start", {
864
+ toolsetServers: toolsets ? Object.keys(toolsets) : [],
865
+ messageCount: formattedMessages.length
866
+ });
867
+ let streamedAnyText = false;
832
868
  const stream = await agent.stream(formattedMessages, {
833
- maxSteps: 10
869
+ maxSteps: 10,
834
870
  // Allow up to 10 steps for tool usage
871
+ toolsets,
872
+ onStepFinish: ({ text, toolCalls, toolResults }) => {
873
+ try {
874
+ if (text && streamController && encoder) {
875
+ streamedAnyText = true;
876
+ streamController.enqueue(
877
+ encoder.encode(
878
+ `data: ${JSON.stringify({ type: "text", content: text })}
879
+
880
+ `
881
+ )
882
+ );
883
+ }
884
+ const tcList = toolCalls;
885
+ if (tcList && Array.isArray(tcList)) {
886
+ for (const call of tcList) {
887
+ const currentToolCallId = ++toolCallId;
888
+ lastEmittedToolCallId = currentToolCallId;
889
+ if (streamController && encoder) {
890
+ streamController.enqueue(
891
+ encoder.encode(
892
+ `data: ${JSON.stringify({
893
+ type: "tool_call",
894
+ toolCall: {
895
+ id: currentToolCallId,
896
+ name: call.name || call.toolName,
897
+ parameters: call.params || call.args || {},
898
+ timestamp: /* @__PURE__ */ new Date(),
899
+ status: "executing"
900
+ }
901
+ })}
902
+
903
+ `
904
+ )
905
+ );
906
+ }
907
+ }
908
+ }
909
+ const trList = toolResults;
910
+ if (trList && Array.isArray(trList)) {
911
+ for (const result of trList) {
912
+ const currentToolCallId = lastEmittedToolCallId != null ? lastEmittedToolCallId : ++toolCallId;
913
+ if (streamController && encoder) {
914
+ streamController.enqueue(
915
+ encoder.encode(
916
+ `data: ${JSON.stringify({
917
+ type: "tool_result",
918
+ toolResult: {
919
+ id: currentToolCallId,
920
+ toolCallId: currentToolCallId,
921
+ result: result.result,
922
+ error: result.error,
923
+ timestamp: /* @__PURE__ */ new Date()
924
+ }
925
+ })}
926
+
927
+ `
928
+ )
929
+ );
930
+ }
931
+ }
932
+ }
933
+ } catch (err) {
934
+ dbg("onStepFinish error", err);
935
+ }
936
+ },
937
+ onFinish: ({ text, finishReason }) => {
938
+ dbg("onFinish called", { finishReason, hasText: Boolean(text) });
939
+ try {
940
+ if (text && streamController && encoder) {
941
+ streamedAnyText = true;
942
+ streamController.enqueue(
943
+ encoder.encode(
944
+ `data: ${JSON.stringify({ type: "text", content: text })}
945
+
946
+ `
947
+ )
948
+ );
949
+ }
950
+ } catch (err) {
951
+ dbg("onFinish enqueue error", err);
952
+ }
953
+ }
835
954
  });
836
- encoder = new TextEncoder();
955
+ encoder = new TextEncoder2();
837
956
  const readableStream = new ReadableStream({
838
957
  async start(controller) {
839
958
  streamController = controller;
840
959
  try {
841
960
  let hasContent = false;
961
+ let chunkCount = 0;
842
962
  for await (const chunk of stream.textStream) {
843
963
  if (chunk && chunk.trim()) {
844
964
  hasContent = true;
965
+ chunkCount++;
845
966
  controller.enqueue(
846
967
  encoder.encode(
847
968
  `data: ${JSON.stringify({ type: "text", content: chunk })}
@@ -851,14 +972,43 @@ chat.post("/", async (c) => {
851
972
  );
852
973
  }
853
974
  }
854
- if (!hasContent) {
855
- controller.enqueue(
856
- encoder.encode(
857
- `data: ${JSON.stringify({ type: "text", content: "I apologize, but I couldn't generate a response. Please try again." })}
975
+ dbg("Streaming finished", { hasContent, chunkCount });
976
+ if (!hasContent && !streamedAnyText) {
977
+ dbg("No content from textStream/callbacks; falling back to generate()");
978
+ try {
979
+ const gen = await agent.generate(formattedMessages, {
980
+ maxSteps: 10,
981
+ toolsets
982
+ });
983
+ const finalText = gen.text || "";
984
+ if (finalText) {
985
+ controller.enqueue(
986
+ encoder.encode(
987
+ `data: ${JSON.stringify({ type: "text", content: finalText })}
858
988
 
859
989
  `
860
- )
861
- );
990
+ )
991
+ );
992
+ } else {
993
+ dbg("generate() also returned empty text");
994
+ controller.enqueue(
995
+ encoder.encode(
996
+ `data: ${JSON.stringify({ type: "text", content: "I apologize, but I couldn't generate a response. Please try again." })}
997
+
998
+ `
999
+ )
1000
+ );
1001
+ }
1002
+ } catch (fallbackErr) {
1003
+ console.error("[mcp/chat] Fallback generate() error:", fallbackErr);
1004
+ controller.enqueue(
1005
+ encoder.encode(
1006
+ `data: ${JSON.stringify({ type: "error", error: fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr) })}
1007
+
1008
+ `
1009
+ )
1010
+ );
1011
+ }
862
1012
  }
863
1013
  controller.enqueue(
864
1014
  encoder.encode(
@@ -873,7 +1023,7 @@ chat.post("/", async (c) => {
873
1023
 
874
1024
  `));
875
1025
  } catch (error) {
876
- console.error("Streaming error:", error);
1026
+ console.error("[mcp/chat] Streaming error:", error);
877
1027
  controller.enqueue(
878
1028
  encoder.encode(
879
1029
  `data: ${JSON.stringify({
@@ -890,7 +1040,7 @@ chat.post("/", async (c) => {
890
1040
  await client.disconnect();
891
1041
  } catch (cleanupError) {
892
1042
  console.warn(
893
- "Error cleaning up MCP client after streaming:",
1043
+ "[mcp/chat] Error cleaning up MCP client after streaming:",
894
1044
  cleanupError
895
1045
  );
896
1046
  }
@@ -907,7 +1057,7 @@ chat.post("/", async (c) => {
907
1057
  }
908
1058
  });
909
1059
  } catch (error) {
910
- console.error("Error in chat API:", error);
1060
+ console.error("[mcp/chat] Error in chat API:", error);
911
1061
  if (client) {
912
1062
  try {
913
1063
  await client.disconnect();
@@ -938,8 +1088,8 @@ var getLlmModel = (modelDefinition, apiKey, ollamaBaseUrl) => {
938
1088
  case "ollama":
939
1089
  const baseUrl = ollamaBaseUrl || "http://localhost:11434";
940
1090
  return createOllama({
941
- baseURL: `${baseUrl}/api`
942
- // Configurable Ollama API endpoint
1091
+ // The provider expects the root Ollama URL; it internally targets the /api endpoints
1092
+ baseURL: `${baseUrl}`
943
1093
  })(modelDefinition.id, {
944
1094
  simulateStreaming: true
945
1095
  // Enable streaming for Ollama models
@@ -1093,7 +1243,7 @@ if (process.env.NODE_ENV === "production") {
1093
1243
  } else {
1094
1244
  app.get("/", (c) => {
1095
1245
  return c.json({
1096
- message: "MCP Inspector API Server",
1246
+ message: "MCPJam API Server",
1097
1247
  environment: "development",
1098
1248
  frontend: `http://localhost:${serverPort}`
1099
1249
  });