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