@mcpjam/inspector 0.8.1 → 0.9.0

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.
Files changed (180) hide show
  1. package/README.md +28 -10
  2. package/bin/start.js +78 -66
  3. package/dist/client/assets/index-C4nE74q5.css +1 -0
  4. package/dist/client/assets/index-CcfG-2LO.js +357 -0
  5. package/dist/client/assets/index-CcfG-2LO.js.map +1 -0
  6. package/dist/client/catalyst.png +0 -0
  7. package/dist/client/index.html +14 -0
  8. package/dist/main/main.cjs +1330 -0
  9. package/dist/preload/preload.js +26 -0
  10. package/dist/renderer/assets/index-CYiU4_x2.css +1 -0
  11. package/dist/renderer/assets/index-woGCpEdp.js +356 -0
  12. package/dist/renderer/catalyst.png +0 -0
  13. package/dist/renderer/demo_1.png +0 -0
  14. package/dist/renderer/demo_2.png +0 -0
  15. package/dist/renderer/demo_3.png +0 -0
  16. package/dist/renderer/file.svg +1 -0
  17. package/dist/renderer/globe.svg +1 -0
  18. package/dist/renderer/index.html +14 -0
  19. package/dist/renderer/mcp.svg +1 -0
  20. package/dist/renderer/mcp_jam.svg +12 -0
  21. package/dist/renderer/mcp_jam_dark.png +0 -0
  22. package/dist/renderer/mcp_jam_light.png +0 -0
  23. package/dist/renderer/next.svg +1 -0
  24. package/dist/renderer/vercel.svg +1 -0
  25. package/dist/renderer/window.svg +1 -0
  26. package/dist/server/index.js +1101 -0
  27. package/dist/server/index.js.map +1 -0
  28. package/package.json +32 -17
  29. package/.next/BUILD_ID +0 -1
  30. package/.next/app-build-manifest.json +0 -89
  31. package/.next/app-path-routes-manifest.json +0 -13
  32. package/.next/build-manifest.json +0 -33
  33. package/.next/cache/.previewinfo +0 -1
  34. package/.next/cache/.rscinfo +0 -1
  35. package/.next/cache/.tsbuildinfo +0 -1
  36. package/.next/cache/eslint/.cache_1pr0xfn +0 -1
  37. package/.next/cache/webpack/client-production/0.pack +0 -0
  38. package/.next/cache/webpack/client-production/index.pack +0 -0
  39. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  40. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  41. package/.next/cache/webpack/server-production/0.pack +0 -0
  42. package/.next/cache/webpack/server-production/index.pack +0 -0
  43. package/.next/diagnostics/build-diagnostics.json +0 -6
  44. package/.next/diagnostics/framework.json +0 -1
  45. package/.next/export-marker.json +0 -6
  46. package/.next/images-manifest.json +0 -57
  47. package/.next/next-minimal-server.js.nft.json +0 -1
  48. package/.next/next-server.js.nft.json +0 -1
  49. package/.next/package.json +0 -1
  50. package/.next/prerender-manifest.json +0 -41
  51. package/.next/react-loadable-manifest.json +0 -1
  52. package/.next/required-server-files.json +0 -318
  53. package/.next/routes-manifest.json +0 -65
  54. package/.next/server/app/_not-found/page.js +0 -2
  55. package/.next/server/app/_not-found/page.js.nft.json +0 -1
  56. package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  57. package/.next/server/app/api/mcp/chat/route.js +0 -45
  58. package/.next/server/app/api/mcp/chat/route.js.nft.json +0 -1
  59. package/.next/server/app/api/mcp/chat/route_client-reference-manifest.js +0 -1
  60. package/.next/server/app/api/mcp/connect/route.js +0 -1
  61. package/.next/server/app/api/mcp/connect/route.js.nft.json +0 -1
  62. package/.next/server/app/api/mcp/connect/route_client-reference-manifest.js +0 -1
  63. package/.next/server/app/api/mcp/prompts/get/route.js +0 -1
  64. package/.next/server/app/api/mcp/prompts/get/route.js.nft.json +0 -1
  65. package/.next/server/app/api/mcp/prompts/get/route_client-reference-manifest.js +0 -1
  66. package/.next/server/app/api/mcp/prompts/list/route.js +0 -1
  67. package/.next/server/app/api/mcp/prompts/list/route.js.nft.json +0 -1
  68. package/.next/server/app/api/mcp/prompts/list/route_client-reference-manifest.js +0 -1
  69. package/.next/server/app/api/mcp/resources/list/route.js +0 -1
  70. package/.next/server/app/api/mcp/resources/list/route.js.nft.json +0 -1
  71. package/.next/server/app/api/mcp/resources/list/route_client-reference-manifest.js +0 -1
  72. package/.next/server/app/api/mcp/resources/read/route.js +0 -1
  73. package/.next/server/app/api/mcp/resources/read/route.js.nft.json +0 -1
  74. package/.next/server/app/api/mcp/resources/read/route_client-reference-manifest.js +0 -1
  75. package/.next/server/app/api/mcp/tools/route.js +0 -21
  76. package/.next/server/app/api/mcp/tools/route.js.nft.json +0 -1
  77. package/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +0 -1
  78. package/.next/server/app/favicon.ico/route.js +0 -1
  79. package/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  80. package/.next/server/app/favicon.ico.body +0 -0
  81. package/.next/server/app/favicon.ico.meta +0 -1
  82. package/.next/server/app/oauth/callback/page.js +0 -2
  83. package/.next/server/app/oauth/callback/page.js.nft.json +0 -1
  84. package/.next/server/app/oauth/callback/page_client-reference-manifest.js +0 -1
  85. package/.next/server/app/page.js +0 -16
  86. package/.next/server/app/page.js.nft.json +0 -1
  87. package/.next/server/app/page_client-reference-manifest.js +0 -1
  88. package/.next/server/app-paths-manifest.json +0 -13
  89. package/.next/server/chunks/175.js +0 -8
  90. package/.next/server/chunks/260.js +0 -82
  91. package/.next/server/chunks/546.js +0 -1
  92. package/.next/server/chunks/548.js +0 -6
  93. package/.next/server/chunks/55.js +0 -1
  94. package/.next/server/chunks/985.js +0 -22
  95. package/.next/server/functions-config-manifest.json +0 -4
  96. package/.next/server/interception-route-rewrite-manifest.js +0 -1
  97. package/.next/server/middleware-build-manifest.js +0 -1
  98. package/.next/server/middleware-manifest.json +0 -6
  99. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  100. package/.next/server/next-font-manifest.js +0 -1
  101. package/.next/server/next-font-manifest.json +0 -1
  102. package/.next/server/pages/500.html +0 -1
  103. package/.next/server/pages/_app.js +0 -1
  104. package/.next/server/pages/_app.js.nft.json +0 -1
  105. package/.next/server/pages/_document.js +0 -1
  106. package/.next/server/pages/_document.js.nft.json +0 -1
  107. package/.next/server/pages/_error.js +0 -19
  108. package/.next/server/pages/_error.js.nft.json +0 -1
  109. package/.next/server/pages-manifest.json +0 -5
  110. package/.next/server/server-reference-manifest.js +0 -1
  111. package/.next/server/server-reference-manifest.json +0 -1
  112. package/.next/server/webpack-runtime.js +0 -1
  113. package/.next/static/Pq27RIJUfrYOGSjOrYx4E/_buildManifest.js +0 -1
  114. package/.next/static/Pq27RIJUfrYOGSjOrYx4E/_ssgManifest.js +0 -1
  115. package/.next/static/chunks/14-ae3a01e72ea53777.js +0 -1
  116. package/.next/static/chunks/214-cc4c35d88f2695ed.js +0 -1
  117. package/.next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +0 -1
  118. package/.next/static/chunks/866-04c19dda4c52f2bf.js +0 -1
  119. package/.next/static/chunks/964-eda38e26c0391a47.js +0 -1
  120. package/.next/static/chunks/app/_not-found/page-8601c49989b0be94.js +0 -1
  121. package/.next/static/chunks/app/api/mcp/chat/route-0341498a8bf5f2da.js +0 -1
  122. package/.next/static/chunks/app/api/mcp/connect/route-0341498a8bf5f2da.js +0 -1
  123. package/.next/static/chunks/app/api/mcp/prompts/get/route-0341498a8bf5f2da.js +0 -1
  124. package/.next/static/chunks/app/api/mcp/prompts/list/route-0341498a8bf5f2da.js +0 -1
  125. package/.next/static/chunks/app/api/mcp/resources/list/route-0341498a8bf5f2da.js +0 -1
  126. package/.next/static/chunks/app/api/mcp/resources/read/route-0341498a8bf5f2da.js +0 -1
  127. package/.next/static/chunks/app/api/mcp/tools/route-0341498a8bf5f2da.js +0 -1
  128. package/.next/static/chunks/app/layout-9e8115d4bf656fa0.js +0 -1
  129. package/.next/static/chunks/app/oauth/callback/page-cf6cb1ac31175f40.js +0 -1
  130. package/.next/static/chunks/app/page-fdd433623879220d.js +0 -1
  131. package/.next/static/chunks/framework-7c95b8e5103c9e90.js +0 -1
  132. package/.next/static/chunks/main-app-4e9e28316818cdde.js +0 -1
  133. package/.next/static/chunks/main-bbdafee21a7bd1d6.js +0 -1
  134. package/.next/static/chunks/pages/_app-0a0020ddd67f79cf.js +0 -1
  135. package/.next/static/chunks/pages/_error-03529f2c21436739.js +0 -1
  136. package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  137. package/.next/static/chunks/webpack-cdfccaf38062dd25.js +0 -1
  138. package/.next/static/css/1e852d83e9c1d0c6.css +0 -1
  139. package/.next/static/css/f30152c0704fba31.css +0 -1
  140. package/.next/static/css/fe751fdbe975e9ca.css +0 -1
  141. package/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  142. package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  143. package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  144. package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  145. package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  146. package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  147. package/.next/trace +0 -35
  148. package/.next/types/app/api/mcp/chat/route.ts +0 -347
  149. package/.next/types/app/api/mcp/connect/route.ts +0 -347
  150. package/.next/types/app/api/mcp/prompts/get/route.ts +0 -347
  151. package/.next/types/app/api/mcp/prompts/list/route.ts +0 -347
  152. package/.next/types/app/api/mcp/resources/list/route.ts +0 -347
  153. package/.next/types/app/api/mcp/resources/read/route.ts +0 -347
  154. package/.next/types/app/api/mcp/tools/route.ts +0 -347
  155. package/.next/types/app/layout.ts +0 -84
  156. package/.next/types/app/oauth/callback/page.ts +0 -84
  157. package/.next/types/app/page.ts +0 -84
  158. package/.next/types/cache-life.d.ts +0 -141
  159. package/.next/types/package.json +0 -1
  160. package/next.config.ts +0 -7
  161. /package/{public → dist/client}/claude_logo.png +0 -0
  162. /package/{public → dist/client}/demo_1.png +0 -0
  163. /package/{public → dist/client}/demo_2.png +0 -0
  164. /package/{public → dist/client}/demo_3.png +0 -0
  165. /package/{public → dist/client}/file.svg +0 -0
  166. /package/{public → dist/client}/globe.svg +0 -0
  167. /package/{public → dist/client}/mcp.svg +0 -0
  168. /package/{public → dist/client}/mcp_jam.svg +0 -0
  169. /package/{public → dist/client}/mcp_jam_dark.png +0 -0
  170. /package/{public → dist/client}/mcp_jam_light.png +0 -0
  171. /package/{public → dist/client}/next.svg +0 -0
  172. /package/{public → dist/client}/ollama_dark.png +0 -0
  173. /package/{public → dist/client}/ollama_logo.svg +0 -0
  174. /package/{public → dist/client}/openai_logo.png +0 -0
  175. /package/{public → dist/client}/vercel.svg +0 -0
  176. /package/{public → dist/client}/window.svg +0 -0
  177. /package/{.next/static/media/claude_logo.d33b25b0.png → dist/renderer/claude_logo.png} +0 -0
  178. /package/{.next/static/media/ollama_dark.9af45ac0.png → dist/renderer/ollama_dark.png} +0 -0
  179. /package/{.next/static/media/ollama_logo.9f08a95b.svg → dist/renderer/ollama_logo.svg} +0 -0
  180. /package/{.next/static/media/openai_logo.3f83154a.png → dist/renderer/openai_logo.png} +0 -0
