@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.
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env node
2
+ import { serve } from '@hono/node-server';
3
+ import { Hono } from 'hono';
4
+ import { cors } from 'hono/cors';
5
+ import { logger } from 'hono/logger';
6
+ import { existsSync } from 'node:fs';
7
+ import { join, dirname } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { exec } from 'node:child_process';
10
+ import { promisify } from 'node:util';
11
+ import faviconProxy from '../server/favicon-proxy.js';
12
+ import { MCPInspector } from '../server/mcp-inspector.js';
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+ const execAsync = promisify(exec);
16
+ // Find available port starting from 8080
17
+ async function findAvailablePort(startPort = 8080) {
18
+ const net = await import('node:net');
19
+ for (let port = startPort; port < startPort + 100; port++) {
20
+ try {
21
+ await new Promise((resolve, reject) => {
22
+ const server = net.createServer();
23
+ server.listen(port, () => {
24
+ server.close(() => resolve());
25
+ });
26
+ server.on('error', () => reject(new Error(`Port ${port} is in use`)));
27
+ });
28
+ return port;
29
+ }
30
+ catch {
31
+ continue;
32
+ }
33
+ }
34
+ throw new Error(`No available port found starting from ${startPort}`);
35
+ }
36
+ // Parse command line arguments
37
+ const args = process.argv.slice(2);
38
+ let mcpUrl;
39
+ let startPort = 8080;
40
+ for (let i = 0; i < args.length; i++) {
41
+ if (args[i] === '--url' && i + 1 < args.length) {
42
+ mcpUrl = args[i + 1];
43
+ i++;
44
+ }
45
+ else if (args[i] === '--port' && i + 1 < args.length) {
46
+ startPort = parseInt(args[i + 1], 10);
47
+ i++;
48
+ }
49
+ else if (args[i] === '--help' || args[i] === '-h') {
50
+ console.log(`
51
+ MCP Inspector - Inspect and debug MCP servers
52
+
53
+ Usage:
54
+ npx @mcp-use/inspect [options]
55
+
56
+ Options:
57
+ --url <url> MCP server URL to auto-connect to (e.g., http://localhost:3000/mcp)
58
+ --port <port> Starting port to try (default: 8080, will find next available)
59
+ --help, -h Show this help message
60
+
61
+ Examples:
62
+ # Run inspector with auto-connect
63
+ npx @mcp-use/inspect --url http://localhost:3000/mcp
64
+
65
+ # Run starting from custom port
66
+ npx @mcp-use/inspect --url http://localhost:3000/mcp --port 9000
67
+
68
+ # Run without auto-connect
69
+ npx @mcp-use/inspect
70
+ `);
71
+ process.exit(0);
72
+ }
73
+ }
74
+ const app = new Hono();
75
+ // Middleware
76
+ app.use('*', cors());
77
+ app.use('*', logger());
78
+ // Mount favicon proxy
79
+ app.route('/api/favicon', faviconProxy);
80
+ // Health check
81
+ app.get('/health', (c) => {
82
+ return c.json({ status: 'ok', timestamp: new Date().toISOString() });
83
+ });
84
+ // MCP Inspector routes
85
+ const mcpInspector = new MCPInspector();
86
+ // List available MCP servers
87
+ app.get('/api/servers', async (c) => {
88
+ try {
89
+ const servers = await mcpInspector.listServers();
90
+ return c.json({ servers });
91
+ }
92
+ catch {
93
+ return c.json({ error: 'Failed to list servers' }, 500);
94
+ }
95
+ });
96
+ // Connect to an MCP server
97
+ app.post('/api/servers/connect', async (c) => {
98
+ try {
99
+ const { url, command } = await c.req.json();
100
+ const server = await mcpInspector.connectToServer(url, command);
101
+ return c.json({ server });
102
+ }
103
+ catch {
104
+ return c.json({ error: 'Failed to connect to server' }, 500);
105
+ }
106
+ });
107
+ // Get server details
108
+ app.get('/api/servers/:id', async (c) => {
109
+ try {
110
+ const id = c.req.param('id');
111
+ const server = await mcpInspector.getServer(id);
112
+ if (!server) {
113
+ return c.json({ error: 'Server not found' }, 404);
114
+ }
115
+ return c.json({ server });
116
+ }
117
+ catch {
118
+ return c.json({ error: 'Failed to get server details' }, 500);
119
+ }
120
+ });
121
+ // Execute a tool on a server
122
+ app.post('/api/servers/:id/tools/:toolName/execute', async (c) => {
123
+ try {
124
+ const id = c.req.param('id');
125
+ const toolName = c.req.param('toolName');
126
+ const input = await c.req.json();
127
+ const result = await mcpInspector.executeTool(id, toolName, input);
128
+ return c.json({ result });
129
+ }
130
+ catch {
131
+ return c.json({ error: 'Failed to execute tool' }, 500);
132
+ }
133
+ });
134
+ // Get server tools
135
+ app.get('/api/servers/:id/tools', async (c) => {
136
+ try {
137
+ const id = c.req.param('id');
138
+ const tools = await mcpInspector.getServerTools(id);
139
+ return c.json({ tools });
140
+ }
141
+ catch {
142
+ return c.json({ error: 'Failed to get server tools' }, 500);
143
+ }
144
+ });
145
+ // Get server resources
146
+ app.get('/api/servers/:id/resources', async (c) => {
147
+ try {
148
+ const id = c.req.param('id');
149
+ const resources = await mcpInspector.getServerResources(id);
150
+ return c.json({ resources });
151
+ }
152
+ catch {
153
+ return c.json({ error: 'Failed to get server resources' }, 500);
154
+ }
155
+ });
156
+ // Disconnect from a server
157
+ app.delete('/api/servers/:id', async (c) => {
158
+ try {
159
+ const id = c.req.param('id');
160
+ await mcpInspector.disconnectServer(id);
161
+ return c.json({ success: true });
162
+ }
163
+ catch {
164
+ return c.json({ error: 'Failed to disconnect server' }, 500);
165
+ }
166
+ });
167
+ // Serve static assets from the built client
168
+ const clientDistPath = join(__dirname, '../../dist/client');
169
+ if (existsSync(clientDistPath)) {
170
+ // Serve static assets from /inspector/assets/* (matching Vite's base path)
171
+ app.get('/inspector/assets/*', async (c) => {
172
+ const path = c.req.path.replace('/inspector/assets/', 'assets/');
173
+ const fullPath = join(clientDistPath, path);
174
+ if (existsSync(fullPath)) {
175
+ const content = await import('node:fs').then(fs => fs.readFileSync(fullPath));
176
+ // Set appropriate content type based on file extension
177
+ if (path.endsWith('.js')) {
178
+ c.header('Content-Type', 'application/javascript');
179
+ }
180
+ else if (path.endsWith('.css')) {
181
+ c.header('Content-Type', 'text/css');
182
+ }
183
+ else if (path.endsWith('.svg')) {
184
+ c.header('Content-Type', 'image/svg+xml');
185
+ }
186
+ return c.body(content);
187
+ }
188
+ return c.notFound();
189
+ });
190
+ // Redirect root path to /inspector
191
+ app.get('/', (c) => {
192
+ return c.redirect('/inspector');
193
+ });
194
+ // Serve the main HTML file for /inspector and all other routes (SPA routing)
195
+ app.get('*', (c) => {
196
+ const indexPath = join(clientDistPath, 'index.html');
197
+ if (existsSync(indexPath)) {
198
+ const content = import('node:fs').then(fs => fs.readFileSync(indexPath, 'utf-8'));
199
+ return c.html(content);
200
+ }
201
+ return c.html(`
202
+ <!DOCTYPE html>
203
+ <html>
204
+ <head>
205
+ <title>MCP Inspector</title>
206
+ </head>
207
+ <body>
208
+ <h1>MCP Inspector</h1>
209
+ <p>Client files not found. Please run 'yarn build' to build the UI.</p>
210
+ <p>API is available at <a href="/api/servers">/api/servers</a></p>
211
+ </body>
212
+ </html>
213
+ `);
214
+ });
215
+ }
216
+ else {
217
+ console.warn(`⚠️ MCP Inspector client files not found at ${clientDistPath}`);
218
+ console.warn(` Run 'yarn build' in the inspector package to build the UI`);
219
+ // Fallback for when client is not built
220
+ app.get('*', (c) => {
221
+ return c.html(`
222
+ <!DOCTYPE html>
223
+ <html>
224
+ <head>
225
+ <title>MCP Inspector</title>
226
+ </head>
227
+ <body>
228
+ <h1>MCP Inspector</h1>
229
+ <p>Client files not found. Please run 'yarn build' to build the UI.</p>
230
+ <p>API is available at <a href="/api/servers">/api/servers</a></p>
231
+ </body>
232
+ </html>
233
+ `);
234
+ });
235
+ }
236
+ // Start the server with automatic port selection
237
+ async function startServer() {
238
+ try {
239
+ const port = await findAvailablePort(startPort);
240
+ serve({
241
+ fetch: app.fetch,
242
+ port,
243
+ });
244
+ console.log(`🚀 MCP Inspector running on http://localhost:${port}`);
245
+ if (mcpUrl) {
246
+ console.log(`📡 Auto-connecting to: ${mcpUrl}`);
247
+ }
248
+ // Auto-open browser
249
+ try {
250
+ const command = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
251
+ await execAsync(`${command} http://localhost:${port}`);
252
+ console.log(`🌐 Browser opened automatically`);
253
+ }
254
+ catch (error) {
255
+ console.log(`🌐 Please open http://localhost:${port} in your browser`);
256
+ }
257
+ return { port, fetch: app.fetch };
258
+ }
259
+ catch (error) {
260
+ console.error('Failed to start server:', error);
261
+ process.exit(1);
262
+ }
263
+ }
264
+ // Start the server
265
+ startServer();