@mcp-use/inspector 0.3.10 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -49
- package/dist/cli/cli/inspect.js +262 -0
- package/dist/cli/inspect.js +382 -0
- package/dist/cli/server/mcp-inspector.js +121 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -25,29 +25,29 @@
|
|
|
25
25
|
|
|
26
26
|
## 📦 Related Packages
|
|
27
27
|
|
|
28
|
-
| Package
|
|
29
|
-
|
|
30
|
-
| [mcp-use](https://github.com/mcp-use/mcp-use-ts/tree/main/packages/mcp-use)
|
|
31
|
-
| [@mcp-use/cli](https://github.com/mcp-use/mcp-use-ts/tree/main/packages/cli)
|
|
32
|
-
| [create-mcp-use-app](https://github.com/mcp-use/mcp-use-ts/tree/main/packages/create-mcp-use-app) | Create MCP apps
|
|
28
|
+
| Package | Description | Version |
|
|
29
|
+
| ------------------------------------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| [mcp-use](https://github.com/mcp-use/mcp-use-ts/tree/main/packages/mcp-use) | Core MCP framework | [](https://www.npmjs.com/package/mcp-use) |
|
|
31
|
+
| [@mcp-use/cli](https://github.com/mcp-use/mcp-use-ts/tree/main/packages/cli) | Build tool for MCP apps | [](https://www.npmjs.com/package/@mcp-use/cli) |
|
|
32
|
+
| [create-mcp-use-app](https://github.com/mcp-use/mcp-use-ts/tree/main/packages/create-mcp-use-app) | Create MCP apps | [](https://www.npmjs.com/package/create-mcp-use-app) |
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
36
|
## ✨ Key Features
|
|
37
37
|
|
|
38
|
-
| Feature
|
|
39
|
-
|
|
40
|
-
| **🚀 Auto-Mount**
|
|
41
|
-
| **🔌 Multi-Connection**
|
|
42
|
-
| **🎯 Interactive Testing** | Test tools with live execution and real-time results
|
|
43
|
-
| **📊 Real-time Status**
|
|
44
|
-
| **🔐 OAuth Support**
|
|
45
|
-
| **💾 Persistent Sessions** | Connections saved to localStorage and auto-reconnect
|
|
46
|
-
| **🎨 Beautiful UI**
|
|
47
|
-
| **🔍 Tool Explorer**
|
|
48
|
-
| **📁 Resource Browser**
|
|
49
|
-
| **💬 Prompt Manager**
|
|
50
|
-
| **🌐 Universal Support**
|
|
38
|
+
| Feature | Description |
|
|
39
|
+
| -------------------------- | --------------------------------------------------------------- |
|
|
40
|
+
| **🚀 Auto-Mount** | Automatically available at `/inspector` for all MCP-Use servers |
|
|
41
|
+
| **🔌 Multi-Connection** | Connect to and manage multiple MCP servers simultaneously |
|
|
42
|
+
| **🎯 Interactive Testing** | Test tools with live execution and real-time results |
|
|
43
|
+
| **📊 Real-time Status** | Monitor connection states, errors, and server health |
|
|
44
|
+
| **🔐 OAuth Support** | Built-in OAuth flow handling with popup authentication |
|
|
45
|
+
| **💾 Persistent Sessions** | Connections saved to localStorage and auto-reconnect |
|
|
46
|
+
| **🎨 Beautiful UI** | Modern, responsive interface built with React and Tailwind |
|
|
47
|
+
| **🔍 Tool Explorer** | Browse and execute all available tools with schema validation |
|
|
48
|
+
| **📁 Resource Browser** | View and copy resource URIs with syntax highlighting |
|
|
49
|
+
| **💬 Prompt Manager** | Test and manage prompts with argument templates |
|
|
50
|
+
| **🌐 Universal Support** | Works with HTTP/SSE and WebSocket connections |
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
@@ -61,7 +61,7 @@ When you create an MCP server with `mcp-use`, the inspector is automatically ava
|
|
|
61
61
|
import { createMCPServer } from 'mcp-use/server'
|
|
62
62
|
|
|
63
63
|
const server = createMCPServer('my-server', {
|
|
64
|
-
version: '1.0.0'
|
|
64
|
+
version: '1.0.0',
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
// Add your tools, resources, prompts...
|
|
@@ -72,6 +72,7 @@ server.listen(3000)
|
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
**That's it!** No additional configuration needed. The inspector:
|
|
75
|
+
|
|
75
76
|
- Automatically mounts at `/inspector`
|
|
76
77
|
- Auto-connects to your local MCP server
|
|
77
78
|
- Provides instant debugging capabilities
|
|
@@ -83,13 +84,13 @@ Use the inspector with any MCP server (local or remote):
|
|
|
83
84
|
|
|
84
85
|
```bash
|
|
85
86
|
# Inspect a remote server
|
|
86
|
-
npx mcp-
|
|
87
|
+
npx @mcp-use/inspector --url https://mcp.linear.app/sse
|
|
87
88
|
|
|
88
89
|
# Custom port
|
|
89
|
-
npx mcp-
|
|
90
|
+
npx @mcp-use/inspector --url http://localhost:3000/mcp --port 8080
|
|
90
91
|
|
|
91
92
|
# Open inspector without auto-connect
|
|
92
|
-
npx mcp-
|
|
93
|
+
npx @mcp-use/inspector
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
### Method 3: Custom Integration
|
|
@@ -116,6 +117,7 @@ app.listen(3000)
|
|
|
116
117
|
### Dashboard Overview
|
|
117
118
|
|
|
118
119
|
The main dashboard shows:
|
|
120
|
+
|
|
119
121
|
- **Connection Overview**: Total servers, active connections, available tools
|
|
120
122
|
- **Server List**: All configured servers with their current status
|
|
121
123
|
- **Quick Actions**: Add new server, refresh all, clear sessions
|
|
@@ -123,10 +125,12 @@ The main dashboard shows:
|
|
|
123
125
|
### Adding Servers
|
|
124
126
|
|
|
125
127
|
Click "Add New MCP Server" and provide:
|
|
128
|
+
|
|
126
129
|
- **Server Name** (optional): Friendly name for identification
|
|
127
130
|
- **Server URL**: The MCP endpoint URL
|
|
128
131
|
|
|
129
132
|
Example URLs:
|
|
133
|
+
|
|
130
134
|
- Local: `http://localhost:3000/mcp`
|
|
131
135
|
- Linear: `https://mcp.linear.app/sse`
|
|
132
136
|
- WebSocket: `ws://localhost:8080`
|
|
@@ -135,15 +139,15 @@ Example URLs:
|
|
|
135
139
|
|
|
136
140
|
The inspector displays real-time connection states:
|
|
137
141
|
|
|
138
|
-
| State
|
|
139
|
-
|
|
140
|
-
| 🔍 **discovering**
|
|
141
|
-
| 🔄 **connecting**
|
|
142
|
-
| 🔐 **authenticating** | OAuth flow in progress
|
|
143
|
-
| 📥 **loading**
|
|
144
|
-
| ✅ **ready**
|
|
145
|
-
| ❌ **failed**
|
|
146
|
-
| ⏳ **pending_auth**
|
|
142
|
+
| State | Description | Action |
|
|
143
|
+
| --------------------- | -------------------------- | ------------------ |
|
|
144
|
+
| 🔍 **discovering** | Finding the server | Wait |
|
|
145
|
+
| 🔄 **connecting** | Establishing connection | Wait |
|
|
146
|
+
| 🔐 **authenticating** | OAuth flow in progress | Complete auth |
|
|
147
|
+
| 📥 **loading** | Loading tools & resources | Wait |
|
|
148
|
+
| ✅ **ready** | Connected and operational | Use tools |
|
|
149
|
+
| ❌ **failed** | Connection failed | Retry |
|
|
150
|
+
| ⏳ **pending_auth** | Waiting for authentication | Click Authenticate |
|
|
147
151
|
|
|
148
152
|
### Testing Tools
|
|
149
153
|
|
|
@@ -183,6 +187,7 @@ For servers requiring OAuth (like Linear):
|
|
|
183
187
|
4. Connection automatically completes
|
|
184
188
|
|
|
185
189
|
If popup is blocked:
|
|
190
|
+
|
|
186
191
|
- Click "open auth page" link
|
|
187
192
|
- Complete authentication manually
|
|
188
193
|
- Return to inspector
|
|
@@ -190,6 +195,7 @@ If popup is blocked:
|
|
|
190
195
|
### Resource Management
|
|
191
196
|
|
|
192
197
|
Browse available resources:
|
|
198
|
+
|
|
193
199
|
- View resource descriptions
|
|
194
200
|
- Copy resource URIs
|
|
195
201
|
- Check MIME types
|
|
@@ -198,6 +204,7 @@ Browse available resources:
|
|
|
198
204
|
### Prompt Testing
|
|
199
205
|
|
|
200
206
|
Test prompts with the inspector:
|
|
207
|
+
|
|
201
208
|
1. Navigate to **Prompts** tab
|
|
202
209
|
2. Select a prompt
|
|
203
210
|
3. Fill in required arguments
|
|
@@ -211,6 +218,7 @@ Test prompts with the inspector:
|
|
|
211
218
|
### Server Card
|
|
212
219
|
|
|
213
220
|
Each server displays:
|
|
221
|
+
|
|
214
222
|
- Connection status indicator
|
|
215
223
|
- Server name and URL
|
|
216
224
|
- Available tools count
|
|
@@ -220,6 +228,7 @@ Each server displays:
|
|
|
220
228
|
### Tool Explorer
|
|
221
229
|
|
|
222
230
|
The tool explorer shows:
|
|
231
|
+
|
|
223
232
|
- Tool name and description
|
|
224
233
|
- Input schema with types
|
|
225
234
|
- Output schema
|
|
@@ -229,6 +238,7 @@ The tool explorer shows:
|
|
|
229
238
|
### Chat Interface
|
|
230
239
|
|
|
231
240
|
Interactive chat for testing conversational flows:
|
|
241
|
+
|
|
232
242
|
- Send messages to test prompts
|
|
233
243
|
- View tool calls in real-time
|
|
234
244
|
- See formatted responses
|
|
@@ -251,6 +261,7 @@ Manage multiple servers efficiently:
|
|
|
251
261
|
### Session Management
|
|
252
262
|
|
|
253
263
|
Sessions are automatically saved to localStorage:
|
|
264
|
+
|
|
254
265
|
- Preserves server configurations
|
|
255
266
|
- Maintains connection preferences
|
|
256
267
|
- Restores on page reload
|
|
@@ -259,18 +270,19 @@ Sessions are automatically saved to localStorage:
|
|
|
259
270
|
### Custom Themes
|
|
260
271
|
|
|
261
272
|
The inspector respects system theme preferences:
|
|
273
|
+
|
|
262
274
|
- Light mode for better readability
|
|
263
275
|
- Dark mode for reduced eye strain
|
|
264
276
|
- Automatic switching based on OS settings
|
|
265
277
|
|
|
266
278
|
### Keyboard Shortcuts
|
|
267
279
|
|
|
268
|
-
| Shortcut
|
|
269
|
-
|
|
270
|
-
| `Cmd/Ctrl + K` | Quick server search
|
|
271
|
-
| `Cmd/Ctrl + N` | Add new server
|
|
280
|
+
| Shortcut | Action |
|
|
281
|
+
| -------------- | ----------------------- |
|
|
282
|
+
| `Cmd/Ctrl + K` | Quick server search |
|
|
283
|
+
| `Cmd/Ctrl + N` | Add new server |
|
|
272
284
|
| `Cmd/Ctrl + R` | Refresh all connections |
|
|
273
|
-
| `Esc`
|
|
285
|
+
| `Esc` | Close modals |
|
|
274
286
|
|
|
275
287
|
---
|
|
276
288
|
|
|
@@ -284,18 +296,18 @@ import { createMCPServer } from 'mcp-use/server'
|
|
|
284
296
|
|
|
285
297
|
const server = createMCPServer('dev-server', {
|
|
286
298
|
version: '1.0.0',
|
|
287
|
-
description: 'Development MCP Server'
|
|
299
|
+
description: 'Development MCP Server',
|
|
288
300
|
})
|
|
289
301
|
|
|
290
302
|
server.tool('debug_tool', {
|
|
291
303
|
description: 'Debug tool for testing',
|
|
292
304
|
parameters: z.object({
|
|
293
|
-
message: z.string()
|
|
305
|
+
message: z.string(),
|
|
294
306
|
}),
|
|
295
307
|
execute: async ({ message }) => {
|
|
296
308
|
console.log('Debug:', message)
|
|
297
309
|
return { received: message, timestamp: Date.now() }
|
|
298
|
-
}
|
|
310
|
+
},
|
|
299
311
|
})
|
|
300
312
|
|
|
301
313
|
server.listen(3000)
|
|
@@ -311,8 +323,8 @@ const server = createMCPServer('production-server', {
|
|
|
311
323
|
clientId: process.env.OAUTH_CLIENT_ID,
|
|
312
324
|
clientSecret: process.env.OAUTH_CLIENT_SECRET,
|
|
313
325
|
authorizationUrl: 'https://api.example.com/oauth/authorize',
|
|
314
|
-
tokenUrl: 'https://api.example.com/oauth/token'
|
|
315
|
-
}
|
|
326
|
+
tokenUrl: 'https://api.example.com/oauth/token',
|
|
327
|
+
},
|
|
316
328
|
})
|
|
317
329
|
|
|
318
330
|
// Inspector handles OAuth flow automatically
|
|
@@ -341,6 +353,7 @@ URL: https://api.example.com/mcp
|
|
|
341
353
|
The inspector is built with modern web technologies:
|
|
342
354
|
|
|
343
355
|
### Frontend Stack
|
|
356
|
+
|
|
344
357
|
- **React 19**: UI framework
|
|
345
358
|
- **React Router**: Navigation
|
|
346
359
|
- **Tailwind CSS**: Styling
|
|
@@ -368,6 +381,7 @@ src/client/
|
|
|
368
381
|
### Connection Management
|
|
369
382
|
|
|
370
383
|
The `useMcp` hook handles:
|
|
384
|
+
|
|
371
385
|
- WebSocket/SSE connections
|
|
372
386
|
- Automatic reconnection
|
|
373
387
|
- OAuth flow management
|
|
@@ -381,6 +395,7 @@ The `useMcp` hook handles:
|
|
|
381
395
|
### Common Issues and Solutions
|
|
382
396
|
|
|
383
397
|
**Inspector not loading:**
|
|
398
|
+
|
|
384
399
|
```bash
|
|
385
400
|
# Check server is running
|
|
386
401
|
curl http://localhost:3000/inspector
|
|
@@ -390,23 +405,27 @@ curl http://localhost:3000/inspector
|
|
|
390
405
|
```
|
|
391
406
|
|
|
392
407
|
**Connection fails immediately:**
|
|
408
|
+
|
|
393
409
|
- Check CORS configuration
|
|
394
410
|
- Verify server URL is correct
|
|
395
411
|
- Ensure server supports SSE/WebSocket
|
|
396
412
|
- Check network/firewall settings
|
|
397
413
|
|
|
398
414
|
**OAuth popup blocked:**
|
|
415
|
+
|
|
399
416
|
- Allow popups for the inspector domain
|
|
400
417
|
- Use the manual auth link provided
|
|
401
418
|
- Check browser console for errors
|
|
402
419
|
|
|
403
420
|
**Tools not executing:**
|
|
421
|
+
|
|
404
422
|
- Verify tool schemas are valid
|
|
405
423
|
- Check server logs for errors
|
|
406
424
|
- Ensure proper authentication
|
|
407
425
|
- Validate input parameters
|
|
408
426
|
|
|
409
427
|
**Session not persisting:**
|
|
428
|
+
|
|
410
429
|
- Check localStorage is enabled
|
|
411
430
|
- Clear browser cache
|
|
412
431
|
- Try incognito/private mode
|
|
@@ -422,7 +441,7 @@ curl http://localhost:3000/inspector
|
|
|
422
441
|
// Use pagination for many tools
|
|
423
442
|
server.configurePagination({
|
|
424
443
|
toolsPerPage: 50,
|
|
425
|
-
enableSearch: true
|
|
444
|
+
enableSearch: true,
|
|
426
445
|
})
|
|
427
446
|
```
|
|
428
447
|
|
|
@@ -433,7 +452,7 @@ server.configurePagination({
|
|
|
433
452
|
const inspector = {
|
|
434
453
|
maxConnections: 5,
|
|
435
454
|
connectionTimeout: 30000,
|
|
436
|
-
keepAlive: true
|
|
455
|
+
keepAlive: true,
|
|
437
456
|
}
|
|
438
457
|
```
|
|
439
458
|
|
|
@@ -443,7 +462,7 @@ const inspector = {
|
|
|
443
462
|
// Cache tool results
|
|
444
463
|
server.enableCache({
|
|
445
464
|
ttl: 300, // 5 minutes
|
|
446
|
-
maxSize: 100 // MB
|
|
465
|
+
maxSize: 100, // MB
|
|
447
466
|
})
|
|
448
467
|
```
|
|
449
468
|
|
|
@@ -457,7 +476,7 @@ server.enableCache({
|
|
|
457
476
|
// Configure CORS for inspector access
|
|
458
477
|
server.configureCORS({
|
|
459
478
|
origin: ['http://localhost:3000'],
|
|
460
|
-
credentials: true
|
|
479
|
+
credentials: true,
|
|
461
480
|
})
|
|
462
481
|
```
|
|
463
482
|
|
|
@@ -474,7 +493,7 @@ server.use(authMiddleware)
|
|
|
474
493
|
// Prevent abuse
|
|
475
494
|
server.configureRateLimit({
|
|
476
495
|
windowMs: 60000, // 1 minute
|
|
477
|
-
max: 100 // requests
|
|
496
|
+
max: 100, // requests
|
|
478
497
|
})
|
|
479
498
|
```
|
|
480
499
|
|
|
@@ -499,9 +518,9 @@ configureInspector(options: InspectorOptions): void
|
|
|
499
518
|
|
|
500
519
|
```typescript
|
|
501
520
|
interface InspectorOptions {
|
|
502
|
-
autoConnect?: boolean
|
|
521
|
+
autoConnect?: boolean // Auto-connect to local server
|
|
503
522
|
theme?: 'light' | 'dark' | 'auto'
|
|
504
|
-
persistence?: boolean
|
|
523
|
+
persistence?: boolean // Save sessions
|
|
505
524
|
maxConnections?: number
|
|
506
525
|
connectionTimeout?: number
|
|
507
526
|
}
|
|
@@ -512,6 +531,7 @@ interface InspectorOptions {
|
|
|
512
531
|
## 🤝 Contributing
|
|
513
532
|
|
|
514
533
|
We welcome contributions! Areas for improvement:
|
|
534
|
+
|
|
515
535
|
- Additional UI themes
|
|
516
536
|
- More keyboard shortcuts
|
|
517
537
|
- Enhanced tool testing features
|
|
@@ -534,4 +554,4 @@ See our [contributing guide](https://github.com/mcp-use/mcp-use-ts/blob/main/CON
|
|
|
534
554
|
|
|
535
555
|
## 📜 License
|
|
536
556
|
|
|
537
|
-
MIT © [MCP-Use](https://github.com/mcp-use)
|
|
557
|
+
MIT © [MCP-Use](https://github.com/mcp-use)
|
|
@@ -0,0 +1,262 @@
|
|
|
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 { MCPInspector } from '../server/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
|
+
// Parse command line arguments
|
|
36
|
+
const args = process.argv.slice(2);
|
|
37
|
+
let mcpUrl;
|
|
38
|
+
let startPort = 8080;
|
|
39
|
+
for (let i = 0; i < args.length; i++) {
|
|
40
|
+
if (args[i] === '--url' && i + 1 < args.length) {
|
|
41
|
+
mcpUrl = args[i + 1];
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else if (args[i] === '--port' && i + 1 < args.length) {
|
|
45
|
+
startPort = parseInt(args[i + 1], 10);
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
else if (args[i] === '--help' || args[i] === '-h') {
|
|
49
|
+
console.log(`
|
|
50
|
+
MCP Inspector - Inspect and debug MCP servers
|
|
51
|
+
|
|
52
|
+
Usage:
|
|
53
|
+
npx @mcp-use/inspect [options]
|
|
54
|
+
|
|
55
|
+
Options:
|
|
56
|
+
--url <url> MCP server URL to auto-connect to (e.g., http://localhost:3000/mcp)
|
|
57
|
+
--port <port> Starting port to try (default: 8080, will find next available)
|
|
58
|
+
--help, -h Show this help message
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
# Run inspector with auto-connect
|
|
62
|
+
npx @mcp-use/inspect --url http://localhost:3000/mcp
|
|
63
|
+
|
|
64
|
+
# Run starting from custom port
|
|
65
|
+
npx @mcp-use/inspect --url http://localhost:3000/mcp --port 9000
|
|
66
|
+
|
|
67
|
+
# Run without auto-connect
|
|
68
|
+
npx @mcp-use/inspect
|
|
69
|
+
`);
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const app = new Hono();
|
|
74
|
+
// Middleware
|
|
75
|
+
app.use('*', cors());
|
|
76
|
+
app.use('*', logger());
|
|
77
|
+
// Health check
|
|
78
|
+
app.get('/health', (c) => {
|
|
79
|
+
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
80
|
+
});
|
|
81
|
+
// MCP Inspector routes
|
|
82
|
+
const mcpInspector = new MCPInspector();
|
|
83
|
+
// List available MCP servers
|
|
84
|
+
app.get('/api/servers', async (c) => {
|
|
85
|
+
try {
|
|
86
|
+
const servers = await mcpInspector.listServers();
|
|
87
|
+
return c.json({ servers });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return c.json({ error: 'Failed to list servers' }, 500);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
// Connect to an MCP server
|
|
94
|
+
app.post('/api/servers/connect', async (c) => {
|
|
95
|
+
try {
|
|
96
|
+
const { url, command } = await c.req.json();
|
|
97
|
+
const server = await mcpInspector.connectToServer(url, command);
|
|
98
|
+
return c.json({ server });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return c.json({ error: 'Failed to connect to server' }, 500);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// Get server details
|
|
105
|
+
app.get('/api/servers/:id', async (c) => {
|
|
106
|
+
try {
|
|
107
|
+
const id = c.req.param('id');
|
|
108
|
+
const server = await mcpInspector.getServer(id);
|
|
109
|
+
if (!server) {
|
|
110
|
+
return c.json({ error: 'Server not found' }, 404);
|
|
111
|
+
}
|
|
112
|
+
return c.json({ server });
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return c.json({ error: 'Failed to get server details' }, 500);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
// Execute a tool on a server
|
|
119
|
+
app.post('/api/servers/:id/tools/:toolName/execute', async (c) => {
|
|
120
|
+
try {
|
|
121
|
+
const id = c.req.param('id');
|
|
122
|
+
const toolName = c.req.param('toolName');
|
|
123
|
+
const input = await c.req.json();
|
|
124
|
+
const result = await mcpInspector.executeTool(id, toolName, input);
|
|
125
|
+
return c.json({ result });
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return c.json({ error: 'Failed to execute tool' }, 500);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
// Get server tools
|
|
132
|
+
app.get('/api/servers/:id/tools', async (c) => {
|
|
133
|
+
try {
|
|
134
|
+
const id = c.req.param('id');
|
|
135
|
+
const tools = await mcpInspector.getServerTools(id);
|
|
136
|
+
return c.json({ tools });
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return c.json({ error: 'Failed to get server tools' }, 500);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
// Get server resources
|
|
143
|
+
app.get('/api/servers/:id/resources', async (c) => {
|
|
144
|
+
try {
|
|
145
|
+
const id = c.req.param('id');
|
|
146
|
+
const resources = await mcpInspector.getServerResources(id);
|
|
147
|
+
return c.json({ resources });
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return c.json({ error: 'Failed to get server resources' }, 500);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
// Disconnect from a server
|
|
154
|
+
app.delete('/api/servers/:id', async (c) => {
|
|
155
|
+
try {
|
|
156
|
+
const id = c.req.param('id');
|
|
157
|
+
await mcpInspector.disconnectServer(id);
|
|
158
|
+
return c.json({ success: true });
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return c.json({ error: 'Failed to disconnect server' }, 500);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
// Serve static assets from the built client
|
|
165
|
+
const clientDistPath = join(__dirname, '../../dist/client');
|
|
166
|
+
if (existsSync(clientDistPath)) {
|
|
167
|
+
// Serve static assets from /inspector/assets/* (matching Vite's base path)
|
|
168
|
+
app.get('/inspector/assets/*', async (c) => {
|
|
169
|
+
const path = c.req.path.replace('/inspector/assets/', 'assets/');
|
|
170
|
+
const fullPath = join(clientDistPath, path);
|
|
171
|
+
if (existsSync(fullPath)) {
|
|
172
|
+
const content = await import('node:fs').then(fs => fs.readFileSync(fullPath));
|
|
173
|
+
// Set appropriate content type based on file extension
|
|
174
|
+
if (path.endsWith('.js')) {
|
|
175
|
+
c.header('Content-Type', 'application/javascript');
|
|
176
|
+
}
|
|
177
|
+
else if (path.endsWith('.css')) {
|
|
178
|
+
c.header('Content-Type', 'text/css');
|
|
179
|
+
}
|
|
180
|
+
else if (path.endsWith('.svg')) {
|
|
181
|
+
c.header('Content-Type', 'image/svg+xml');
|
|
182
|
+
}
|
|
183
|
+
return c.body(content);
|
|
184
|
+
}
|
|
185
|
+
return c.notFound();
|
|
186
|
+
});
|
|
187
|
+
// Redirect root path to /inspector
|
|
188
|
+
app.get('/', (c) => {
|
|
189
|
+
return c.redirect('/inspector');
|
|
190
|
+
});
|
|
191
|
+
// Serve the main HTML file for /inspector and all other routes (SPA routing)
|
|
192
|
+
app.get('*', (c) => {
|
|
193
|
+
const indexPath = join(clientDistPath, 'index.html');
|
|
194
|
+
if (existsSync(indexPath)) {
|
|
195
|
+
const content = import('node:fs').then(fs => fs.readFileSync(indexPath, 'utf-8'));
|
|
196
|
+
return c.html(content);
|
|
197
|
+
}
|
|
198
|
+
return c.html(`
|
|
199
|
+
<!DOCTYPE html>
|
|
200
|
+
<html>
|
|
201
|
+
<head>
|
|
202
|
+
<title>MCP Inspector</title>
|
|
203
|
+
</head>
|
|
204
|
+
<body>
|
|
205
|
+
<h1>MCP Inspector</h1>
|
|
206
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
207
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
208
|
+
</body>
|
|
209
|
+
</html>
|
|
210
|
+
`);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
console.warn(`⚠️ MCP Inspector client files not found at ${clientDistPath}`);
|
|
215
|
+
console.warn(` Run 'yarn build' in the inspector package to build the UI`);
|
|
216
|
+
// Fallback for when client is not built
|
|
217
|
+
app.get('*', (c) => {
|
|
218
|
+
return c.html(`
|
|
219
|
+
<!DOCTYPE html>
|
|
220
|
+
<html>
|
|
221
|
+
<head>
|
|
222
|
+
<title>MCP Inspector</title>
|
|
223
|
+
</head>
|
|
224
|
+
<body>
|
|
225
|
+
<h1>MCP Inspector</h1>
|
|
226
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
227
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
228
|
+
</body>
|
|
229
|
+
</html>
|
|
230
|
+
`);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// Start the server with automatic port selection
|
|
234
|
+
async function startServer() {
|
|
235
|
+
try {
|
|
236
|
+
const port = await findAvailablePort(startPort);
|
|
237
|
+
serve({
|
|
238
|
+
fetch: app.fetch,
|
|
239
|
+
port,
|
|
240
|
+
});
|
|
241
|
+
console.log(`🚀 MCP Inspector running on http://localhost:${port}`);
|
|
242
|
+
if (mcpUrl) {
|
|
243
|
+
console.log(`📡 Auto-connecting to: ${mcpUrl}`);
|
|
244
|
+
}
|
|
245
|
+
// Auto-open browser
|
|
246
|
+
try {
|
|
247
|
+
const command = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
248
|
+
await execAsync(`${command} http://localhost:${port}`);
|
|
249
|
+
console.log(`🌐 Browser opened automatically`);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
console.log(`🌐 Please open http://localhost:${port} in your browser`);
|
|
253
|
+
}
|
|
254
|
+
return { port, fetch: app.fetch };
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
console.error('Failed to start server:', error);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Start the server
|
|
262
|
+
startServer();
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
|
|
6
|
+
// src/cli/inspect.ts
|
|
7
|
+
import { serve } from "@hono/node-server";
|
|
8
|
+
import { Hono } from "hono";
|
|
9
|
+
import { cors } from "hono/cors";
|
|
10
|
+
import { logger } from "hono/logger";
|
|
11
|
+
import { existsSync, readFileSync } from "fs";
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import open from "open";
|
|
15
|
+
|
|
16
|
+
// src/server/mcp-inspector.ts
|
|
17
|
+
import { MCPClient } from "mcp-use";
|
|
18
|
+
var MCPInspector = class {
|
|
19
|
+
constructor() {
|
|
20
|
+
__publicField(this, "servers", /* @__PURE__ */ new Map());
|
|
21
|
+
__publicField(this, "client");
|
|
22
|
+
this.client = new MCPClient();
|
|
23
|
+
}
|
|
24
|
+
async listServers() {
|
|
25
|
+
return Array.from(this.servers.values());
|
|
26
|
+
}
|
|
27
|
+
async connectToServer(url, command) {
|
|
28
|
+
const id = Date.now().toString();
|
|
29
|
+
const name = url || command || "Unknown Server";
|
|
30
|
+
try {
|
|
31
|
+
const serverName = `server_${id}`;
|
|
32
|
+
const serverConfig = url ? { url } : { command };
|
|
33
|
+
this.client.addServer(serverName, serverConfig);
|
|
34
|
+
const session = await this.client.createSession(serverName, true);
|
|
35
|
+
const tools = [
|
|
36
|
+
{
|
|
37
|
+
name: "get_weather",
|
|
38
|
+
description: "Get current weather for a location",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
location: { type: "string", description: "City name" }
|
|
43
|
+
},
|
|
44
|
+
required: ["location"]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
const resources = [
|
|
49
|
+
{
|
|
50
|
+
uri: "file:///home/user/documents",
|
|
51
|
+
name: "Documents",
|
|
52
|
+
description: "User documents directory",
|
|
53
|
+
mimeType: "application/x-directory"
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
const server = {
|
|
57
|
+
id,
|
|
58
|
+
name,
|
|
59
|
+
url,
|
|
60
|
+
command,
|
|
61
|
+
status: "connected",
|
|
62
|
+
session,
|
|
63
|
+
tools,
|
|
64
|
+
resources,
|
|
65
|
+
lastActivity: /* @__PURE__ */ new Date()
|
|
66
|
+
};
|
|
67
|
+
this.servers.set(id, server);
|
|
68
|
+
return server;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
const server = {
|
|
71
|
+
id,
|
|
72
|
+
name,
|
|
73
|
+
url,
|
|
74
|
+
command,
|
|
75
|
+
status: "error",
|
|
76
|
+
tools: [],
|
|
77
|
+
resources: [],
|
|
78
|
+
lastActivity: /* @__PURE__ */ new Date()
|
|
79
|
+
};
|
|
80
|
+
this.servers.set(id, server);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async getServer(id) {
|
|
85
|
+
return this.servers.get(id) || null;
|
|
86
|
+
}
|
|
87
|
+
async executeTool(serverId, toolName, input) {
|
|
88
|
+
const server = this.servers.get(serverId);
|
|
89
|
+
if (!server || !server.session) {
|
|
90
|
+
throw new Error("Server not found or not connected");
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const result = {
|
|
94
|
+
tool: toolName,
|
|
95
|
+
input,
|
|
96
|
+
result: `Mock result for ${toolName} with input: ${JSON.stringify(input)}`,
|
|
97
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
98
|
+
};
|
|
99
|
+
server.lastActivity = /* @__PURE__ */ new Date();
|
|
100
|
+
return result;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
server.status = "error";
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async getServerTools(serverId) {
|
|
107
|
+
const server = this.servers.get(serverId);
|
|
108
|
+
if (!server) {
|
|
109
|
+
throw new Error("Server not found");
|
|
110
|
+
}
|
|
111
|
+
return server.tools;
|
|
112
|
+
}
|
|
113
|
+
async getServerResources(serverId) {
|
|
114
|
+
const server = this.servers.get(serverId);
|
|
115
|
+
if (!server) {
|
|
116
|
+
throw new Error("Server not found");
|
|
117
|
+
}
|
|
118
|
+
return server.resources;
|
|
119
|
+
}
|
|
120
|
+
async disconnectServer(serverId) {
|
|
121
|
+
const server = this.servers.get(serverId);
|
|
122
|
+
if (server && server.session) {
|
|
123
|
+
try {
|
|
124
|
+
await server.session.disconnect();
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error("Error disconnecting from server:", error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this.servers.delete(serverId);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/cli/inspect.ts
|
|
134
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
135
|
+
var __dirname = dirname(__filename);
|
|
136
|
+
function isValidUrl(urlString) {
|
|
137
|
+
try {
|
|
138
|
+
const url = new URL(urlString);
|
|
139
|
+
return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "ws:" || url.protocol === "wss:";
|
|
140
|
+
} catch {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function findAvailablePort(startPort2 = 8080, maxAttempts = 100) {
|
|
145
|
+
const net = await import("net");
|
|
146
|
+
for (let port = startPort2; port < startPort2 + maxAttempts; port++) {
|
|
147
|
+
try {
|
|
148
|
+
await new Promise((resolve, reject) => {
|
|
149
|
+
const server = net.createServer();
|
|
150
|
+
server.listen(port, () => {
|
|
151
|
+
server.close(() => resolve());
|
|
152
|
+
});
|
|
153
|
+
server.on("error", (err) => reject(err));
|
|
154
|
+
});
|
|
155
|
+
return port;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
throw new Error(`No available port found after trying ${maxAttempts} ports starting from ${startPort2}`);
|
|
161
|
+
}
|
|
162
|
+
var args = process.argv.slice(2);
|
|
163
|
+
var mcpUrl;
|
|
164
|
+
var startPort = 8080;
|
|
165
|
+
for (let i = 0; i < args.length; i++) {
|
|
166
|
+
if (args[i] === "--url" && i + 1 < args.length) {
|
|
167
|
+
const url = args[i + 1];
|
|
168
|
+
if (!isValidUrl(url)) {
|
|
169
|
+
console.error(`Error: Invalid URL format: ${url}`);
|
|
170
|
+
console.error("URL must start with http://, https://, ws://, or wss://");
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
mcpUrl = url;
|
|
174
|
+
i++;
|
|
175
|
+
} else if (args[i] === "--port" && i + 1 < args.length) {
|
|
176
|
+
const parsedPort = Number.parseInt(args[i + 1], 10);
|
|
177
|
+
if (Number.isNaN(parsedPort) || parsedPort < 1 || parsedPort > 65535) {
|
|
178
|
+
console.error(`Error: Port must be a number between 1 and 65535, got: ${args[i + 1]}`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
startPort = parsedPort;
|
|
182
|
+
i++;
|
|
183
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
184
|
+
console.log(`
|
|
185
|
+
MCP Inspector - Inspect and debug MCP servers
|
|
186
|
+
|
|
187
|
+
Usage:
|
|
188
|
+
npx @mcp-use/inspector [options]
|
|
189
|
+
|
|
190
|
+
Options:
|
|
191
|
+
--url <url> MCP server URL to auto-connect to (e.g., http://localhost:3000/mcp)
|
|
192
|
+
--port <port> Starting port to try (default: 8080, will find next available)
|
|
193
|
+
--help, -h Show this help message
|
|
194
|
+
|
|
195
|
+
Examples:
|
|
196
|
+
# Run inspector with auto-connect
|
|
197
|
+
npx @mcp-use/inspector --url http://localhost:3000/mcp
|
|
198
|
+
|
|
199
|
+
# Run starting from custom port
|
|
200
|
+
npx @mcp-use/inspector --url http://localhost:3000/mcp --port 9000
|
|
201
|
+
|
|
202
|
+
# Run without auto-connect
|
|
203
|
+
npx @mcp-use/inspector
|
|
204
|
+
`);
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
var app = new Hono();
|
|
209
|
+
app.use("*", cors());
|
|
210
|
+
app.use("*", logger());
|
|
211
|
+
app.get("/health", (c) => {
|
|
212
|
+
return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
213
|
+
});
|
|
214
|
+
var mcpInspector = new MCPInspector();
|
|
215
|
+
app.get("/api/servers", async (c) => {
|
|
216
|
+
try {
|
|
217
|
+
const servers = await mcpInspector.listServers();
|
|
218
|
+
return c.json({ servers });
|
|
219
|
+
} catch (error) {
|
|
220
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
221
|
+
console.error("Failed to list servers:", message, error);
|
|
222
|
+
return c.json({ error: "Failed to list servers", details: message }, 500);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
app.post("/api/servers/connect", async (c) => {
|
|
226
|
+
try {
|
|
227
|
+
const { url, command } = await c.req.json();
|
|
228
|
+
if (url && !isValidUrl(url)) {
|
|
229
|
+
return c.json({ error: "Invalid URL format. Must start with http://, https://, ws://, or wss://" }, 400);
|
|
230
|
+
}
|
|
231
|
+
const server = await mcpInspector.connectToServer(url, command);
|
|
232
|
+
return c.json({ server });
|
|
233
|
+
} catch (error) {
|
|
234
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
235
|
+
console.error("Failed to connect to server:", message, error);
|
|
236
|
+
return c.json({ error: "Failed to connect to server", details: message }, 500);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
app.get("/api/servers/:id", async (c) => {
|
|
240
|
+
try {
|
|
241
|
+
const id = c.req.param("id");
|
|
242
|
+
const server = await mcpInspector.getServer(id);
|
|
243
|
+
if (!server) {
|
|
244
|
+
return c.json({ error: "Server not found" }, 404);
|
|
245
|
+
}
|
|
246
|
+
return c.json({ server });
|
|
247
|
+
} catch (error) {
|
|
248
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
249
|
+
console.error("Failed to get server details:", message, error);
|
|
250
|
+
return c.json({ error: "Failed to get server details", details: message }, 500);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
app.post("/api/servers/:id/tools/:toolName/execute", async (c) => {
|
|
254
|
+
try {
|
|
255
|
+
const id = c.req.param("id");
|
|
256
|
+
const toolName = c.req.param("toolName");
|
|
257
|
+
const input = await c.req.json();
|
|
258
|
+
const result = await mcpInspector.executeTool(id, toolName, input);
|
|
259
|
+
return c.json({ result });
|
|
260
|
+
} catch (error) {
|
|
261
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
262
|
+
console.error("Failed to execute tool:", message, error);
|
|
263
|
+
return c.json({ error: "Failed to execute tool", details: message }, 500);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
app.get("/api/servers/:id/tools", async (c) => {
|
|
267
|
+
try {
|
|
268
|
+
const id = c.req.param("id");
|
|
269
|
+
const tools = await mcpInspector.getServerTools(id);
|
|
270
|
+
return c.json({ tools });
|
|
271
|
+
} catch (error) {
|
|
272
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
273
|
+
console.error("Failed to get server tools:", message, error);
|
|
274
|
+
return c.json({ error: "Failed to get server tools", details: message }, 500);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
app.get("/api/servers/:id/resources", async (c) => {
|
|
278
|
+
try {
|
|
279
|
+
const id = c.req.param("id");
|
|
280
|
+
const resources = await mcpInspector.getServerResources(id);
|
|
281
|
+
return c.json({ resources });
|
|
282
|
+
} catch (error) {
|
|
283
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
284
|
+
console.error("Failed to get server resources:", message, error);
|
|
285
|
+
return c.json({ error: "Failed to get server resources", details: message }, 500);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
app.delete("/api/servers/:id", async (c) => {
|
|
289
|
+
try {
|
|
290
|
+
const id = c.req.param("id");
|
|
291
|
+
await mcpInspector.disconnectServer(id);
|
|
292
|
+
return c.json({ success: true });
|
|
293
|
+
} catch (error) {
|
|
294
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
295
|
+
console.error("Failed to disconnect server:", message, error);
|
|
296
|
+
return c.json({ error: "Failed to disconnect server", details: message }, 500);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
var clientDistPath = join(__dirname, "../../dist/client");
|
|
300
|
+
if (existsSync(clientDistPath)) {
|
|
301
|
+
app.get("/inspector/assets/*", (c) => {
|
|
302
|
+
const path = c.req.path.replace("/inspector/assets/", "assets/");
|
|
303
|
+
const fullPath = join(clientDistPath, path);
|
|
304
|
+
if (existsSync(fullPath)) {
|
|
305
|
+
const content = readFileSync(fullPath);
|
|
306
|
+
if (path.endsWith(".js")) {
|
|
307
|
+
c.header("Content-Type", "application/javascript");
|
|
308
|
+
} else if (path.endsWith(".css")) {
|
|
309
|
+
c.header("Content-Type", "text/css");
|
|
310
|
+
} else if (path.endsWith(".svg")) {
|
|
311
|
+
c.header("Content-Type", "image/svg+xml");
|
|
312
|
+
}
|
|
313
|
+
return c.body(content);
|
|
314
|
+
}
|
|
315
|
+
return c.notFound();
|
|
316
|
+
});
|
|
317
|
+
app.get("/", (c) => {
|
|
318
|
+
return c.redirect("/inspector");
|
|
319
|
+
});
|
|
320
|
+
app.get("*", (c) => {
|
|
321
|
+
const indexPath = join(clientDistPath, "index.html");
|
|
322
|
+
if (existsSync(indexPath)) {
|
|
323
|
+
const content = readFileSync(indexPath, "utf-8");
|
|
324
|
+
return c.html(content);
|
|
325
|
+
}
|
|
326
|
+
return c.html(`
|
|
327
|
+
<!DOCTYPE html>
|
|
328
|
+
<html>
|
|
329
|
+
<head>
|
|
330
|
+
<title>MCP Inspector</title>
|
|
331
|
+
</head>
|
|
332
|
+
<body>
|
|
333
|
+
<h1>MCP Inspector</h1>
|
|
334
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
335
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
336
|
+
</body>
|
|
337
|
+
</html>
|
|
338
|
+
`);
|
|
339
|
+
});
|
|
340
|
+
} else {
|
|
341
|
+
console.warn(`\u26A0\uFE0F MCP Inspector client files not found at ${clientDistPath}`);
|
|
342
|
+
console.warn(` Run 'yarn build' in the inspector package to build the UI`);
|
|
343
|
+
app.get("*", (c) => {
|
|
344
|
+
return c.html(`
|
|
345
|
+
<!DOCTYPE html>
|
|
346
|
+
<html>
|
|
347
|
+
<head>
|
|
348
|
+
<title>MCP Inspector</title>
|
|
349
|
+
</head>
|
|
350
|
+
<body>
|
|
351
|
+
<h1>MCP Inspector</h1>
|
|
352
|
+
<p>Client files not found. Please run 'yarn build' to build the UI.</p>
|
|
353
|
+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
|
|
354
|
+
</body>
|
|
355
|
+
</html>
|
|
356
|
+
`);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
async function startServer() {
|
|
360
|
+
try {
|
|
361
|
+
const port = await findAvailablePort(startPort);
|
|
362
|
+
serve({
|
|
363
|
+
fetch: app.fetch,
|
|
364
|
+
port
|
|
365
|
+
});
|
|
366
|
+
console.log(`\u{1F680} MCP Inspector running on http://localhost:${port}`);
|
|
367
|
+
if (mcpUrl) {
|
|
368
|
+
console.log(`\u{1F4E1} Auto-connecting to: ${mcpUrl}`);
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
await open(`http://localhost:${port}`);
|
|
372
|
+
console.log(`\u{1F310} Browser opened automatically`);
|
|
373
|
+
} catch {
|
|
374
|
+
console.log(`\u{1F310} Please open http://localhost:${port} in your browser`);
|
|
375
|
+
}
|
|
376
|
+
return { port, fetch: app.fetch };
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error("Failed to start server:", error);
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
startServer();
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { MCPClient } from 'mcp-use';
|
|
2
|
+
export class MCPInspector {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.servers = new Map();
|
|
5
|
+
this.client = new MCPClient();
|
|
6
|
+
}
|
|
7
|
+
async listServers() {
|
|
8
|
+
return Array.from(this.servers.values());
|
|
9
|
+
}
|
|
10
|
+
async connectToServer(url, command) {
|
|
11
|
+
const id = Date.now().toString();
|
|
12
|
+
const name = url || command || 'Unknown Server';
|
|
13
|
+
try {
|
|
14
|
+
// Configure server in MCP client
|
|
15
|
+
const serverName = `server_${id}`;
|
|
16
|
+
const serverConfig = url ? { url } : { command };
|
|
17
|
+
this.client.addServer(serverName, serverConfig);
|
|
18
|
+
// Create session
|
|
19
|
+
const session = await this.client.createSession(serverName, true);
|
|
20
|
+
// Mock tools and resources for now
|
|
21
|
+
const tools = [
|
|
22
|
+
{
|
|
23
|
+
name: 'get_weather',
|
|
24
|
+
description: 'Get current weather for a location',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
location: { type: 'string', description: 'City name' },
|
|
29
|
+
},
|
|
30
|
+
required: ['location'],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
const resources = [
|
|
35
|
+
{
|
|
36
|
+
uri: 'file:///home/user/documents',
|
|
37
|
+
name: 'Documents',
|
|
38
|
+
description: 'User documents directory',
|
|
39
|
+
mimeType: 'application/x-directory',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
const server = {
|
|
43
|
+
id,
|
|
44
|
+
name,
|
|
45
|
+
url,
|
|
46
|
+
command,
|
|
47
|
+
status: 'connected',
|
|
48
|
+
session,
|
|
49
|
+
tools,
|
|
50
|
+
resources,
|
|
51
|
+
lastActivity: new Date(),
|
|
52
|
+
};
|
|
53
|
+
this.servers.set(id, server);
|
|
54
|
+
return server;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const server = {
|
|
58
|
+
id,
|
|
59
|
+
name,
|
|
60
|
+
url,
|
|
61
|
+
command,
|
|
62
|
+
status: 'error',
|
|
63
|
+
tools: [],
|
|
64
|
+
resources: [],
|
|
65
|
+
lastActivity: new Date(),
|
|
66
|
+
};
|
|
67
|
+
this.servers.set(id, server);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async getServer(id) {
|
|
72
|
+
return this.servers.get(id) || null;
|
|
73
|
+
}
|
|
74
|
+
async executeTool(serverId, toolName, input) {
|
|
75
|
+
const server = this.servers.get(serverId);
|
|
76
|
+
if (!server || !server.session) {
|
|
77
|
+
throw new Error('Server not found or not connected');
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
// Mock tool execution for now
|
|
81
|
+
const result = {
|
|
82
|
+
tool: toolName,
|
|
83
|
+
input,
|
|
84
|
+
result: `Mock result for ${toolName} with input: ${JSON.stringify(input)}`,
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
server.lastActivity = new Date();
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
server.status = 'error';
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async getServerTools(serverId) {
|
|
96
|
+
const server = this.servers.get(serverId);
|
|
97
|
+
if (!server) {
|
|
98
|
+
throw new Error('Server not found');
|
|
99
|
+
}
|
|
100
|
+
return server.tools;
|
|
101
|
+
}
|
|
102
|
+
async getServerResources(serverId) {
|
|
103
|
+
const server = this.servers.get(serverId);
|
|
104
|
+
if (!server) {
|
|
105
|
+
throw new Error('Server not found');
|
|
106
|
+
}
|
|
107
|
+
return server.resources;
|
|
108
|
+
}
|
|
109
|
+
async disconnectServer(serverId) {
|
|
110
|
+
const server = this.servers.get(serverId);
|
|
111
|
+
if (server && server.session) {
|
|
112
|
+
try {
|
|
113
|
+
await server.session.disconnect();
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error('Error disconnecting from server:', error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
this.servers.delete(serverId);
|
|
120
|
+
}
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-use/inspector",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.11",
|
|
5
5
|
"description": "MCP Inspector - A tool for inspecting and debugging MCP servers",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"lucide-react": "^0.545.0",
|
|
54
54
|
"motion": "^12.23.22",
|
|
55
55
|
"next-themes": "^0.4.6",
|
|
56
|
+
"open": "^10.0.0",
|
|
56
57
|
"react": "^19.2.0",
|
|
57
58
|
"react-dom": "^19.2.0",
|
|
58
59
|
"react-resizable-panels": "^3.0.6",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"sonner": "^2.0.7",
|
|
62
63
|
"tailwind-merge": "^3.3.1",
|
|
63
64
|
"vite-express": "^0.21.1",
|
|
64
|
-
"mcp-use": "1.0.
|
|
65
|
+
"mcp-use": "1.0.7"
|
|
65
66
|
},
|
|
66
67
|
"publishConfig": {
|
|
67
68
|
"access": "public"
|
|
@@ -98,7 +99,7 @@
|
|
|
98
99
|
"build": "npm run build:client && npm run build:server && npm run build:cli",
|
|
99
100
|
"build:client": "vite build",
|
|
100
101
|
"build:server": "tsup src/server/*.ts --format esm --out-dir dist/server && tsc -p tsconfig.server.json --emitDeclarationOnly --declaration",
|
|
101
|
-
"build:cli": "tsup src/cli/inspect.ts --format
|
|
102
|
+
"build:cli": "tsup src/cli/inspect.ts --format esm --out-dir dist/cli",
|
|
102
103
|
"start": "node dist/server/server.js",
|
|
103
104
|
"preview": "vite preview",
|
|
104
105
|
"type-check": "tsc --noEmit",
|