22
33import open from "open" ;
44import { resolve , dirname } from "path" ;
5- import { spawnPromise } from "spawn-rx" ;
5+ import { spawnPromise , spawn } from "spawn-rx" ;
66import { fileURLToPath } from "url" ;
7+ import { randomBytes } from "crypto" ;
78
89const __dirname = dirname ( fileURLToPath ( import . meta. url ) ) ;
910
@@ -18,6 +19,7 @@ async function main() {
1819 const mcpServerArgs = [ ] ;
1920 let command = null ;
2021 let parsingFlags = true ;
22+ let isDev = false ;
2123
2224 for ( let i = 0 ; i < args . length ; i ++ ) {
2325 const arg = args [ i ] ;
@@ -27,6 +29,11 @@ async function main() {
2729 continue ;
2830 }
2931
32+ if ( parsingFlags && arg === "--dev" ) {
33+ isDev = true ;
34+ continue ;
35+ }
36+
3037 if ( parsingFlags && arg === "-e" && i + 1 < args . length ) {
3138 const envVar = args [ ++ i ] ;
3239 const equalsIndex = envVar . indexOf ( "=" ) ;
@@ -38,34 +45,25 @@ async function main() {
3845 } else {
3946 envVars [ envVar ] = "" ;
4047 }
41- } else if ( ! command ) {
48+ } else if ( ! command && ! isDev ) {
4249 command = arg ;
43- } else {
50+ } else if ( ! isDev ) {
4451 mcpServerArgs . push ( arg ) ;
4552 }
4653 }
4754
48- const inspectorServerPath = resolve (
49- __dirname ,
50- "../.." ,
51- "server" ,
52- "build" ,
53- "index.js" ,
54- ) ;
55-
56- // Path to the client entry point
57- const inspectorClientPath = resolve (
58- __dirname ,
59- "../.." ,
60- "client" ,
61- "bin" ,
62- "client.js" ,
63- ) ;
64-
6555 const CLIENT_PORT = process . env . CLIENT_PORT ?? "6274" ;
6656 const SERVER_PORT = process . env . SERVER_PORT ?? "6277" ;
6757
68- console . log ( "Starting MCP inspector..." ) ;
58+ console . log (
59+ isDev
60+ ? "Starting MCP inspector in development mode..."
61+ : "Starting MCP inspector..." ,
62+ ) ;
63+
64+ // Generate session token for authentication
65+ const sessionToken = randomBytes ( 32 ) . toString ( "hex" ) ;
66+ const authDisabled = ! ! process . env . DANGEROUSLY_OMIT_AUTH ;
6967
7068 const abort = new AbortController ( ) ;
7169
@@ -74,42 +72,150 @@ async function main() {
7472 cancelled = true ;
7573 abort . abort ( ) ;
7674 } ) ;
75+
7776 let server , serverOk ;
77+
7878 try {
79- server = spawnPromise (
80- "node" ,
81- [
82- inspectorServerPath ,
83- ...( command ? [ `--env` , command ] : [ ] ) ,
84- ...( mcpServerArgs ? [ `--args=${ mcpServerArgs . join ( " " ) } ` ] : [ ] ) ,
85- ] ,
86- {
79+ if ( isDev ) {
80+ // Development mode - use tsx watch
81+ const serverCommand = "npx" ;
82+ const serverArgs = [
83+ "tsx" ,
84+ "watch" ,
85+ "--clear-screen=false" ,
86+ "src/index.ts" ,
87+ ] ;
88+ const isWindows = process . platform === "win32" ;
89+
90+ const serverOptions = {
91+ cwd : resolve ( __dirname , "../.." , "server" ) ,
8792 env : {
8893 ...process . env ,
8994 PORT : SERVER_PORT ,
95+ CLIENT_PORT : CLIENT_PORT ,
96+ MCP_PROXY_TOKEN : sessionToken ,
9097 MCP_ENV_VARS : JSON . stringify ( envVars ) ,
9198 } ,
9299 signal : abort . signal ,
93100 echoOutput : true ,
94- } ,
95- ) ;
101+ } ;
102+
103+ // For Windows, we need to use stdin: 'ignore' to simulate < NUL
104+ if ( isWindows ) {
105+ serverOptions . stdin = "ignore" ;
106+ }
107+
108+ server = spawn ( serverCommand , serverArgs , serverOptions ) ;
109+
110+ // Give server time to start
111+ serverOk = await Promise . race ( [
112+ new Promise ( ( resolve ) => {
113+ server . subscribe ( {
114+ complete : ( ) => resolve ( false ) ,
115+ error : ( ) => resolve ( false ) ,
116+ next : ( ) => { } , // We're using echoOutput
117+ } ) ;
118+ } ) ,
119+ delay ( 3000 ) . then ( ( ) => true ) ,
120+ ] ) ;
121+ } else {
122+ // Production mode - use built files
123+ const inspectorServerPath = resolve (
124+ __dirname ,
125+ "../.." ,
126+ "server" ,
127+ "build" ,
128+ "index.js" ,
129+ ) ;
130+
131+ server = spawnPromise (
132+ "node" ,
133+ [
134+ inspectorServerPath ,
135+ ...( command ? [ `--env` , command ] : [ ] ) ,
136+ ...( mcpServerArgs ? [ `--args=${ mcpServerArgs . join ( " " ) } ` ] : [ ] ) ,
137+ ] ,
138+ {
139+ env : {
140+ ...process . env ,
141+ PORT : SERVER_PORT ,
142+ CLIENT_PORT : CLIENT_PORT ,
143+ MCP_PROXY_TOKEN : sessionToken ,
144+ MCP_ENV_VARS : JSON . stringify ( envVars ) ,
145+ } ,
146+ signal : abort . signal ,
147+ echoOutput : true ,
148+ } ,
149+ ) ;
96150
97- // Make sure server started before starting client
98- serverOk = await Promise . race ( [ server , delay ( 2 * 1000 ) ] ) ;
151+ // Make sure server started before starting client
152+ serverOk = await Promise . race ( [ server , delay ( 2 * 1000 ) ] ) ;
153+ }
99154 } catch ( error ) { }
100155
101156 if ( serverOk ) {
102157 try {
103- // Only auto-open when auth is disabled
104- const authDisabled = ! ! process . env . DANGEROUSLY_OMIT_AUTH ;
105- if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" && authDisabled ) {
106- open ( `http://127.0.0.1:${ CLIENT_PORT } ` ) ;
158+ if ( isDev ) {
159+ // Development mode - use vite
160+ const clientCommand = "npx" ;
161+ const clientArgs = [ "vite" , "--port" , CLIENT_PORT ] ;
162+
163+ const client = spawn ( clientCommand , clientArgs , {
164+ cwd : resolve ( __dirname , ".." ) ,
165+ env : { ...process . env , PORT : CLIENT_PORT } ,
166+ signal : abort . signal ,
167+ echoOutput : true ,
168+ } ) ;
169+
170+ // Auto-open browser after vite starts
171+ if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" ) {
172+ const url = authDisabled
173+ ? `http://127.0.0.1:${ CLIENT_PORT } `
174+ : `http://127.0.0.1:${ CLIENT_PORT } /?MCP_PROXY_AUTH_TOKEN=${ sessionToken } ` ;
175+
176+ // Give vite time to start before opening browser
177+ setTimeout ( ( ) => {
178+ open ( url ) ;
179+ console . log ( `\n🔗 Opening browser at: ${ url } \n` ) ;
180+ } , 3000 ) ;
181+ }
182+
183+ await new Promise ( ( resolve ) => {
184+ client . subscribe ( {
185+ complete : resolve ,
186+ error : ( err ) => {
187+ if ( ! cancelled || process . env . DEBUG ) {
188+ console . error ( "Client error:" , err ) ;
189+ }
190+ resolve ( null ) ;
191+ } ,
192+ next : ( ) => { } , // We're using echoOutput
193+ } ) ;
194+ } ) ;
195+ } else {
196+ // Production mode - use client.js
197+ const inspectorClientPath = resolve (
198+ __dirname ,
199+ "../.." ,
200+ "client" ,
201+ "bin" ,
202+ "client.js" ,
203+ ) ;
204+
205+ // Auto-open browser with token
206+ if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" ) {
207+ const url = authDisabled
208+ ? `http://127.0.0.1:${ CLIENT_PORT } `
209+ : `http://127.0.0.1:${ CLIENT_PORT } /?MCP_PROXY_AUTH_TOKEN=${ sessionToken } ` ;
210+ open ( url ) ;
211+ }
212+
213+ await spawnPromise ( "node" , [ inspectorClientPath ] , {
214+ env : { ...process . env , PORT : CLIENT_PORT } ,
215+ signal : abort . signal ,
216+ echoOutput : true ,
217+ } ) ;
107218 }
108- await spawnPromise ( "node" , [ inspectorClientPath ] , {
109- env : { ...process . env , PORT : CLIENT_PORT } ,
110- signal : abort . signal ,
111- echoOutput : true ,
112- } ) ;
113219 } catch ( e ) {
114220 if ( ! cancelled || process . env . DEBUG ) throw e ;
115221 }
0 commit comments