@mcpjam/inspector 0.9.1 → 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.
- package/LICENSE +13 -0
- package/README.md +23 -0
- package/bin/start.js +38 -12
- package/dist/client/assets/index-Bv0p7Vhi.css +1 -0
- package/dist/client/assets/index-DzF9hvwr.js +1713 -0
- package/dist/client/assets/index-DzF9hvwr.js.map +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +190 -17
- package/dist/server/index.js.map +1 -1
- package/package.json +36 -5
- package/dist/client/assets/index-C4nE74q5.css +0 -1
- package/dist/client/assets/index-CcfG-2LO.js +0 -357
- package/dist/client/assets/index-CcfG-2LO.js.map +0 -1
- package/dist/main/main.cjs +0 -1330
- package/dist/preload/preload.js +0 -26
- package/dist/renderer/assets/index-CYiU4_x2.css +0 -1
- package/dist/renderer/assets/index-woGCpEdp.js +0 -356
- package/dist/renderer/catalyst.png +0 -0
- package/dist/renderer/claude_logo.png +0 -0
- package/dist/renderer/demo_1.png +0 -0
- package/dist/renderer/demo_2.png +0 -0
- package/dist/renderer/demo_3.png +0 -0
- package/dist/renderer/file.svg +0 -1
- package/dist/renderer/globe.svg +0 -1
- package/dist/renderer/index.html +0 -14
- package/dist/renderer/mcp.svg +0 -1
- package/dist/renderer/mcp_jam.svg +0 -12
- package/dist/renderer/mcp_jam_dark.png +0 -0
- package/dist/renderer/mcp_jam_light.png +0 -0
- package/dist/renderer/next.svg +0 -1
- package/dist/renderer/ollama_dark.png +0 -0
- package/dist/renderer/ollama_logo.svg +0 -7
- package/dist/renderer/openai_logo.png +0 -0
- package/dist/renderer/vercel.svg +0 -1
- package/dist/renderer/window.svg +0 -1
package/dist/client/index.html
CHANGED
|
@@ -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-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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>
|
package/dist/server/index.js
CHANGED
|
@@ -29,7 +29,10 @@ function validateServerConfig(serverConfig) {
|
|
|
29
29
|
if (config.url) {
|
|
30
30
|
try {
|
|
31
31
|
if (typeof config.url === "string") {
|
|
32
|
-
|
|
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
|
|
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
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
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
|
-
|
|
942
|
-
|
|
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
|
|
@@ -1035,6 +1185,20 @@ function logBox(content, title) {
|
|
|
1035
1185
|
});
|
|
1036
1186
|
console.log("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
1037
1187
|
}
|
|
1188
|
+
function getMCPConfigFromEnv() {
|
|
1189
|
+
const command = process.env.MCP_SERVER_COMMAND;
|
|
1190
|
+
if (!command) {
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
const argsString = process.env.MCP_SERVER_ARGS;
|
|
1194
|
+
const args = argsString ? JSON.parse(argsString) : [];
|
|
1195
|
+
return {
|
|
1196
|
+
command,
|
|
1197
|
+
args,
|
|
1198
|
+
name: "CLI Server"
|
|
1199
|
+
// Default name for CLI-provided servers
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1038
1202
|
var app = new Hono8();
|
|
1039
1203
|
app.use("*", logger());
|
|
1040
1204
|
var serverPort = process.env.PORT || "3001";
|
|
@@ -1056,6 +1220,10 @@ app.route("/api/mcp", mcp_default);
|
|
|
1056
1220
|
app.get("/health", (c) => {
|
|
1057
1221
|
return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1058
1222
|
});
|
|
1223
|
+
app.get("/api/mcp-cli-config", (c) => {
|
|
1224
|
+
const mcpConfig = getMCPConfigFromEnv();
|
|
1225
|
+
return c.json({ config: mcpConfig });
|
|
1226
|
+
});
|
|
1059
1227
|
if (process.env.NODE_ENV === "production") {
|
|
1060
1228
|
app.use("/*", serveStatic({ root: "./dist/client" }));
|
|
1061
1229
|
app.get("*", async (c) => {
|
|
@@ -1064,13 +1232,18 @@ if (process.env.NODE_ENV === "production") {
|
|
|
1064
1232
|
return c.notFound();
|
|
1065
1233
|
}
|
|
1066
1234
|
const indexPath = join(process.cwd(), "dist", "client", "index.html");
|
|
1067
|
-
|
|
1235
|
+
let htmlContent = readFileSync(indexPath, "utf-8");
|
|
1236
|
+
const mcpConfig = getMCPConfigFromEnv();
|
|
1237
|
+
if (mcpConfig) {
|
|
1238
|
+
const configScript = `<script>window.MCP_CLI_CONFIG = ${JSON.stringify(mcpConfig)};</script>`;
|
|
1239
|
+
htmlContent = htmlContent.replace("</head>", `${configScript}</head>`);
|
|
1240
|
+
}
|
|
1068
1241
|
return c.html(htmlContent);
|
|
1069
1242
|
});
|
|
1070
1243
|
} else {
|
|
1071
1244
|
app.get("/", (c) => {
|
|
1072
1245
|
return c.json({
|
|
1073
|
-
message: "
|
|
1246
|
+
message: "MCPJam API Server",
|
|
1074
1247
|
environment: "development",
|
|
1075
1248
|
frontend: `http://localhost:${serverPort}`
|
|
1076
1249
|
});
|