@mcpjam/inspector 0.9.4 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,213 +1,58 @@
1
1
  // index.ts
2
2
  import { serve } from "@hono/node-server";
3
- import { Hono as Hono8 } from "hono";
3
+ import { Hono as Hono10 } from "hono";
4
4
  import { cors } from "hono/cors";
5
5
  import { logger } from "hono/logger";
6
6
  import { serveStatic } from "@hono/node-server/serve-static";
7
7
  import { readFileSync } from "fs";
8
- import { join } from "path";
8
+ import { join as join2 } from "path";
9
9
 
10
10
  // routes/mcp/index.ts
11
- import { Hono as Hono7 } from "hono";
11
+ import { Hono as Hono9 } from "hono";
12
12
 
13
13
  // routes/mcp/connect.ts
14
14
  import { Hono } from "hono";
15
-
16
- // utils/mcp-utils.ts
17
- import { MCPClient } from "@mastra/mcp";
18
- function validateServerConfig(serverConfig) {
19
- if (!serverConfig) {
20
- return {
21
- success: false,
22
- error: {
23
- message: "Server configuration is required",
24
- status: 400
25
- }
26
- };
27
- }
28
- const config = { ...serverConfig };
29
- if (config.url) {
30
- try {
31
- if (typeof config.url === "string") {
32
- const parsed = new URL(config.url);
33
- parsed.search = "";
34
- parsed.hash = "";
35
- config.url = parsed;
36
- } else if (typeof config.url === "object" && !config.url.href) {
37
- return {
38
- success: false,
39
- error: {
40
- message: "Invalid URL configuration",
41
- status: 400
42
- }
43
- };
44
- }
45
- if (config.oauth?.access_token) {
46
- const authHeaders = {
47
- Authorization: `Bearer ${config.oauth.access_token}`,
48
- ...config.requestInit?.headers || {}
49
- };
50
- config.requestInit = {
51
- ...config.requestInit,
52
- headers: authHeaders
53
- };
54
- config.eventSourceInit = {
55
- fetch(input, init) {
56
- const headers = new Headers(init?.headers || {});
57
- headers.set(
58
- "Authorization",
59
- `Bearer ${config.oauth.access_token}`
60
- );
61
- if (config.requestInit?.headers) {
62
- const requestHeaders = new Headers(config.requestInit.headers);
63
- requestHeaders.forEach((value, key) => {
64
- if (key.toLowerCase() !== "authorization") {
65
- headers.set(key, value);
66
- }
67
- });
68
- }
69
- return fetch(input, {
70
- ...init,
71
- headers
72
- });
73
- }
74
- };
75
- } else if (config.requestInit?.headers) {
76
- config.eventSourceInit = {
77
- fetch(input, init) {
78
- const headers = new Headers(init?.headers || {});
79
- const requestHeaders = new Headers(config.requestInit.headers);
80
- requestHeaders.forEach((value, key) => {
81
- headers.set(key, value);
82
- });
83
- return fetch(input, {
84
- ...init,
85
- headers
86
- });
87
- }
88
- };
89
- }
90
- } catch (error) {
91
- return {
92
- success: false,
93
- error: {
94
- message: `Invalid URL format: ${error}`,
95
- status: 400
96
- }
97
- };
98
- }
99
- }
100
- return {
101
- success: true,
102
- config
103
- };
104
- }
105
- function createMCPClient(config, id) {
106
- return new MCPClient({
107
- id,
108
- servers: {
109
- server: config
110
- }
111
- });
112
- }
113
- var validateMultipleServerConfigs = (serverConfigs) => {
114
- if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
115
- return {
116
- success: false,
117
- error: {
118
- message: "At least one server configuration is required",
119
- status: 400
120
- }
121
- };
122
- }
123
- const validConfigs = {};
124
- const errors = {};
125
- let hasErrors = false;
126
- for (const [serverName, serverConfig] of Object.entries(serverConfigs)) {
127
- const validationResult = validateServerConfig(serverConfig);
128
- if (validationResult.success && validationResult.config) {
129
- validConfigs[serverName] = validationResult.config;
130
- } else {
131
- hasErrors = true;
132
- let errorMessage = "Configuration validation failed";
133
- if (validationResult.error) {
134
- errorMessage = validationResult.error.message;
135
- }
136
- errors[serverName] = errorMessage;
137
- }
138
- }
139
- if (!hasErrors) {
140
- return {
141
- success: true,
142
- validConfigs
143
- };
144
- }
145
- if (Object.keys(validConfigs).length > 0) {
146
- return {
147
- success: false,
148
- validConfigs,
149
- errors
150
- };
151
- }
152
- return {
153
- success: false,
154
- errors,
155
- error: {
156
- message: "All server configurations failed validation",
157
- status: 400
158
- }
159
- };
160
- };
161
- function createMCPClientWithMultipleConnections(serverConfigs) {
162
- const normalizedConfigs = {};
163
- for (const [serverName, config] of Object.entries(serverConfigs)) {
164
- const normalizedName = normalizeServerConfigName(serverName);
165
- normalizedConfigs[normalizedName] = config;
166
- }
167
- return new MCPClient({
168
- id: `chat-${Date.now()}`,
169
- servers: normalizedConfigs
170
- });
171
- }
172
- function normalizeServerConfigName(serverName) {
173
- return serverName.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
174
- }
175
-
176
- // routes/mcp/connect.ts
177
15
  var connect = new Hono();
