Skip to content

Commit c558b6b

Browse files
committed
feat(inspector): enhance development and server functionality
- Introduce new development scripts for concurrent client and server execution. - Update Vite configuration to allow external connections and add proxy settings for API requests. - Replace standalone server implementation with unified server logic for better maintainability. - Add new dependencies for improved functionality, including vite-express for serving static assets. - Remove deprecated standalone server file and streamline server startup process with port availability checks.
1 parent 073e14e commit c558b6b

File tree

5 files changed

+251
-78
lines changed

5 files changed

+251
-78
lines changed

packages/inspector/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626
"dist"
2727
],
2828
"scripts": {
29-
"dev": "tsx watch src/server/standalone.ts",
29+
"dev": "concurrently \"npm run dev:client\" \"npm run dev:server\"",
30+
"dev:client": "vite --port 3000",
31+
"dev:server": "VITE_DEV=true tsx watch src/server/server.ts",
32+
"dev:standalone": "tsx watch src/server/server.ts",
3033
"build": "npm run build:client && npm run build:server",
3134
"build:client": "vite build",
3235
"build:server": "tsc -p tsconfig.server.json",
33-
"start": "node dist/server/standalone.js",
36+
"start": "node dist/server/server.js",
3437
"preview": "vite preview",
3538
"type-check": "tsc --noEmit",
3639
"lint": "eslint .",
@@ -74,7 +77,8 @@
7477
"react-router-dom": "^7.9.3",
7578
"react-syntax-highlighter": "^15.6.6",
7679
"sonner": "^2.0.7",
77-
"tailwind-merge": "^3.3.1"
80+
"tailwind-merge": "^3.3.1",
81+
"vite-express": "^0.21.1"
7882
},
7983
"publishConfig": {
8084
"access": "public"

packages/inspector/src/server/standalone.ts renamed to packages/inspector/src/server/server.ts

Lines changed: 101 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,17 @@ const __filename = fileURLToPath(import.meta.url)
1414
const __dirname = dirname(__filename)
1515
const execAsync = promisify(exec)
1616

17-
// Find available port starting from 8080
18-
async function findAvailablePort(startPort = 8080): Promise<number> {
17+
// Check if a specific port is available
18+
async function isPortAvailable(port: number): Promise<boolean> {
1919
const net = await import('node:net')
2020

21-
for (let port = startPort; port < startPort + 100; port++) {
22-
try {
23-
await new Promise<void>((resolve, reject) => {
24-
const server = net.createServer()
25-
server.listen(port, () => {
26-
server.close(() => resolve())
27-
})
28-
server.on('error', () => reject(new Error(`Port ${port} is in use`)))
29-
})
30-
return port
31-
}
32-
catch {
33-
continue
34-
}
35-
}
36-
throw new Error(`No available port found starting from ${startPort}`)
21+
return new Promise((resolve) => {
22+
const server = net.createServer()
23+
server.listen(port, () => {
24+
server.close(() => resolve(true))
25+
})
26+
server.on('error', () => resolve(false))
27+
})
3728
}
3829

3930
const app = new Hono()
@@ -142,10 +133,63 @@ app.delete('/api/servers/:id', async (c) => {
142133
}
143134
})
144135

136+
// Check if we're in development mode (Vite dev server running)
137+
const isDev = process.env.NODE_ENV === 'development' || process.env.VITE_DEV === 'true'
138+
145139
// Serve static assets from the built client
146140
const clientDistPath = join(__dirname, '../../dist/client')
147141

148-
if (existsSync(clientDistPath)) {
142+
if (isDev) {
143+
// Development mode: proxy client requests to Vite dev server
144+
console.warn('🔧 Development mode: Proxying client requests to Vite dev server')
145+
146+
// Proxy all non-API requests to Vite dev server
147+
app.get('*', async (c) => {
148+
const path = c.req.path
149+
150+
// Skip API routes
151+
if (path.startsWith('/api/')) {
152+
return c.notFound()
153+
}
154+
155+
try {
156+
// Vite dev server should be running on port 3000
157+
const viteUrl = `http://localhost:3000${path}`
158+
const response = await fetch(viteUrl, {
159+
signal: AbortSignal.timeout(1000), // 1 second timeout
160+
})
161+
162+
if (response.ok) {
163+
const content = await response.text()
164+
const contentType = response.headers.get('content-type') || 'text/html'
165+
166+
c.header('Content-Type', contentType)
167+
return c.html(content)
168+
}
169+
}
170+
catch (error) {
171+
console.warn(`Failed to proxy to Vite dev server: ${error}`)
172+
}
173+
174+
// Fallback HTML if Vite dev server is not running
175+
return c.html(`
176+
<!DOCTYPE html>
177+
<html>
178+
<head>
179+
<title>MCP Inspector - Development</title>
180+
</head>
181+
<body>
182+
<h1>MCP Inspector - Development Mode</h1>
183+
<p>Vite dev server is not running. Please start it with:</p>
184+
<pre>yarn dev:client</pre>
185+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
186+
</body>
187+
</html>
188+
`)
189+
})
190+
}
191+
else if (existsSync(clientDistPath)) {
192+
// Production mode: serve static assets from built client
149193
// Serve static assets from /inspector/assets/* (matching Vite's base path)
150194
app.get('/inspector/assets/*', async (c) => {
151195
const path = c.req.path.replace('/inspector/assets/', 'assets/')
@@ -220,27 +264,58 @@ else {
220264
})
221265
}
222266

223-
// Start the server with automatic port selection
267+
// Start the server
224268
async function startServer() {
225269
try {
226-
const port = await findAvailablePort()
270+
// In development mode, use port 3001 for API server
271+
// In production/standalone mode, try 3001 first, then 3002 as fallback
272+
const isDev = process.env.NODE_ENV === 'development' || process.env.VITE_DEV === 'true'
273+
274+
let port = 3001
275+
const available = await isPortAvailable(port)
276+
277+
if (!available) {
278+
if (isDev) {
279+
console.error(`❌ Port ${port} is not available. Please stop the process using this port and try again.`)
280+
process.exit(1)
281+
} else {
282+
// In standalone mode, try fallback port
283+
const fallbackPort = 3002
284+
console.warn(`⚠️ Port ${port} is not available, trying ${fallbackPort}`)
285+
const fallbackAvailable = await isPortAvailable(fallbackPort)
286+
287+
if (!fallbackAvailable) {
288+
console.error(`❌ Neither port ${port} nor ${fallbackPort} is available. Please stop the processes using these ports and try again.`)
289+
process.exit(1)
290+
}
291+
292+
port = fallbackPort
293+
}
294+
}
227295

228296
serve({
229297
fetch: app.fetch,
230298
port,
231299
})
232300

233-
console.log(`🚀 MCP Inspector running on http://localhost:${port}`)
301+
if (isDev) {
302+
console.warn(`🚀 MCP Inspector API server running on http://localhost:${port}`)
303+
console.warn(`🌐 Vite dev server should be running on http://localhost:3000`)
304+
} else {
305+
console.warn(`🚀 MCP Inspector running on http://localhost:${port}`)
306+
}
234307

235308
// Auto-open browser in development
236309
if (process.env.NODE_ENV !== 'production') {
237310
try {
238311
const command = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open'
239-
await execAsync(`${command} http://localhost:${port}`)
240-
console.log(`🌐 Browser opened automatically`)
312+
const url = isDev ? 'http://localhost:3000' : `http://localhost:${port}`
313+
await execAsync(`${command} ${url}`)
314+
console.warn(`🌐 Browser opened automatically`)
241315
}
242-
catch (error) {
243-
console.log(`🌐 Please open http://localhost:${port} in your browser`)
316+
catch {
317+
const url = isDev ? 'http://localhost:3000' : `http://localhost:${port}`
318+
console.warn(`🌐 Please open ${url} in your browser`)
244319
}
245320
}
246321

packages/inspector/src/server/unified.ts

Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,17 @@ const __filename = fileURLToPath(import.meta.url)
1414
const __dirname = dirname(__filename)
1515
const execAsync = promisify(exec)
1616

17-
// Find available port starting from 8080
18-
async function findAvailablePort(startPort = 8080): Promise<number> {
17+
// Check if a specific port is available
18+
async function isPortAvailable(port: number): Promise<boolean> {
1919
const net = await import('node:net')
2020

21-
for (let port = startPort; port < startPort + 100; port++) {
22-
try {
23-
await new Promise<void>((resolve, reject) => {
24-
const server = net.createServer()
25-
server.listen(port, () => {
26-
server.close(() => resolve())
27-
})
28-
server.on('error', () => reject(new Error(`Port ${port} is in use`)))
29-
})
30-
return port
31-
}
32-
catch {
33-
continue
34-
}
35-
}
36-
throw new Error(`No available port found starting from ${startPort}`)
21+
return new Promise((resolve) => {
22+
const server = net.createServer()
23+
server.listen(port, () => {
24+
server.close(() => resolve(true))
25+
})
26+
server.on('error', () => resolve(false))
27+
})
3728
}
3829

3930
const app = new Hono()
@@ -142,10 +133,63 @@ app.delete('/api/servers/:id', async (c) => {
142133
}
143134
})
144135

136+
// Check if we're in development mode (Vite dev server running)
137+
const isDev = process.env.NODE_ENV === 'development' || process.env.VITE_DEV === 'true'
138+
145139
// Serve static assets from the built client
146140
const clientDistPath = join(__dirname, '../../dist/client')
147141

148-
if (existsSync(clientDistPath)) {
142+
if (isDev) {
143+
// Development mode: proxy client requests to Vite dev server
144+
console.warn('🔧 Development mode: Proxying client requests to Vite dev server')
145+
146+
// Proxy all non-API requests to Vite dev server
147+
app.get('*', async (c) => {
148+
const path = c.req.path
149+
150+
// Skip API routes
151+
if (path.startsWith('/api/')) {
152+
return c.notFound()
153+
}
154+
155+
try {
156+
// Vite dev server should be running on port 3000
157+
const viteUrl = `http://localhost:3000${path}`
158+
const response = await fetch(viteUrl, {
159+
signal: AbortSignal.timeout(1000), // 1 second timeout
160+
})
161+
162+
if (response.ok) {
163+
const content = await response.text()
164+
const contentType = response.headers.get('content-type') || 'text/html'
165+
166+
c.header('Content-Type', contentType)
167+
return c.html(content)
168+
}
169+
}
170+
catch (error) {
171+
console.warn(`Failed to proxy to Vite dev server: ${error}`)
172+
}
173+
174+
// Fallback HTML if Vite dev server is not running
175+
return c.html(`
176+
<!DOCTYPE html>
177+
<html>
178+
<head>
179+
<title>MCP Inspector - Development</title>
180+
</head>
181+
<body>
182+
<h1>MCP Inspector - Development Mode</h1>
183+
<p>Vite dev server is not running. Please start it with:</p>
184+
<pre>yarn dev:client</pre>
185+
<p>API is available at <a href="/api/servers">/api/servers</a></p>
186+
</body>
187+
</html>
188+
`)
189+
})
190+
}
191+
else if (existsSync(clientDistPath)) {
192+
// Production mode: serve static assets from built client
149193
// Serve static assets from /inspector/assets/* (matching Vite's base path)
150194
app.get('/inspector/assets/*', async (c) => {
151195
const path = c.req.path.replace('/inspector/assets/', 'assets/')
@@ -220,27 +264,60 @@ else {
220264
})
221265
}
222266

223-
// Start the server with automatic port selection
267+
// Start the server
224268
async function startServer() {
225269
try {
226-
const port = await findAvailablePort()
270+
// In development mode, use port 3001 for API server
271+
// In production/standalone mode, try 3001 first, then 3002 as fallback
272+
const isDev = process.env.NODE_ENV === 'development' || process.env.VITE_DEV === 'true'
273+
274+
let port = 3001
275+
const available = await isPortAvailable(port)
276+
277+
if (!available) {
278+
if (isDev) {
279+
console.error(`❌ Port ${port} is not available. Please stop the process using this port and try again.`)
280+
process.exit(1)
281+
}
282+
else {
283+
// In standalone mode, try fallback port
284+
const fallbackPort = 3002
285+
console.warn(`⚠️ Port ${port} is not available, trying ${fallbackPort}`)
286+
const fallbackAvailable = await isPortAvailable(fallbackPort)
287+
288+
if (!fallbackAvailable) {
289+
console.error(`❌ Neither port ${port} nor ${fallbackPort} is available. Please stop the processes using these ports and try again.`)
290+
process.exit(1)
291+
}
292+
293+
port = fallbackPort
294+
}
295+
}
227296

228297
serve({
229298
fetch: app.fetch,
230299
port,
231300
})
232301

233-
console.log(`🚀 MCP Inspector running on http://localhost:${port}`)
302+
if (isDev) {
303+
console.warn(`🚀 MCP Inspector API server running on http://localhost:${port}`)
304+
console.warn(`🌐 Vite dev server should be running on http://localhost:3000`)
305+
}
306+
else {
307+
console.warn(`🚀 MCP Inspector running on http://localhost:${port}`)
308+
}
234309

235310
// Auto-open browser in development
236311
if (process.env.NODE_ENV !== 'production') {
237312
try {
238313
const command = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open'
239-
await execAsync(`${command} http://localhost:${port}`)
240-
console.log(`🌐 Browser opened automatically`)
314+
const url = isDev ? 'http://localhost:3000' : `http://localhost:${port}`
315+
await execAsync(`${command} ${url}`)
316+
console.warn(`🌐 Browser opened automatically`)
241317
}
242-
catch (error) {
243-
console.log(`🌐 Please open http://localhost:${port} in your browser`)
318+
catch {
319+
const url = isDev ? 'http://localhost:3000' : `http://localhost:${port}`
320+
console.warn(`🌐 Please open ${url} in your browser`)
244321
}
245322
}
246323

packages/inspector/vite.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ export default defineConfig({
4646
},
4747
server: {
4848
port: 3000,
49+
host: true, // Allow external connections
50+
proxy: {
51+
'/api': {
52+
target: 'http://localhost:3001',
53+
changeOrigin: true,
54+
},
55+
},
4956
},
5057
build: {
5158
outDir: 'dist/client',

0 commit comments

Comments
 (0)