@mcpjam/inspector 0.9.8 → 0.9.9

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