@@ -0,0 +1,1330 @@
1
+ "use strict";
2
+ const electron = require("electron");
3
+ const nodeServer = require("@hono/node-server");
4
+ const path = require("path");
5
+ const hono = require("hono");
6
+ const cors = require("hono/cors");
7
+ const logger = require("hono/logger");
8
+ const serveStatic = require("@hono/node-server/serve-static");
9
+ const mcp$1 = require("@mastra/mcp");
10
+ const zodToJsonSchema = require("zod-to-json-schema");
11
+ const agent = require("@mastra/core/agent");
12
+ const anthropic = require("@ai-sdk/anthropic");
13
+ const openai = require("@ai-sdk/openai");
14
+ const ollamaAiProvider = require("ollama-ai-provider");
15
+ const log = require("electron-log");
16
+ const updateElectronApp = require("update-electron-app");
17
+ const fs = require("fs/promises");
18
+ function validateServerConfig(serverConfig) {
19
+ var _a, _b, _c;
20
+ if (!serverConfig) {
21
+ return {
22
+ success: false,
23
+ error: {
24
+ message: "Server configuration is required",
25
+ status: 400
26
+ }
27
+ };
28
+ }
29
+ const config = { ...serverConfig };
30
+ if (config.url) {
31
+ try {
32
+ if (typeof config.url === "string") {
33
+ config.url = new URL(config.url);
34
+ } else if (typeof config.url === "object" && !config.url.href) {
35
+ return {
36
+ success: false,
37
+ error: {
38
+ message: "Invalid URL configuration",
39
+ status: 400
40
+ }
41
+ };
42
+ }
43
+ if ((_a = config.oauth) == null ? void 0 : _a.access_token) {
44
+ const authHeaders = {
45
+ Authorization: `Bearer ${config.oauth.access_token}`,
46
+ ...((_b = config.requestInit) == null ? void 0 : _b.headers) || {}
47
+ };
48
+ config.requestInit = {
49
+ ...config.requestInit,
50
+ headers: authHeaders
51
+ };
52
+ config.eventSourceInit = {
53
+ fetch(input, init) {
54
+ var _a2;
55
+ const headers = new Headers((init == null ? void 0 : init.headers) || {});
56
+ headers.set(
57
+ "Authorization",
58
+ `Bearer ${config.oauth.access_token}`
59
+ );
60
+ if ((_a2 = config.requestInit) == null ? void 0 : _a2.headers) {
61
+ const requestHeaders = new Headers(config.requestInit.headers);
62
+ requestHeaders.forEach((value, key) => {
63
+ if (key.toLowerCase() !== "authorization") {
64
+ headers.set(key, value);
65
+ }
66
+ });
67
+ }
68
+ return fetch(input, {
69
+ ...init,
70
+ headers
71
+ });
72
+ }
73
+ };
74
+ } else if ((_c = config.requestInit) == null ? void 0 : _c.headers) {
75
+ config.eventSourceInit = {
76
+ fetch(input, init) {
77
+ const headers = new Headers((init == null ? void 0 : init.headers) || {});
78
+ const requestHeaders = new Headers(config.requestInit.headers);
79
+ requestHeaders.forEach((value, key) => {
80
+ headers.set(key, value);
81
+ });
82
+ return fetch(input, {
83
+ ...init,
84
+ headers
85
+ });
86
+ }
87
+ };
88
+ }
89
+ } catch (error) {
90
+ return {
91
+ success: false,
92
+ error: {
93
+ message: `Invalid URL format: ${error}`,
94
+ status: 400
95
+ }
96
+ };
97
+ }
98
+ }
99
+ return {
100
+ success: true,
101
+ config
102
+ };
103
+ }
104
+ function createMCPClient(config, id) {
105
+ return new mcp$1.MCPClient({
106
+ id,
107
+ servers: {
108
+ server: config
109
+ }
110
+ });
111
+ }
112
+ const validateMultipleServerConfigs = (serverConfigs) => {
113
+ if (!serverConfigs || Object.keys(serverConfigs).length === 0) {
114
+ return {
115
+ success: false,
116
+ error: {
117
+ message: "At least one server configuration is required",
118
+ status: 400
119
+ }
120
+ };
121
+ }
122
+ const validConfigs = {};
123
+ const errors = {};
124
+ let hasErrors = false;
125
+ for (const [serverName, serverConfig] of Object.entries(serverConfigs)) {
126
+ const validationResult = validateServerConfig(serverConfig);
127
+ if (validationResult.success && validationResult.config) {
128
+ validConfigs[serverName] = validationResult.config;
129
+ } else {
130
+ hasErrors = true;
131
+ let errorMessage = "Configuration validation failed";
132
+ if (validationResult.error) {
133
+ errorMessage = validationResult.error.message;
134
+ }
135
+ errors[serverName] = errorMessage;
136
+ }
137
+ }
138
+ if (!hasErrors) {
139
+ return {
140
+ success: true,
141
+ validConfigs
142
+ };
143
+ }
144
+ if (Object.keys(validConfigs).length > 0) {
145
+ return {
146
+ success: false,
147
+ validConfigs,
148
+ errors
149
+ };
150
+ }
151
+ return {
152
+ success: false,
153
+ errors,
154
+ error: {
155
+ message: "All server configurations failed validation",
156
+ status: 400
157
+ }
158
+ };
159
+ };
160
+ function createMCPClientWithMultipleConnections(serverConfigs) {
161
+ const normalizedConfigs = {};
162
+ for (const [serverName, config] of Object.entries(serverConfigs)) {
163
+ const normalizedName = normalizeServerConfigName(serverName);
164
+ normalizedConfigs[normalizedName] = config;
165
+ }
166
+ return new mcp$1.MCPClient({
167
+ id: `chat-${Date.now()}`,
168
+ servers: normalizedConfigs
169
+ });
170
+ }
171
+ function normalizeServerConfigName(serverName) {
172
+ return serverName.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
173
+ }
174
+ const connect = new hono.Hono();
175
+ connect.post("/", async (c) => {
176
+ try {
177
+ const { serverConfig } = await c.req.json();
178
+ const validation = validateServerConfig(serverConfig);
179
+ if (!validation.success) {
180
+ const error = validation.error;
181
+ return c.json(
182
+ {
183
+ success: false,
184
+ error: error.message
185
+ },
186
+ error.status
187
+ );
188
+ }
189
+ let client;
190
+ try {
191
+ client = createMCPClient(validation.config, `test-${Date.now()}`);
192
+ } catch (error) {
193
+ return c.json(
194
+ {
195
+ success: false,
196
+ error: `Failed to create a MCP client. Please double check your server configuration: ${JSON.stringify(serverConfig)}`,
197
+ details: error instanceof Error ? error.message : "Unknown error"
198
+ },
199
+ 500
200
+ );
201
+ }
202
+ try {
203
+ await client.getTools();
204
+ await client.disconnect();
205
+ return c.json({
206
+ success: true
207
+ });
208
+ } catch (error) {
209
+ return c.json(
210
+ {
211
+ success: false,
212
+ error: `MCP configuration is invalid. Please double check your server configuration: ${JSON.stringify(serverConfig)}`,
213
+ details: error instanceof Error ? error.message : "Unknown error"
214
+ },
215
+ 500
216
+ );
217
+ }
218
+ } catch (error) {
219
+ return c.json(
220
+ {
221
+ success: false,
222
+ error: "Failed to parse request body",
223
+ details: error instanceof Error ? error.message : "Unknown error"
224
+ },
225
+ 400
226
+ );
227
+ }
228
+ });
229
+ const tools = new hono.Hono();
230
+ const pendingElicitations$1 = /* @__PURE__ */ new Map();
231
+ tools.post("/", async (c) => {
232
+ let client = null;
233
+ let encoder = null;
234
+ let streamController = null;
235
+ let action;
236
+ let toolName;
237
+ try {
238
+ const requestData = await c.req.json();
239
+ action = requestData.action;
240
+ toolName = requestData.toolName;
241
+ const { serverConfig, parameters, requestId, response } = requestData;
242
+ if (!action || !["list", "execute", "respond"].includes(action)) {
243
+ return c.json(
244
+ {
245
+ success: false,
246
+ error: "Action must be 'list', 'execute', or 'respond'"
247
+ },
248
+ 400
249
+ );
250
+ }
251
+ if (action === "respond") {
252
+ if (!requestId) {
253
+ return c.json(
254
+ {
255
+ success: false,
256
+ error: "requestId is required for respond action"
257
+ },
258
+ 400
259
+ );
260
+ }
261
+ const pending = pendingElicitations$1.get(requestId);
262
+ if (!pending) {
263
+ return c.json(
264
+ {
265
+ success: false,
266
+ error: "No pending elicitation found for this requestId"
267
+ },
268
+ 404
269
+ );
270
+ }
271
+ pending.resolve(response);
272
+ pendingElicitations$1.delete(requestId);
273
+ return c.json({ success: true });
274
+ }
275
+ const validation = validateServerConfig(serverConfig);
276
+ if (!validation.success) {
277
+ return c.json(
278
+ { success: false, error: validation.error.message },
279
+ validation.error.status
280
+ );
281
+ }
282
+ encoder = new TextEncoder();
283
+ const readableStream = new ReadableStream({
284
+ async start(controller) {
285
+ streamController = controller;
286
+ try {
287
+ const clientId = `tools-${action}-${Date.now()}`;
288
+ client = createMCPClient(validation.config, clientId);
289
+ if (action === "list") {
290
+ controller.enqueue(
291
+ encoder.encode(
292
+ `data: ${JSON.stringify({
293
+ type: "tools_loading",
294
+ message: "Fetching tools from server..."
295
+ })}
296
+
297
+ `
298
+ )
299
+ );
300
+ const tools2 = await client.getTools();
301
+ const toolsWithJsonSchema = Object.fromEntries(
302
+ Object.entries(tools2).map(([toolName2, tool]) => {
303
+ return [
304
+ toolName2,
305
+ {
306
+ ...tool,
307
+ inputSchema: zodToJsonSchema.zodToJsonSchema(
308
+ tool.inputSchema
309
+ )
310
+ }
311
+ ];
312
+ })
313
+ );
314
+ controller.enqueue(
315
+ encoder.encode(
316
+ `data: ${JSON.stringify({
317
+ type: "tools_list",
318
+ tools: toolsWithJsonSchema
319
+ })}
320
+
321
+ `
322
+ )
323
+ );
324
+ } else if (action === "execute") {
325
+ if (!toolName) {
326
+ controller.enqueue(
327
+ encoder.encode(
328
+ `data: ${JSON.stringify({
329
+ type: "tool_error",
330
+ error: "Tool name is required for execution"
331
+ })}
332
+
333
+ `
334
+ )
335
+ );
336
+ return;
337
+ }
338
+ controller.enqueue(
339
+ encoder.encode(
340
+ `data: ${JSON.stringify({
341
+ type: "tool_executing",
342
+ toolName,
343
+ parameters: parameters || {},
344
+ message: "Executing tool..."
345
+ })}
346
+
347
+ `
348
+ )
349
+ );
350
+ const tools2 = await client.getTools();
351
+ const tool = tools2[toolName];
352
+ if (!tool) {
353
+ controller.enqueue(
354
+ encoder.encode(
355
+ `data: ${JSON.stringify({
356
+ type: "tool_error",
357
+ error: `Tool '${toolName}' not found`
358
+ })}
359
+
360
+ `
361
+ )
362
+ );
363
+ return;
364
+ }
365
+ const toolArgs = parameters && typeof parameters === "object" ? parameters : {};
366
+ const elicitationHandler = async (elicitationRequest) => {
367
+ const requestId2 = `elicit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
368
+ if (streamController && encoder) {
369
+ streamController.enqueue(
370
+ encoder.encode(
371
+ `data: ${JSON.stringify({
372
+ type: "elicitation_request",
373
+ requestId: requestId2,
374
+ message: elicitationRequest.message,
375
+ schema: elicitationRequest.requestedSchema,
376
+ timestamp: /* @__PURE__ */ new Date()
377
+ })}
378
+
379
+ `
380
+ )
381
+ );
382
+ }
383
+ return new Promise((resolve, reject) => {
384
+ pendingElicitations$1.set(requestId2, { resolve, reject });
385
+ setTimeout(() => {
386
+ if (pendingElicitations$1.has(requestId2)) {
387
+ pendingElicitations$1.delete(requestId2);
388
+ reject(new Error("Elicitation timeout"));
389
+ }
390
+ }, 3e5);
391
+ });
392
+ };
393
+ if (client.elicitation && client.elicitation.onRequest) {
394
+ const serverName = "server";
395
+ client.elicitation.onRequest(serverName, elicitationHandler);
396
+ }
397
+ const result = await tool.execute({
398
+ context: toolArgs
399
+ });
400
+ controller.enqueue(
401
+ encoder.encode(
402
+ `data: ${JSON.stringify({
403
+ type: "tool_result",
404
+ toolName,
405
+ result
406
+ })}
407
+
408
+ `
409
+ )
410
+ );
411
+ controller.enqueue(
412
+ encoder.encode(
413
+ `data: ${JSON.stringify({
414
+ type: "elicitation_complete",
415
+ toolName
416
+ })}
417
+
418
+ `
419
+ )
420
+ );
421
+ }
422
+ controller.enqueue(encoder.encode(`data: [DONE]
423
+
424
+ `));
425
+ } catch (error) {
426
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
427
+ controller.enqueue(
428
+ encoder.encode(
429
+ `data: ${JSON.stringify({
430
+ type: "tool_error",
431
+ error: errorMsg
432
+ })}
433
+
434
+ `
435
+ )
436
+ );
437
+ } finally {
438
+ if (client) {
439
+ await client.disconnect();
440
+ }
441
+ controller.close();
442
+ }
443
+ }
444
+ });
445
+ return new Response(readableStream, {
446
+ headers: {
447
+ "Content-Type": "text/event-stream",
448
+ "Cache-Control": "no-cache",
449
+ Connection: "keep-alive"
450
+ }
451
+ });
452
+ } catch (error) {
453
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
454
+ if (client) {
455
+ try {
456
+ await client.disconnect();
457
+ } catch (cleanupError) {
458
+ }
459
+ }
460
+ return c.json(
461
+ {
462
+ success: false,
463
+ error: errorMsg
464
+ },
465
+ 500
466
+ );
467
+ }
468
+ });
469
+ const resources = new hono.Hono();
470
+ resources.post("/list", async (c) => {
471
+ try {
472
+ const { serverConfig } = await c.req.json();
473
+ const validation = validateServerConfig(serverConfig);
474
+ if (!validation.success) {
475
+ return c.json(
476
+ { success: false, error: validation.error.message },
477
+ validation.error.status
478
+ );
479
+ }
480
+ const client = createMCPClient(
481
+ validation.config,
482
+ `resources-list-${Date.now()}`
483
+ );
484
+ try {
485
+ const resources2 = await client.resources.list();
486
+ await client.disconnect();
487
+ return c.json({ resources: resources2 });
488
+ } catch (error) {
489
+ await client.disconnect();
490
+ throw error;
491
+ }
492
+ } catch (error) {
493
+ console.error("Error fetching resources:", 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
+ resources.post("/read", async (c) => {
504
+ try {
505
+ const { serverConfig, uri } = await c.req.json();
506
+ const validation = validateServerConfig(serverConfig);
507
+ if (!validation.success) {
508
+ return c.json(
509
+ { success: false, error: validation.error.message },
510
+ validation.error.status
511
+ );
512
+ }
513
+ if (!uri) {
514
+ return c.json(
515
+ {
516
+ success: false,
517
+ error: "Resource URI is required"
518
+ },
519
+ 400
520
+ );
521
+ }
522
+ const client = createMCPClient(
523
+ validation.config,
524
+ `resources-read-${Date.now()}`
525
+ );
526
+ try {
527
+ const content = await client.resources.read("server", uri);
528
+ await client.disconnect();
529
+ return c.json({ content });
530
+ } catch (error) {
531
+ await client.disconnect();
532
+ throw error;
533
+ }
534
+ } catch (error) {
535
+ console.error("Error reading resource:", error);
536
+ return c.json(
537
+ {
538
+ success: false,
539
+ error: error instanceof Error ? error.message : "Unknown error"
540
+ },
541
+ 500
542
+ );
543
+ }
544
+ });
545
+ const prompts = new hono.Hono();
546
+ prompts.post("/list", async (c) => {
547
+ try {
548
+ const { serverConfig } = await c.req.json();
549
+ const validation = validateServerConfig(serverConfig);
550
+ if (!validation.success) {
551
+ return c.json(
552
+ { success: false, error: validation.error.message },
553
+ validation.error.status
554
+ );
555
+ }
556
+ const client = createMCPClient(
557
+ validation.config,
558
+ `prompts-list-${Date.now()}`
559
+ );
560
+ try {
561
+ const prompts2 = await client.prompts.list();
562
+ await client.disconnect();
563
+ return c.json({ prompts: prompts2 });
564
+ } catch (error) {
565
+ await client.disconnect();
566
+ throw error;
567
+ }
568
+ } catch (error) {
569
+ console.error("Error fetching prompts:", error);
570
+ return c.json(
571
+ {
572
+ success: false,
573
+ error: error instanceof Error ? error.message : "Unknown error"
574
+ },
575
+ 500
576
+ );
577
+ }
578
+ });
579
+ prompts.post("/get", async (c) => {
580
+ try {
581
+ const { serverConfig, name, args } = await c.req.json();
582
+ const validation = validateServerConfig(serverConfig);
583
+ if (!validation.success) {
584
+ return c.json(
585
+ { success: false, error: validation.error.message },
586
+ validation.error.status
587
+ );
588
+ }
589
+ if (!name) {
590
+ return c.json(
591
+ {
592
+ success: false,
593
+ error: "Prompt name is required"
594
+ },
595
+ 400
596
+ );
597
+ }
598
+ const client = createMCPClient(
599
+ validation.config,
600
+ `prompts-get-${Date.now()}`
601
+ );
602
+ try {
603
+ const content = await client.prompts.get({
604
+ serverName: "server",
605
+ name,
606
+ args: args || {}
607
+ });
608
+ await client.disconnect();
609
+ return c.json({ content });
610
+ } catch (error) {
611
+ await client.disconnect();
612
+ throw error;
613
+ }
614
+ } catch (error) {
615
+ console.error("Error getting prompt:", error);
616
+ return c.json(
617
+ {
618
+ success: false,
619
+ error: error instanceof Error ? error.message : "Unknown error"
620
+ },
621
+ 500
622
+ );
623
+ }
624
+ });
625
+ const chat = new hono.Hono();
626
+ const pendingElicitations = /* @__PURE__ */ new Map();
627
+ chat.post("/", async (c) => {
628
+ let client = null;
629
+ try {
630
+ const requestData = await c.req.json();
631
+ const {
632
+ serverConfigs,
633
+ model,
634
+ apiKey,
635
+ systemPrompt,
636
+ messages,
637
+ ollamaBaseUrl,
638
+ action,
639
+ requestId,
640
+ response
641
+ } = requestData;
642
+ if (action === "elicitation_response") {
643
+ if (!requestId) {
644
+ return c.json(
645
+ {
646
+ success: false,
647
+ error: "requestId is required for elicitation_response action"
648
+ },
649
+ 400
650
+ );
651
+ }
652
+ const pending = pendingElicitations.get(requestId);
653
+ if (!pending) {
654
+ return c.json(
655
+ {
656
+ success: false,
657
+ error: "No pending elicitation found for this requestId"
658
+ },
659
+ 404
660
+ );
661
+ }
662
+ pending.resolve(response);
663
+ pendingElicitations.delete(requestId);
664
+ return c.json({ success: true });
665
+ }
666
+ if (!model || !model.id || !apiKey || !messages) {
667
+ return c.json(
668
+ {
669
+ success: false,
670
+ error: "model (with id), apiKey, and messages are required"
671
+ },
672
+ 400
673
+ );
674
+ }
675
+ if (serverConfigs && Object.keys(serverConfigs).length > 0) {
676
+ const validation = validateMultipleServerConfigs(serverConfigs);
677
+ if (!validation.success) {
678
+ return c.json(
679
+ {
680
+ success: false,
681
+ error: validation.error.message,
682
+ details: validation.errors
683
+ },
684
+ validation.error.status
685
+ );
686
+ }
687
+ client = createMCPClientWithMultipleConnections(validation.validConfigs);
688
+ } else {
689
+ client = new mcp$1.MCPClient({
690
+ id: `chat-${Date.now()}`,
691
+ servers: {}
692
+ });
693
+ }
694
+ const tools2 = await client.getTools();
695
+ const llmModel = getLlmModel(model, apiKey, ollamaBaseUrl);
696
+ let toolCallId = 0;
697
+ let streamController = null;
698
+ let encoder = null;
699
+ const elicitationHandler = async (elicitationRequest) => {
700
+ const requestId2 = `elicit_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
701
+ if (streamController && encoder) {
702
+ streamController.enqueue(
703
+ encoder.encode(
704
+ `data: ${JSON.stringify({
705
+ type: "elicitation_request",
706
+ requestId: requestId2,
707
+ message: elicitationRequest.message,
708
+ schema: elicitationRequest.requestedSchema,
709
+ timestamp: /* @__PURE__ */ new Date()
710
+ })}
711
+
712
+ `
713
+ )
714
+ );
715
+ }
716
+ return new Promise((resolve, reject) => {
717
+ pendingElicitations.set(requestId2, { resolve, reject });
718
+ setTimeout(() => {
719
+ if (pendingElicitations.has(requestId2)) {
720
+ pendingElicitations.delete(requestId2);
721
+ reject(new Error("Elicitation timeout"));
722
+ }
723
+ }, 3e5);
724
+ });
725
+ };
726
+ if (client.elicitation && client.elicitation.onRequest && serverConfigs) {
727
+ for (const serverName of Object.keys(serverConfigs)) {
728
+ const normalizedName = serverName.toLowerCase().replace(/[\s\-]+/g, "_").replace(/[^a-z0-9_]/g, "");
729
+ client.elicitation.onRequest(normalizedName, elicitationHandler);
730
+ }
731
+ }
732
+ const originalTools = tools2 && Object.keys(tools2).length > 0 ? tools2 : {};
733
+ const wrappedTools = {};
734
+ for (const [name, tool] of Object.entries(originalTools)) {
735
+ wrappedTools[name] = {
736
+ ...tool,
737
+ execute: async (params) => {
738
+ const currentToolCallId = ++toolCallId;
739
+ if (streamController && encoder) {
740
+ streamController.enqueue(
741
+ encoder.encode(
742
+ `data: ${JSON.stringify({
743
+ type: "tool_call",
744
+ toolCall: {
745
+ id: currentToolCallId,
746
+ name,
747
+ parameters: params,
748
+ timestamp: /* @__PURE__ */ new Date(),
749
+ status: "executing"
750
+ }
751
+ })}
752
+
753
+ `
754
+ )
755
+ );
756
+ }
757
+ try {
758
+ const result = await tool.execute(params);
759
+ if (streamController && encoder) {
760
+ streamController.enqueue(
761
+ encoder.encode(
762
+ `data: ${JSON.stringify({
763
+ type: "tool_result",
764
+ toolResult: {
765
+ id: currentToolCallId,
766
+ toolCallId: currentToolCallId,
767
+ result,
768
+ timestamp: /* @__PURE__ */ new Date()
769
+ }
770
+ })}
771
+
772
+ `
773
+ )
774
+ );
775
+ }
776
+ return result;
777
+ } catch (error) {
778
+ if (streamController && encoder) {
779
+ streamController.enqueue(
780
+ encoder.encode(
781
+ `data: ${JSON.stringify({
782
+ type: "tool_result",
783
+ toolResult: {
784
+ id: currentToolCallId,
785
+ toolCallId: currentToolCallId,
786
+ error: error instanceof Error ? error.message : String(error),
787
+ timestamp: /* @__PURE__ */ new Date()
788
+ }
789
+ })}
790
+
791
+ `
792
+ )
793
+ );
794
+ }
795
+ throw error;
796
+ }
797
+ }
798
+ };
799
+ }
800
+ const agent$1 = new agent.Agent({
801
+ name: "MCP Chat Agent",
802
+ instructions: systemPrompt || "You are a helpful assistant with access to MCP tools.",
803
+ model: llmModel,
804
+ tools: Object.keys(wrappedTools).length > 0 ? wrappedTools : void 0
805
+ });
806
+ const formattedMessages = messages.map((msg) => ({
807
+ role: msg.role,
808
+ content: msg.content
809
+ }));
810
+ const stream = await agent$1.stream(formattedMessages, {
811
+ maxSteps: 10
812
+ // Allow up to 10 steps for tool usage
813
+ });
814
+ encoder = new TextEncoder();
815
+ const readableStream = new ReadableStream({
816
+ async start(controller) {
817
+ streamController = controller;
818
+ try {
819
+ let hasContent = false;
820
+ for await (const chunk of stream.textStream) {
821
+ if (chunk && chunk.trim()) {
822
+ hasContent = true;
823
+ controller.enqueue(
824
+ encoder.encode(
825
+ `data: ${JSON.stringify({ type: "text", content: chunk })}
826
+
827
+ `
828
+ )
829
+ );
830
+ }
831
+ }
832
+ if (!hasContent) {
833
+ controller.enqueue(
834
+ encoder.encode(
835
+ `data: ${JSON.stringify({ type: "text", content: "I apologize, but I couldn't generate a response. Please try again." })}
836
+
837
+ `
838
+ )
839
+ );
840
+ }
841
+ controller.enqueue(
842
+ encoder.encode(
843
+ `data: ${JSON.stringify({
844
+ type: "elicitation_complete"
845
+ })}
846
+
847
+ `
848
+ )
849
+ );
850
+ controller.enqueue(encoder.encode(`data: [DONE]
851
+
852
+ `));
853
+ } catch (error) {
854
+ console.error("Streaming error:", error);
855
+ controller.enqueue(
856
+ encoder.encode(
857
+ `data: ${JSON.stringify({
858
+ type: "error",
859
+ error: error instanceof Error ? error.message : "Unknown error"
860
+ })}
861
+
862
+ `
863
+ )
864
+ );
865
+ } finally {
866
+ if (client) {
867
+ try {
868
+ await client.disconnect();
869
+ } catch (cleanupError) {
870
+ console.warn(
871
+ "Error cleaning up MCP client after streaming:",
872
+ cleanupError
873
+ );
874
+ }
875
+ }
876
+ controller.close();
877
+ }
878
+ }
879
+ });
880
+ return new Response(readableStream, {
881
+ headers: {
882
+ "Content-Type": "text/event-stream",
883
+ "Cache-Control": "no-cache",
884
+ Connection: "keep-alive"
885
+ }
886
+ });
887
+ } catch (error) {
888
+ console.error("Error in chat API:", error);
889
+ if (client) {
890
+ try {
891
+ await client.disconnect();
892
+ } catch (cleanupError) {
893
+ console.warn("Error cleaning up MCP client after error:", cleanupError);
894
+ }
895
+ }
896
+ return c.json(
897
+ {
898
+ success: false,
899
+ error: error instanceof Error ? error.message : "Unknown error"
900
+ },
901
+ 500
902
+ );
903
+ }
904
+ });
905
+ const getLlmModel = (modelDefinition, apiKey, ollamaBaseUrl) => {
906
+ if (!modelDefinition || !modelDefinition.id || !modelDefinition.provider) {
907
+ throw new Error(
908
+ `Invalid model definition: ${JSON.stringify(modelDefinition)}`
909
+ );
910
+ }
911
+ switch (modelDefinition.provider) {
912
+ case "anthropic":
913
+ return anthropic.createAnthropic({ apiKey })(modelDefinition.id);
914
+ case "openai":
915
+ return openai.createOpenAI({ apiKey })(modelDefinition.id);
916
+ case "ollama":
917
+ const baseUrl = ollamaBaseUrl || "http://localhost:11434";
918
+ return ollamaAiProvider.createOllama({
919
+ baseURL: `${baseUrl}/api`
920
+ // Configurable Ollama API endpoint
921
+ })(modelDefinition.id, {
922
+ simulateStreaming: true
923
+ // Enable streaming for Ollama models
924
+ });
925
+ default:
926
+ throw new Error(
927
+ `Unsupported provider: ${modelDefinition.provider} for model: ${modelDefinition.id}`
928
+ );
929
+ }
930
+ };
931
+ const oauth = new hono.Hono();
932
+ oauth.get("/metadata", async (c) => {
933
+ try {
934
+ const url = c.req.query("url");
935
+ if (!url) {
936
+ return c.json({ error: "Missing url parameter" }, 400);
937
+ }
938
+ let metadataUrl;
939
+ try {
940
+ metadataUrl = new URL(url);
941
+ if (metadataUrl.protocol !== "https:") {
942
+ return c.json({ error: "Only HTTPS URLs are allowed" }, 400);
943
+ }
944
+ } catch (error) {
945
+ return c.json({ error: "Invalid URL format" }, 400);
946
+ }
947
+ const response = await fetch(metadataUrl.toString(), {
948
+ method: "GET",
949
+ headers: {
950
+ Accept: "application/json",
951
+ "User-Agent": "MCP-Inspector/1.0"
952
+ }
953
+ });
954
+ if (!response.ok) {
955
+ return c.json(
956
+ {
957
+ error: `Failed to fetch OAuth metadata: ${response.status} ${response.statusText}`
958
+ },
959
+ response.status
960
+ );
961
+ }
962
+ const metadata = await response.json();
963
+ return c.json(metadata);
964
+ } catch (error) {
965
+ console.error("OAuth metadata proxy error:", error);
966
+ return c.json(
967
+ {
968
+ error: error instanceof Error ? error.message : "Unknown error occurred"
969
+ },
970
+ 500
971
+ );
972
+ }
973
+ });
974
+ const mcp = new hono.Hono();
975
+ mcp.get("/health", (c) => {
976
+ return c.json({
977
+ service: "MCP API",
978
+ status: "ready",
979
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
980
+ });
981
+ });
982
+ mcp.route("/chat", chat);
983
+ mcp.route("/connect", connect);
984
+ mcp.route("/tools", tools);
985
+ mcp.route("/resources", resources);
986
+ mcp.route("/prompts", prompts);
987
+ mcp.route("/oauth", oauth);
988
+ function createHonoApp() {
989
+ const app = new hono.Hono();
990
+ app.use("*", logger.logger());
991
+ app.use(
992
+ "*",
993
+ cors.cors({
994
+ origin: [
995
+ "http://localhost:8080",
996
+ "http://localhost:3000",
997
+ "http://localhost:3001",
998
+ "http://127.0.0.1:3001",
999
+ "http://127.0.0.1:3000"
1000
+ ],
1001
+ credentials: true
1002
+ })
1003
+ );
1004
+ app.route("/api/mcp", mcp);
1005
+ app.get("/health", (c) => {
1006
+ return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1007
+ });
1008
+ const isElectron = process.env.ELECTRON_APP === "true";
1009
+ if (process.env.NODE_ENV === "production" || isElectron) {
1010
+ app.use("/*", serveStatic.serveStatic({ root: "./dist/client" }));
1011
+ app.get("*", (c) => {
1012
+ const path2 = c.req.path;
1013
+ if (path2.startsWith("/api/")) {
1014
+ return c.notFound();
1015
+ }
1016
+ return serveStatic.serveStatic({ path: "./dist/client/index.html" })(c);
1017
+ });
1018
+ } else {
1019
+ app.get("/", (c) => {
1020
+ return c.json({
1021
+ message: "MCP Inspector API Server",
1022
+ environment: "development",
1023
+ frontend: "http://localhost:8080"
1024
+ });
1025
+ });
1026
+ }
1027
+ return app;
1028
+ }
1029
+ function registerAppListeners(mainWindow2) {
1030
+ electron.ipcMain.handle("app:version", () => {
1031
+ return electron.app.getVersion();
1032
+ });
1033
+ electron.ipcMain.handle("app:platform", () => {
1034
+ return process.platform;
1035
+ });
1036
+ }
1037
+ function registerWindowListeners(mainWindow2) {
1038
+ electron.ipcMain.on("window:minimize", () => {
1039
+ mainWindow2.minimize();
1040
+ });
1041
+ electron.ipcMain.on("window:maximize", () => {
1042
+ if (mainWindow2.isMaximized()) {
1043
+ mainWindow2.unmaximize();
1044
+ } else {
1045
+ mainWindow2.maximize();
1046
+ }
1047
+ });
1048
+ electron.ipcMain.on("window:close", () => {
1049
+ mainWindow2.close();
1050
+ });
1051
+ electron.ipcMain.handle("window:is-maximized", () => {
1052
+ return mainWindow2.isMaximized();
1053
+ });
1054
+ }
1055
+ function registerFileListeners(mainWindow2) {
1056
+ electron.ipcMain.handle("dialog:open", async (event, options = {}) => {
1057
+ const result = await electron.dialog.showOpenDialog(mainWindow2, {
1058
+ properties: ["openFile"],
1059
+ filters: [
1060
+ { name: "JSON Files", extensions: ["json"] },
1061
+ { name: "All Files", extensions: ["*"] }
1062
+ ],
1063
+ ...options
1064
+ });
1065
+ return result.canceled ? void 0 : result.filePaths;
1066
+ });
1067
+ electron.ipcMain.handle("dialog:save", async (event, data) => {
1068
+ const result = await electron.dialog.showSaveDialog(mainWindow2, {
1069
+ filters: [
1070
+ { name: "JSON Files", extensions: ["json"] },
1071
+ { name: "All Files", extensions: ["*"] }
1072
+ ]
1073
+ });
1074
+ if (!result.canceled && result.filePath) {
1075
+ try {
1076
+ await fs.writeFile(result.filePath, JSON.stringify(data, null, 2));
1077
+ return result.filePath;
1078
+ } catch (error) {
1079
+ throw new Error(`Failed to save file: ${error}`);
1080
+ }
1081
+ }
1082
+ return void 0;
1083
+ });
1084
+ electron.ipcMain.handle("dialog:message", async (event, options) => {
1085
+ return await electron.dialog.showMessageBox(mainWindow2, options);
1086
+ });
1087
+ }
1088
+ function registerMcpListeners(mainWindow2) {
1089
+ electron.ipcMain.handle("mcp:connect", async (event, config) => {
1090
+ console.log("MCP connect requested:", config);
1091
+ return { success: true, id: "temp-id" };
1092
+ });
1093
+ electron.ipcMain.handle("mcp:disconnect", async (event, id) => {
1094
+ console.log("MCP disconnect requested:", id);
1095
+ return { success: true };
1096
+ });
1097
+ electron.ipcMain.handle("mcp:list-servers", async () => {
1098
+ console.log("MCP list servers requested");
1099
+ return [];
1100
+ });
1101
+ }
1102
+ function registerListeners(mainWindow2) {
1103
+ registerAppListeners();
1104
+ registerWindowListeners(mainWindow2);
1105
+ registerFileListeners(mainWindow2);
1106
+ registerMcpListeners();
1107
+ }
1108
+ log.transports.file.level = "info";
1109
+ log.transports.console.level = "debug";
1110
+ updateElectronApp.updateElectronApp();
1111
+ if (process.platform === "win32") {
1112
+ electron.app.setAppUserModelId("com.mcpjam.inspector");
1113
+ }
1114
+ let mainWindow = null;
1115
+ let server = null;
1116
+ let serverPort = 0;
1117
+ const isDev = process.env.NODE_ENV === "development";
1118
+ async function findAvailablePort(startPort = 3001) {
1119
+ return new Promise((resolve, reject) => {
1120
+ const net = require("net");
1121
+ const server2 = net.createServer();
1122
+ server2.listen(startPort, () => {
1123
+ var _a;
1124
+ const port = (_a = server2.address()) == null ? void 0 : _a.port;
1125
+ server2.close(() => {
1126
+ resolve(port);
1127
+ });
1128
+ });
1129
+ server2.on("error", () => {
1130
+ findAvailablePort(startPort + 1).then(resolve).catch(reject);
1131
+ });
1132
+ });
1133
+ }
1134
+ async function startHonoServer() {
1135
+ try {
1136
+ const port = await findAvailablePort(3001);
1137
+ process.env.ELECTRON_APP = "true";
1138
+ const honoApp = createHonoApp();
1139
+ server = nodeServer.serve({
1140
+ fetch: honoApp.fetch,
1141
+ port,
1142
+ hostname: "127.0.0.1"
1143
+ });
1144
+ log.info(`🚀 MCP Inspector Server started on port ${port}`);
1145
+ return port;
1146
+ } catch (error) {
1147
+ log.error("Failed to start Hono server:", error);
1148
+ throw error;
1149
+ }
1150
+ }
1151
+ function createMainWindow(serverUrl) {
1152
+ const window = new electron.BrowserWindow({
1153
+ width: 1400,
1154
+ height: 900,
1155
+ minWidth: 800,
1156
+ minHeight: 600,
1157
+ icon: path.join(__dirname, "../assets/icon.png"),
1158
+ // You can add an icon later
1159
+ webPreferences: {
1160
+ nodeIntegration: false,
1161
+ contextIsolation: true,
1162
+ enableRemoteModule: false,
1163
+ preload: path.join(__dirname, "../preload/index.js")
1164
+ },
1165
+ titleBarStyle: process.platform === "darwin" ? "hiddenInset" : "default",
1166
+ show: false
1167
+ // Don't show until ready
1168
+ });
1169
+ registerListeners(window);
1170
+ window.loadURL(serverUrl);
1171
+ if (isDev) {
1172
+ window.webContents.openDevTools();
1173
+ }
1174
+ window.once("ready-to-show", () => {
1175
+ window.show();
1176
+ if (isDev) {
1177
+ window.webContents.openDevTools();
1178
+ }
1179
+ });
1180
+ window.webContents.setWindowOpenHandler(({ url }) => {
1181
+ electron.shell.openExternal(url);
1182
+ return { action: "deny" };
1183
+ });
1184
+ window.on("closed", () => {
1185
+ mainWindow = null;
1186
+ });
1187
+ return window;
1188
+ }
1189
+ function createAppMenu() {
1190
+ const isMac = process.platform === "darwin";
1191
+ const template = [
1192
+ ...isMac ? [{
1193
+ label: electron.app.getName(),
1194
+ submenu: [
1195
+ { role: "about" },
1196
+ { type: "separator" },
1197
+ { role: "services" },
1198
+ { type: "separator" },
1199
+ { role: "hide" },
1200
+ { role: "hideothers" },
1201
+ { role: "unhide" },
1202
+ { type: "separator" },
1203
+ { role: "quit" }
1204
+ ]
1205
+ }] : [],
1206
+ {
1207
+ label: "File",
1208
+ submenu: [
1209
+ isMac ? { role: "close" } : { role: "quit" }
1210
+ ]
1211
+ },
1212
+ {
1213
+ label: "Edit",
1214
+ submenu: [
1215
+ { role: "undo" },
1216
+ { role: "redo" },
1217
+ { type: "separator" },
1218
+ { role: "cut" },
1219
+ { role: "copy" },
1220
+ { role: "paste" },
1221
+ ...isMac ? [
1222
+ { role: "pasteAndMatchStyle" },
1223
+ { role: "delete" },
1224
+ { role: "selectAll" },
1225
+ { type: "separator" },
1226
+ {
1227
+ label: "Speech",
1228
+ submenu: [
1229
+ { role: "startSpeaking" },
1230
+ { role: "stopSpeaking" }
1231
+ ]
1232
+ }
1233
+ ] : [
1234
+ { role: "delete" },
1235
+ { type: "separator" },
1236
+ { role: "selectAll" }
1237
+ ]
1238
+ ]
1239
+ },
1240
+ {
1241
+ label: "View",
1242
+ submenu: [
1243
+ { role: "reload" },
1244
+ { role: "forceReload" },
1245
+ { role: "toggleDevTools" },
1246
+ { type: "separator" },
1247
+ { role: "resetZoom" },
1248
+ { role: "zoomIn" },
1249
+ { role: "zoomOut" },
1250
+ { type: "separator" },
1251
+ { role: "togglefullscreen" }
1252
+ ]
1253
+ },
1254
+ {
1255
+ label: "Window",
1256
+ submenu: [
1257
+ { role: "minimize" },
1258
+ { role: "close" },
1259
+ ...isMac ? [
1260
+ { type: "separator" },
1261
+ { role: "front" },
1262
+ { type: "separator" },
1263
+ { role: "window" }
1264
+ ] : []
1265
+ ]
1266
+ }
1267
+ ];
1268
+ const menu = electron.Menu.buildFromTemplate(template);
1269
+ electron.Menu.setApplicationMenu(menu);
1270
+ }
1271
+ electron.app.whenReady().then(async () => {
1272
+ try {
1273
+ serverPort = await startHonoServer();
1274
+ const serverUrl = `http://127.0.0.1:${serverPort}`;
1275
+ createAppMenu();
1276
+ mainWindow = createMainWindow(serverUrl);
1277
+ log.info("MCP Inspector Electron app ready");
1278
+ } catch (error) {
1279
+ log.error("Failed to initialize app:", error);
1280
+ electron.app.quit();
1281
+ }
1282
+ });
1283
+ electron.app.on("window-all-closed", () => {
1284
+ var _a;
1285
+ if (server) {
1286
+ (_a = server.close) == null ? void 0 : _a.call(server);
1287
+ }
1288
+ if (process.platform !== "darwin") {
1289
+ electron.app.quit();
1290
+ }
1291
+ });
1292
+ electron.app.on("activate", async () => {
1293
+ if (electron.BrowserWindow.getAllWindows().length === 0) {
1294
+ if (serverPort > 0) {
1295
+ const serverUrl = `http://127.0.0.1:${serverPort}`;
1296
+ mainWindow = createMainWindow(serverUrl);
1297
+ } else {
1298
+ try {
1299
+ serverPort = await startHonoServer();
1300
+ const serverUrl = `http://127.0.0.1:${serverPort}`;
1301
+ mainWindow = createMainWindow(serverUrl);
1302
+ } catch (error) {
1303
+ log.error("Failed to restart server:", error);
1304
+ }
1305
+ }
1306
+ }
1307
+ });
1308
+ electron.app.on("web-contents-created", (_, contents) => {
1309
+ contents.on("new-window", (navigationEvent, navigationUrl) => {
1310
+ navigationEvent.preventDefault();
1311
+ electron.shell.openExternal(navigationUrl);
1312
+ });
1313
+ });
1314
+ electron.app.on("before-quit", () => {
1315
+ var _a;
1316
+ if (server) {
1317
+ (_a = server.close) == null ? void 0 : _a.call(server);
1318
+ }
1319
+ });
1320
+ const gotTheLock = electron.app.requestSingleInstanceLock();
1321
+ if (!gotTheLock) {
1322
+ electron.app.quit();
1323
+ } else {
1324
+ electron.app.on("second-instance", () => {
1325
+ if (mainWindow) {
1326
+ if (mainWindow.isMinimized()) mainWindow.restore();
1327
+ mainWindow.focus();
1328
+ }
1329
+ });
1330
+ }