@mcpjam/inspector 0.9.8 → 0.9.9
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/package.json +2 -3
- package/dist/client/assets/index-C_Mzo-Ly.js +0 -1738
- package/dist/client/assets/index-C_Mzo-Ly.js.map +0 -1
- package/dist/client/assets/index-CxSz4W4P.css +0 -1
- package/dist/client/catalyst.png +0 -0
- package/dist/client/claude_logo.png +0 -0
- package/dist/client/deepseek_logo.svg +0 -1
- package/dist/client/demo_1.png +0 -0
- package/dist/client/demo_2.png +0 -0
- package/dist/client/demo_3.png +0 -0
- package/dist/client/file.svg +0 -1
- package/dist/client/globe.svg +0 -1
- package/dist/client/index.html +0 -14
- package/dist/client/mcp.svg +0 -1
- package/dist/client/mcp_jam.svg +0 -12
- package/dist/client/mcp_jam_dark.png +0 -0
- package/dist/client/mcp_jam_light.png +0 -0
- package/dist/client/next.svg +0 -1
- package/dist/client/ollama_dark.png +0 -0
- package/dist/client/ollama_logo.svg +0 -7
- package/dist/client/openai_logo.png +0 -0
- package/dist/client/vercel.svg +0 -1
- package/dist/client/window.svg +0 -1
- package/dist/server/index.js +0 -2022
- package/dist/server/index.js.map +0 -1
package/dist/server/index.js
DELETED
|
@@ -1,2022 +0,0 @@
|
|
|
1
|
-
// index.ts
|
|
2
|
-
import { serve } from "@hono/node-server";
|
|
3
|
-
import { Hono as Hono10 } from "hono";
|
|
4
|
-
import { cors } from "hono/cors";
|
|
5
|
-
import { logger } from "hono/logger";
|
|
6
|
-
import { serveStatic } from "@hono/node-server/serve-static";
|
|
7
|
-
import { readFileSync } from "fs";
|
|
8
|
-
import { join as join2 } from "path";
|
|
9
|
-
|
|
10
|
-
// routes/mcp/index.ts
|
|
11
|
-
import { Hono as Hono9 } from "hono";
|
|
12
|
-
|
|
13
|
-
// routes/mcp/connect.ts
|
|
14
|
-
import { Hono } from "hono";
|
|
15
|
-
var connect = new Hono();
|
|
16
|
-
connect.post("/", async (c) => {
|
|
17
|
-
try {
|
|
18
|
-
const { serverConfig, serverId } = await c.req.json();
|
|
19
|
-
if (!serverConfig) {
|
|
20
|
-
return c.json(
|
|
21
|
-
{
|
|
22
|
-
success: false,
|
|
23
|
-
error: "serverConfig is required"
|
|
24
|
-
},
|
|
25
|
-
400
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
if (!serverId) {
|
|
29
|
-
return c.json(
|
|
30
|
-
{
|
|
31
|
-
success: false,
|
|
32
|
-
error: "serverId is required"
|
|
33
|
-
},
|
|
34
|
-
400
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
const mcpClientManager = c.mcpJamClientManager;
|
|
38
|
-
try {
|
|
39
|
-
await mcpClientManager.connectToServer(serverId, serverConfig);
|
|
40
|
-
const status = mcpClientManager.getConnectionStatus(serverId);
|
|
41
|
-
if (status === "connected") {
|
|
42
|
-
return c.json({
|
|
43
|
-
success: true,
|
|
44
|
-
status: "connected"
|
|
45
|
-
});
|
|
46
|
-
} else {
|
|
47
|
-
return c.json(
|
|
48
|
-
{
|
|
49
|
-
success: false,
|
|
50
|
-
error: "Connection failed",
|
|
51
|
-
status
|
|
52
|
-
},
|
|
53
|
-
500
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
return c.json(
|
|
58
|
-
{
|
|
59
|
-
success: false,
|
|
60
|
-
error: `MCP configuration is invalid. Please double check your server configuration: ${JSON.stringify(serverConfig)}`,
|
|
61
|
-
details: error instanceof Error ? error.message : "Unknown error"
|
|
62
|
-
},
|
|
63
|
-
500
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
} catch (error) {
|
|
67
|
-
return c.json(
|
|
68
|
-
{
|
|
69
|
-
success: false,
|
|
70
|
-
error: "Failed to parse request body",
|
|
71
|
-
details: error instanceof Error ? error.message : "Unknown error"
|
|
72
|
-
},
|
|
73
|
-
400
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
var connect_default = connect;
|
|
78
|
-
|
|
79
|
-
// routes/mcp/servers.ts
|
|
80
|
-
import { Hono as Hono2 } from "hono";
|
|
81
|
-
var servers = new Hono2();
|
|
82
|
-
servers.get("/", async (c) => {
|
|
83
|
-
try {
|
|
84
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
85
|
-
const connectedServers = mcpJamClientManager2.getConnectedServers();
|
|
86
|
-
const serverList = Object.entries(connectedServers).map(
|
|
87
|
-
([serverId, serverInfo]) => ({
|
|
88
|
-
id: serverId,
|
|
89
|
-
name: serverId,
|
|
90
|
-
status: serverInfo.status,
|
|
91
|
-
config: serverInfo.config
|
|
92
|
-
})
|
|
93
|
-
);
|
|
94
|
-
return c.json({
|
|
95
|
-
success: true,
|
|
96
|
-
servers: serverList
|
|
97
|
-
});
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error("Error listing servers:", error);
|
|
100
|
-
return c.json(
|
|
101
|
-
{
|
|
102
|
-
success: false,
|
|
103
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
104
|
-
},
|
|
105
|
-
500
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
servers.get("/status/:serverId", async (c) => {
|
|
110
|
-
try {
|
|
111
|
-
const serverId = c.req.param("serverId");
|
|
112
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
113
|
-
const status = mcpJamClientManager2.getConnectionStatus(serverId);
|
|
114
|
-
return c.json({
|
|
115
|
-
success: true,
|
|
116
|
-
serverId,
|
|
117
|
-
status
|
|
118
|
-
});
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error("Error getting server status:", error);
|
|
121
|
-
return c.json(
|
|
122
|
-
{
|
|
123
|
-
success: false,
|
|
124
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
125
|
-
},
|
|
126
|
-
500
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
servers.delete("/:serverId", async (c) => {
|
|
131
|
-
try {
|
|
132
|
-
const serverId = c.req.param("serverId");
|
|
133
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
134
|
-
await mcpJamClientManager2.disconnectFromServer(serverId);
|
|
135
|
-
return c.json({
|
|
136
|
-
success: true,
|
|
137
|
-
message: `Disconnected from server: ${serverId}`
|
|
138
|
-
});
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.error("Error disconnecting server:", error);
|
|
141
|
-
return c.json(
|
|
142
|
-
{
|
|
143
|
-
success: false,
|
|
144
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
145
|
-
},
|
|
146
|
-
500
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
servers.post("/reconnect", async (c) => {
|
|
151
|
-
try {
|
|
152
|
-
const { serverId, serverConfig } = await c.req.json();
|
|
153
|
-
if (!serverId || !serverConfig) {
|
|
154
|
-
return c.json(
|
|
155
|
-
{
|
|
156
|
-
success: false,
|
|
157
|
-
error: "serverId and serverConfig are required"
|
|
158
|
-
},
|
|
159
|
-
400
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
163
|
-
await mcpJamClientManager2.disconnectFromServer(serverId);
|
|
164
|
-
await mcpJamClientManager2.connectToServer(serverId, serverConfig);
|
|
165
|
-
const status = mcpJamClientManager2.getConnectionStatus(serverId);
|
|
166
|
-
return c.json({
|
|
167
|
-
success: true,
|
|
168
|
-
serverId,
|
|
169
|
-
status,
|
|
170
|
-
message: `Reconnected to server: ${serverId}`
|
|
171
|
-
});
|
|
172
|
-
} catch (error) {
|
|
173
|
-
console.error("Error reconnecting server:", error);
|
|
174
|
-
return c.json(
|
|
175
|
-
{
|
|
176
|
-
success: false,
|
|
177
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
178
|
-
},
|
|
179
|
-
500
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
var servers_default = servers;
|
|
184
|
-
|
|
185
|
-
// routes/mcp/tools.ts
|
|
186
|
-
import { Hono as Hono3 } from "hono";
|
|
187
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
188
|
-
import { TextEncoder as TextEncoder2 } from "util";
|
|
189
|
-
var tools = new Hono3();
|
|
190
|
-
var pendingElicitations = /* @__PURE__ */ new Map();
|
|
191
|
-
tools.post("/", async (c) => {
|
|
192
|
-
let action;
|
|
193
|
-
let toolName;
|
|
194
|
-
try {
|
|
195
|
-
const requestData = await c.req.json();
|
|
196
|
-
action = requestData.action;
|
|
197
|
-
toolName = requestData.toolName;
|
|
198
|
-
const { serverConfig, parameters, requestId, response } = requestData;
|
|
199
|
-
if (!action || !["list", "execute", "respond"].includes(action)) {
|
|
200
|
-
return c.json(
|
|
201
|
-
{
|
|
202
|
-
success: false,
|
|
203
|
-
error: "Action must be 'list', 'execute', or 'respond'"
|
|
204
|
-
},
|
|
205
|
-
400
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
if (action === "respond") {
|
|
209
|
-
if (!requestId) {
|
|
210
|
-
return c.json(
|
|
211
|
-
{
|
|
212
|
-
success: false,
|
|
213
|
-
error: "requestId is required for respond action"
|
|
214
|
-
},
|
|
215
|
-
400
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
219
|
-
const success = mcpJamClientManager2.respondToElicitation(
|
|
220
|
-
requestId,
|
|
221
|
-
response
|
|
222
|
-
);
|
|
223
|
-
if (!success) {
|
|
224
|
-
const pending = pendingElicitations.get(requestId);
|
|
225
|
-
if (pending) {
|
|
226
|
-
pending.resolve(response);
|
|
227
|
-
pendingElicitations.delete(requestId);
|
|
228
|
-
return c.json({ success: true });
|
|
229
|
-
}
|
|
230
|
-
return c.json(
|
|
231
|
-
{
|
|
232
|
-
success: false,
|
|
233
|
-
error: "No pending elicitation found for this requestId"
|
|
234
|
-
},
|
|
235
|
-
404
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
return c.json({ success: true });
|
|
239
|
-
}
|
|
240
|
-
const encoder = new TextEncoder2();
|
|
241
|
-
const readableStream = new ReadableStream({
|
|
242
|
-
async start(controller) {
|
|
243
|
-
try {
|
|
244
|
-
if (!serverConfig) {
|
|
245
|
-
controller.enqueue(
|
|
246
|
-
encoder.encode(
|
|
247
|
-
`data: ${JSON.stringify({ type: "tool_error", error: "serverConfig is required" })}
|
|
248
|
-
|
|
249
|
-
`
|
|
250
|
-
)
|
|
251
|
-
);
|
|
252
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
253
|
-
|
|
254
|
-
`));
|
|
255
|
-
controller.close();
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
259
|
-
const serverId = serverConfig.name || serverConfig.id || "server";
|
|
260
|
-
await mcpJamClientManager2.connectToServer(serverId, serverConfig);
|
|
261
|
-
mcpJamClientManager2.setElicitationCallback(async (request) => {
|
|
262
|
-
const { requestId: requestId2, message, schema } = request;
|
|
263
|
-
controller.enqueue(
|
|
264
|
-
encoder.encode(
|
|
265
|
-
`data: ${JSON.stringify({
|
|
266
|
-
type: "elicitation_request",
|
|
267
|
-
requestId: requestId2,
|
|
268
|
-
message,
|
|
269
|
-
schema,
|
|
270
|
-
toolName: toolName || "unknown",
|
|
271
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
272
|
-
})}
|
|
273
|
-
|
|
274
|
-
`
|
|
275
|
-
)
|
|
276
|
-
);
|
|
277
|
-
return new Promise((resolve, reject) => {
|
|
278
|
-
pendingElicitations.set(requestId2, {
|
|
279
|
-
resolve: (response2) => {
|
|
280
|
-
resolve(response2);
|
|
281
|
-
pendingElicitations.delete(requestId2);
|
|
282
|
-
},
|
|
283
|
-
reject: (error) => {
|
|
284
|
-
reject(error);
|
|
285
|
-
pendingElicitations.delete(requestId2);
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
setTimeout(() => {
|
|
289
|
-
if (pendingElicitations.has(requestId2)) {
|
|
290
|
-
pendingElicitations.delete(requestId2);
|
|
291
|
-
reject(new Error("Elicitation timeout"));
|
|
292
|
-
}
|
|
293
|
-
}, 3e5);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
if (action === "list") {
|
|
297
|
-
try {
|
|
298
|
-
const flattenedTools = await mcpJamClientManager2.getToolsetsForServer(serverId);
|
|
299
|
-
const toolsWithJsonSchema = {};
|
|
300
|
-
for (const [name, tool] of Object.entries(flattenedTools)) {
|
|
301
|
-
let inputSchema = tool.inputSchema;
|
|
302
|
-
try {
|
|
303
|
-
inputSchema = zodToJsonSchema(inputSchema);
|
|
304
|
-
} catch {
|
|
305
|
-
}
|
|
306
|
-
toolsWithJsonSchema[name] = {
|
|
307
|
-
name,
|
|
308
|
-
description: tool.description,
|
|
309
|
-
inputSchema,
|
|
310
|
-
outputSchema: tool.outputSchema
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
controller.enqueue(
|
|
314
|
-
encoder.encode(
|
|
315
|
-
`data: ${JSON.stringify({ type: "tools_list", tools: toolsWithJsonSchema })}
|
|
316
|
-
|
|
317
|
-
`
|
|
318
|
-
)
|
|
319
|
-
);
|
|
320
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
321
|
-
|
|
322
|
-
`));
|
|
323
|
-
controller.close();
|
|
324
|
-
return;
|
|
325
|
-
} catch (err) {
|
|
326
|
-
controller.enqueue(
|
|
327
|
-
encoder.encode(
|
|
328
|
-
`data: ${JSON.stringify({ type: "tool_error", error: err instanceof Error ? err.message : String(err) })}
|
|
329
|
-
|
|
330
|
-
`
|
|
331
|
-
)
|
|
332
|
-
);
|
|
333
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
334
|
-
|
|
335
|
-
`));
|
|
336
|
-
controller.close();
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
if (action === "execute") {
|
|
341
|
-
if (!toolName) {
|
|
342
|
-
controller.enqueue(
|
|
343
|
-
encoder.encode(
|
|
344
|
-
`data: ${JSON.stringify({ type: "tool_error", error: "Tool name is required for execution" })}
|
|
345
|
-
|
|
346
|
-
`
|
|
347
|
-
)
|
|
348
|
-
);
|
|
349
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
350
|
-
|
|
351
|
-
`));
|
|
352
|
-
controller.close();
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
controller.enqueue(
|
|
356
|
-
encoder.encode(
|
|
357
|
-
`data: ${JSON.stringify({ type: "tool_executing", toolName, parameters: parameters || {}, message: "Executing tool..." })}
|
|
358
|
-
|
|
359
|
-
`
|
|
360
|
-
)
|
|
361
|
-
);
|
|
362
|
-
const exec = await mcpJamClientManager2.executeToolDirect(
|
|
363
|
-
toolName,
|
|
364
|
-
parameters || {}
|
|
365
|
-
);
|
|
366
|
-
controller.enqueue(
|
|
367
|
-
encoder.encode(
|
|
368
|
-
`data: ${JSON.stringify({ type: "tool_result", toolName, result: exec.result })}
|
|
369
|
-
|
|
370
|
-
`
|
|
371
|
-
)
|
|
372
|
-
);
|
|
373
|
-
controller.enqueue(
|
|
374
|
-
encoder.encode(
|
|
375
|
-
`data: ${JSON.stringify({ type: "elicitation_complete", toolName })}
|
|
376
|
-
|
|
377
|
-
`
|
|
378
|
-
)
|
|
379
|
-
);
|
|
380
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
381
|
-
|
|
382
|
-
`));
|
|
383
|
-
controller.close();
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
} catch (err) {
|
|
387
|
-
controller.enqueue(
|
|
388
|
-
encoder.encode(
|
|
389
|
-
`data: ${JSON.stringify({ type: "tool_error", error: err instanceof Error ? err.message : String(err) })}
|
|
390
|
-
|
|
391
|
-
`
|
|
392
|
-
)
|
|
393
|
-
);
|
|
394
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
395
|
-
|
|
396
|
-
`));
|
|
397
|
-
controller.close();
|
|
398
|
-
} finally {
|
|
399
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
400
|
-
if (mcpJamClientManager2) {
|
|
401
|
-
mcpJamClientManager2.clearElicitationCallback();
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
return new Response(readableStream, {
|
|
407
|
-
headers: {
|
|
408
|
-
"Content-Type": "text/event-stream",
|
|
409
|
-
"Cache-Control": "no-cache",
|
|
410
|
-
Connection: "keep-alive"
|
|
411
|
-
}
|
|
412
|
-
});
|
|
413
|
-
} catch (error) {
|
|
414
|
-
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
415
|
-
return c.json(
|
|
416
|
-
{
|
|
417
|
-
success: false,
|
|
418
|
-
error: errorMsg
|
|
419
|
-
},
|
|
420
|
-
500
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
var tools_default = tools;
|
|
425
|
-
|
|
426
|
-
// routes/mcp/resources.ts
|
|
427
|
-
import { Hono as Hono4 } from "hono";
|
|
428
|
-
var resources = new Hono4();
|
|
429
|
-
resources.post("/list", async (c) => {
|
|
430
|
-
try {
|
|
431
|
-
const { serverId } = await c.req.json();
|
|
432
|
-
if (!serverId) {
|
|
433
|
-
return c.json({ success: false, error: "serverId is required" }, 400);
|
|
434
|
-
}
|
|
435
|
-
const mcpClientManager = c.mcpJamClientManager;
|
|
436
|
-
const serverResources = mcpClientManager.getResourcesForServer(serverId);
|
|
437
|
-
return c.json({ resources: { [serverId]: serverResources } });
|
|
438
|
-
} catch (error) {
|
|
439
|
-
console.error("Error fetching resources:", error);
|
|
440
|
-
return c.json(
|
|
441
|
-
{
|
|
442
|
-
success: false,
|
|
443
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
444
|
-
},
|
|
445
|
-
500
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
resources.post("/read", async (c) => {
|
|
450
|
-
try {
|
|
451
|
-
const { serverId, uri } = await c.req.json();
|
|
452
|
-
if (!serverId) {
|
|
453
|
-
return c.json({ success: false, error: "serverId is required" }, 400);
|
|
454
|
-
}
|
|
455
|
-
if (!uri) {
|
|
456
|
-
return c.json(
|
|
457
|
-
{
|
|
458
|
-
success: false,
|
|
459
|
-
error: "Resource URI is required"
|
|
460
|
-
},
|
|
461
|
-
400
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
const mcpClientManager = c.mcpJamClientManager;
|
|
465
|
-
const content = await mcpClientManager.getResource(uri, serverId);
|
|
466
|
-
return c.json({ content });
|
|
467
|
-
} catch (error) {
|
|
468
|
-
console.error("Error reading resource:", error);
|
|
469
|
-
return c.json(
|
|
470
|
-
{
|
|
471
|
-
success: false,
|
|
472
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
473
|
-
},
|
|
474
|
-
500
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
});
|
|
478
|
-
var resources_default = resources;
|
|
479
|
-
|
|
480
|
-
// routes/mcp/prompts.ts
|
|
481
|
-
import { Hono as Hono5 } from "hono";
|
|
482
|
-
var prompts = new Hono5();
|
|
483
|
-
prompts.post("/list", async (c) => {
|
|
484
|
-
try {
|
|
485
|
-
const { serverId } = await c.req.json();
|
|
486
|
-
if (!serverId) {
|
|
487
|
-
return c.json({ success: false, error: "serverId is required" }, 400);
|
|
488
|
-
}
|
|
489
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
490
|
-
const serverPrompts = mcpJamClientManager2.getPromptsForServer(serverId);
|
|
491
|
-
return c.json({ prompts: { [serverId]: serverPrompts } });
|
|
492
|
-
} catch (error) {
|
|
493
|
-
console.error("Error fetching prompts:", error);
|
|
494
|
-
return c.json(
|
|
495
|
-
{
|
|
496
|
-
success: false,
|
|
497
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
498
|
-
},
|
|
499
|
-
500
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
});
|
|
503
|
-
prompts.post("/get", async (c) => {
|
|
504
|
-
try {
|
|
505
|
-
const { serverId, name, args } = await c.req.json();
|
|
506
|
-
if (!serverId) {
|
|
507
|
-
return c.json({ success: false, error: "serverId is required" }, 400);
|
|
508
|
-
}
|
|
509
|
-
if (!name) {
|
|
510
|
-
return c.json(
|
|
511
|
-
{
|
|
512
|
-
success: false,
|
|
513
|
-
error: "Prompt name is required"
|
|
514
|
-
},
|
|
515
|
-
400
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
const mcpJamClientManager2 = c.mcpJamClientManager;
|
|
519
|
-
const content = await mcpJamClientManager2.getPrompt(
|
|
520
|
-
name,
|
|
521
|
-
serverId,
|
|
522
|
-
args || {}
|
|
523
|
-
);
|
|
524
|
-
return c.json({ content });
|
|
525
|
-
} catch (error) {
|
|
526
|
-
console.error("Error getting prompt:", error);
|
|
527
|
-
return c.json(
|
|
528
|
-
{
|
|
529
|
-
success: false,
|
|
530
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
531
|
-
},
|
|
532
|
-
500
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
});
|
|
536
|
-
var prompts_default = prompts;
|
|
537
|
-
|
|
538
|
-
// routes/mcp/chat.ts
|
|
539
|
-
import { Hono as Hono6 } from "hono";
|
|
540
|
-
import { Agent } from "@mastra/core/agent";
|
|
541
|
-
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
542
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
543
|
-
import { createOllama } from "ollama-ai-provider";
|
|
544
|
-
import { TextEncoder as TextEncoder3 } from "util";
|
|
545
|
-
|
|
546
|
-
// ../client/src/lib/chat-utils.ts
|
|
547
|
-
import { clsx } from "clsx";
|
|
548
|
-
import { twMerge } from "tailwind-merge";
|
|
549
|
-
function getDefaultTemperatureByProvider(provider) {
|
|
550
|
-
switch (provider) {
|
|
551
|
-
case "openai":
|
|
552
|
-
return 1;
|
|
553
|
-
case "anthropic":
|
|
554
|
-
return 0;
|
|
555
|
-
default:
|
|
556
|
-
return 0;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// routes/mcp/chat.ts
|
|
561
|
-
var DEBUG_ENABLED = process.env.MCP_DEBUG !== "false";
|
|
562
|
-
var ELICITATION_TIMEOUT = 3e5;
|
|
563
|
-
var MAX_AGENT_STEPS = 10;
|
|
564
|
-
var dbg = (...args) => {
|
|
565
|
-
if (DEBUG_ENABLED) console.log("[mcp/chat]", ...args);
|
|
566
|
-
};
|
|
567
|
-
try {
|
|
568
|
-
process.setMaxListeners?.(50);
|
|
569
|
-
} catch {
|
|
570
|
-
}
|
|
571
|
-
var pendingElicitations2 = /* @__PURE__ */ new Map();
|
|
572
|
-
var chat = new Hono6();
|
|
573
|
-
var createLlmModel = (modelDefinition, apiKey, ollamaBaseUrl) => {
|
|
574
|
-
if (!modelDefinition?.id || !modelDefinition?.provider) {
|
|
575
|
-
throw new Error(
|
|
576
|
-
`Invalid model definition: ${JSON.stringify(modelDefinition)}`
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
switch (modelDefinition.provider) {
|
|
580
|
-
case "anthropic":
|
|
581
|
-
return createAnthropic({ apiKey })(modelDefinition.id);
|
|
582
|
-
case "openai":
|
|
583
|
-
return createOpenAI({ apiKey })(modelDefinition.id);
|
|
584
|
-
case "deepseek":
|
|
585
|
-
return createOpenAI({ apiKey, baseURL: "https://api.deepseek.com/v1" })(
|
|
586
|
-
modelDefinition.id
|
|
587
|
-
);
|
|
588
|
-
case "ollama":
|
|
589
|
-
const baseUrl = ollamaBaseUrl || "http://localhost:11434";
|
|
590
|
-
return createOllama({
|
|
591
|
-
baseURL: `${baseUrl}`
|
|
592
|
-
})(modelDefinition.id, {
|
|
593
|
-
simulateStreaming: true
|
|
594
|
-
});
|
|
595
|
-
default:
|
|
596
|
-
throw new Error(
|
|
597
|
-
`Unsupported provider: ${modelDefinition.provider} for model: ${modelDefinition.id}`
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
};
|
|
601
|
-
var wrapToolsWithStreaming = (tools2, streamingContext) => {
|
|
602
|
-
const wrappedTools = {};
|
|
603
|
-
for (const [name, tool] of Object.entries(tools2)) {
|
|
604
|
-
wrappedTools[name] = {
|
|
605
|
-
...tool,
|
|
606
|
-
execute: async (params) => {
|
|
607
|
-
const currentToolCallId = ++streamingContext.toolCallId;
|
|
608
|
-
const startedAt = Date.now();
|
|
609
|
-
if (streamingContext.controller && streamingContext.encoder) {
|
|
610
|
-
streamingContext.controller.enqueue(
|
|
611
|
-
streamingContext.encoder.encode(
|
|
612
|
-
`data: ${JSON.stringify({
|
|
613
|
-
type: "tool_call",
|
|
614
|
-
toolCall: {
|
|
615
|
-
id: currentToolCallId,
|
|
616
|
-
name,
|
|
617
|
-
parameters: params,
|
|
618
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
619
|
-
status: "executing"
|
|
620
|
-
}
|
|
621
|
-
})}
|
|
622
|
-
|
|
623
|
-
`
|
|
624
|
-
)
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
dbg("Tool executing", { name, currentToolCallId, params });
|
|
628
|
-
try {
|
|
629
|
-
const result = await tool.execute(params);
|
|
630
|
-
dbg("Tool result", {
|
|
631
|
-
name,
|
|
632
|
-
currentToolCallId,
|
|
633
|
-
ms: Date.now() - startedAt
|
|
634
|
-
});
|
|
635
|
-
if (streamingContext.controller && streamingContext.encoder) {
|
|
636
|
-
streamingContext.controller.enqueue(
|
|
637
|
-
streamingContext.encoder.encode(
|
|
638
|
-
`data: ${JSON.stringify({
|
|
639
|
-
type: "tool_result",
|
|
640
|
-
toolResult: {
|
|
641
|
-
id: currentToolCallId,
|
|
642
|
-
toolCallId: currentToolCallId,
|
|
643
|
-
result,
|
|
644
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
645
|
-
}
|
|
646
|
-
})}
|
|
647
|
-
|
|
648
|
-
`
|
|
649
|
-
)
|
|
650
|
-
);
|
|
651
|
-
}
|
|
652
|
-
return result;
|
|
653
|
-
} catch (error) {
|
|
654
|
-
dbg("Tool error", {
|
|
655
|
-
name,
|
|
656
|
-
currentToolCallId,
|
|
657
|
-
error: error instanceof Error ? error.message : String(error)
|
|
658
|
-
});
|
|
659
|
-
if (streamingContext.controller && streamingContext.encoder) {
|
|
660
|
-
streamingContext.controller.enqueue(
|
|
661
|
-
streamingContext.encoder.encode(
|
|
662
|
-
`data: ${JSON.stringify({
|
|
663
|
-
type: "tool_result",
|
|
664
|
-
toolResult: {
|
|
665
|
-
id: currentToolCallId,
|
|
666
|
-
toolCallId: currentToolCallId,
|
|
667
|
-
error: error instanceof Error ? error.message : String(error),
|
|
668
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
669
|
-
}
|
|
670
|
-
})}
|
|
671
|
-
|
|
672
|
-
`
|
|
673
|
-
)
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
throw error;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
return wrappedTools;
|
|
682
|
-
};
|
|
683
|
-
var handleAgentStepFinish = (streamingContext, text, toolCalls, toolResults) => {
|
|
684
|
-
try {
|
|
685
|
-
if (toolCalls && Array.isArray(toolCalls)) {
|
|
686
|
-
for (const call of toolCalls) {
|
|
687
|
-
const currentToolCallId = ++streamingContext.toolCallId;
|
|
688
|
-
streamingContext.lastEmittedToolCallId = currentToolCallId;
|
|
689
|
-
if (streamingContext.controller && streamingContext.encoder) {
|
|
690
|
-
streamingContext.controller.enqueue(
|
|
691
|
-
streamingContext.encoder.encode(
|
|
692
|
-
`data: ${JSON.stringify({
|
|
693
|
-
type: "tool_call",
|
|
694
|
-
toolCall: {
|
|
695
|
-
id: currentToolCallId,
|
|
696
|
-
name: call.name || call.toolName,
|
|
697
|
-
parameters: call.params || call.args || {},
|
|
698
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
699
|
-
status: "executing"
|
|
700
|
-
}
|
|
701
|
-
})}
|
|
702
|
-
|
|
703
|
-
`
|
|
704
|
-
)
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
if (toolResults && Array.isArray(toolResults)) {
|
|
710
|
-
for (const result of toolResults) {
|
|
711
|
-
const currentToolCallId = streamingContext.lastEmittedToolCallId != null ? streamingContext.lastEmittedToolCallId : ++streamingContext.toolCallId;
|
|
712
|
-
if (streamingContext.controller && streamingContext.encoder) {
|
|
713
|
-
streamingContext.controller.enqueue(
|
|
714
|
-
streamingContext.encoder.encode(
|
|
715
|
-
`data: ${JSON.stringify({
|
|
716
|
-
type: "tool_result",
|
|
717
|
-
toolResult: {
|
|
718
|
-
id: currentToolCallId,
|
|
719
|
-
toolCallId: currentToolCallId,
|
|
720
|
-
result: result.result,
|
|
721
|
-
error: result.error,
|
|
722
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
723
|
-
}
|
|
724
|
-
})}
|
|
725
|
-
|
|
726
|
-
`
|
|
727
|
-
)
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
streamingContext.stepIndex = (streamingContext.stepIndex || 0) + 1;
|
|
733
|
-
if (streamingContext.controller && streamingContext.encoder) {
|
|
734
|
-
streamingContext.controller.enqueue(
|
|
735
|
-
streamingContext.encoder.encode(
|
|
736
|
-
`data: ${JSON.stringify({
|
|
737
|
-
type: "trace_step",
|
|
738
|
-
step: streamingContext.stepIndex,
|
|
739
|
-
text,
|
|
740
|
-
toolCalls: (toolCalls || []).map((c) => ({
|
|
741
|
-
name: c.name || c.toolName,
|
|
742
|
-
params: c.params || c.args || {}
|
|
743
|
-
})),
|
|
744
|
-
toolResults: (toolResults || []).map((r) => ({
|
|
745
|
-
result: r.result,
|
|
746
|
-
error: r.error
|
|
747
|
-
})),
|
|
748
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
749
|
-
})}
|
|
750
|
-
|
|
751
|
-
`
|
|
752
|
-
)
|
|
753
|
-
);
|
|
754
|
-
}
|
|
755
|
-
} catch (err) {
|
|
756
|
-
dbg("onStepFinish error", err);
|
|
757
|
-
}
|
|
758
|
-
};
|
|
759
|
-
var streamAgentResponse = async (streamingContext, stream) => {
|
|
760
|
-
let hasContent = false;
|
|
761
|
-
let chunkCount = 0;
|
|
762
|
-
for await (const chunk of stream.textStream) {
|
|
763
|
-
if (chunk && chunk.trim()) {
|
|
764
|
-
hasContent = true;
|
|
765
|
-
chunkCount++;
|
|
766
|
-
streamingContext.controller.enqueue(
|
|
767
|
-
streamingContext.encoder.encode(
|
|
768
|
-
`data: ${JSON.stringify({ type: "text", content: chunk })}
|
|
769
|
-
|
|
770
|
-
`
|
|
771
|
-
)
|
|
772
|
-
);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
dbg("Streaming finished", { hasContent, chunkCount });
|
|
776
|
-
return { hasContent, chunkCount };
|
|
777
|
-
};
|
|
778
|
-
var fallbackToCompletion = async (agent, messages, streamingContext, provider) => {
|
|
779
|
-
try {
|
|
780
|
-
const result = await agent.generate(messages, {
|
|
781
|
-
temperature: getDefaultTemperatureByProvider(provider)
|
|
782
|
-
});
|
|
783
|
-
if (result.text && result.text.trim()) {
|
|
784
|
-
streamingContext.controller.enqueue(
|
|
785
|
-
streamingContext.encoder.encode(
|
|
786
|
-
`data: ${JSON.stringify({
|
|
787
|
-
type: "text",
|
|
788
|
-
content: result.text
|
|
789
|
-
})}
|
|
790
|
-
|
|
791
|
-
`
|
|
792
|
-
)
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
} catch (fallbackErr) {
|
|
796
|
-
streamingContext.controller.enqueue(
|
|
797
|
-
streamingContext.encoder.encode(
|
|
798
|
-
`data: ${JSON.stringify({
|
|
799
|
-
type: "text",
|
|
800
|
-
content: "Failed to generate response. Please try again. ",
|
|
801
|
-
error: fallbackErr instanceof Error ? fallbackErr.message : "Unknown error"
|
|
802
|
-
})}
|
|
803
|
-
|
|
804
|
-
`
|
|
805
|
-
)
|
|
806
|
-
);
|
|
807
|
-
}
|
|
808
|
-
};
|
|
809
|
-
var createStreamingResponse = async (agent, messages, toolsets, streamingContext, provider) => {
|
|
810
|
-
const stream = await agent.stream(messages, {
|
|
811
|
-
maxSteps: MAX_AGENT_STEPS,
|
|
812
|
-
temperature: getDefaultTemperatureByProvider(provider),
|
|
813
|
-
toolsets,
|
|
814
|
-
onStepFinish: ({ text, toolCalls, toolResults }) => {
|
|
815
|
-
handleAgentStepFinish(streamingContext, text, toolCalls, toolResults);
|
|
816
|
-
}
|
|
817
|
-
});
|
|
818
|
-
const { hasContent } = await streamAgentResponse(streamingContext, stream);
|
|
819
|
-
if (!hasContent) {
|
|
820
|
-
dbg("No content from textStream; falling back to completion");
|
|
821
|
-
await fallbackToCompletion(agent, messages, streamingContext, provider);
|
|
822
|
-
}
|
|
823
|
-
streamingContext.controller.enqueue(
|
|
824
|
-
streamingContext.encoder.encode(
|
|
825
|
-
`data: ${JSON.stringify({
|
|
826
|
-
type: "elicitation_complete"
|
|
827
|
-
})}
|
|
828
|
-
|
|
829
|
-
`
|
|
830
|
-
)
|
|
831
|
-
);
|
|
832
|
-
streamingContext.controller.enqueue(
|
|
833
|
-
streamingContext.encoder.encode(`data: [DONE]
|
|
834
|
-
|
|
835
|
-
`)
|
|
836
|
-
);
|
|
837
|
-
};
|
|
838
|
-
chat.post("/", async (c) => {
|
|
839
|
-
const mcpClientManager = c.mcpJamClientManager;
|
|
840
|
-
try {
|
|
841
|
-
const requestData = await c.req.json();
|
|
842
|
-
const {
|
|
843
|
-
serverConfigs,
|
|
844
|
-
model,
|
|
845
|
-
provider,
|
|
846
|
-
apiKey,
|
|
847
|
-
systemPrompt,
|
|
848
|
-
messages,
|
|
849
|
-
ollamaBaseUrl,
|
|
850
|
-
action,
|
|
851
|
-
requestId,
|
|
852
|
-
response
|
|
853
|
-
} = requestData;
|
|
854
|
-
if (action === "elicitation_response") {
|
|
855
|
-
if (!requestId) {
|
|
856
|
-
return c.json(
|
|
857
|
-
{
|
|
858
|
-
success: false,
|
|
859
|
-
error: "requestId is required for elicitation_response action"
|
|
860
|
-
},
|
|
861
|
-
400
|
|
862
|
-
);
|
|
863
|
-
}
|
|
864
|
-
const pending = pendingElicitations2.get(requestId);
|
|
865
|
-
if (!pending) {
|
|
866
|
-
return c.json(
|
|
867
|
-
{
|
|
868
|
-
success: false,
|
|
869
|
-
error: "No pending elicitation found for this requestId"
|
|
870
|
-
},
|
|
871
|
-
404
|
|
872
|
-
);
|
|
873
|
-
}
|
|
874
|
-
pending.resolve(response);
|
|
875
|
-
pendingElicitations2.delete(requestId);
|
|
876
|
-
return c.json({ success: true });
|
|
877
|
-
}
|
|
878
|
-
if (!model?.id || !apiKey || !messages) {
|
|
879
|
-
return c.json(
|
|
880
|
-
{
|
|
881
|
-
success: false,
|
|
882
|
-
error: "model (with id), apiKey, and messages are required"
|
|
883
|
-
},
|
|
884
|
-
400
|
|
885
|
-
);
|
|
886
|
-
}
|
|
887
|
-
if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
|
|
888
|
-
return c.json(
|
|
889
|
-
{
|
|
890
|
-
success: false,
|
|
891
|
-
error: "No server configs provided"
|
|
892
|
-
},
|
|
893
|
-
400
|
|
894
|
-
);
|
|
895
|
-
}
|
|
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) {
|
|
910
|
-
return c.json(
|
|
911
|
-
{
|
|
912
|
-
success: false,
|
|
913
|
-
error: "Failed to connect to any servers",
|
|
914
|
-
details: serverErrors
|
|
915
|
-
},
|
|
916
|
-
400
|
|
917
|
-
);
|
|
918
|
-
}
|
|
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
|
-
}
|
|
926
|
-
const llmModel = createLlmModel(model, apiKey, ollamaBaseUrl);
|
|
927
|
-
const agent = new Agent({
|
|
928
|
-
name: "MCP Chat Agent",
|
|
929
|
-
instructions: systemPrompt || "You are a helpful assistant with access to MCP tools.",
|
|
930
|
-
model: llmModel,
|
|
931
|
-
tools: void 0
|
|
932
|
-
// Start without tools, add them in streaming context
|
|
933
|
-
});
|
|
934
|
-
const formattedMessages = messages.map((msg) => ({
|
|
935
|
-
role: msg.role,
|
|
936
|
-
content: msg.content
|
|
937
|
-
}));
|
|
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
|
-
}
|
|
955
|
-
dbg("Streaming start", {
|
|
956
|
-
connectedServers,
|
|
957
|
-
toolCount: allTools.length,
|
|
958
|
-
messageCount: formattedMessages.length
|
|
959
|
-
});
|
|
960
|
-
const encoder = new TextEncoder3();
|
|
961
|
-
const readableStream = new ReadableStream({
|
|
962
|
-
async start(controller) {
|
|
963
|
-
const streamingContext = {
|
|
964
|
-
controller,
|
|
965
|
-
encoder,
|
|
966
|
-
toolCallId: 0,
|
|
967
|
-
lastEmittedToolCallId: null,
|
|
968
|
-
stepIndex: 0
|
|
969
|
-
};
|
|
970
|
-
const flattenedTools = {};
|
|
971
|
-
Object.values(toolsByServer).forEach((serverTools) => {
|
|
972
|
-
Object.assign(flattenedTools, serverTools);
|
|
973
|
-
});
|
|
974
|
-
const streamingWrappedTools = wrapToolsWithStreaming(
|
|
975
|
-
flattenedTools,
|
|
976
|
-
streamingContext
|
|
977
|
-
);
|
|
978
|
-
const streamingAgent = new Agent({
|
|
979
|
-
name: agent.name,
|
|
980
|
-
instructions: agent.instructions,
|
|
981
|
-
model: agent.model,
|
|
982
|
-
tools: Object.keys(streamingWrappedTools).length > 0 ? streamingWrappedTools : void 0
|
|
983
|
-
});
|
|
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
|
-
)
|
|
1002
|
-
);
|
|
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
|
-
);
|
|
1022
|
-
} catch (error) {
|
|
1023
|
-
controller.enqueue(
|
|
1024
|
-
encoder.encode(
|
|
1025
|
-
`data: ${JSON.stringify({
|
|
1026
|
-
type: "error",
|
|
1027
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1028
|
-
})}
|
|
1029
|
-
|
|
1030
|
-
`
|
|
1031
|
-
)
|
|
1032
|
-
);
|
|
1033
|
-
} finally {
|
|
1034
|
-
mcpClientManager.clearElicitationCallback();
|
|
1035
|
-
controller.close();
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
});
|
|
1039
|
-
return new Response(readableStream, {
|
|
1040
|
-
headers: {
|
|
1041
|
-
"Content-Type": "text/event-stream",
|
|
1042
|
-
"Cache-Control": "no-cache",
|
|
1043
|
-
Connection: "keep-alive"
|
|
1044
|
-
}
|
|
1045
|
-
});
|
|
1046
|
-
} catch (error) {
|
|
1047
|
-
console.error("[mcp/chat] Error in chat API:", error);
|
|
1048
|
-
mcpClientManager.clearElicitationCallback();
|
|
1049
|
-
return c.json(
|
|
1050
|
-
{
|
|
1051
|
-
success: false,
|
|
1052
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1053
|
-
},
|
|
1054
|
-
500
|
|
1055
|
-
);
|
|
1056
|
-
}
|
|
1057
|
-
});
|
|
1058
|
-
var chat_default = chat;
|
|
1059
|
-
|
|
1060
|
-
// routes/mcp/tests.ts
|
|
1061
|
-
import { Hono as Hono7 } from "hono";
|
|
1062
|
-
import { writeFile, mkdir } from "fs/promises";
|
|
1063
|
-
import { join, dirname } from "path";
|
|
1064
|
-
import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
|
|
1065
|
-
import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
1066
|
-
import { createOllama as createOllama2 } from "ollama-ai-provider";
|
|
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
|
|
1244
|
-
var tests = new Hono7();
|
|
1245
|
-
tests.post("/generate", async (c) => {
|
|
1246
|
-
try {
|
|
1247
|
-
const body = await c.req.json();
|
|
1248
|
-
const test = body?.test;
|
|
1249
|
-
const servers2 = body?.servers;
|
|
1250
|
-
const model = body?.model;
|
|
1251
|
-
if (!test?.id || !test?.prompt || !servers2 || Object.keys(servers2).length === 0) {
|
|
1252
|
-
return c.json(
|
|
1253
|
-
{ success: false, error: "Missing test, servers, or prompt" },
|
|
1254
|
-
400
|
|
1255
|
-
);
|
|
1256
|
-
}
|
|
1257
|
-
const safeName = String(test.title || test.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
1258
|
-
const filename = `@TestAgent_${safeName || test.id}.ts`;
|
|
1259
|
-
const fileContents = `import { Agent } from "@mastra/core/agent";
|
|
1260
|
-
import { MCPClient } from "@mastra/mcp";
|
|
1261
|
-
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
1262
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
1263
|
-
import { createOllama } from "ollama-ai-provider";
|
|
1264
|
-
|
|
1265
|
-
const servers = ${JSON.stringify(servers2, null, 2)} as const;
|
|
1266
|
-
|
|
1267
|
-
function createModel() {
|
|
1268
|
-
const def = ${JSON.stringify(model || null)} as any;
|
|
1269
|
-
if (!def) throw new Error("Model not provided by UI when generating test agent");
|
|
1270
|
-
switch (def.provider) {
|
|
1271
|
-
case "anthropic": return createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY! })(def.id);
|
|
1272
|
-
case "openai": return createOpenAI({ apiKey: process.env.OPENAI_API_KEY! })(def.id);
|
|
1273
|
-
case "deepseek": return createOpenAI({ apiKey: process.env.DEEPSEEK_API_KEY!, baseURL: "https://api.deepseek.com/v1" })(def.id);
|
|
1274
|
-
case "ollama": return createOllama({ baseURL: process.env.OLLAMA_BASE_URL || "http://localhost:11434" })(def.id, { simulateStreaming: true });
|
|
1275
|
-
default: throw new Error("Unsupported provider: " + def.provider);
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
export const createTestAgent = async () => {
|
|
1280
|
-
const mcp = new MCPClient({ servers });
|
|
1281
|
-
const toolsets = await mcp.getToolsets();
|
|
1282
|
-
return new Agent({
|
|
1283
|
-
name: ${JSON.stringify(test.title || "Test Agent")},
|
|
1284
|
-
instructions: ${JSON.stringify(test.prompt)},
|
|
1285
|
-
model: createModel(),
|
|
1286
|
-
tools: undefined,
|
|
1287
|
-
defaultGenerateOptions: { toolChoice: "auto" }
|
|
1288
|
-
});
|
|
1289
|
-
};
|
|
1290
|
-
`;
|
|
1291
|
-
const targetPath = join(process.cwd(), "server", "agents", filename);
|
|
1292
|
-
await mkdir(dirname(targetPath), { recursive: true });
|
|
1293
|
-
await writeFile(targetPath, fileContents, "utf8");
|
|
1294
|
-
return c.json({ success: true, file: `server/agents/${filename}` });
|
|
1295
|
-
} catch (err) {
|
|
1296
|
-
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
1297
|
-
return c.json({ success: false, error: msg }, 500);
|
|
1298
|
-
}
|
|
1299
|
-
});
|
|
1300
|
-
var tests_default = tests;
|
|
1301
|
-
tests.post("/run-all", async (c) => {
|
|
1302
|
-
const encoder = new TextEncoder();
|
|
1303
|
-
try {
|
|
1304
|
-
let createModel2 = function(model) {
|
|
1305
|
-
switch (model.provider) {
|
|
1306
|
-
case "anthropic":
|
|
1307
|
-
return createAnthropic2({
|
|
1308
|
-
apiKey: providerApiKeys?.anthropic || process.env.ANTHROPIC_API_KEY || ""
|
|
1309
|
-
})(model.id);
|
|
1310
|
-
case "openai":
|
|
1311
|
-
return createOpenAI2({
|
|
1312
|
-
apiKey: providerApiKeys?.openai || process.env.OPENAI_API_KEY || ""
|
|
1313
|
-
})(model.id);
|
|
1314
|
-
case "deepseek":
|
|
1315
|
-
return createOpenAI2({
|
|
1316
|
-
apiKey: providerApiKeys?.deepseek || process.env.DEEPSEEK_API_KEY || "",
|
|
1317
|
-
baseURL: "https://api.deepseek.com/v1"
|
|
1318
|
-
})(model.id);
|
|
1319
|
-
case "ollama":
|
|
1320
|
-
return createOllama2({
|
|
1321
|
-
baseURL: ollamaBaseUrl || process.env.OLLAMA_BASE_URL || "http://localhost:11434"
|
|
1322
|
-
})(model.id, { simulateStreaming: true });
|
|
1323
|
-
default:
|
|
1324
|
-
throw new Error(`Unsupported provider: ${model.provider}`);
|
|
1325
|
-
}
|
|
1326
|
-
};
|
|
1327
|
-
var createModel = createModel2;
|
|
1328
|
-
const body = await c.req.json();
|
|
1329
|
-
const testsInput = body?.tests || [];
|
|
1330
|
-
const allServers = body?.allServers || {};
|
|
1331
|
-
const providerApiKeys = body?.providerApiKeys || {};
|
|
1332
|
-
const ollamaBaseUrl = body?.ollamaBaseUrl;
|
|
1333
|
-
const maxConcurrency = Math.max(
|
|
1334
|
-
1,
|
|
1335
|
-
Math.min(8, body?.concurrency ?? 5)
|
|
1336
|
-
);
|
|
1337
|
-
if (!Array.isArray(testsInput) || testsInput.length === 0) {
|
|
1338
|
-
return c.json({ success: false, error: "No tests provided" }, 400);
|
|
1339
|
-
}
|
|
1340
|
-
const readableStream = new ReadableStream({
|
|
1341
|
-
async start(controller) {
|
|
1342
|
-
let active = 0;
|
|
1343
|
-
let index = 0;
|
|
1344
|
-
let failed = false;
|
|
1345
|
-
const runNext = async () => {
|
|
1346
|
-
if (index >= testsInput.length) {
|
|
1347
|
-
if (active === 0) {
|
|
1348
|
-
controller.enqueue(
|
|
1349
|
-
encoder.encode(
|
|
1350
|
-
`data: ${JSON.stringify({ type: "run_complete", passed: !failed })}
|
|
1351
|
-
|
|
1352
|
-
`
|
|
1353
|
-
)
|
|
1354
|
-
);
|
|
1355
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
1356
|
-
|
|
1357
|
-
`));
|
|
1358
|
-
controller.close();
|
|
1359
|
-
}
|
|
1360
|
-
return;
|
|
1361
|
-
}
|
|
1362
|
-
const test = testsInput[index++];
|
|
1363
|
-
active++;
|
|
1364
|
-
(async () => {
|
|
1365
|
-
const calledTools = /* @__PURE__ */ new Set();
|
|
1366
|
-
const expectedSet = new Set(test.expectedTools || []);
|
|
1367
|
-
let step = 0;
|
|
1368
|
-
let client = null;
|
|
1369
|
-
try {
|
|
1370
|
-
let serverConfigs = {};
|
|
1371
|
-
if (test.selectedServers && test.selectedServers.length > 0) {
|
|
1372
|
-
for (const name of test.selectedServers) {
|
|
1373
|
-
if (allServers[name]) serverConfigs[name] = allServers[name];
|
|
1374
|
-
}
|
|
1375
|
-
} else {
|
|
1376
|
-
for (const [name, cfg] of Object.entries(allServers)) {
|
|
1377
|
-
serverConfigs[name] = cfg;
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
const validation = validateMultipleServerConfigs(serverConfigs);
|
|
1381
|
-
let finalServers = {};
|
|
1382
|
-
if (validation.success && validation.validConfigs) {
|
|
1383
|
-
finalServers = validation.validConfigs;
|
|
1384
|
-
} else if (validation.validConfigs && Object.keys(validation.validConfigs).length > 0) {
|
|
1385
|
-
finalServers = validation.validConfigs;
|
|
1386
|
-
} else {
|
|
1387
|
-
throw new Error("No valid MCP server configs for test");
|
|
1388
|
-
}
|
|
1389
|
-
client = createMCPClientWithMultipleConnections(finalServers);
|
|
1390
|
-
const model = createModel2(test.model);
|
|
1391
|
-
const agent = new Agent2({
|
|
1392
|
-
name: `TestAgent-${test.id}`,
|
|
1393
|
-
instructions: "You are a helpful assistant with access to MCP tools",
|
|
1394
|
-
model
|
|
1395
|
-
});
|
|
1396
|
-
const toolsets = await client.getToolsets();
|
|
1397
|
-
const stream = await agent.stream(
|
|
1398
|
-
[{ role: "user", content: test.prompt || "" }],
|
|
1399
|
-
{
|
|
1400
|
-
maxSteps: 10,
|
|
1401
|
-
toolsets,
|
|
1402
|
-
onStepFinish: ({ text, toolCalls, toolResults }) => {
|
|
1403
|
-
step += 1;
|
|
1404
|
-
(toolCalls || []).forEach((c2) => {
|
|
1405
|
-
const toolName = c2?.name || c2?.toolName;
|
|
1406
|
-
if (toolName) {
|
|
1407
|
-
calledTools.add(toolName);
|
|
1408
|
-
}
|
|
1409
|
-
});
|
|
1410
|
-
controller.enqueue(
|
|
1411
|
-
encoder.encode(
|
|
1412
|
-
`data: ${JSON.stringify({
|
|
1413
|
-
type: "trace_step",
|
|
1414
|
-
testId: test.id,
|
|
1415
|
-
step,
|
|
1416
|
-
text,
|
|
1417
|
-
toolCalls,
|
|
1418
|
-
toolResults
|
|
1419
|
-
})}
|
|
1420
|
-
|
|
1421
|
-
`
|
|
1422
|
-
)
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
);
|
|
1427
|
-
for await (const _ of stream.textStream) {
|
|
1428
|
-
}
|
|
1429
|
-
const called = Array.from(calledTools);
|
|
1430
|
-
const missing = Array.from(expectedSet).filter(
|
|
1431
|
-
(t) => !calledTools.has(t)
|
|
1432
|
-
);
|
|
1433
|
-
const unexpected = called.filter((t) => !expectedSet.has(t));
|
|
1434
|
-
const passed = missing.length === 0 && unexpected.length === 0;
|
|
1435
|
-
if (!passed) failed = true;
|
|
1436
|
-
controller.enqueue(
|
|
1437
|
-
encoder.encode(
|
|
1438
|
-
`data: ${JSON.stringify({ type: "result", testId: test.id, passed, calledTools: called, missingTools: missing, unexpectedTools: unexpected })}
|
|
1439
|
-
|
|
1440
|
-
`
|
|
1441
|
-
)
|
|
1442
|
-
);
|
|
1443
|
-
} catch (err) {
|
|
1444
|
-
failed = true;
|
|
1445
|
-
controller.enqueue(
|
|
1446
|
-
encoder.encode(
|
|
1447
|
-
`data: ${JSON.stringify({ type: "result", testId: test.id, passed: false, error: err?.message })}
|
|
1448
|
-
|
|
1449
|
-
`
|
|
1450
|
-
)
|
|
1451
|
-
);
|
|
1452
|
-
} finally {
|
|
1453
|
-
try {
|
|
1454
|
-
await client?.disconnect();
|
|
1455
|
-
} catch {
|
|
1456
|
-
}
|
|
1457
|
-
active--;
|
|
1458
|
-
runNext();
|
|
1459
|
-
}
|
|
1460
|
-
})();
|
|
1461
|
-
};
|
|
1462
|
-
for (let i = 0; i < Math.min(maxConcurrency, testsInput.length); i++) {
|
|
1463
|
-
runNext();
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
});
|
|
1467
|
-
return new Response(readableStream, {
|
|
1468
|
-
headers: {
|
|
1469
|
-
"Content-Type": "text/event-stream",
|
|
1470
|
-
"Cache-Control": "no-cache",
|
|
1471
|
-
Connection: "keep-alive"
|
|
1472
|
-
}
|
|
1473
|
-
});
|
|
1474
|
-
} catch (err) {
|
|
1475
|
-
return c.json(
|
|
1476
|
-
{ success: false, error: err?.message || "Unknown error" },
|
|
1477
|
-
500
|
|
1478
|
-
);
|
|
1479
|
-
}
|
|
1480
|
-
});
|
|
1481
|
-
|
|
1482
|
-
// routes/mcp/oauth.ts
|
|
1483
|
-
import { Hono as Hono8 } from "hono";
|
|
1484
|
-
var oauth = new Hono8();
|
|
1485
|
-
oauth.get("/metadata", async (c) => {
|
|
1486
|
-
try {
|
|
1487
|
-
const url = c.req.query("url");
|
|
1488
|
-
if (!url) {
|
|
1489
|
-
return c.json({ error: "Missing url parameter" }, 400);
|
|
1490
|
-
}
|
|
1491
|
-
let metadataUrl;
|
|
1492
|
-
try {
|
|
1493
|
-
metadataUrl = new URL(url);
|
|
1494
|
-
if (metadataUrl.protocol !== "https:") {
|
|
1495
|
-
return c.json({ error: "Only HTTPS URLs are allowed" }, 400);
|
|
1496
|
-
}
|
|
1497
|
-
} catch (error) {
|
|
1498
|
-
return c.json({ error: "Invalid URL format" }, 400);
|
|
1499
|
-
}
|
|
1500
|
-
const response = await fetch(metadataUrl.toString(), {
|
|
1501
|
-
method: "GET",
|
|
1502
|
-
headers: {
|
|
1503
|
-
Accept: "application/json",
|
|
1504
|
-
"User-Agent": "MCP-Inspector/1.0"
|
|
1505
|
-
}
|
|
1506
|
-
});
|
|
1507
|
-
if (!response.ok) {
|
|
1508
|
-
return c.json(
|
|
1509
|
-
{
|
|
1510
|
-
error: `Failed to fetch OAuth metadata: ${response.status} ${response.statusText}`
|
|
1511
|
-
},
|
|
1512
|
-
response.status
|
|
1513
|
-
);
|
|
1514
|
-
}
|
|
1515
|
-
const metadata = await response.json();
|
|
1516
|
-
return c.json(metadata);
|
|
1517
|
-
} catch (error) {
|
|
1518
|
-
console.error("OAuth metadata proxy error:", error);
|
|
1519
|
-
return c.json(
|
|
1520
|
-
{
|
|
1521
|
-
error: error instanceof Error ? error.message : "Unknown error occurred"
|
|
1522
|
-
},
|
|
1523
|
-
500
|
|
1524
|
-
);
|
|
1525
|
-
}
|
|
1526
|
-
});
|
|
1527
|
-
var oauth_default = oauth;
|
|
1528
|
-
|
|
1529
|
-
// routes/mcp/index.ts
|
|
1530
|
-
var mcp = new Hono9();
|
|
1531
|
-
mcp.get("/health", (c) => {
|
|
1532
|
-
return c.json({
|
|
1533
|
-
service: "MCP API",
|
|
1534
|
-
status: "ready",
|
|
1535
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1536
|
-
});
|
|
1537
|
-
});
|
|
1538
|
-
mcp.route("/chat", chat_default);
|
|
1539
|
-
mcp.route("/connect", connect_default);
|
|
1540
|
-
mcp.route("/servers", servers_default);
|
|
1541
|
-
mcp.route("/tools", tools_default);
|
|
1542
|
-
mcp.route("/tests", tests_default);
|
|
1543
|
-
mcp.route("/resources", resources_default);
|
|
1544
|
-
mcp.route("/prompts", prompts_default);
|
|
1545
|
-
mcp.route("/oauth", oauth_default);
|
|
1546
|
-
var mcp_default = mcp;
|
|
1547
|
-
|
|
1548
|
-
// services/mcpjam-client-manager.ts
|
|
1549
|
-
import { MCPClient as MCPClient2 } from "@mastra/mcp";
|
|
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}`;
|
|
1555
|
-
}
|
|
1556
|
-
var MCPJamClientManager = class {
|
|
1557
|
-
mcpClients = /* @__PURE__ */ new Map();
|
|
1558
|
-
statuses = /* @__PURE__ */ new Map();
|
|
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();
|
|
1564
|
-
toolRegistry = /* @__PURE__ */ new Map();
|
|
1565
|
-
resourceRegistry = /* @__PURE__ */ new Map();
|
|
1566
|
-
promptRegistry = /* @__PURE__ */ new Map();
|
|
1567
|
-
// Store for pending elicitation requests with Promise resolvers
|
|
1568
|
-
pendingElicitations = /* @__PURE__ */ new Map();
|
|
1569
|
-
// Optional callback for handling elicitation requests
|
|
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
|
-
}
|
|
1579
|
-
async connectToServer(serverId, serverConfig) {
|
|
1580
|
-
const pending = this.pendingConnections.get(serverId);
|
|
1581
|
-
if (pending) {
|
|
1582
|
-
await pending;
|
|
1583
|
-
return;
|
|
1584
|
-
}
|
|
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);
|
|
1596
|
-
}
|
|
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
|
-
});
|
|
1603
|
-
try {
|
|
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;
|
|
1624
|
-
}
|
|
1625
|
-
})().finally(() => {
|
|
1626
|
-
this.pendingConnections.delete(serverId);
|
|
1627
|
-
});
|
|
1628
|
-
this.pendingConnections.set(serverId, connectPromise);
|
|
1629
|
-
await connectPromise;
|
|
1630
|
-
}
|
|
1631
|
-
async disconnectFromServer(serverId) {
|
|
1632
|
-
const id = this.getServerUniqueId(serverId);
|
|
1633
|
-
if (!id) return;
|
|
1634
|
-
const client = this.mcpClients.get(id);
|
|
1635
|
-
if (client) {
|
|
1636
|
-
try {
|
|
1637
|
-
await client.disconnect();
|
|
1638
|
-
} catch {
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
this.mcpClients.delete(id);
|
|
1642
|
-
this.statuses.set(id, "disconnected");
|
|
1643
|
-
this.serverIdMapping.delete(serverId);
|
|
1644
|
-
for (const key of Array.from(this.toolRegistry.keys())) {
|
|
1645
|
-
const item = this.toolRegistry.get(key);
|
|
1646
|
-
if (item.serverId === id) this.toolRegistry.delete(key);
|
|
1647
|
-
}
|
|
1648
|
-
for (const key of Array.from(this.resourceRegistry.keys())) {
|
|
1649
|
-
const item = this.resourceRegistry.get(key);
|
|
1650
|
-
if (item.serverId === id) this.resourceRegistry.delete(key);
|
|
1651
|
-
}
|
|
1652
|
-
for (const key of Array.from(this.promptRegistry.keys())) {
|
|
1653
|
-
const item = this.promptRegistry.get(key);
|
|
1654
|
-
if (item.serverId === id) this.promptRegistry.delete(key);
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
getConnectionStatus(serverId) {
|
|
1658
|
-
const id = this.getServerUniqueId(serverId);
|
|
1659
|
-
return id ? this.statuses.get(id) || "disconnected" : "disconnected";
|
|
1660
|
-
}
|
|
1661
|
-
getConnectedServers() {
|
|
1662
|
-
const servers2 = {};
|
|
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)
|
|
1667
|
-
};
|
|
1668
|
-
}
|
|
1669
|
-
return servers2;
|
|
1670
|
-
}
|
|
1671
|
-
async discoverAllResources() {
|
|
1672
|
-
const serverIds = Array.from(this.mcpClients.keys());
|
|
1673
|
-
await Promise.all(serverIds.map((id) => this.discoverServerResources(id)));
|
|
1674
|
-
}
|
|
1675
|
-
async discoverServerResources(serverId) {
|
|
1676
|
-
const client = this.mcpClients.get(serverId);
|
|
1677
|
-
if (!client) return;
|
|
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}`, {
|
|
1685
|
-
name,
|
|
1686
|
-
description: tool.description,
|
|
1687
|
-
inputSchema: tool.inputSchema,
|
|
1688
|
-
outputSchema: tool.outputSchema,
|
|
1689
|
-
serverId
|
|
1690
|
-
});
|
|
1691
|
-
}
|
|
1692
|
-
try {
|
|
1693
|
-
const res = await client.resources.list();
|
|
1694
|
-
for (const [, list] of Object.entries(res)) {
|
|
1695
|
-
for (const r of list) {
|
|
1696
|
-
this.resourceRegistry.set(`${serverId}:${r.uri}`, {
|
|
1697
|
-
uri: r.uri,
|
|
1698
|
-
name: r.name,
|
|
1699
|
-
description: r.description,
|
|
1700
|
-
mimeType: r.mimeType,
|
|
1701
|
-
serverId
|
|
1702
|
-
});
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
} catch {
|
|
1706
|
-
}
|
|
1707
|
-
try {
|
|
1708
|
-
const prompts2 = await client.prompts.list();
|
|
1709
|
-
for (const [, list] of Object.entries(prompts2)) {
|
|
1710
|
-
for (const p of list) {
|
|
1711
|
-
this.promptRegistry.set(`${serverId}:${p.name}`, {
|
|
1712
|
-
name: p.name,
|
|
1713
|
-
description: p.description,
|
|
1714
|
-
arguments: p.arguments,
|
|
1715
|
-
serverId
|
|
1716
|
-
});
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
} catch {
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
getAvailableTools() {
|
|
1723
|
-
return Array.from(this.toolRegistry.values());
|
|
1724
|
-
}
|
|
1725
|
-
async getToolsetsForServer(serverId) {
|
|
1726
|
-
const id = this.getServerUniqueId(serverId);
|
|
1727
|
-
if (!id) {
|
|
1728
|
-
throw new Error(`No MCP client available for server: ${serverId}`);
|
|
1729
|
-
}
|
|
1730
|
-
const client = this.mcpClients.get(id);
|
|
1731
|
-
if (!client) {
|
|
1732
|
-
throw new Error(`No MCP client available for server: ${serverId}`);
|
|
1733
|
-
}
|
|
1734
|
-
const toolsets = await client.getToolsets();
|
|
1735
|
-
const flattenedTools = {};
|
|
1736
|
-
Object.values(toolsets).forEach((serverTools) => {
|
|
1737
|
-
Object.assign(flattenedTools, serverTools);
|
|
1738
|
-
});
|
|
1739
|
-
return flattenedTools;
|
|
1740
|
-
}
|
|
1741
|
-
getAvailableResources() {
|
|
1742
|
-
return Array.from(this.resourceRegistry.values());
|
|
1743
|
-
}
|
|
1744
|
-
getResourcesForServer(serverId) {
|
|
1745
|
-
const id = this.getServerUniqueId(serverId);
|
|
1746
|
-
if (!id) return [];
|
|
1747
|
-
return Array.from(this.resourceRegistry.values()).filter(
|
|
1748
|
-
(r) => r.serverId === id
|
|
1749
|
-
);
|
|
1750
|
-
}
|
|
1751
|
-
getAvailablePrompts() {
|
|
1752
|
-
return Array.from(this.promptRegistry.values());
|
|
1753
|
-
}
|
|
1754
|
-
getPromptsForServer(serverId) {
|
|
1755
|
-
const id = this.getServerUniqueId(serverId);
|
|
1756
|
-
if (!id) return [];
|
|
1757
|
-
return Array.from(this.promptRegistry.values()).filter(
|
|
1758
|
-
(p) => p.serverId === id
|
|
1759
|
-
);
|
|
1760
|
-
}
|
|
1761
|
-
async executeToolDirect(toolName, parameters = {}) {
|
|
1762
|
-
let serverId = "";
|
|
1763
|
-
let name = toolName;
|
|
1764
|
-
if (toolName.includes(":")) {
|
|
1765
|
-
const [sid, n] = toolName.split(":", 2);
|
|
1766
|
-
const mappedId = this.getServerUniqueId(sid);
|
|
1767
|
-
serverId = mappedId || (this.mcpClients.has(sid) ? sid : "");
|
|
1768
|
-
name = n;
|
|
1769
|
-
} else {
|
|
1770
|
-
for (const tool2 of this.toolRegistry.values()) {
|
|
1771
|
-
if (tool2.name === toolName) {
|
|
1772
|
-
serverId = tool2.serverId;
|
|
1773
|
-
name = toolName;
|
|
1774
|
-
break;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
if (!serverId) {
|
|
1779
|
-
for (const [clientServerId, client2] of this.mcpClients.entries()) {
|
|
1780
|
-
try {
|
|
1781
|
-
const toolsets2 = await client2.getToolsets();
|
|
1782
|
-
const flattenedTools2 = {};
|
|
1783
|
-
Object.values(toolsets2).forEach((serverTools) => {
|
|
1784
|
-
Object.assign(flattenedTools2, serverTools);
|
|
1785
|
-
});
|
|
1786
|
-
if (flattenedTools2[toolName]) {
|
|
1787
|
-
serverId = clientServerId;
|
|
1788
|
-
name = toolName;
|
|
1789
|
-
break;
|
|
1790
|
-
}
|
|
1791
|
-
} catch {
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
if (!serverId) {
|
|
1796
|
-
throw new Error(`Tool not found in any connected server: ${toolName}`);
|
|
1797
|
-
}
|
|
1798
|
-
const client = this.mcpClients.get(serverId);
|
|
1799
|
-
if (!client)
|
|
1800
|
-
throw new Error(`No MCP client available for server: ${serverId}`);
|
|
1801
|
-
const toolsets = await client.getToolsets();
|
|
1802
|
-
const flattenedTools = {};
|
|
1803
|
-
Object.values(toolsets).forEach((serverTools) => {
|
|
1804
|
-
Object.assign(flattenedTools, serverTools);
|
|
1805
|
-
});
|
|
1806
|
-
const tool = flattenedTools[name];
|
|
1807
|
-
if (!tool)
|
|
1808
|
-
throw new Error(`Tool '${name}' not found in server '${serverId}'`);
|
|
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;
|
|
1828
|
-
}
|
|
1829
|
-
async getResource(resourceUri, serverId) {
|
|
1830
|
-
let uri = resourceUri;
|
|
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);
|
|
1837
|
-
if (!client) throw new Error("No MCP client available");
|
|
1838
|
-
const content = await client.resources.read(resolvedServerId, uri);
|
|
1839
|
-
return { contents: content?.contents || [] };
|
|
1840
|
-
}
|
|
1841
|
-
async getPrompt(promptName, serverId, args) {
|
|
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);
|
|
1848
|
-
if (!client) throw new Error("No MCP client available");
|
|
1849
|
-
const content = await client.prompts.get({
|
|
1850
|
-
serverName: resolvedServerId,
|
|
1851
|
-
name: promptName,
|
|
1852
|
-
args: args || {}
|
|
1853
|
-
});
|
|
1854
|
-
return { content };
|
|
1855
|
-
}
|
|
1856
|
-
/**
|
|
1857
|
-
* Handles elicitation requests from MCP servers during direct tool execution
|
|
1858
|
-
*/
|
|
1859
|
-
async handleElicitationRequest(elicitationRequest) {
|
|
1860
|
-
const requestId = `elicit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1861
|
-
return new Promise((resolve, reject) => {
|
|
1862
|
-
this.pendingElicitations.set(requestId, { resolve, reject });
|
|
1863
|
-
if (this.elicitationCallback) {
|
|
1864
|
-
this.elicitationCallback({
|
|
1865
|
-
requestId,
|
|
1866
|
-
message: elicitationRequest.message,
|
|
1867
|
-
schema: elicitationRequest.requestedSchema
|
|
1868
|
-
}).then(resolve).catch(reject);
|
|
1869
|
-
} else {
|
|
1870
|
-
const error = new Error("ELICITATION_REQUIRED");
|
|
1871
|
-
error.elicitationRequest = {
|
|
1872
|
-
requestId,
|
|
1873
|
-
message: elicitationRequest.message,
|
|
1874
|
-
schema: elicitationRequest.requestedSchema
|
|
1875
|
-
};
|
|
1876
|
-
reject(error);
|
|
1877
|
-
}
|
|
1878
|
-
});
|
|
1879
|
-
}
|
|
1880
|
-
/**
|
|
1881
|
-
* Responds to a pending elicitation request
|
|
1882
|
-
*/
|
|
1883
|
-
respondToElicitation(requestId, response) {
|
|
1884
|
-
const pending = this.pendingElicitations.get(requestId);
|
|
1885
|
-
if (!pending) {
|
|
1886
|
-
return false;
|
|
1887
|
-
}
|
|
1888
|
-
pending.resolve(response);
|
|
1889
|
-
this.pendingElicitations.delete(requestId);
|
|
1890
|
-
return true;
|
|
1891
|
-
}
|
|
1892
|
-
/**
|
|
1893
|
-
* Gets the pending elicitations map for external access
|
|
1894
|
-
*/
|
|
1895
|
-
getPendingElicitations() {
|
|
1896
|
-
return this.pendingElicitations;
|
|
1897
|
-
}
|
|
1898
|
-
/**
|
|
1899
|
-
* Sets a callback to handle elicitation requests
|
|
1900
|
-
*/
|
|
1901
|
-
setElicitationCallback(callback) {
|
|
1902
|
-
this.elicitationCallback = callback;
|
|
1903
|
-
}
|
|
1904
|
-
/**
|
|
1905
|
-
* Clears the elicitation callback
|
|
1906
|
-
*/
|
|
1907
|
-
clearElicitationCallback() {
|
|
1908
|
-
this.elicitationCallback = void 0;
|
|
1909
|
-
}
|
|
1910
|
-
};
|
|
1911
|
-
|
|
1912
|
-
// index.ts
|
|
1913
|
-
function logBox(content, title) {
|
|
1914
|
-
const lines = content.split("\n");
|
|
1915
|
-
const maxLength = Math.max(...lines.map((line) => line.length));
|
|
1916
|
-
const width = maxLength + 4;
|
|
1917
|
-
console.log("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
1918
|
-
if (title) {
|
|
1919
|
-
const titlePadding = Math.floor((width - title.length - 2) / 2);
|
|
1920
|
-
console.log(
|
|
1921
|
-
"\u2502" + " ".repeat(titlePadding) + title + " ".repeat(width - title.length - titlePadding) + "\u2502"
|
|
1922
|
-
);
|
|
1923
|
-
console.log("\u251C" + "\u2500".repeat(width) + "\u2524");
|
|
1924
|
-
}
|
|
1925
|
-
lines.forEach((line) => {
|
|
1926
|
-
const padding = width - line.length - 2;
|
|
1927
|
-
console.log("\u2502 " + line + " ".repeat(padding) + " \u2502");
|
|
1928
|
-
});
|
|
1929
|
-
console.log("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
1930
|
-
}
|
|
1931
|
-
function getMCPConfigFromEnv() {
|
|
1932
|
-
const command = process.env.MCP_SERVER_COMMAND;
|
|
1933
|
-
if (!command) {
|
|
1934
|
-
return null;
|
|
1935
|
-
}
|
|
1936
|
-
const argsString = process.env.MCP_SERVER_ARGS;
|
|
1937
|
-
const args = argsString ? JSON.parse(argsString) : [];
|
|
1938
|
-
return {
|
|
1939
|
-
command,
|
|
1940
|
-
args,
|
|
1941
|
-
name: "CLI Server"
|
|
1942
|
-
// Default name for CLI-provided servers
|
|
1943
|
-
};
|
|
1944
|
-
}
|
|
1945
|
-
var app = new Hono10();
|
|
1946
|
-
var mcpJamClientManager = new MCPJamClientManager();
|
|
1947
|
-
app.use("*", async (c, next) => {
|
|
1948
|
-
c.mcpJamClientManager = mcpJamClientManager;
|
|
1949
|
-
await next();
|
|
1950
|
-
});
|
|
1951
|
-
app.use("*", logger());
|
|
1952
|
-
var serverPort = process.env.PORT || "3001";
|
|
1953
|
-
var corsOrigins = [
|
|
1954
|
-
`http://localhost:${serverPort}`,
|
|
1955
|
-
"http://localhost:3000",
|
|
1956
|
-
// Keep for development
|
|
1957
|
-
"http://localhost:3001"
|
|
1958
|
-
// Keep for development
|
|
1959
|
-
];
|
|
1960
|
-
app.use(
|
|
1961
|
-
"*",
|
|
1962
|
-
cors({
|
|
1963
|
-
origin: corsOrigins,
|
|
1964
|
-
credentials: true
|
|
1965
|
-
})
|
|
1966
|
-
);
|
|
1967
|
-
app.route("/api/mcp", mcp_default);
|
|
1968
|
-
app.get("/health", (c) => {
|
|
1969
|
-
return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1970
|
-
});
|
|
1971
|
-
app.get("/api/mcp-cli-config", (c) => {
|
|
1972
|
-
const mcpConfig = getMCPConfigFromEnv();
|
|
1973
|
-
return c.json({ config: mcpConfig });
|
|
1974
|
-
});
|
|
1975
|
-
if (process.env.NODE_ENV === "production") {
|
|
1976
|
-
app.use("/*", serveStatic({ root: "./dist/client" }));
|
|
1977
|
-
app.get("*", async (c) => {
|
|
1978
|
-
const path = c.req.path;
|
|
1979
|
-
if (path.startsWith("/api/")) {
|
|
1980
|
-
return c.notFound();
|
|
1981
|
-
}
|
|
1982
|
-
const indexPath = join2(process.cwd(), "dist", "client", "index.html");
|
|
1983
|
-
let htmlContent = readFileSync(indexPath, "utf-8");
|
|
1984
|
-
const mcpConfig = getMCPConfigFromEnv();
|
|
1985
|
-
if (mcpConfig) {
|
|
1986
|
-
const configScript = `<script>window.MCP_CLI_CONFIG = ${JSON.stringify(mcpConfig)};</script>`;
|
|
1987
|
-
htmlContent = htmlContent.replace("</head>", `${configScript}</head>`);
|
|
1988
|
-
}
|
|
1989
|
-
return c.html(htmlContent);
|
|
1990
|
-
});
|
|
1991
|
-
} else {
|
|
1992
|
-
app.get("/", (c) => {
|
|
1993
|
-
return c.json({
|
|
1994
|
-
message: "MCPJam API Server",
|
|
1995
|
-
environment: "development",
|
|
1996
|
-
frontend: `http://localhost:${serverPort}`
|
|
1997
|
-
});
|
|
1998
|
-
});
|
|
1999
|
-
}
|
|
2000
|
-
var port = parseInt(process.env.PORT || "3001");
|
|
2001
|
-
logBox(`http://localhost:${port}`, "\u{1F680} Inspector Launched");
|
|
2002
|
-
var server = serve({
|
|
2003
|
-
fetch: app.fetch,
|
|
2004
|
-
port,
|
|
2005
|
-
hostname: "0.0.0.0"
|
|
2006
|
-
// Bind to all interfaces for Docker
|
|
2007
|
-
});
|
|
2008
|
-
process.on("SIGINT", () => {
|
|
2009
|
-
console.log("\n\u{1F6D1} Shutting down gracefully...");
|
|
2010
|
-
server.close();
|
|
2011
|
-
process.exit(0);
|
|
2012
|
-
});
|
|
2013
|
-
process.on("SIGTERM", () => {
|
|
2014
|
-
console.log("\n\u{1F6D1} Shutting down gracefully...");
|
|
2015
|
-
server.close();
|
|
2016
|
-
process.exit(0);
|
|
2017
|
-
});
|
|
2018
|
-
var index_default = app;
|
|
2019
|
-
export {
|
|
2020
|
-
index_default as default
|
|
2021
|
-
};
|
|
2022
|
-
//# sourceMappingURL=index.js.map
|