@@ -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,45 @@ 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 &&
211+ getServerStatus ( ) === ServerStatus . RUNNING
212+ ) {
213+ hasOpened = true ;
214+ open ( url ) ;
215+ console . log ( `\n🔗 Opening browser at: ${ url } \n` ) ;
216+ }
124217 } , 3000 ) ;
125218 }
126219
127220 await new Promise ( ( resolve ) => {
128221 client . subscribe ( {
129- complete : resolve ,
222+ complete : ( ) => {
223+ // Clear timeout if process completes
224+ if ( autoOpenTimeout ) {
225+ clearTimeout ( autoOpenTimeout ) ;
226+ }
227+ resolve ( null ) ;
228+ } ,
130229 error : ( err ) => {
230+ // Clear timeout on error
231+ if ( autoOpenTimeout ) {
232+ clearTimeout ( autoOpenTimeout ) ;
233+ }
131234 if ( ! cancelled || process . env . DEBUG ) {
132235 console . error ( "Client error:" , err ) ;
133236 }
@@ -139,7 +242,8 @@ async function startDevClient(clientOptions) {
139242}
140243
141244async function startProdClient ( clientOptions ) {
142- const { CLIENT_PORT , authDisabled, sessionToken, abort } = clientOptions ;
245+ const { CLIENT_PORT , authDisabled, sessionToken, abort, cancelled } =
246+ clientOptions ;
143247 const inspectorClientPath = resolve (
144248 __dirname ,
145249 "../.." ,
@@ -148,12 +252,13 @@ async function startProdClient(clientOptions) {
148252 "client.js" ,
149253 ) ;
150254
151- // Auto -open browser with token
152- if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" ) {
255+ // Only auto -open browser if not cancelled
256+ if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" && ! cancelled ) {
153257 const url = authDisabled
154258 ? `http://127.0.0.1:${ CLIENT_PORT } `
155259 : `http://127.0.0.1:${ CLIENT_PORT } /?MCP_PROXY_AUTH_TOKEN=${ sessionToken } ` ;
156260 open ( url ) ;
261+ console . log ( `\n🔗 Opening browser at: ${ url } \n` ) ;
157262 }
158263
159264 await spawnPromise ( "node" , [ inspectorClientPath ] , {
@@ -224,7 +329,8 @@ async function main() {
224329 abort . abort ( ) ;
225330 } ) ;
226331
227- let server , serverOk ;
332+ let server ;
333+ let getServerStatus = ( ) => ServerStatus . UNKNOWN ;
228334
229335 try {
230336 const serverOptions = {
@@ -242,17 +348,19 @@ async function main() {
242348 : await startProdServer ( serverOptions ) ;
243349
244350 server = result . server ;
245- serverOk = result . serverOk ;
351+ getServerStatus = result . getServerStatus ;
246352 } catch ( error ) { }
247353
248- if ( serverOk ) {
354+ if ( getServerStatus ( ) === ServerStatus . RUNNING ) {
355+ console . log ( "Server is confirmed running, starting client..." ) ;
249356 try {
250357 const clientOptions = {
251358 CLIENT_PORT ,
252359 authDisabled,
253360 sessionToken,
254361 abort,
255362 cancelled,
363+ getServerStatus,
256364 } ;
257365
258366 await ( isDev
@@ -261,6 +369,10 @@ async function main() {
261369 } catch ( e ) {
262370 if ( ! cancelled || process . env . DEBUG ) throw e ;
263371 }
372+ } else {
373+ console . log (
374+ `Server failed to start (status: ${ getServerStatus ( ) } ), not starting client` ,
375+ ) ;
264376 }
265377
266378 return 0 ;
0 commit comments