@mcpjam/inspector 0.9.9 → 0.9.11

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.
@@ -0,0 +1,1994 @@
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 } 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 { createGoogleGenerativeAI } from "@ai-sdk/google";
544
+ import { createOllama } from "ollama-ai-provider";
545
+ import { TextEncoder as TextEncoder3 } from "util";
546
+
547
+ // ../client/src/lib/chat-utils.ts
548
+ import { clsx } from "clsx";
549
+ import { twMerge } from "tailwind-merge";
550
+ function getDefaultTemperatureByProvider(provider) {
551
+ switch (provider) {
552
+ case "openai":
553
+ return 1;
554
+ case "anthropic":
555
+ return 0;
556
+ case "google":
557
+ return 0.9;
558
+ // Google's recommended default
559
+ default:
560
+ return 0;
561
+ }
562
+ }
563
+
564
+ // routes/mcp/chat.ts
565
+ var DEBUG_ENABLED = process.env.MCP_DEBUG !== "false";
566
+ var ELICITATION_TIMEOUT = 3e5;
567
+ var MAX_AGENT_STEPS = 10;
568
+ var dbg = (...args) => {
569
+ if (DEBUG_ENABLED) console.log("[mcp/chat]", ...args);
570
+ };
571
+ try {
572
+ process.setMaxListeners?.(50);
573
+ } catch {
574
+ }
575
+ var pendingElicitations2 = /* @__PURE__ */ new Map();
576
+ var chat = new Hono6();
577
+ var createLlmModel = (modelDefinition, apiKey, ollamaBaseUrl) => {
578
+ if (!modelDefinition?.id || !modelDefinition?.provider) {
579
+ throw new Error(
580
+ `Invalid model definition: ${JSON.stringify(modelDefinition)}`
581
+ );
582
+ }
583
+ switch (modelDefinition.provider) {
584
+ case "anthropic":
585
+ return createAnthropic({ apiKey })(modelDefinition.id);
586
+ case "openai":
587
+ return createOpenAI({ apiKey })(modelDefinition.id);
588
+ case "deepseek":
589
+ return createOpenAI({ apiKey, baseURL: "https://api.deepseek.com/v1" })(
590
+ modelDefinition.id
591
+ );
592
+ case "google":
593
+ return createGoogleGenerativeAI({ apiKey })(modelDefinition.id);
594
+ case "ollama":
595
+ const baseUrl = ollamaBaseUrl || "http://localhost:11434";
596
+ return createOllama({
597
+ baseURL: `${baseUrl}`
598
+ })(modelDefinition.id, {
599
+ simulateStreaming: true
600
+ });
601
+ default:
602
+ throw new Error(
603
+ `Unsupported provider: ${modelDefinition.provider} for model: ${modelDefinition.id}`
604
+ );
605
+ }
606
+ };
607
+ var wrapToolsWithStreaming = (tools2, streamingContext) => {
608
+ const wrappedTools = {};
609
+ for (const [name, tool] of Object.entries(tools2)) {
610
+ wrappedTools[name] = {
611
+ ...tool,
612
+ execute: async (params) => {
613
+ const currentToolCallId = ++streamingContext.toolCallId;
614
+ const startedAt = Date.now();
615
+ if (streamingContext.controller && streamingContext.encoder) {
616
+ streamingContext.controller.enqueue(
617
+ streamingContext.encoder.encode(
618
+ `data: ${JSON.stringify({
619
+ type: "tool_call",
620
+ toolCall: {
621
+ id: currentToolCallId,
622
+ name,
623
+ parameters: params,
624
+ timestamp: /* @__PURE__ */ new Date(),
625
+ status: "executing"
626
+ }
627
+ })}
628
+
629
+ `
630
+ )
631
+ );
632
+ }
633
+ dbg("Tool executing", { name, currentToolCallId, params });
634
+ try {
635
+ const result = await tool.execute(params);
636
+ dbg("Tool result", {
637
+ name,
638
+ currentToolCallId,
639
+ ms: Date.now() - startedAt
640
+ });
641
+ if (streamingContext.controller && streamingContext.encoder) {
642
+ streamingContext.controller.enqueue(
643
+ streamingContext.encoder.encode(
644
+ `data: ${JSON.stringify({
645
+ type: "tool_result",
646
+ toolResult: {
647
+ id: currentToolCallId,
648
+ toolCallId: currentToolCallId,
649
+ result,
650
+ timestamp: /* @__PURE__ */ new Date()
651
+ }
652
+ })}
653
+
654
+ `
655
+ )
656
+ );
657
+ }
658
+ return result;
659
+ } catch (error) {
660
+ dbg("Tool error", {
661
+ name,
662
+ currentToolCallId,
663
+ error: error instanceof Error ? error.message : String(error)
664
+ });
665
+ if (streamingContext.controller && streamingContext.encoder) {
666
+ streamingContext.controller.enqueue(
667
+ streamingContext.encoder.encode(
668
+ `data: ${JSON.stringify({
669
+ type: "tool_result",
670
+ toolResult: {
671
+ id: currentToolCallId,
672
+ toolCallId: currentToolCallId,
673
+ error: error instanceof Error ? error.message : String(error),
674
+ timestamp: /* @__PURE__ */ new Date()
675
+ }
676
+ })}
677
+
678
+ `
679
+ )
680
+ );
681
+ }
682
+ throw error;
683
+ }
684
+ }
685
+ };
686
+ }
687
+ return wrappedTools;
688
+ };
689
+ var handleAgentStepFinish = (streamingContext, text, toolCalls, toolResults) => {
690
+ try {
691
+ if (toolCalls && Array.isArray(toolCalls)) {
692
+ for (const call of toolCalls) {
693
+ const currentToolCallId = ++streamingContext.toolCallId;
694
+ streamingContext.lastEmittedToolCallId = currentToolCallId;
695
+ if (streamingContext.controller && streamingContext.encoder) {
696
+ streamingContext.controller.enqueue(
697
+ streamingContext.encoder.encode(
698
+ `data: ${JSON.stringify({
699
+ type: "tool_call",
700
+ toolCall: {
701
+ id: currentToolCallId,
702
+ name: call.name || call.toolName,
703
+ parameters: call.params || call.args || {},
704
+ timestamp: /* @__PURE__ */ new Date(),
705
+ status: "executing"
706
+ }
707
+ })}
708
+
709
+ `
710
+ )
711
+ );
712
+ }
713
+ }
714
+ }
715
+ if (toolResults && Array.isArray(toolResults)) {
716
+ for (const result of toolResults) {
717
+ const currentToolCallId = streamingContext.lastEmittedToolCallId != null ? streamingContext.lastEmittedToolCallId : ++streamingContext.toolCallId;
718
+ if (streamingContext.controller && streamingContext.encoder) {
719
+ streamingContext.controller.enqueue(
720
+ streamingContext.encoder.encode(
721
+ `data: ${JSON.stringify({
722
+ type: "tool_result",
723
+ toolResult: {
724
+ id: currentToolCallId,
725
+ toolCallId: currentToolCallId,
726
+ result: result.result,
727
+ error: result.error,
728
+ timestamp: /* @__PURE__ */ new Date()
729
+ }
730
+ })}
731
+
732
+ `
733
+ )
734
+ );
735
+ }
736
+ }
737
+ }
738
+ streamingContext.stepIndex = (streamingContext.stepIndex || 0) + 1;
739
+ if (streamingContext.controller && streamingContext.encoder) {
740
+ streamingContext.controller.enqueue(
741
+ streamingContext.encoder.encode(
742
+ `data: ${JSON.stringify({
743
+ type: "trace_step",
744
+ step: streamingContext.stepIndex,
745
+ text,
746
+ toolCalls: (toolCalls || []).map((c) => ({
747
+ name: c.name || c.toolName,
748
+ params: c.params || c.args || {}
749
+ })),
750
+ toolResults: (toolResults || []).map((r) => ({
751
+ result: r.result,
752
+ error: r.error
753
+ })),
754
+ timestamp: /* @__PURE__ */ new Date()
755
+ })}
756
+
757
+ `
758
+ )
759
+ );
760
+ }
761
+ } catch (err) {
762
+ dbg("onStepFinish error", err);
763
+ }
764
+ };
765
+ var streamAgentResponse = async (streamingContext, stream) => {
766
+ let hasContent = false;
767
+ let chunkCount = 0;
768
+ for await (const chunk of stream.textStream) {
769
+ if (chunk && chunk.trim()) {
770
+ hasContent = true;
771
+ chunkCount++;
772
+ streamingContext.controller.enqueue(
773
+ streamingContext.encoder.encode(
774
+ `data: ${JSON.stringify({ type: "text", content: chunk })}
775
+
776
+ `
777
+ )
778
+ );
779
+ }
780
+ }
781
+ dbg("Streaming finished", { hasContent, chunkCount });
782
+ return { hasContent, chunkCount };
783
+ };
784
+ var fallbackToCompletion = async (agent, messages, streamingContext, provider, temperature) => {
785
+ try {
786
+ const result = await agent.generate(messages, {
787
+ temperature: temperature == null || void 0 ? getDefaultTemperatureByProvider(provider) : temperature
788
+ });
789
+ if (result.text && result.text.trim()) {
790
+ streamingContext.controller.enqueue(
791
+ streamingContext.encoder.encode(
792
+ `data: ${JSON.stringify({
793
+ type: "text",
794
+ content: result.text
795
+ })}
796
+
797
+ `
798
+ )
799
+ );
800
+ }
801
+ } catch (fallbackErr) {
802
+ streamingContext.controller.enqueue(
803
+ streamingContext.encoder.encode(
804
+ `data: ${JSON.stringify({
805
+ type: "text",
806
+ content: "Failed to generate response. Please try again. ",
807
+ error: fallbackErr instanceof Error ? fallbackErr.message : "Unknown error"
808
+ })}
809
+
810
+ `
811
+ )
812
+ );
813
+ }
814
+ };
815
+ var createStreamingResponse = async (agent, messages, toolsets, streamingContext, provider, temperature) => {
816
+ const stream = await agent.stream(messages, {
817
+ maxSteps: MAX_AGENT_STEPS,
818
+ temperature: temperature == null || void 0 ? getDefaultTemperatureByProvider(provider) : temperature,
819
+ toolsets,
820
+ onStepFinish: ({ text, toolCalls, toolResults }) => {
821
+ handleAgentStepFinish(streamingContext, text, toolCalls, toolResults);
822
+ }
823
+ });
824
+ const { hasContent } = await streamAgentResponse(streamingContext, stream);
825
+ if (!hasContent) {
826
+ dbg("No content from textStream; falling back to completion");
827
+ await fallbackToCompletion(
828
+ agent,
829
+ messages,
830
+ streamingContext,
831
+ provider,
832
+ temperature
833
+ );
834
+ }
835
+ streamingContext.controller.enqueue(
836
+ streamingContext.encoder.encode(
837
+ `data: ${JSON.stringify({
838
+ type: "elicitation_complete"
839
+ })}
840
+
841
+ `
842
+ )
843
+ );
844
+ streamingContext.controller.enqueue(
845
+ streamingContext.encoder.encode(`data: [DONE]
846
+
847
+ `)
848
+ );
849
+ };
850
+ chat.post("/", async (c) => {
851
+ const mcpClientManager = c.mcpJamClientManager;
852
+ try {
853
+ const requestData = await c.req.json();
854
+ const {
855
+ serverConfigs,
856
+ model,
857
+ provider,
858
+ apiKey,
859
+ systemPrompt,
860
+ temperature,
861
+ messages,
862
+ ollamaBaseUrl,
863
+ action,
864
+ requestId,
865
+ response
866
+ } = requestData;
867
+ if (action === "elicitation_response") {
868
+ if (!requestId) {
869
+ return c.json(
870
+ {
871
+ success: false,
872
+ error: "requestId is required for elicitation_response action"
873
+ },
874
+ 400
875
+ );
876
+ }
877
+ const pending = pendingElicitations2.get(requestId);
878
+ if (!pending) {
879
+ return c.json(
880
+ {
881
+ success: false,
882
+ error: "No pending elicitation found for this requestId"
883
+ },
884
+ 404
885
+ );
886
+ }
887
+ pending.resolve(response);
888
+ pendingElicitations2.delete(requestId);
889
+ return c.json({ success: true });
890
+ }
891
+ if (!model?.id || !apiKey || !messages) {
892
+ return c.json(
893
+ {
894
+ success: false,
895
+ error: "model (with id), apiKey, and messages are required"
896
+ },
897
+ 400
898
+ );
899
+ }
900
+ if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
901
+ return c.json(
902
+ {
903
+ success: false,
904
+ error: "No server configs provided"
905
+ },
906
+ 400
907
+ );
908
+ }
909
+ const serverErrors = {};
910
+ const connectedServers = [];
911
+ for (const [serverName, serverConfig] of Object.entries(serverConfigs)) {
912
+ try {
913
+ await mcpClientManager.connectToServer(serverName, serverConfig);
914
+ connectedServers.push(serverName);
915
+ dbg("Connected to server", { serverName });
916
+ } catch (error) {
917
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
918
+ serverErrors[serverName] = errorMessage;
919
+ dbg("Failed to connect to server", { serverName, error: errorMessage });
920
+ }
921
+ }
922
+ if (connectedServers.length === 0) {
923
+ return c.json(
924
+ {
925
+ success: false,
926
+ error: "Failed to connect to any servers",
927
+ details: serverErrors
928
+ },
929
+ 400
930
+ );
931
+ }
932
+ if (Object.keys(serverErrors).length > 0) {
933
+ dbg("Some servers failed to connect", {
934
+ connectedServers,
935
+ failedServers: Object.keys(serverErrors),
936
+ errors: serverErrors
937
+ });
938
+ }
939
+ const llmModel = createLlmModel(model, apiKey, ollamaBaseUrl);
940
+ const agent = new Agent({
941
+ name: "MCP Chat Agent",
942
+ instructions: systemPrompt || "You are a helpful assistant with access to MCP tools.",
943
+ model: llmModel,
944
+ tools: void 0
945
+ // Start without tools, add them in streaming context
946
+ });
947
+ const formattedMessages = messages.map((msg) => ({
948
+ role: msg.role,
949
+ content: msg.content
950
+ }));
951
+ const allTools = mcpClientManager.getAvailableTools();
952
+ const toolsByServer = {};
953
+ for (const tool of allTools) {
954
+ if (!toolsByServer[tool.serverId]) {
955
+ toolsByServer[tool.serverId] = {};
956
+ }
957
+ toolsByServer[tool.serverId][tool.name] = {
958
+ description: tool.description,
959
+ inputSchema: tool.inputSchema,
960
+ execute: async (params) => {
961
+ return await mcpClientManager.executeToolDirect(
962
+ `${tool.serverId}:${tool.name}`,
963
+ params
964
+ );
965
+ }
966
+ };
967
+ }
968
+ dbg("Streaming start", {
969
+ connectedServers,
970
+ toolCount: allTools.length,
971
+ messageCount: formattedMessages.length
972
+ });
973
+ const encoder = new TextEncoder3();
974
+ const readableStream = new ReadableStream({
975
+ async start(controller) {
976
+ const streamingContext = {
977
+ controller,
978
+ encoder,
979
+ toolCallId: 0,
980
+ lastEmittedToolCallId: null,
981
+ stepIndex: 0
982
+ };
983
+ const flattenedTools = {};
984
+ Object.values(toolsByServer).forEach((serverTools) => {
985
+ Object.assign(flattenedTools, serverTools);
986
+ });
987
+ const streamingWrappedTools = wrapToolsWithStreaming(
988
+ flattenedTools,
989
+ streamingContext
990
+ );
991
+ const streamingAgent = new Agent({
992
+ name: agent.name,
993
+ instructions: agent.instructions,
994
+ model: agent.model,
995
+ tools: Object.keys(streamingWrappedTools).length > 0 ? streamingWrappedTools : void 0
996
+ });
997
+ mcpClientManager.setElicitationCallback(async (request) => {
998
+ const elicitationRequest = {
999
+ message: request.message,
1000
+ requestedSchema: request.schema
1001
+ };
1002
+ if (streamingContext.controller && streamingContext.encoder) {
1003
+ streamingContext.controller.enqueue(
1004
+ streamingContext.encoder.encode(
1005
+ `data: ${JSON.stringify({
1006
+ type: "elicitation_request",
1007
+ requestId: request.requestId,
1008
+ message: elicitationRequest.message,
1009
+ schema: elicitationRequest.requestedSchema,
1010
+ timestamp: /* @__PURE__ */ new Date()
1011
+ })}
1012
+
1013
+ `
1014
+ )
1015
+ );
1016
+ }
1017
+ return new Promise((resolve, reject) => {
1018
+ pendingElicitations2.set(request.requestId, { resolve, reject });
1019
+ setTimeout(() => {
1020
+ if (pendingElicitations2.has(request.requestId)) {
1021
+ pendingElicitations2.delete(request.requestId);
1022
+ reject(new Error("Elicitation timeout"));
1023
+ }
1024
+ }, ELICITATION_TIMEOUT);
1025
+ });
1026
+ });
1027
+ try {
1028
+ await createStreamingResponse(
1029
+ streamingAgent,
1030
+ formattedMessages,
1031
+ toolsByServer,
1032
+ streamingContext,
1033
+ provider,
1034
+ temperature
1035
+ );
1036
+ } catch (error) {
1037
+ controller.enqueue(
1038
+ encoder.encode(
1039
+ `data: ${JSON.stringify({
1040
+ type: "error",
1041
+ error: error instanceof Error ? error.message : "Unknown error"
1042
+ })}
1043
+
1044
+ `
1045
+ )
1046
+ );
1047
+ } finally {
1048
+ mcpClientManager.clearElicitationCallback();
1049
+ controller.close();
1050
+ }
1051
+ }
1052
+ });
1053
+ return new Response(readableStream, {
1054
+ headers: {
1055
+ "Content-Type": "text/event-stream",
1056
+ "Cache-Control": "no-cache",
1057
+ Connection: "keep-alive"
1058
+ }
1059
+ });
1060
+ } catch (error) {
1061
+ console.error("[mcp/chat] Error in chat API:", error);
1062
+ mcpClientManager.clearElicitationCallback();
1063
+ return c.json(
1064
+ {
1065
+ success: false,
1066
+ error: error instanceof Error ? error.message : "Unknown error"
1067
+ },
1068
+ 500
1069
+ );
1070
+ }
1071
+ });
1072
+ var chat_default = chat;
1073
+
1074
+ // routes/mcp/tests.ts
1075
+ import { Hono as Hono7 } from "hono";
1076
+ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
1077
+ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
1078
+ import { createOllama as createOllama2 } from "ollama-ai-provider";
1079
+ import { Agent as Agent2 } from "@mastra/core/agent";
1080
+
1081
+ // utils/mcp-utils.ts
1082
+ import { MCPClient } from "@mastra/mcp";
1083
+ function validateServerConfig(serverConfig) {
1084
+ if (!serverConfig) {
1085
+ return {
1086
+ success: false,
1087
+ error: {
1088
+ message: "Server configuration is required",
1089
+ status: 400
1090
+ }
1091
+ };
1092
+ }
1093
+ const config = { ...serverConfig };
1094
+ if (config.url) {
1095
+ try {
1096
+ if (typeof config.url === "string") {
1097
+ const parsed = new URL(config.url);
1098
+ parsed.search = "";
1099
+ parsed.hash = "";
1100
+ config.url = parsed;
1101
+ } else if (typeof config.url === "object" && !config.url.href) {
1102
+ return {
1103
+ success: false,
1104
+ error: {
1105
+ message: "Invalid URL configuration",
1106
+ status: 400
1107
+ }
1108
+ };
1109
+ }
1110
+ if (config.oauth?.access_token) {
1111
+ const authHeaders = {
1112
+ Authorization: `Bearer ${config.oauth.access_token}`,
1113
+ ...config.requestInit?.headers || {}
1114
+ };
1115
+ config.requestInit = {
1116
+ ...config.requestInit,
1117
+ headers: authHeaders
1118
+ };
1119
+ config.eventSourceInit = {
1120
+ fetch(input, init) {
1121
+ const headers = new Headers(init?.headers || {});
1122
+ headers.set(
1123
+ "Authorization",
1124
+ `Bearer ${config.oauth.access_token}`
1125
+ );
1126
+ if (config.requestInit?.headers) {
1127
+ const requestHeaders = new Headers(config.requestInit.headers);
1128
+ requestHeaders.forEach((value, key) => {
1129
+ if (key.toLowerCase() !== "authorization") {
1130
+ headers.set(key, value);
1131
+ }
1132
+ });
1133
+ }
1134
+ return fetch(input, {
1135
+ ...init,
1136
+ headers
1137
+ });
1138
+ }
1139
+ };
1140
+ } else if (config.requestInit?.headers) {
1141
+ config.eventSourceInit = {
1142
+ fetch(input, init) {
1143
+ const headers = new Headers(init?.headers || {});
1144
+ const requestHeaders = new Headers(config.requestInit.headers);
1145
+ requestHeaders.forEach((value, key) => {
1146
+ headers.set(key, value);
1147
+ });
1148
+ return fetch(input, {
1149
+ ...init,
1150
+ headers
1151
+ });
1152
+ }
1153
+ };
1154
+ }
1155
+ } catch (error) {
1156
+ return {
1157
+ success: false,
1158
+ error: {
1159
+ message: `Invalid URL format: ${error}`,
1160
+ status: 400
1161
+ }
1162
+ };
1163
+ }
1164
+ }
1165
+ return {
1166
+ success: true,
1167
+ config
1168
+ };
1169
+ }
1170
+ function generateUniqueServerID(serverName) {
1171
+ const normalizedBase = normalizeServerConfigName(serverName);
1172
+ const timestamp = Date.now().toString(36);
1173
+ const random = Math.random().toString(36).substring(2, 8);
1174
+ return `${normalizedBase}_${timestamp}_${random}`;
1175
+ }
1176
+ var validateMultipleServerConfigs = (serverConfigs) => {
1177
+ if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
1178
+ return {
1179
+ success: false,
1180
+ error: {
1181
+ message: "At least one server configuration is required",
1182
+ status: 400
1183
+ }
1184
+ };
1185
+ }
1186
+ const validConfigs = {};
1187
+ const serverNameMapping = {};
1188
+ const errors = {};
1189
+ let hasErrors = false;
1190
+ for (const [serverName, serverConfig] of Object.entries(serverConfigs)) {
1191
+ const validationResult = validateServerConfig(serverConfig);
1192
+ if (validationResult.success && validationResult.config) {
1193
+ const serverID = generateUniqueServerID(serverName);
1194
+ validConfigs[serverID] = validationResult.config;
1195
+ serverNameMapping[serverID] = serverName;
1196
+ } else {
1197
+ hasErrors = true;
1198
+ let errorMessage = "Configuration validation failed";
1199
+ if (validationResult.error) {
1200
+ errorMessage = validationResult.error.message;
1201
+ }
1202
+ errors[serverName] = errorMessage;
1203
+ }
1204
+ }
1205
+ if (!hasErrors) {
1206
+ return {
1207
+ success: true,
1208
+ validConfigs,
1209
+ serverNameMapping
1210
+ };
1211
+ }
1212
+ if (Object.keys(validConfigs).length > 0) {
1213
+ return {
1214
+ success: false,
1215
+ validConfigs,
1216
+ serverNameMapping,
1217
+ errors
1218
+ };
1219
+ }
1220
+ return {
1221
+ success: false,
1222
+ errors,
1223
+ error: {
1224
+ message: "All server configurations failed validation",
1225
+ status: 400
1226
+ }
1227
+ };
1228
+ };
1229
+ function createMCPClientWithMultipleConnections(serverConfigs) {
1230
+ const originalMCPClient = new MCPClient({
1231
+ id: `chat-${Date.now()}`,
1232
+ servers: serverConfigs
1233
+ });
1234
+ const originalGetTools = originalMCPClient.getTools.bind(originalMCPClient);
1235
+ originalMCPClient.getTools = async () => {
1236
+ const tools2 = await originalGetTools();
1237
+ const fixedTools = {};
1238
+ for (const [toolName, toolConfig] of Object.entries(tools2)) {
1239
+ const parts = toolName.split("_");
1240
+ if (parts.length >= 3 && parts[0] === parts[1]) {
1241
+ const fixedName = parts.slice(1).join("_");
1242
+ fixedTools[fixedName] = toolConfig;
1243
+ } else {
1244
+ fixedTools[toolName] = toolConfig;
1245
+ }
1246
+ }
1247
+ return fixedTools;
1248
+ };
1249
+ return originalMCPClient;
1250
+ }
1251
+ function normalizeServerConfigName(serverName) {
1252
+ return serverName.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
1253
+ }
1254
+
1255
+ // routes/mcp/tests.ts
1256
+ var tests = new Hono7();
1257
+ var tests_default = tests;
1258
+ tests.post("/run-all", async (c) => {
1259
+ const encoder = new TextEncoder();
1260
+ try {
1261
+ let createModel2 = function(model) {
1262
+ switch (model.provider) {
1263
+ case "anthropic":
1264
+ return createAnthropic2({
1265
+ apiKey: providerApiKeys?.anthropic || process.env.ANTHROPIC_API_KEY || ""
1266
+ })(model.id);
1267
+ case "openai":
1268
+ return createOpenAI2({
1269
+ apiKey: providerApiKeys?.openai || process.env.OPENAI_API_KEY || ""
1270
+ })(model.id);
1271
+ case "deepseek":
1272
+ return createOpenAI2({
1273
+ apiKey: providerApiKeys?.deepseek || process.env.DEEPSEEK_API_KEY || "",
1274
+ baseURL: "https://api.deepseek.com/v1"
1275
+ })(model.id);
1276
+ case "ollama":
1277
+ return createOllama2({
1278
+ baseURL: ollamaBaseUrl || process.env.OLLAMA_BASE_URL || "http://localhost:11434"
1279
+ })(model.id, { simulateStreaming: true });
1280
+ default:
1281
+ throw new Error(`Unsupported provider: ${model.provider}`);
1282
+ }
1283
+ };
1284
+ var createModel = createModel2;
1285
+ const body = await c.req.json();
1286
+ const testsInput = body?.tests || [];
1287
+ const allServers = body?.allServers || {};
1288
+ const providerApiKeys = body?.providerApiKeys || {};
1289
+ const ollamaBaseUrl = body?.ollamaBaseUrl;
1290
+ const maxConcurrency = Math.max(
1291
+ 1,
1292
+ Math.min(8, body?.concurrency ?? 5)
1293
+ );
1294
+ if (!Array.isArray(testsInput) || testsInput.length === 0) {
1295
+ return c.json({ success: false, error: "No tests provided" }, 400);
1296
+ }
1297
+ const readableStream = new ReadableStream({
1298
+ async start(controller) {
1299
+ let active = 0;
1300
+ let index = 0;
1301
+ let failed = false;
1302
+ const runNext = async () => {
1303
+ if (index >= testsInput.length) {
1304
+ if (active === 0) {
1305
+ controller.enqueue(
1306
+ encoder.encode(
1307
+ `data: ${JSON.stringify({ type: "run_complete", passed: !failed })}
1308
+
1309
+ `
1310
+ )
1311
+ );
1312
+ controller.enqueue(encoder.encode(`data: [DONE]
1313
+
1314
+ `));
1315
+ controller.close();
1316
+ }
1317
+ return;
1318
+ }
1319
+ const test = testsInput[index++];
1320
+ active++;
1321
+ (async () => {
1322
+ const calledTools = /* @__PURE__ */ new Set();
1323
+ const expectedSet = new Set(test.expectedTools || []);
1324
+ let step = 0;
1325
+ let client = null;
1326
+ try {
1327
+ let serverConfigs = {};
1328
+ if (test.selectedServers && test.selectedServers.length > 0) {
1329
+ for (const name of test.selectedServers) {
1330
+ if (allServers[name]) serverConfigs[name] = allServers[name];
1331
+ }
1332
+ } else {
1333
+ for (const [name, cfg] of Object.entries(allServers)) {
1334
+ serverConfigs[name] = cfg;
1335
+ }
1336
+ }
1337
+ const validation = validateMultipleServerConfigs(serverConfigs);
1338
+ let finalServers = {};
1339
+ if (validation.success && validation.validConfigs) {
1340
+ finalServers = validation.validConfigs;
1341
+ } else if (validation.validConfigs && Object.keys(validation.validConfigs).length > 0) {
1342
+ finalServers = validation.validConfigs;
1343
+ } else {
1344
+ throw new Error("No valid MCP server configs for test");
1345
+ }
1346
+ client = createMCPClientWithMultipleConnections(finalServers);
1347
+ const model = createModel2(test.model);
1348
+ const agent = new Agent2({
1349
+ name: `TestAgent-${test.id}`,
1350
+ instructions: "You are a helpful assistant with access to MCP tools",
1351
+ model
1352
+ });
1353
+ const toolsets = await client.getToolsets();
1354
+ const stream = await agent.stream(
1355
+ [{ role: "user", content: test.prompt || "" }],
1356
+ {
1357
+ maxSteps: 10,
1358
+ toolsets,
1359
+ onStepFinish: ({ text, toolCalls, toolResults }) => {
1360
+ step += 1;
1361
+ (toolCalls || []).forEach((c2) => {
1362
+ const toolName = c2?.name || c2?.toolName;
1363
+ if (toolName) {
1364
+ calledTools.add(toolName);
1365
+ }
1366
+ });
1367
+ controller.enqueue(
1368
+ encoder.encode(
1369
+ `data: ${JSON.stringify({
1370
+ type: "trace_step",
1371
+ testId: test.id,
1372
+ step,
1373
+ text,
1374
+ toolCalls,
1375
+ toolResults
1376
+ })}
1377
+
1378
+ `
1379
+ )
1380
+ );
1381
+ }
1382
+ }
1383
+ );
1384
+ for await (const _ of stream.textStream) {
1385
+ }
1386
+ const called = Array.from(calledTools);
1387
+ const missing = Array.from(expectedSet).filter(
1388
+ (t) => !calledTools.has(t)
1389
+ );
1390
+ const unexpected = called.filter((t) => !expectedSet.has(t));
1391
+ const passed = missing.length === 0 && unexpected.length === 0;
1392
+ if (!passed) failed = true;
1393
+ controller.enqueue(
1394
+ encoder.encode(
1395
+ `data: ${JSON.stringify({ type: "result", testId: test.id, passed, calledTools: called, missingTools: missing, unexpectedTools: unexpected })}
1396
+
1397
+ `
1398
+ )
1399
+ );
1400
+ } catch (err) {
1401
+ failed = true;
1402
+ controller.enqueue(
1403
+ encoder.encode(
1404
+ `data: ${JSON.stringify({ type: "result", testId: test.id, passed: false, error: err?.message })}
1405
+
1406
+ `
1407
+ )
1408
+ );
1409
+ } finally {
1410
+ try {
1411
+ await client?.disconnect();
1412
+ } catch {
1413
+ }
1414
+ active--;
1415
+ runNext();
1416
+ }
1417
+ })();
1418
+ };
1419
+ for (let i = 0; i < Math.min(maxConcurrency, testsInput.length); i++) {
1420
+ runNext();
1421
+ }
1422
+ }
1423
+ });
1424
+ return new Response(readableStream, {
1425
+ headers: {
1426
+ "Content-Type": "text/event-stream",
1427
+ "Cache-Control": "no-cache",
1428
+ Connection: "keep-alive"
1429
+ }
1430
+ });
1431
+ } catch (err) {
1432
+ return c.json(
1433
+ { success: false, error: err?.message || "Unknown error" },
1434
+ 500
1435
+ );
1436
+ }
1437
+ });
1438
+
1439
+ // routes/mcp/oauth.ts
1440
+ import { Hono as Hono8 } from "hono";
1441
+ var oauth = new Hono8();
1442
+ oauth.get("/metadata", async (c) => {
1443
+ try {
1444
+ const url = c.req.query("url");
1445
+ if (!url) {
1446
+ return c.json({ error: "Missing url parameter" }, 400);
1447
+ }
1448
+ let metadataUrl;
1449
+ try {
1450
+ metadataUrl = new URL(url);
1451
+ if (metadataUrl.protocol !== "https:") {
1452
+ return c.json({ error: "Only HTTPS URLs are allowed" }, 400);
1453
+ }
1454
+ } catch (error) {
1455
+ return c.json({ error: "Invalid URL format" }, 400);
1456
+ }
1457
+ const response = await fetch(metadataUrl.toString(), {
1458
+ method: "GET",
1459
+ headers: {
1460
+ Accept: "application/json",
1461
+ "User-Agent": "MCP-Inspector/1.0"
1462
+ }
1463
+ });
1464
+ if (!response.ok) {
1465
+ return c.json(
1466
+ {
1467
+ error: `Failed to fetch OAuth metadata: ${response.status} ${response.statusText}`
1468
+ },
1469
+ response.status
1470
+ );
1471
+ }
1472
+ const metadata = await response.json();
1473
+ return c.json(metadata);
1474
+ } catch (error) {
1475
+ console.error("OAuth metadata proxy error:", error);
1476
+ return c.json(
1477
+ {
1478
+ error: error instanceof Error ? error.message : "Unknown error occurred"
1479
+ },
1480
+ 500
1481
+ );
1482
+ }
1483
+ });
1484
+ var oauth_default = oauth;
1485
+
1486
+ // routes/mcp/index.ts
1487
+ var mcp = new Hono9();
1488
+ mcp.get("/health", (c) => {
1489
+ return c.json({
1490
+ service: "MCP API",
1491
+ status: "ready",
1492
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1493
+ });
1494
+ });
1495
+ mcp.route("/chat", chat_default);
1496
+ mcp.route("/connect", connect_default);
1497
+ mcp.route("/servers", servers_default);
1498
+ mcp.route("/tools", tools_default);
1499
+ mcp.route("/tests", tests_default);
1500
+ mcp.route("/resources", resources_default);
1501
+ mcp.route("/prompts", prompts_default);
1502
+ mcp.route("/oauth", oauth_default);
1503
+ var mcp_default = mcp;
1504
+
1505
+ // services/mcpjam-client-manager.ts
1506
+ import { MCPClient as MCPClient2 } from "@mastra/mcp";
1507
+ function generateUniqueServerId(serverId) {
1508
+ const normalizedBase = serverId.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
1509
+ const timestamp = Date.now().toString(36);
1510
+ const random = Math.random().toString(36).substring(2, 8);
1511
+ return `${normalizedBase}_${timestamp}_${random}`;
1512
+ }
1513
+ var MCPJamClientManager = class {
1514
+ mcpClients = /* @__PURE__ */ new Map();
1515
+ statuses = /* @__PURE__ */ new Map();
1516
+ configs = /* @__PURE__ */ new Map();
1517
+ // Map original server names to unique IDs
1518
+ serverIdMapping = /* @__PURE__ */ new Map();
1519
+ // Track in-flight connections to avoid duplicate concurrent connects
1520
+ pendingConnections = /* @__PURE__ */ new Map();
1521
+ toolRegistry = /* @__PURE__ */ new Map();
1522
+ resourceRegistry = /* @__PURE__ */ new Map();
1523
+ promptRegistry = /* @__PURE__ */ new Map();
1524
+ // Store for pending elicitation requests with Promise resolvers
1525
+ pendingElicitations = /* @__PURE__ */ new Map();
1526
+ // Optional callback for handling elicitation requests
1527
+ elicitationCallback;
1528
+ // Helper method to get unique ID for a server name
1529
+ getServerUniqueId(serverName) {
1530
+ return this.serverIdMapping.get(serverName);
1531
+ }
1532
+ // Public method to get server ID for external use (like frontend)
1533
+ getServerIdForName(serverName) {
1534
+ return this.serverIdMapping.get(serverName);
1535
+ }
1536
+ // Public method to get original server name from a unique server ID
1537
+ getOriginalNameForId(uniqueServerId) {
1538
+ for (const [originalName, uid] of this.serverIdMapping.entries()) {
1539
+ if (uid === uniqueServerId) return originalName;
1540
+ }
1541
+ return void 0;
1542
+ }
1543
+ // Convenience: map an array of unique IDs to their original names (fallback to ID if not found)
1544
+ mapIdsToOriginalNames(uniqueIds) {
1545
+ return uniqueIds.map((id) => this.getOriginalNameForId(id) || id);
1546
+ }
1547
+ async connectToServer(serverId, serverConfig) {
1548
+ const pending = this.pendingConnections.get(serverId);
1549
+ if (pending) {
1550
+ await pending;
1551
+ return;
1552
+ }
1553
+ const connectPromise = (async () => {
1554
+ let id = this.serverIdMapping.get(serverId);
1555
+ if (!id) {
1556
+ id = generateUniqueServerId(serverId);
1557
+ this.serverIdMapping.set(serverId, id);
1558
+ }
1559
+ if (this.mcpClients.has(id)) return;
1560
+ const validation = validateServerConfig(serverConfig);
1561
+ if (!validation.success) {
1562
+ this.statuses.set(id, "error");
1563
+ throw new Error(validation.error.message);
1564
+ }
1565
+ this.configs.set(id, validation.config);
1566
+ this.statuses.set(id, "connecting");
1567
+ const client = new MCPClient2({
1568
+ id: `mcpjam-${id}`,
1569
+ servers: { [id]: validation.config }
1570
+ });
1571
+ try {
1572
+ await client.getTools();
1573
+ this.mcpClients.set(id, client);
1574
+ this.statuses.set(id, "connected");
1575
+ if (client.elicitation?.onRequest) {
1576
+ client.elicitation.onRequest(
1577
+ id,
1578
+ async (elicitationRequest) => {
1579
+ return await this.handleElicitationRequest(elicitationRequest);
1580
+ }
1581
+ );
1582
+ }
1583
+ await this.discoverServerResources(id);
1584
+ } catch (err) {
1585
+ this.statuses.set(id, "error");
1586
+ try {
1587
+ await client.disconnect();
1588
+ } catch {
1589
+ }
1590
+ this.mcpClients.delete(id);
1591
+ throw err;
1592
+ }
1593
+ })().finally(() => {
1594
+ this.pendingConnections.delete(serverId);
1595
+ });
1596
+ this.pendingConnections.set(serverId, connectPromise);
1597
+ await connectPromise;
1598
+ }
1599
+ async disconnectFromServer(serverId) {
1600
+ const id = this.getServerUniqueId(serverId);
1601
+ if (!id) return;
1602
+ const client = this.mcpClients.get(id);
1603
+ if (client) {
1604
+ try {
1605
+ await client.disconnect();
1606
+ } catch {
1607
+ }
1608
+ }
1609
+ this.mcpClients.delete(id);
1610
+ this.statuses.set(id, "disconnected");
1611
+ this.serverIdMapping.delete(serverId);
1612
+ for (const key of Array.from(this.toolRegistry.keys())) {
1613
+ const item = this.toolRegistry.get(key);
1614
+ if (item.serverId === id) this.toolRegistry.delete(key);
1615
+ }
1616
+ for (const key of Array.from(this.resourceRegistry.keys())) {
1617
+ const item = this.resourceRegistry.get(key);
1618
+ if (item.serverId === id) this.resourceRegistry.delete(key);
1619
+ }
1620
+ for (const key of Array.from(this.promptRegistry.keys())) {
1621
+ const item = this.promptRegistry.get(key);
1622
+ if (item.serverId === id) this.promptRegistry.delete(key);
1623
+ }
1624
+ }
1625
+ getConnectionStatus(serverId) {
1626
+ const id = this.getServerUniqueId(serverId);
1627
+ return id ? this.statuses.get(id) || "disconnected" : "disconnected";
1628
+ }
1629
+ getConnectedServers() {
1630
+ const servers2 = {};
1631
+ for (const [originalName, uniqueId] of this.serverIdMapping.entries()) {
1632
+ servers2[originalName] = {
1633
+ status: this.statuses.get(uniqueId) || "disconnected",
1634
+ config: this.configs.get(uniqueId)
1635
+ };
1636
+ }
1637
+ return servers2;
1638
+ }
1639
+ async discoverAllResources() {
1640
+ const serverIds = Array.from(this.mcpClients.keys());
1641
+ await Promise.all(serverIds.map((id) => this.discoverServerResources(id)));
1642
+ }
1643
+ async discoverServerResources(serverId) {
1644
+ const client = this.mcpClients.get(serverId);
1645
+ if (!client) return;
1646
+ const toolsets = await client.getToolsets();
1647
+ const flattenedTools = {};
1648
+ Object.values(toolsets).forEach((serverTools) => {
1649
+ Object.assign(flattenedTools, serverTools);
1650
+ });
1651
+ for (const [name, tool] of Object.entries(flattenedTools)) {
1652
+ this.toolRegistry.set(`${serverId}:${name}`, {
1653
+ name,
1654
+ description: tool.description,
1655
+ inputSchema: tool.inputSchema,
1656
+ outputSchema: tool.outputSchema,
1657
+ serverId
1658
+ });
1659
+ }
1660
+ try {
1661
+ const res = await client.resources.list();
1662
+ for (const [, list] of Object.entries(res)) {
1663
+ for (const r of list) {
1664
+ this.resourceRegistry.set(`${serverId}:${r.uri}`, {
1665
+ uri: r.uri,
1666
+ name: r.name,
1667
+ description: r.description,
1668
+ mimeType: r.mimeType,
1669
+ serverId
1670
+ });
1671
+ }
1672
+ }
1673
+ } catch {
1674
+ }
1675
+ try {
1676
+ const prompts2 = await client.prompts.list();
1677
+ for (const [, list] of Object.entries(prompts2)) {
1678
+ for (const p of list) {
1679
+ this.promptRegistry.set(`${serverId}:${p.name}`, {
1680
+ name: p.name,
1681
+ description: p.description,
1682
+ arguments: p.arguments,
1683
+ serverId
1684
+ });
1685
+ }
1686
+ }
1687
+ } catch {
1688
+ }
1689
+ }
1690
+ getAvailableTools() {
1691
+ return Array.from(this.toolRegistry.values());
1692
+ }
1693
+ async getToolsetsForServer(serverId) {
1694
+ const id = this.getServerUniqueId(serverId);
1695
+ if (!id) {
1696
+ throw new Error(`No MCP client available for server: ${serverId}`);
1697
+ }
1698
+ const client = this.mcpClients.get(id);
1699
+ if (!client) {
1700
+ throw new Error(`No MCP client available for server: ${serverId}`);
1701
+ }
1702
+ const toolsets = await client.getToolsets();
1703
+ const flattenedTools = {};
1704
+ Object.values(toolsets).forEach((serverTools) => {
1705
+ Object.assign(flattenedTools, serverTools);
1706
+ });
1707
+ return flattenedTools;
1708
+ }
1709
+ getAvailableResources() {
1710
+ return Array.from(this.resourceRegistry.values());
1711
+ }
1712
+ getResourcesForServer(serverId) {
1713
+ const id = this.getServerUniqueId(serverId);
1714
+ if (!id) return [];
1715
+ return Array.from(this.resourceRegistry.values()).filter(
1716
+ (r) => r.serverId === id
1717
+ );
1718
+ }
1719
+ getAvailablePrompts() {
1720
+ return Array.from(this.promptRegistry.values());
1721
+ }
1722
+ getPromptsForServer(serverId) {
1723
+ const id = this.getServerUniqueId(serverId);
1724
+ if (!id) return [];
1725
+ return Array.from(this.promptRegistry.values()).filter(
1726
+ (p) => p.serverId === id
1727
+ );
1728
+ }
1729
+ async executeToolDirect(toolName, parameters = {}) {
1730
+ let serverId = "";
1731
+ let name = toolName;
1732
+ if (toolName.includes(":")) {
1733
+ const [sid, n] = toolName.split(":", 2);
1734
+ const mappedId = this.getServerUniqueId(sid);
1735
+ serverId = mappedId || (this.mcpClients.has(sid) ? sid : "");
1736
+ name = n;
1737
+ } else {
1738
+ for (const tool2 of this.toolRegistry.values()) {
1739
+ if (tool2.name === toolName) {
1740
+ serverId = tool2.serverId;
1741
+ name = toolName;
1742
+ break;
1743
+ }
1744
+ }
1745
+ }
1746
+ if (!serverId) {
1747
+ for (const [clientServerId, client2] of this.mcpClients.entries()) {
1748
+ try {
1749
+ const toolsets2 = await client2.getToolsets();
1750
+ const flattenedTools2 = {};
1751
+ Object.values(toolsets2).forEach((serverTools) => {
1752
+ Object.assign(flattenedTools2, serverTools);
1753
+ });
1754
+ if (flattenedTools2[toolName]) {
1755
+ serverId = clientServerId;
1756
+ name = toolName;
1757
+ break;
1758
+ }
1759
+ } catch {
1760
+ }
1761
+ }
1762
+ }
1763
+ if (!serverId) {
1764
+ throw new Error(`Tool not found in any connected server: ${toolName}`);
1765
+ }
1766
+ const client = this.mcpClients.get(serverId);
1767
+ if (!client)
1768
+ throw new Error(`No MCP client available for server: ${serverId}`);
1769
+ const toolsets = await client.getToolsets();
1770
+ const flattenedTools = {};
1771
+ Object.values(toolsets).forEach((serverTools) => {
1772
+ Object.assign(flattenedTools, serverTools);
1773
+ });
1774
+ const tool = flattenedTools[name];
1775
+ if (!tool)
1776
+ throw new Error(`Tool '${name}' not found in server '${serverId}'`);
1777
+ const schema = tool.inputSchema;
1778
+ const hasContextProperty = schema && typeof schema === "object" && schema.properties && Object.prototype.hasOwnProperty.call(
1779
+ schema.properties,
1780
+ "context"
1781
+ );
1782
+ const requiresContext = hasContextProperty || schema && Array.isArray(schema.required) && schema.required.includes("context");
1783
+ const contextWrapped = { context: parameters || {} };
1784
+ const direct = parameters || {};
1785
+ const attempts = requiresContext ? [contextWrapped, direct] : [direct, contextWrapped];
1786
+ let lastError = void 0;
1787
+ for (const args of attempts) {
1788
+ try {
1789
+ const result = await tool.execute(args);
1790
+ if (result && result.isError) {
1791
+ const errorText = result.content && result.content[0] && result.content[0].text ? result.content[0].text : "Unknown error";
1792
+ throw new Error(errorText);
1793
+ }
1794
+ return { result };
1795
+ } catch (err) {
1796
+ lastError = err;
1797
+ }
1798
+ }
1799
+ throw lastError;
1800
+ }
1801
+ async getResource(resourceUri, serverId) {
1802
+ let uri = resourceUri;
1803
+ const mappedId = this.getServerUniqueId(serverId);
1804
+ const resolvedServerId = mappedId || (this.mcpClients.has(serverId) ? serverId : void 0);
1805
+ if (!resolvedServerId) {
1806
+ throw new Error(`No MCP client available for server: ${serverId}`);
1807
+ }
1808
+ const client = this.mcpClients.get(resolvedServerId);
1809
+ if (!client) throw new Error("No MCP client available");
1810
+ const content = await client.resources.read(resolvedServerId, uri);
1811
+ return { contents: content?.contents || [] };
1812
+ }
1813
+ async getPrompt(promptName, serverId, args) {
1814
+ const mappedId = this.getServerUniqueId(serverId);
1815
+ const resolvedServerId = mappedId || (this.mcpClients.has(serverId) ? serverId : void 0);
1816
+ if (!resolvedServerId) {
1817
+ throw new Error(`No MCP client available for server: ${serverId}`);
1818
+ }
1819
+ const client = this.mcpClients.get(resolvedServerId);
1820
+ if (!client) throw new Error("No MCP client available");
1821
+ const content = await client.prompts.get({
1822
+ serverName: resolvedServerId,
1823
+ name: promptName,
1824
+ args: args || {}
1825
+ });
1826
+ return { content };
1827
+ }
1828
+ /**
1829
+ * Handles elicitation requests from MCP servers during direct tool execution
1830
+ */
1831
+ async handleElicitationRequest(elicitationRequest) {
1832
+ const requestId = `elicit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1833
+ return new Promise((resolve, reject) => {
1834
+ this.pendingElicitations.set(requestId, { resolve, reject });
1835
+ if (this.elicitationCallback) {
1836
+ this.elicitationCallback({
1837
+ requestId,
1838
+ message: elicitationRequest.message,
1839
+ schema: elicitationRequest.requestedSchema
1840
+ }).then(resolve).catch(reject);
1841
+ } else {
1842
+ const error = new Error("ELICITATION_REQUIRED");
1843
+ error.elicitationRequest = {
1844
+ requestId,
1845
+ message: elicitationRequest.message,
1846
+ schema: elicitationRequest.requestedSchema
1847
+ };
1848
+ reject(error);
1849
+ }
1850
+ });
1851
+ }
1852
+ /**
1853
+ * Responds to a pending elicitation request
1854
+ */
1855
+ respondToElicitation(requestId, response) {
1856
+ const pending = this.pendingElicitations.get(requestId);
1857
+ if (!pending) {
1858
+ return false;
1859
+ }
1860
+ pending.resolve(response);
1861
+ this.pendingElicitations.delete(requestId);
1862
+ return true;
1863
+ }
1864
+ /**
1865
+ * Gets the pending elicitations map for external access
1866
+ */
1867
+ getPendingElicitations() {
1868
+ return this.pendingElicitations;
1869
+ }
1870
+ /**
1871
+ * Sets a callback to handle elicitation requests
1872
+ */
1873
+ setElicitationCallback(callback) {
1874
+ this.elicitationCallback = callback;
1875
+ }
1876
+ /**
1877
+ * Clears the elicitation callback
1878
+ */
1879
+ clearElicitationCallback() {
1880
+ this.elicitationCallback = void 0;
1881
+ }
1882
+ };
1883
+
1884
+ // index.ts
1885
+ function logBox(content, title) {
1886
+ const lines = content.split("\n");
1887
+ const maxLength = Math.max(...lines.map((line) => line.length));
1888
+ const width = maxLength + 4;
1889
+ console.log("\u250C" + "\u2500".repeat(width) + "\u2510");
1890
+ if (title) {
1891
+ const titlePadding = Math.floor((width - title.length - 2) / 2);
1892
+ console.log(
1893
+ "\u2502" + " ".repeat(titlePadding) + title + " ".repeat(width - title.length - titlePadding) + "\u2502"
1894
+ );
1895
+ console.log("\u251C" + "\u2500".repeat(width) + "\u2524");
1896
+ }
1897
+ lines.forEach((line) => {
1898
+ const padding = width - line.length - 2;
1899
+ console.log("\u2502 " + line + " ".repeat(padding) + " \u2502");
1900
+ });
1901
+ console.log("\u2514" + "\u2500".repeat(width) + "\u2518");
1902
+ }
1903
+ function getMCPConfigFromEnv() {
1904
+ const command = process.env.MCP_SERVER_COMMAND;
1905
+ if (!command) {
1906
+ return null;
1907
+ }
1908
+ const argsString = process.env.MCP_SERVER_ARGS;
1909
+ const args = argsString ? JSON.parse(argsString) : [];
1910
+ return {
1911
+ command,
1912
+ args,
1913
+ name: "CLI Server"
1914
+ // Default name for CLI-provided servers
1915
+ };
1916
+ }
1917
+ var app = new Hono10();
1918
+ var mcpJamClientManager = new MCPJamClientManager();
1919
+ app.use("*", async (c, next) => {
1920
+ c.mcpJamClientManager = mcpJamClientManager;
1921
+ await next();
1922
+ });
1923
+ app.use("*", logger());
1924
+ var serverPort = process.env.PORT || "3001";
1925
+ var corsOrigins = [
1926
+ `http://localhost:${serverPort}`,
1927
+ "http://localhost:3000",
1928
+ // Keep for development
1929
+ "http://localhost:3001"
1930
+ // Keep for development
1931
+ ];
1932
+ app.use(
1933
+ "*",
1934
+ cors({
1935
+ origin: corsOrigins,
1936
+ credentials: true
1937
+ })
1938
+ );
1939
+ app.route("/api/mcp", mcp_default);
1940
+ app.get("/health", (c) => {
1941
+ return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1942
+ });
1943
+ app.get("/api/mcp-cli-config", (c) => {
1944
+ const mcpConfig = getMCPConfigFromEnv();
1945
+ return c.json({ config: mcpConfig });
1946
+ });
1947
+ if (process.env.NODE_ENV === "production") {
1948
+ app.use("/*", serveStatic({ root: "./dist/client" }));
1949
+ app.get("*", async (c) => {
1950
+ const path = c.req.path;
1951
+ if (path.startsWith("/api/")) {
1952
+ return c.notFound();
1953
+ }
1954
+ const indexPath = join(process.cwd(), "dist", "client", "index.html");
1955
+ let htmlContent = readFileSync(indexPath, "utf-8");
1956
+ const mcpConfig = getMCPConfigFromEnv();
1957
+ if (mcpConfig) {
1958
+ const configScript = `<script>window.MCP_CLI_CONFIG = ${JSON.stringify(mcpConfig)};</script>`;
1959
+ htmlContent = htmlContent.replace("</head>", `${configScript}</head>`);
1960
+ }
1961
+ return c.html(htmlContent);
1962
+ });
1963
+ } else {
1964
+ app.get("/", (c) => {
1965
+ return c.json({
1966
+ message: "MCPJam API Server",
1967
+ environment: "development",
1968
+ frontend: `http://localhost:${serverPort}`
1969
+ });
1970
+ });
1971
+ }
1972
+ var port = parseInt(process.env.PORT || "3001");
1973
+ logBox(`http://localhost:${port}`, "\u{1F680} Inspector Launched");
1974
+ var server = serve({
1975
+ fetch: app.fetch,
1976
+ port,
1977
+ hostname: "0.0.0.0"
1978
+ // Bind to all interfaces for Docker
1979
+ });
1980
+ process.on("SIGINT", () => {
1981
+ console.log("\n\u{1F6D1} Shutting down gracefully...");
1982
+ server.close();
1983
+ process.exit(0);
1984
+ });
1985
+ process.on("SIGTERM", () => {
1986
+ console.log("\n\u{1F6D1} Shutting down gracefully...");
1987
+ server.close();
1988
+ process.exit(0);
1989
+ });
1990
+ var index_default = app;
1991
+ export {
1992
+ index_default as default
1993
+ };
1994
+ //# sourceMappingURL=index.js.map