@@ -12,6 +12,14 @@ function delay(ms) {
1212 return new Promise ( ( resolve ) => setTimeout ( resolve , ms , true ) ) ;
1313}
1414
15+ const ServerStatus = {
16+ UNKNOWN : "unknown" ,
17+ STARTING : "starting" ,
18+ RUNNING : "running" ,
19+ FAILED : "failed" ,
20+ STOPPED : "stopped" ,
21+ } ;
22+
1523async function startDevServer ( serverOptions ) {
1624 const { SERVER_PORT , CLIENT_PORT , sessionToken, envVars, abort } =
1725 serverOptions ;
@@ -39,19 +47,48 @@ async function startDevServer(serverOptions) {
3947
4048 const server = spawn ( serverCommand , serverArgs , spawnOptions ) ;
4149
42- // Give server time to start
43- const serverOk = await Promise . race ( [
44- new Promise ( ( resolve ) => {
45- server . subscribe ( {
46- complete : ( ) => resolve ( false ) ,
47- error : ( ) => resolve ( false ) ,
48- next : ( ) => { } , // We're using echoOutput
50+ // Track server status
51+ let serverStatus = ServerStatus . STARTING ;
52+
53+ server . subscribe ( {
54+ complete : ( ) => {
55+ // Server process ended - could be normal or abnormal
56+ if ( serverStatus === ServerStatus . STARTING ) {
57+ serverStatus = ServerStatus . FAILED ; // Failed during startup
58+ console . log ( "Server failed during startup (process completed)" ) ;
59+ } else {
60+ serverStatus = ServerStatus . STOPPED ; // Stopped after running
61+ }
62+ } ,
63+ error : ( ) => {
64+ serverStatus = ServerStatus . FAILED ;
65+ console . log ( "Server failed with error" ) ;
66+ } ,
67+ next : ( ) => { } , // We're using echoOutput
68+ } ) ;
69+
70+ // Wait for server to start and actually test the connection
71+ await delay ( 3000 ) ;
72+ if ( serverStatus === ServerStatus . STARTING ) {
73+ // Check if server is actually listening by attempting a connection
74+ try {
75+ const response = await fetch ( `http://127.0.0.1:${ SERVER_PORT } /health` , {
76+ signal : AbortSignal . timeout ( 2000 ) ,
4977 } ) ;
50- } ) ,
51- delay ( 3000 ) . then ( ( ) => true ) ,
52- ] ) ;
78+ if ( response . ok ) {
79+ serverStatus = ServerStatus . RUNNING ;
80+ console . log ( "Server confirmed healthy via /health endpoint" ) ;
81+ } else {
82+ serverStatus = ServerStatus . FAILED ;
83+ console . log ( `Server health check failed: ${ response . status } ` ) ;
84+ }
85+ } catch ( err ) {
86+ serverStatus = ServerStatus . FAILED ;
87+ console . log ( `Server health check failed: ${ err . message } ` ) ;
88+ }
89+ }
5390
54- return { server, serverOk } ;
91+ return { server, getServerStatus : ( ) => serverStatus } ;
5592}
5693
5794async function startProdServer ( serverOptions ) {
@@ -72,6 +109,9 @@ async function startProdServer(serverOptions) {
72109 "index.js" ,
73110 ) ;
74111
112+ // Track server status
113+ let serverStatus = ServerStatus . STARTING ;
114+
75115 const server = spawnPromise (
76116 "node" ,
77117 [
@@ -92,15 +132,56 @@ async function startProdServer(serverOptions) {
92132 } ,
93133 ) ;
94134
95- // Make sure server started before starting client
96- const serverOk = await Promise . race ( [ server , delay ( 2 * 1000 ) ] ) ;
135+ // Monitor for server completion
136+ server
137+ . then ( ( ) => {
138+ // Server completed normally
139+ if ( serverStatus === ServerStatus . STARTING ) {
140+ serverStatus = ServerStatus . FAILED ; // Never started properly
141+ console . log ( "Server failed during startup (process completed)" ) ;
142+ } else {
143+ serverStatus = ServerStatus . STOPPED ;
144+ }
145+ } )
146+ . catch ( ( ) => {
147+ serverStatus = ServerStatus . FAILED ;
148+ console . log ( "Server failed with error" ) ;
149+ } ) ;
150+
151+ // Wait for server to start - but actually test the connection
152+ await delay ( 2000 ) ; // Initial delay for server to attempt startup
153+
154+ if ( serverStatus === ServerStatus . STARTING ) {
155+ // Check if server is actually listening by attempting a connection
156+ try {
157+ const response = await fetch ( `http://127.0.0.1:${ SERVER_PORT } /health` , {
158+ signal : AbortSignal . timeout ( 2000 ) ,
159+ } ) ;
160+ if ( response . ok ) {
161+ serverStatus = ServerStatus . RUNNING ;
162+ console . log ( "Server confirmed healthy via /health endpoint" ) ;
163+ } else {
164+ serverStatus = ServerStatus . FAILED ;
165+ console . log ( `Server health check failed: ${ response . status } ` ) ;
166+ }
167+ } catch ( err ) {
168+ serverStatus = ServerStatus . FAILED ;
169+ console . log ( `Server health check failed: ${ err . message } ` ) ;
170+ }
171+ }
97172
98- return { server, serverOk } ;
173+ return { server, getServerStatus : ( ) => serverStatus } ;
99174}
100175
101176async function startDevClient ( clientOptions ) {
102- const { CLIENT_PORT , authDisabled, sessionToken, abort, cancelled } =
103- clientOptions ;
177+ const {
178+ CLIENT_PORT ,
179+ authDisabled,
180+ sessionToken,
181+ abort,
182+ cancelled,
183+ getServerStatus,
184+ } = clientOptions ;
104185 const clientCommand = "npx" ;
105186 const clientArgs = [ "vite" , "--port" , CLIENT_PORT ] ;
106187
@@ -111,23 +192,44 @@ async function startDevClient(clientOptions) {
111192 echoOutput : true ,
112193 } ) ;
113194
195+ let autoOpenTimeout = null ;
196+ let hasOpened = false ;
197+
114198 // Auto-open browser after vite starts
115199 if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" ) {
116200 const url = authDisabled
117201 ? `http://127.0.0.1:${ CLIENT_PORT } `
118202 : `http://127.0.0.1:${ CLIENT_PORT } /?MCP_PROXY_AUTH_TOKEN=${ sessionToken } ` ;
119203
120204 // Give vite time to start before opening browser
121- setTimeout ( ( ) => {
122- open ( url ) ;
123- console . log ( `\n🔗 Opening browser at: ${ url } \n` ) ;
205+ autoOpenTimeout = setTimeout ( ( ) => {
206+ // Check if server is still running before opening
207+ if (
208+ ! cancelled &&
209+ ! hasOpened &&
210+ getServerStatus ( ) === ServerStatus . RUNNING
211+ ) {
212+ hasOpened = true ;
213+ open ( url ) ;
214+ console . log ( `\n🔗 Opening browser at: ${ url } \n` ) ;
215+ }
124216 } , 3000 ) ;
125217 }
126218
127219 await new Promise ( ( resolve ) => {
128220 client . subscribe ( {
129- complete : resolve ,
221+ complete : ( ) => {
222+ // Clear timeout if process completes
223+ if ( autoOpenTimeout ) {
224+ clearTimeout ( autoOpenTimeout ) ;
225+ }
226+ resolve ( null ) ;
227+ } ,
130228 error : ( err ) => {
229+ // Clear timeout on error
230+ if ( autoOpenTimeout ) {
231+ clearTimeout ( autoOpenTimeout ) ;
232+ }
131233 if ( ! cancelled || process . env . DEBUG ) {
132234 console . error ( "Client error:" , err ) ;
133235 }
@@ -139,7 +241,8 @@ async function startDevClient(clientOptions) {
139241}
140242
141243async function startProdClient ( clientOptions ) {
142- const { CLIENT_PORT , authDisabled, sessionToken, abort } = clientOptions ;
244+ const { CLIENT_PORT , authDisabled, sessionToken, abort, cancelled } =
245+ clientOptions ;
143246 const inspectorClientPath = resolve (
144247 __dirname ,
145248 "../.." ,
@@ -148,12 +251,13 @@ async function startProdClient(clientOptions) {
148251 "client.js" ,
149252 ) ;
150253
151- // Auto -open browser with token
152- if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" ) {
254+ // Only auto -open browser if not cancelled
255+ if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" && ! cancelled ) {
153256 const url = authDisabled
154257 ? `http://127.0.0.1:${ CLIENT_PORT } `
155258 : `http://127.0.0.1:${ CLIENT_PORT } /?MCP_PROXY_AUTH_TOKEN=${ sessionToken } ` ;
156259 open ( url ) ;
260+ console . log ( `\n🔗 Opening browser at: ${ url } \n` ) ;
157261 }
158262
159263 await spawnPromise ( "node" , [ inspectorClientPath ] , {
@@ -224,7 +328,8 @@ async function main() {
224328 abort . abort ( ) ;
225329 } ) ;
226330
227- let server , serverOk ;
331+ let server ;
332+ let getServerStatus = ( ) => ServerStatus . UNKNOWN ;
228333
229334 try {
230335 const serverOptions = {
@@ -242,17 +347,19 @@ async function main() {
242347 : await startProdServer ( serverOptions ) ;
243348
244349 server = result . server ;
245- serverOk = result . serverOk ;
350+ getServerStatus = result . getServerStatus ;
246351 } catch ( error ) { }
247352
248- if ( serverOk ) {
353+ if ( getServerStatus ( ) === ServerStatus . RUNNING ) {
354+ console . log ( "Server is confirmed running, starting client..." ) ;
249355 try {
250356 const clientOptions = {
251357 CLIENT_PORT ,
252358 authDisabled,
253359 sessionToken,
254360 abort,
255361 cancelled,
362+ getServerStatus,
256363 } ;
257364
258365 await ( isDev
@@ -261,6 +368,10 @@ async function main() {
261368 } catch ( e ) {
262369 if ( ! cancelled || process . env . DEBUG ) throw e ;
263370 }
371+ } else {
372+ console . log (
373+ `Server failed to start (status: ${ getServerStatus ( ) } ), not starting client` ,
374+ ) ;
264375 }
265376
266377 return 0 ;
0 commit comments