178
16
  connect.post("/", async (c) => {
179
17
  try {
180
- const { serverConfig } = await c.req.json();
181
- const validation = validateServerConfig(serverConfig);
182
- if (!validation.success) {
183
- const error = validation.error;
18
+ const { serverConfig, serverId } = await c.req.json();
19
+ if (!serverConfig) {
184
20
  return c.json(
185
21
  {
186
22
  success: false,
187
- error: error.message
23
+ error: "serverConfig is required"
188
24
  },
189
- error.status
25
+ 400
190
26
  );
191
27
  }
192
- let client;
193
- try {
194
- client = createMCPClient(validation.config, `test-${Date.now()}`);
195
- } catch (error) {
28
+ if (!serverId) {
196
29
  return c.json(
197
30
  {
198
31
  success: false,
199
- error: `Failed to create a MCP client. Please double check your server configuration: ${JSON.stringify(serverConfig)}`,
200
- details: error instanceof Error ? error.message : "Unknown error"
32
+ error: "serverId is required"
201
33
  },
202
- 500
34
+ 400
203
35
  );
204
36
  }
37
+ const mcpClientManager = c.mcpJamClientManager;
205
38
  try {
206
- await client.getTools();
207
- await client.disconnect();
208
- return c.json({
209
- success: true
210
- });
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
+ }
211
56
  } catch (error) {
212
57
  return c.json(
213
58
  {
@@ -231,16 +76,119 @@ connect.post("/", async (c) => {
231
76
  });
232
77
  var connect_default = connect;
233
78
 
234
- // routes/mcp/tools.ts
79
+ // routes/mcp/servers.ts
235
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";
236
187
  import { zodToJsonSchema } from "zod-to-json-schema";
237
- import { TextEncoder } from "util";
238
- var tools = new Hono2();
188
+ import { TextEncoder as TextEncoder2 } from "util";
189
+ var tools = new Hono3();
239
190
  var pendingElicitations = /* @__PURE__ */ new Map();
240
191
  tools.post("/", async (c) => {
241
- let client = null;
242
- let encoder = null;
243
- let streamController = null;
244
192
  let action;
245
193
  let toolName;
246
194
  try {
@@ -267,8 +215,18 @@ tools.post("/", async (c) => {
267
215
  400
268
216
  );
269
217
  }
270
- const pending = pendingElicitations.get(requestId);
271
- if (!pending) {
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
+ }
272
230
  return c.json(
273
231
  {
274
232
  success: false,
@@ -277,177 +235,171 @@ tools.post("/", async (c) => {
277
235
  404
278
236
  );
279
237
  }
280
- pending.resolve(response);
281
- pendingElicitations.delete(requestId);
282
238
  return c.json({ success: true });
283
239
  }
284
- const validation = validateServerConfig(serverConfig);
285
- if (!validation.success) {
286
- return c.json(
287
- { success: false, error: validation.error.message },
288
- validation.error.status
289
- );
290
- }
291
- encoder = new TextEncoder();
240
+ const encoder = new TextEncoder2();
292
241
  const readableStream = new ReadableStream({
293
242
  async start(controller) {
294
- streamController = controller;
295
243
  try {
296
- const clientId = `tools-${action}-${Date.now()}`;
297
- client = createMCPClient(validation.config, clientId);
298
- if (action === "list") {
244
+ if (!serverConfig) {
299
245
  controller.enqueue(
300
246
  encoder.encode(
301
- `data: ${JSON.stringify({
302
- type: "tools_loading",
303
- message: "Fetching tools from server..."
304
- })}
247
+ `data: ${JSON.stringify({ type: "tool_error", error: "serverConfig is required" })}
305
248
 
306
249
  `
307
250
  )
308
251
  );
309
- const tools2 = await client.getTools();
310
- const toolsWithJsonSchema = Object.fromEntries(
311
- Object.entries(tools2).map(([toolName2, tool]) => {
312
- return [
313
- toolName2,
314
- {
315
- ...tool,
316
- inputSchema: zodToJsonSchema(
317
- tool.inputSchema
318
- )
319
- }
320
- ];
321
- })
322
- );
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;
323
263
  controller.enqueue(
324
264
  encoder.encode(
325
265
  `data: ${JSON.stringify({
326
- type: "tools_list",
327
- tools: toolsWithJsonSchema
266
+ type: "elicitation_request",
267
+ requestId: requestId2,
268
+ message,
269
+ schema,
270
+ toolName: toolName || "unknown",
271
+ timestamp: /* @__PURE__ */ new Date()
328
272
  })}
329
273
 
330
274
  `
331
275
  )
332
276
  );
333
- } else if (action === "execute") {
334
- if (!toolName) {
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
+ }
335
313
  controller.enqueue(
336
314
  encoder.encode(
337
- `data: ${JSON.stringify({
338
- type: "tool_error",
339
- error: "Tool name is required for execution"
340
- })}
315
+ `data: ${JSON.stringify({ type: "tools_list", tools: toolsWithJsonSchema })}
341
316
 
342
317
  `
343
318
  )
344
319
  );
320
+ controller.enqueue(encoder.encode(`data: [DONE]
321
+
322
+ `));
323
+ controller.close();
345
324
  return;
346
- }
347
- controller.enqueue(
348
- encoder.encode(
349
- `data: ${JSON.stringify({
350
- type: "tool_executing",
351
- toolName,
352
- parameters: parameters || {},
353
- message: "Executing tool..."
354
- })}
325
+ } catch (err) {
326
+ controller.enqueue(
327
+ encoder.encode(
328
+ `data: ${JSON.stringify({ type: "tool_error", error: err instanceof Error ? err.message : String(err) })}
355
329
 
356
330
  `
357
- )
358
- );
359
- const tools2 = await client.getTools();
360
- const tool = tools2[toolName];
361
- if (!tool) {
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) {
362
342
  controller.enqueue(
363
343
  encoder.encode(
364
- `data: ${JSON.stringify({
365
- type: "tool_error",
366
- error: `Tool '${toolName}' not found`
367
- })}
344
+ `data: ${JSON.stringify({ type: "tool_error", error: "Tool name is required for execution" })}
368
345
 
369
346
  `
370
347
  )
371
348
  );
349
+ controller.enqueue(encoder.encode(`data: [DONE]
350
+
351
+ `));
352
+ controller.close();
372
353
  return;
373
354
  }
374
- const toolArgs = parameters && typeof parameters === "object" ? parameters : {};
375
- const elicitationHandler = async (elicitationRequest) => {
376
- const requestId2 = `elicit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
377
- if (streamController && encoder) {
378
- streamController.enqueue(
379
- encoder.encode(
380
- `data: ${JSON.stringify({
381
- type: "elicitation_request",
382
- requestId: requestId2,
383
- message: elicitationRequest.message,
384
- schema: elicitationRequest.requestedSchema,
385
- timestamp: /* @__PURE__ */ new Date()
386
- })}
355
+ controller.enqueue(
356
+ encoder.encode(
357
+ `data: ${JSON.stringify({ type: "tool_executing", toolName, parameters: parameters || {}, message: "Executing tool..." })}
387
358
 
388
359
  `
389
- )
390
- );
391
- }
392
- return new Promise((resolve, reject) => {
393
- pendingElicitations.set(requestId2, { resolve, reject });
394
- setTimeout(() => {
395
- if (pendingElicitations.has(requestId2)) {
396
- pendingElicitations.delete(requestId2);
397
- reject(new Error("Elicitation timeout"));
398
- }
399
- }, 3e5);
400
- });
401
- };
402
- if (client.elicitation && client.elicitation.onRequest) {
403
- const serverName = "server";
404
- client.elicitation.onRequest(serverName, elicitationHandler);
405
- }
406
- const result = await tool.execute({
407
- context: toolArgs
408
- });
360
+ )
361
+ );
362
+ const exec = await mcpJamClientManager2.executeToolDirect(
363
+ toolName,
364
+ parameters || {}
365
+ );
409
366
  controller.enqueue(
410
367
  encoder.encode(
411
- `data: ${JSON.stringify({
412
- type: "tool_result",
413
- toolName,
414
- result
415
- })}
368
+ `data: ${JSON.stringify({ type: "tool_result", toolName, result: exec.result })}
416
369
 
417
370
  `
418
371
  )
419
372
  );
420
373
  controller.enqueue(
421
374
  encoder.encode(
422
- `data: ${JSON.stringify({
423
- type: "elicitation_complete",
424
- toolName
425
- })}
375
+ `data: ${JSON.stringify({ type: "elicitation_complete", toolName })}
426
376
 
427
377
  `
428
378
  )
429
379
  );
430
- }
431
- controller.enqueue(encoder.encode(`data: [DONE]
380
+ controller.enqueue(encoder.encode(`data: [DONE]
432
381
 
433
382
  `));
434
- } catch (error) {
435
- const errorMsg = error instanceof Error ? error.message : "Unknown error";
383
+ controller.close();
384
+ return;
385
+ }
386
+ } catch (err) {
436
387
  controller.enqueue(
437
388
  encoder.encode(
438
- `data: ${JSON.stringify({
439
- type: "tool_error",
440
- error: errorMsg
441
- })}
389
+ `data: ${JSON.stringify({ type: "tool_error", error: err instanceof Error ? err.message : String(err) })}
442
390
 
443
391
  `
444
392
  )
445
393
  );
394
+ controller.enqueue(encoder.encode(`data: [DONE]
395
+
396
+ `));
397
+ controller.close();
446
398
  } finally {
447
- if (client) {
448
- await client.disconnect();
399
+ const mcpJamClientManager2 = c.mcpJamClientManager;
400
+ if (mcpJamClientManager2) {
401
+ mcpJamClientManager2.clearElicitationCallback();
449
402
  }
450
- controller.close();
451
403
  }
452
404
  }
453
405
  });
@@ -460,12 +412,6 @@ tools.post("/", async (c) => {
460
412
  });
461
413
  } catch (error) {
462
414
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
463
- if (client) {
464
- try {
465
- await client.disconnect();
466
- } catch (cleanupError) {
467
- }
468
- }
469
415
  return c.json(
470
416
  {
471
417
  success: false,
@@ -478,30 +424,17 @@ tools.post("/", async (c) => {
478
424
  var tools_default = tools;
479
425
 
480
426
  // routes/mcp/resources.ts
481
- import { Hono as Hono3 } from "hono";
482
- var resources = new Hono3();
427
+ import { Hono as Hono4 } from "hono";
428
+ var resources = new Hono4();
483
429
  resources.post("/list", async (c) => {
484
430
  try {
485
- const { serverConfig } = await c.req.json();
486
- const validation = validateServerConfig(serverConfig);
487
- if (!validation.success) {
488
- return c.json(
489
- { success: false, error: validation.error.message },
490
- validation.error.status
491
- );
492
- }
493
- const client = createMCPClient(
494
- validation.config,
495
- `resources-list-${Date.now()}`
496
- );
497
- try {
498
- const resources2 = await client.resources.list();
499
- await client.disconnect();
500
- return c.json({ resources: resources2 });
501
- } catch (error) {
502
- await client.disconnect();
503
- throw error;
431
+ const { serverId } = await c.req.json();
432
+ if (!serverId) {
433
+ return c.json({ success: false, error: "serverId is required" }, 400);
504
434
  }
435
+ const mcpClientManager = c.mcpJamClientManager;
436
+ const serverResources = mcpClientManager.getResourcesForServer(serverId);
437
+ return c.json({ resources: { [serverId]: serverResources } });
505
438
  } catch (error) {
506
439
  console.error("Error fetching resources:", error);
507
440
  return c.json(
@@ -515,13 +448,9 @@ resources.post("/list", async (c) => {
515
448
  });
516
449
  resources.post("/read", async (c) => {
517
450
  try {
518
- const { serverConfig, uri } = await c.req.json();
519
- const validation = validateServerConfig(serverConfig);
520
- if (!validation.success) {
521
- return c.json(
522
- { success: false, error: validation.error.message },
523
- validation.error.status
524
- );
451
+ const { serverId, uri } = await c.req.json();
452
+ if (!serverId) {
453
+ return c.json({ success: false, error: "serverId is required" }, 400);
525
454
  }
526
455
  if (!uri) {
527
456
  return c.json(
@@ -532,18 +461,9 @@ resources.post("/read", async (c) => {
532
461
  400
533
462
  );
534
463
  }
535
- const client = createMCPClient(
536
- validation.config,
537
- `resources-read-${Date.now()}`
538
- );
539
- try {
540
- const content = await client.resources.read("server", uri);
541
- await client.disconnect();
542
- return c.json({ content });
543
- } catch (error) {
544
- await client.disconnect();
545
- throw error;
546
- }
464
+ const mcpClientManager = c.mcpJamClientManager;
465
+ const content = await mcpClientManager.getResource(uri, serverId);
466
+ return c.json({ content });
547
467
  } catch (error) {
548
468
  console.error("Error reading resource:", error);
549
469
  return c.json(
@@ -558,30 +478,17 @@ resources.post("/read", async (c) => {
558
478
  var resources_default = resources;
559
479
 
560
480
  // routes/mcp/prompts.ts
561
- import { Hono as Hono4 } from "hono";
562
- var prompts = new Hono4();
481
+ import { Hono as Hono5 } from "hono";
482
+ var prompts = new Hono5();
563
483
  prompts.post("/list", async (c) => {
564
484
  try {
565
- const { serverConfig } = await c.req.json();
566
- const validation = validateServerConfig(serverConfig);
567
- if (!validation.success) {
568
- return c.json(
569
- { success: false, error: validation.error.message },
570
- validation.error.status
571
- );
572
- }
573
- const client = createMCPClient(
574
- validation.config,
575
- `prompts-list-${Date.now()}`
576
- );
577
- try {
578
- const prompts2 = await client.prompts.list();
579
- await client.disconnect();
580
- return c.json({ prompts: prompts2 });
581
- } catch (error) {
582
- await client.disconnect();
583
- throw error;
485
+ const { serverId } = await c.req.json();
486
+ if (!serverId) {
487
+ return c.json({ success: false, error: "serverId is required" }, 400);
584
488
  }
489
+ const mcpJamClientManager2 = c.mcpJamClientManager;
490
+ const serverPrompts = mcpJamClientManager2.getPromptsForServer(serverId);
491
+ return c.json({ prompts: { [serverId]: serverPrompts } });
585
492
  } catch (error) {
586
493
  console.error("Error fetching prompts:", error);
587
494
  return c.json(
@@ -595,13 +502,9 @@ prompts.post("/list", async (c) => {
595
502
  });
596
503
  prompts.post("/get", async (c) => {
597
504
  try {
598
- const { serverConfig, name, args } = await c.req.json();
599
- const validation = validateServerConfig(serverConfig);
600
- if (!validation.success) {
601
- return c.json(
602
- { success: false, error: validation.error.message },
603
- validation.error.status
604
- );
505
+ const { serverId, name, args } = await c.req.json();
506
+ if (!serverId) {
507
+ return c.json({ success: false, error: "serverId is required" }, 400);
605
508
  }
606
509
  if (!name) {
607
510
  return c.json(
@@ -612,22 +515,13 @@ prompts.post("/get", async (c) => {
612
515
  400
613
516
  );
614
517
  }
615
- const client = createMCPClient(
616
- validation.config,
617
- `prompts-get-${Date.now()}`
518
+ const mcpJamClientManager2 = c.mcpJamClientManager;
519
+ const content = await mcpJamClientManager2.getPrompt(
520
+ name,
521
+ serverId,
522
+ args || {}
618
523
  );
619
- try {
620
- const content = await client.prompts.get({
621
- serverName: "server",
622
- name,
623
- args: args || {}
624
- });
625
- await client.disconnect();
626
- return c.json({ content });
627
- } catch (error) {
628
- await client.disconnect();
629
- throw error;
630
- }
524
+ return c.json({ content });
631
525
  } catch (error) {
632
526
  console.error("Error getting prompt:", error);
633
527
  return c.json(
@@ -642,14 +536,16 @@ prompts.post("/get", async (c) => {
642
536
  var prompts_default = prompts;
643
537
 
644
538
  // routes/mcp/chat.ts
645
- import { Hono as Hono5 } from "hono";
539
+ import { Hono as Hono6 } from "hono";
646
540
  import { Agent } from "@mastra/core/agent";
647
541
  import { createAnthropic } from "@ai-sdk/anthropic";
648
542
  import { createOpenAI } from "@ai-sdk/openai";
649
543
  import { createOllama } from "ollama-ai-provider";
650
- import { TextEncoder as TextEncoder2 } from "util";
544
+ import { TextEncoder as TextEncoder3 } from "util";
651
545
 
652
546
  // ../client/src/lib/chat-utils.ts
547
+ import { clsx } from "clsx";
548
+ import { twMerge } from "tailwind-merge";
653
549
  function getDefaultTemperatureByProvider(provider) {
654
550
  switch (provider) {
655
551
  case "openai":
@@ -673,7 +569,7 @@ try {
673
569
  } catch {
674
570
  }
675
571
  var pendingElicitations2 = /* @__PURE__ */ new Map();
676
- var chat = new Hono5();
572
+ var chat = new Hono6();
677
573
  var createLlmModel = (modelDefinition, apiKey, ollamaBaseUrl) => {
678
574
  if (!modelDefinition?.id || !modelDefinition?.provider) {
679
575
  throw new Error(
@@ -696,40 +592,11 @@ var createLlmModel = (modelDefinition, apiKey, ollamaBaseUrl) => {
696
592
  })(modelDefinition.id, {
697
593
  simulateStreaming: true
698
594
  });
699
- default:
700
- throw new Error(
701
- `Unsupported provider: ${modelDefinition.provider} for model: ${modelDefinition.id}`
702
- );
703
- }
704
- };
705
- var createElicitationHandler = (streamingContext) => {
706
- return async (elicitationRequest) => {
707
- const requestId = `elicit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
708
- if (streamingContext.controller && streamingContext.encoder) {
709
- streamingContext.controller.enqueue(
710
- streamingContext.encoder.encode(
711
- `data: ${JSON.stringify({
712
- type: "elicitation_request",
713
- requestId,
714
- message: elicitationRequest.message,
715
- schema: elicitationRequest.requestedSchema,
716
- timestamp: /* @__PURE__ */ new Date()
717
- })}
718
-
719
- `
720
- )
721
- );
722
- }
723
- return new Promise((resolve, reject) => {
724
- pendingElicitations2.set(requestId, { resolve, reject });
725
- setTimeout(() => {
726
- if (pendingElicitations2.has(requestId)) {
727
- pendingElicitations2.delete(requestId);
728
- reject(new Error("Elicitation timeout"));
729
- }
730
- }, ELICITATION_TIMEOUT);
731
- });
732
- };
595
+ default:
596
+ throw new Error(
597
+ `Unsupported provider: ${modelDefinition.provider} for model: ${modelDefinition.id}`
598
+ );
599
+ }
733
600
  };
734
601
  var wrapToolsWithStreaming = (tools2, streamingContext) => {
735
602
  const wrappedTools = {};
@@ -862,6 +729,29 @@ var handleAgentStepFinish = (streamingContext, text, toolCalls, toolResults) =>
862
729
  }
863
730
  }
864
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
+ }
865
755
  } catch (err) {
866
756
  dbg("onStepFinish error", err);
867
757
  }
@@ -916,15 +806,6 @@ var fallbackToCompletion = async (agent, messages, streamingContext, provider) =
916
806
  );
917
807
  }
918
808
  };
919
- var safeDisconnect = async (client) => {
920
- if (client) {
921
- try {
922
- await client.disconnect();
923
- } catch (cleanupError) {
924
- console.warn("[mcp/chat] Error cleaning up MCP client:", cleanupError);
925
- }
926
- }
927
- };
928
809
  var createStreamingResponse = async (agent, messages, toolsets, streamingContext, provider) => {
929
810
  const stream = await agent.stream(messages, {
930
811
  maxSteps: MAX_AGENT_STEPS,
@@ -955,7 +836,7 @@ var createStreamingResponse = async (agent, messages, toolsets, streamingContext
955
836
  );
956
837
  };
957
838
  chat.post("/", async (c) => {
958
- let client = null;
839
+ const mcpClientManager = c.mcpJamClientManager;
959
840
  try {
960
841
  const requestData = await c.req.json();
961
842
  const {
@@ -994,110 +875,592 @@ chat.post("/", async (c) => {
994
875
  pendingElicitations2.delete(requestId);
995
876
  return c.json({ success: true });
996
877
  }
997
- if (!model?.id || !apiKey || !messages) {
998
- return c.json(
999
- {
1000
- success: false,
1001
- error: "model (with id), apiKey, and messages are required"
1002
- },
1003
- 400
1004
- );
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
+ }
1005
1234
  }
1006
- if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
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) {
1007
1252
  return c.json(
1008
- {
1009
- success: false,
1010
- error: "No server configs provided"
1011
- },
1253
+ { success: false, error: "Missing test, servers, or prompt" },
1012
1254
  400
1013
1255
  );
1014
1256
  }
1015
- const validation = validateMultipleServerConfigs(serverConfigs);
1016
- if (!validation.success) {
1017
- dbg(
1018
- "Server config validation failed",
1019
- validation.errors || validation.error
1020
- );
1021
- return c.json(
1022
- {
1023
- success: false,
1024
- error: validation.error.message,
1025
- details: validation.errors
1026
- },
1027
- validation.error.status
1028
- );
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);
1029
1339
  }
1030
- client = createMCPClientWithMultipleConnections(validation.validConfigs);
1031
- const llmModel = createLlmModel(model, apiKey, ollamaBaseUrl);
1032
- const tools2 = await client.getTools();
1033
- const agent = new Agent({
1034
- name: "MCP Chat Agent",
1035
- instructions: systemPrompt || "You are a helpful assistant with access to MCP tools.",
1036
- model: llmModel,
1037
- tools: void 0
1038
- // Start without tools, add them in streaming context
1039
- });
1040
- const formattedMessages = messages.map((msg) => ({
1041
- role: msg.role,
1042
- content: msg.content
1043
- }));
1044
- const toolsets = await client.getToolsets();
1045
- dbg("Streaming start", {
1046
- toolsetServers: Object.keys(toolsets),
1047
- messageCount: formattedMessages.length
1048
- });
1049
- const encoder = new TextEncoder2();
1050
1340
  const readableStream = new ReadableStream({
1051
1341
  async start(controller) {
1052
- const streamingContext = {
1053
- controller,
1054
- encoder,
1055
- toolCallId: 0,
1056
- lastEmittedToolCallId: null
1057
- };
1058
- const streamingWrappedTools = wrapToolsWithStreaming(
1059
- tools2,
1060
- streamingContext
1061
- );
1062
- const streamingAgent = new Agent({
1063
- name: agent.name,
1064
- instructions: agent.instructions,
1065
- model: agent.model,
1066
- tools: Object.keys(streamingWrappedTools).length > 0 ? streamingWrappedTools : void 0
1067
- });
1068
- if (client?.elicitation?.onRequest) {
1069
- for (const serverName of Object.keys(serverConfigs)) {
1070
- const normalizedName = normalizeServerConfigName(serverName);
1071
- const elicitationHandler = createElicitationHandler(streamingContext);
1072
- client.elicitation.onRequest(normalizedName, elicitationHandler);
1073
- }
1074
- }
1075
- try {
1076
- if (client) {
1077
- await createStreamingResponse(
1078
- streamingAgent,
1079
- formattedMessages,
1080
- toolsets,
1081
- streamingContext,
1082
- provider
1083
- );
1084
- } else {
1085
- throw new Error("MCP client is null");
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;
1086
1361
  }
1087
- } catch (error) {
1088
- controller.enqueue(
1089
- encoder.encode(
1090
- `data: ${JSON.stringify({
1091
- type: "error",
1092
- error: error instanceof Error ? error.message : "Unknown error"
1093
- })}
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
+ })}
1094
1420
 
1095
1421
  `
1096
- )
1097
- );
1098
- } finally {
1099
- await safeDisconnect(client);
1100
- controller.close();
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();
1101
1464
  }
1102
1465
  }
1103
1466
  });
@@ -1108,23 +1471,17 @@ chat.post("/", async (c) => {
1108
1471
  Connection: "keep-alive"
1109
1472
  }
1110
1473
  });
1111
- } catch (error) {
1112
- console.error("[mcp/chat] Error in chat API:", error);
1113
- await safeDisconnect(client);
1474
+ } catch (err) {
1114
1475
  return c.json(
1115
- {
1116
- success: false,
1117
- error: error instanceof Error ? error.message : "Unknown error"
1118
- },
1476
+ { success: false, error: err?.message || "Unknown error" },
1119
1477
  500
1120
1478
  );
1121
1479
  }
1122
1480
  });
1123
- var chat_default = chat;
1124
1481
 
1125
1482
  // routes/mcp/oauth.ts
1126
- import { Hono as Hono6 } from "hono";
1127
- var oauth = new Hono6();
1483
+ import { Hono as Hono8 } from "hono";
1484
+ var oauth = new Hono8();
1128
1485
  oauth.get("/metadata", async (c) => {
1129
1486
  try {
1130
1487
  const url = c.req.query("url");
@@ -1170,7 +1527,7 @@ oauth.get("/metadata", async (c) => {
1170
1527
  var oauth_default = oauth;
1171
1528
 
1172
1529
  // routes/mcp/index.ts
1173
- var mcp = new Hono7();
1530
+ var mcp = new Hono9();
1174
1531
  mcp.get("/health", (c) => {
1175
1532
  return c.json({
1176
1533
  service: "MCP API",
@@ -1180,12 +1537,378 @@ mcp.get("/health", (c) => {
1180
1537
  });
1181
1538
  mcp.route("/chat", chat_default);
1182
1539
  mcp.route("/connect", connect_default);
1540
+ mcp.route("/servers", servers_default);
1183
1541
  mcp.route("/tools", tools_default);
1542
+ mcp.route("/tests", tests_default);
1184
1543
  mcp.route("/resources", resources_default);
1185
1544
  mcp.route("/prompts", prompts_default);
1186
1545
  mcp.route("/oauth", oauth_default);
1187
1546
  var mcp_default = mcp;
1188
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
+
1189
1912
  // index.ts
1190
1913
  function logBox(content, title) {
1191
1914
  const lines = content.split("\n");
@@ -1219,7 +1942,12 @@ function getMCPConfigFromEnv() {
1219
1942
  // Default name for CLI-provided servers
1220
1943
  };
1221
1944
  }
1222
- var app = new Hono8();
1945
+ var app = new Hono10();
1946
+ var mcpJamClientManager = new MCPJamClientManager();
1947
+ app.use("*", async (c, next) => {
1948
+ c.mcpJamClientManager = mcpJamClientManager;
1949
+ await next();
1950
+ });
1223
1951
  app.use("*", logger());
1224
1952
  var serverPort = process.env.PORT || "3001";
1225
1953
  var corsOrigins = [
@@ -1251,7 +1979,7 @@ if (process.env.NODE_ENV === "production") {
1251
1979
  if (path.startsWith("/api/")) {
1252
1980
  return c.notFound();
1253
1981
  }
1254
- const indexPath = join(process.cwd(), "dist", "client", "index.html");
1982
+ const indexPath = join2(process.cwd(), "dist", "client", "index.html");
1255
1983
  let htmlContent = readFileSync(indexPath, "utf-8");
1256
1984
  const mcpConfig = getMCPConfigFromEnv();
1257
1985
  if (mcpConfig) {