@mcp-use/inspector 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/inspect.js +265 -0
- package/dist/client/assets/{index-Risbd_Le.js → index-DZm4vi0J.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/server/favicon-proxy.js +123 -0
- package/dist/server/index.js +7 -0
- package/dist/server/mcp-inspector.js +132 -0
- package/dist/server/middleware.js +145 -0
- package/dist/server/server.js +294 -0
- package/dist/server/standalone.d.ts +9 -0
- package/dist/server/standalone.d.ts.map +1 -0
- package/dist/server/standalone.js +228 -0
- package/dist/server/unified.js +294 -0
- package/package.json +2 -2
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { serve } from '@hono/node-server';
|
|
7
|
+
import { Hono } from 'hono';
|
|
8
|
+
import { cors } from 'hono/cors';
|
|
9
|
+
import { logger } from 'hono/logger';
|
|
10
|
+
import faviconProxy from './favicon-proxy.js';
|
|
11
|
+
import { MCPInspector } from './mcp-inspector.js';
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
// Check if a specific port is available
|
|
16
|
+
async function isPortAvailable(port) {
|
|
17
|
+
const net = await import('node:net');
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
const server = net.createServer();
|
|
20
|
+
server.listen(port, () => {
|
|
21
|
+
server.close(() => resolve(true));
|
|
22
|
+
});
|
|
23
|
+
server.on('error', () => resolve(false));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const app = new Hono();
|
|
27
|
+
// Middleware
|
|
28
|
+
app.use('*', cors());
|
|
29
|
+
app.use('*', logger());
|
|
30
|
+
// Mount favicon proxy
|
|
31
|
+
app.route('/api/favicon', faviconProxy);
|
|
32
|
+
// Health check
|
|
33
|
+
app.get('/health', (c) => {
|
|
34
|
+
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
35
|
+
});
|
|
36
|
+
// MCP Inspector routes
|
|
37
|
+
const mcpInspector = new MCPInspector();
|
|
38
|
+
// List available MCP servers
|
|
39
|
+
app.get('/api/servers', async (c) => {
|
|
40
|
+
try {
|
|
41
|
+
const servers = await mcpInspector.listServers();
|
|
42
|
+
return c.json({ servers });
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return c.json({ error: 'Failed to list servers' }, 500);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// Connect to an MCP server
|
|
49
|
+
app.post('/api/servers/connect', async (c) => {
|
|
50
|
+
try {
|
|
51
|
+
const { url, command } = await c.req.json();
|
|
52
|
+
const server = await mcpInspector.connectToServer(url, command);
|
|
53
|
+
return c.json({ server });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return c.json({ error: 'Failed to connect to server' }, 500);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
// Get server details
|
|
60
|
+
app.get('/api/servers/:id', async (c) => {
|
|
61
|
+
try {
|
|
62
|
+
const id = c.req.param('id');
|
|
63
|
+
const server = await mcpInspector.getServer(id);
|
|
64
|
+
if (!server) {
|
|
65
|
+
return c.json({ error: 'Server not found' }, 404);
|
|
66
|
+
}
|
|
67
|
+
return c.json({ server });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return c.json({ error: 'Failed to get server details' }, 500);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// Execute a tool on a server
|
|
74
|
+
app.post('/api/servers/:id/tools/:toolName/execute', async (c) => {
|
|
75
|
+
try {
|
|
76
|
+
const id = c.req.param('id');
|
|
77
|
+
const toolName = c.req.param('toolName');
|
|
78
|
+
const input = await c.req.json();
|
|
79
|
+
const result = await mcpInspector.executeTool(id, toolName, input);
|
|
80
|
+
return c.json({ result });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return c.json({ error: 'Failed to execute tool' }, 500);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// Get server tools
|
|
87
|
+
app.get('/api/servers/:id/tools', async (c) => {
|
|
88
|
+
try {
|
|
89
|
+
const id = c.req.param('id');
|
|
90
|
+
const tools = await mcpInspector.getServerTools(id);
|
|
91
|
+
return c.json({ tools });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return c.json({ error: 'Failed to get server tools' }, 500);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
// Get server resources
|
|
98
|
+
app.get('/api/servers/:id/resources', async (c) => {
|
|
99
|
+
try {
|
|
100
|
+
const id = c.req.param('id');
|
|
101
|
+
const resources = await mcpInspector.getServerResources(id);
|
|
102
|
+
return c.json({ resources });
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return c.json({ error: 'Failed to get server resources' }, 500);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// Disconnect from a server
|
|
109
|
+
app.delete('/api/servers/:id', async (c) => {
|
|
110
|
+
try {
|
|
111
|
+
const id = c.req.param('id');
|
|
112
|
+
await mcpInspector.disconnectServer(id);
|
|
113
|
+
return c.json({ success: true });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return c.json({ error: 'Failed to disconnect server' }, 500);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Check if we're in development mode (Vite dev server running)
|
|
120
|
+
const isDev = process.env.NODE_ENV === 'development' || process.env.VITE_DEV === 'true';
|
|
121
|
+
// Serve static assets from the built client
|
|
122
|
+
const clientDistPath = join(__dirname, '../../dist/client');
|
|
123
|
+
if (isDev) {
|
|
124
|
+
// Development mode: proxy client requests to Vite dev server
|
|
125
|
+
console.warn('🔧 Development mode: Proxying client requests to Vite dev server');
|
|
126
|
+
// Proxy all non-API requests to Vite dev server
|
|
127
|
+
app.get('*', async (c) => {
|
|
128
|
+
const path = c.req.path;
|
|
129
|
+
// Skip API routes
|
|
130
|
+
if (path.startsWith('/api/')) {
|
|
131
|
+
return c.notFound();
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
// Vite dev server should be running on port 3000
|
|
135
|
+
const viteUrl = `http://localhost:3000${path}`;
|
|
136
|
+
const response = await fetch(viteUrl, {
|
|
137
|
+
signal: AbortSignal.timeout(1000), // 1 second timeout
|
|
138
|
+
});
|
|
139
|
+
if (response.ok) {
|
|
140
|
+
const content = await response.text();
|
|
141
|
+
const contentType = response.headers.get('content-type') || 'text/html';
|
|
142
|
+
c.header('Content-Type', contentType);
|
|
143
|
+
return c.html(content);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.warn(`Failed to proxy to Vite dev server: ${error}`);
|
|
148
|
+
}
|
|
149
|
+
// Fallback HTML if Vite dev server is not running
|
|
150
|
+
return c.html(`
|
|
151
|
+
<!DOCTYPE html>
|
|
152
|
+
<html>
|
|
153
|
+
<head>
|
|
154
|
+
<title>MCP Inspector - Development</title>
|
|
155
|
+
</head>
|
|
156
|
+
<body>
|
|
157
|
+
<h1>MCP Inspector - Development Mode</h1>
|
|
158
|
+
<p>Vite dev server is not running. Please start it with:</p>
|
|
159
|
+
<pre>yarn dev:client</pre>
|
|
160
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
161
|
+
</body>
|
|
162
|
+
</html>
|
|
163
|
+
`);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
else if (existsSync(clientDistPath)) {
|
|
167
|
+
// Production mode: serve static assets from built client
|
|
168
|
+
// Serve static assets from /inspector/assets/* (matching Vite's base path)
|
|
169
|
+
app.get('/inspector/assets/*', async (c) => {
|
|
170
|
+
const path = c.req.path.replace('/inspector/assets/', 'assets/');
|
|
171
|
+
const fullPath = join(clientDistPath, path);
|
|
172
|
+
if (existsSync(fullPath)) {
|
|
173
|
+
const content = await import('node:fs').then(fs => fs.readFileSync(fullPath));
|
|
174
|
+
// Set appropriate content type based on file extension
|
|
175
|
+
if (path.endsWith('.js')) {
|
|
176
|
+
c.header('Content-Type', 'application/javascript');
|
|
177
|
+
}
|
|
178
|
+
else if (path.endsWith('.css')) {
|
|
179
|
+
c.header('Content-Type', 'text/css');
|
|
180
|
+
}
|
|
181
|
+
else if (path.endsWith('.svg')) {
|
|
182
|
+
c.header('Content-Type', 'image/svg+xml');
|
|
183
|
+
}
|
|
184
|
+
return c.body(content);
|
|
185
|
+
}
|
|
186
|
+
return c.notFound();
|
|
187
|
+
});
|
|
188
|
+
// Redirect root path to /inspector
|
|
189
|
+
app.get('/', (c) => {
|
|
190
|
+
return c.redirect('/inspector');
|
|
191
|
+
});
|
|
192
|
+
// Serve the main HTML file for /inspector and all other routes (SPA routing)
|
|
193
|
+
app.get('*', (c) => {
|
|
194
|
+
const indexPath = join(clientDistPath, 'index.html');
|
|
195
|
+
if (existsSync(indexPath)) {
|
|
196
|
+
const content = import('node:fs').then(fs => fs.readFileSync(indexPath, 'utf-8'));
|
|
197
|
+
return c.html(content);
|
|
198
|
+
}
|
|
199
|
+
return c.html(`
|
|
200
|
+
<!DOCTYPE html>
|
|
201
|
+
<html>
|
|
202
|
+
<head>
|
|
203
|
+
<title>MCP Inspector</title>
|
|
204
|
+
</head>
|
|
205
|
+
<body>
|
|
206
|
+
<h1>MCP Inspector</h1>
|
|
207
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
208
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
209
|
+
</body>
|
|
210
|
+
</html>
|
|
211
|
+
`);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.warn(`⚠️ MCP Inspector client files not found at ${clientDistPath}`);
|
|
216
|
+
console.warn(` Run 'yarn build' in the inspector package to build the UI`);
|
|
217
|
+
// Fallback for when client is not built
|
|
218
|
+
app.get('*', (c) => {
|
|
219
|
+
return c.html(`
|
|
220
|
+
<!DOCTYPE html>
|
|
221
|
+
<html>
|
|
222
|
+
<head>
|
|
223
|
+
<title>MCP Inspector</title>
|
|
224
|
+
</head>
|
|
225
|
+
<body>
|
|
226
|
+
<h1>MCP Inspector</h1>
|
|
227
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
228
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
229
|
+
</body>
|
|
230
|
+
</html>
|
|
231
|
+
`);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
// Start the server
|
|
235
|
+
async function startServer() {
|
|
236
|
+
try {
|
|
237
|
+
// In development mode, use port 3001 for API server
|
|
238
|
+
// In production/standalone mode, try 3001 first, then 3002 as fallback
|
|
239
|
+
const isDev = process.env.NODE_ENV === 'development' || process.env.VITE_DEV === 'true';
|
|
240
|
+
let port = 3001;
|
|
241
|
+
const available = await isPortAvailable(port);
|
|
242
|
+
if (!available) {
|
|
243
|
+
if (isDev) {
|
|
244
|
+
console.error(`❌ Port ${port} is not available. Please stop the process using this port and try again.`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// In standalone mode, try fallback port
|
|
249
|
+
const fallbackPort = 3002;
|
|
250
|
+
console.warn(`⚠️ Port ${port} is not available, trying ${fallbackPort}`);
|
|
251
|
+
const fallbackAvailable = await isPortAvailable(fallbackPort);
|
|
252
|
+
if (!fallbackAvailable) {
|
|
253
|
+
console.error(`❌ Neither port ${port} nor ${fallbackPort} is available. Please stop the processes using these ports and try again.`);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
port = fallbackPort;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
serve({
|
|
260
|
+
fetch: app.fetch,
|
|
261
|
+
port,
|
|
262
|
+
});
|
|
263
|
+
if (isDev) {
|
|
264
|
+
console.warn(`🚀 MCP Inspector API server running on http://localhost:${port}`);
|
|
265
|
+
console.warn(`🌐 Vite dev server should be running on http://localhost:3000`);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
console.warn(`🚀 MCP Inspector running on http://localhost:${port}`);
|
|
269
|
+
}
|
|
270
|
+
// Auto-open browser in development
|
|
271
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
272
|
+
try {
|
|
273
|
+
const command = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
274
|
+
const url = isDev ? 'http://localhost:3000' : `http://localhost:${port}`;
|
|
275
|
+
await execAsync(`${command} ${url}`);
|
|
276
|
+
console.warn(`🌐 Browser opened automatically`);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
const url = isDev ? 'http://localhost:3000' : `http://localhost:${port}`;
|
|
280
|
+
console.warn(`🌐 Please open ${url} in your browser`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return { port, fetch: app.fetch };
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
console.error('Failed to start server:', error);
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Start the server if this file is run directly
|
|
291
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
292
|
+
startServer();
|
|
293
|
+
}
|
|
294
|
+
export default { startServer };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare function startServer(): Promise<{
|
|
2
|
+
port: number;
|
|
3
|
+
fetch: (request: Request, Env?: unknown, executionCtx?: import("hono").ExecutionContext) => Response | Promise<Response>;
|
|
4
|
+
}>;
|
|
5
|
+
declare const _default: {
|
|
6
|
+
startServer: typeof startServer;
|
|
7
|
+
};
|
|
8
|
+
export default _default;
|
|
9
|
+
//# sourceMappingURL=standalone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../../src/server/standalone.ts"],"names":[],"mappings":"AA+NA,iBAAe,WAAW;;;GA6BzB;;;;AAOD,wBAA8B"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { serve } from '@hono/node-server';
|
|
7
|
+
import { Hono } from 'hono';
|
|
8
|
+
import { cors } from 'hono/cors';
|
|
9
|
+
import { logger } from 'hono/logger';
|
|
10
|
+
import faviconProxy from './favicon-proxy.js';
|
|
11
|
+
import { MCPInspector } from './mcp-inspector.js';
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
// Find available port starting from 8080
|
|
16
|
+
async function findAvailablePort(startPort = 8080) {
|
|
17
|
+
const net = await import('node:net');
|
|
18
|
+
for (let port = startPort; port < startPort + 100; port++) {
|
|
19
|
+
try {
|
|
20
|
+
await new Promise((resolve, reject) => {
|
|
21
|
+
const server = net.createServer();
|
|
22
|
+
server.listen(port, () => {
|
|
23
|
+
server.close(() => resolve());
|
|
24
|
+
});
|
|
25
|
+
server.on('error', () => reject(new Error(`Port ${port} is in use`)));
|
|
26
|
+
});
|
|
27
|
+
return port;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`No available port found starting from ${startPort}`);
|
|
34
|
+
}
|
|
35
|
+
const app = new Hono();
|
|
36
|
+
// Middleware
|
|
37
|
+
app.use('*', cors());
|
|
38
|
+
app.use('*', logger());
|
|
39
|
+
// Mount favicon proxy
|
|
40
|
+
app.route('/api/favicon', faviconProxy);
|
|
41
|
+
// Health check
|
|
42
|
+
app.get('/health', (c) => {
|
|
43
|
+
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
44
|
+
});
|
|
45
|
+
// MCP Inspector routes
|
|
46
|
+
const mcpInspector = new MCPInspector();
|
|
47
|
+
// List available MCP servers
|
|
48
|
+
app.get('/api/servers', async (c) => {
|
|
49
|
+
try {
|
|
50
|
+
const servers = await mcpInspector.listServers();
|
|
51
|
+
return c.json({ servers });
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return c.json({ error: 'Failed to list servers' }, 500);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
// Connect to an MCP server
|
|
58
|
+
app.post('/api/servers/connect', async (c) => {
|
|
59
|
+
try {
|
|
60
|
+
const { url, command } = await c.req.json();
|
|
61
|
+
const server = await mcpInspector.connectToServer(url, command);
|
|
62
|
+
return c.json({ server });
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return c.json({ error: 'Failed to connect to server' }, 500);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// Get server details
|
|
69
|
+
app.get('/api/servers/:id', async (c) => {
|
|
70
|
+
try {
|
|
71
|
+
const id = c.req.param('id');
|
|
72
|
+
const server = await mcpInspector.getServer(id);
|
|
73
|
+
if (!server) {
|
|
74
|
+
return c.json({ error: 'Server not found' }, 404);
|
|
75
|
+
}
|
|
76
|
+
return c.json({ server });
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return c.json({ error: 'Failed to get server details' }, 500);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
// Execute a tool on a server
|
|
83
|
+
app.post('/api/servers/:id/tools/:toolName/execute', async (c) => {
|
|
84
|
+
try {
|
|
85
|
+
const id = c.req.param('id');
|
|
86
|
+
const toolName = c.req.param('toolName');
|
|
87
|
+
const input = await c.req.json();
|
|
88
|
+
const result = await mcpInspector.executeTool(id, toolName, input);
|
|
89
|
+
return c.json({ result });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return c.json({ error: 'Failed to execute tool' }, 500);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
// Get server tools
|
|
96
|
+
app.get('/api/servers/:id/tools', async (c) => {
|
|
97
|
+
try {
|
|
98
|
+
const id = c.req.param('id');
|
|
99
|
+
const tools = await mcpInspector.getServerTools(id);
|
|
100
|
+
return c.json({ tools });
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return c.json({ error: 'Failed to get server tools' }, 500);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// Get server resources
|
|
107
|
+
app.get('/api/servers/:id/resources', async (c) => {
|
|
108
|
+
try {
|
|
109
|
+
const id = c.req.param('id');
|
|
110
|
+
const resources = await mcpInspector.getServerResources(id);
|
|
111
|
+
return c.json({ resources });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return c.json({ error: 'Failed to get server resources' }, 500);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// Disconnect from a server
|
|
118
|
+
app.delete('/api/servers/:id', async (c) => {
|
|
119
|
+
try {
|
|
120
|
+
const id = c.req.param('id');
|
|
121
|
+
await mcpInspector.disconnectServer(id);
|
|
122
|
+
return c.json({ success: true });
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return c.json({ error: 'Failed to disconnect server' }, 500);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// Serve static assets from the built client
|
|
129
|
+
const clientDistPath = join(__dirname, '../../dist/client');
|
|
130
|
+
if (existsSync(clientDistPath)) {
|
|
131
|
+
// Serve static assets from /inspector/assets/* (matching Vite's base path)
|
|
132
|
+
app.get('/inspector/assets/*', async (c) => {
|
|
133
|
+
const path = c.req.path.replace('/inspector/assets/', 'assets/');
|
|
134
|
+
const fullPath = join(clientDistPath, path);
|
|
135
|
+
if (existsSync(fullPath)) {
|
|
136
|
+
const content = await import('node:fs').then(fs => fs.readFileSync(fullPath));
|
|
137
|
+
// Set appropriate content type based on file extension
|
|
138
|
+
if (path.endsWith('.js')) {
|
|
139
|
+
c.header('Content-Type', 'application/javascript');
|
|
140
|
+
}
|
|
141
|
+
else if (path.endsWith('.css')) {
|
|
142
|
+
c.header('Content-Type', 'text/css');
|
|
143
|
+
}
|
|
144
|
+
else if (path.endsWith('.svg')) {
|
|
145
|
+
c.header('Content-Type', 'image/svg+xml');
|
|
146
|
+
}
|
|
147
|
+
return c.body(content);
|
|
148
|
+
}
|
|
149
|
+
return c.notFound();
|
|
150
|
+
});
|
|
151
|
+
// Redirect root path to /inspector
|
|
152
|
+
app.get('/', (c) => {
|
|
153
|
+
return c.redirect('/inspector');
|
|
154
|
+
});
|
|
155
|
+
// Serve the main HTML file for /inspector and all other routes (SPA routing)
|
|
156
|
+
app.get('*', (c) => {
|
|
157
|
+
const indexPath = join(clientDistPath, 'index.html');
|
|
158
|
+
if (existsSync(indexPath)) {
|
|
159
|
+
const content = import('node:fs').then(fs => fs.readFileSync(indexPath, 'utf-8'));
|
|
160
|
+
return c.html(content);
|
|
161
|
+
}
|
|
162
|
+
return c.html(`
|
|
163
|
+
<!DOCTYPE html>
|
|
164
|
+
<html>
|
|
165
|
+
<head>
|
|
166
|
+
<title>MCP Inspector</title>
|
|
167
|
+
</head>
|
|
168
|
+
<body>
|
|
169
|
+
<h1>MCP Inspector</h1>
|
|
170
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
171
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
172
|
+
</body>
|
|
173
|
+
</html>
|
|
174
|
+
`);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.warn(`⚠️ MCP Inspector client files not found at ${clientDistPath}`);
|
|
179
|
+
console.warn(` Run 'yarn build' in the inspector package to build the UI`);
|
|
180
|
+
// Fallback for when client is not built
|
|
181
|
+
app.get('*', (c) => {
|
|
182
|
+
return c.html(`
|
|
183
|
+
<!DOCTYPE html>
|
|
184
|
+
<html>
|
|
185
|
+
<head>
|
|
186
|
+
<title>MCP Inspector</title>
|
|
187
|
+
</head>
|
|
188
|
+
<body>
|
|
189
|
+
<h1>MCP Inspector</h1>
|
|
190
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
191
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
192
|
+
</body>
|
|
193
|
+
</html>
|
|
194
|
+
`);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// Start the server with automatic port selection
|
|
198
|
+
async function startServer() {
|
|
199
|
+
try {
|
|
200
|
+
const port = await findAvailablePort();
|
|
201
|
+
serve({
|
|
202
|
+
fetch: app.fetch,
|
|
203
|
+
port,
|
|
204
|
+
});
|
|
205
|
+
console.log(`🚀 MCP Inspector running on http://localhost:${port}`);
|
|
206
|
+
// Auto-open browser in development
|
|
207
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
208
|
+
try {
|
|
209
|
+
const command = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
210
|
+
await execAsync(`${command} http://localhost:${port}`);
|
|
211
|
+
console.log(`🌐 Browser opened automatically`);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.log(`🌐 Please open http://localhost:${port} in your browser`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return { port, fetch: app.fetch };
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
console.error('Failed to start server:', error);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Start the server if this file is run directly
|
|
225
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
226
|
+
startServer();
|
|
227
|
+
}
|
|
228
|
+
export default { startServer };
|