@@ -12,6 +12,14 @@ function delay(ms) {
12
12
return new Promise ( ( resolve ) => setTimeout ( resolve , ms , true ) ) ;
13
13
}
14
14
15
+ const ServerStatus = {
16
+ UNKNOWN : "unknown" ,
17
+ STARTING : "starting" ,
18
+ RUNNING : "running" ,
19
+ FAILED : "failed" ,
20
+ STOPPED : "stopped" ,
21
+ } ;
22
+
15
23
async function startDevServer ( serverOptions ) {
16
24
const { SERVER_PORT , CLIENT_PORT , sessionToken, envVars, abort } =
17
25
serverOptions ;
@@ -39,19 +47,48 @@ async function startDevServer(serverOptions) {
39
47
40
48
const server = spawn ( serverCommand , serverArgs , spawnOptions ) ;
41
49
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 ) ,
49
77
} ) ;
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
+ }
53
90
54
- return { server, serverOk } ;
91
+ return { server, getServerStatus : ( ) => serverStatus } ;
55
92
}
56
93
57
94
async function startProdServer ( serverOptions ) {
@@ -72,6 +109,9 @@ async function startProdServer(serverOptions) {
72
109
"index.js" ,
73
110
) ;
74
111
112
+ // Track server status
113
+ let serverStatus = ServerStatus . STARTING ;
114
+
75
115
const server = spawnPromise (
76
116
"node" ,
77
117
[
@@ -92,15 +132,56 @@ async function startProdServer(serverOptions) {
92
132
} ,
93
133
) ;
94
134
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
+ }
97
172
98
- return { server, serverOk } ;
173
+ return { server, getServerStatus : ( ) => serverStatus } ;
99
174
}
100
175
101
176
async 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 ;
104
185
const clientCommand = "npx" ;
105
186
const clientArgs = [ "vite" , "--port" , CLIENT_PORT ] ;
106
187
@@ -111,23 +192,45 @@ async function startDevClient(clientOptions) {
111
192
echoOutput : true ,
112
193
} ) ;
113
194
195
+ let autoOpenTimeout = null ;
196
+ let hasOpened = false ;
197
+
114
198
// Auto-open browser after vite starts
115
199
if ( process . env . MCP_AUTO_OPEN_ENABLED !== "false" ) {
116
200
const url = authDisabled
117
201
? `http://127.0.0.1:${ CLIENT_PORT } `
118
202
: `http://127.0.0.1:${ CLIENT_PORT } /?MCP_PROXY_AUTH_TOKEN=${ sessionToken } ` ;
119
203
120
204
// 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
+ }
124
217
} , 3000 ) ;
125
218
}
126
219
127
220
await new Promise ( ( resolve ) => {
128
221
client . subscribe ( {
129
- complete : resolve ,
222
+ complete : ( ) => {
223
+ // Clear timeout if process completes
224
+ if ( autoOpenTimeout ) {
225
+ clearTimeout ( autoOpenTimeout ) ;
226
+ }
227
+ resolve ( null ) ;
228
+ } ,
130
229
error : ( err ) => {
230
+ // Clear timeout on error
231
+ if ( autoOpenTimeout ) {
232
+ clearTimeout ( autoOpenTimeout ) ;
233
+ }
131
234
if ( ! cancelled || process . env . DEBUG ) {
132
235
console . error ( "Client error:" , err ) ;
133
236
}
@@ -139,7 +242,8 @@ async function startDevClient(clientOptions) {
139
242
}
140
243
141
244
async function startProdClient ( clientOptions ) {
142
- const { CLIENT_PORT , authDisabled, sessionToken, abort } = clientOptions ;
245
+ const { CLIENT_PORT , authDisabled, sessionToken, abort, cancelled } =
246
+ clientOptions ;
143
247
const inspectorClientPath = resolve (
144
248
__dirname ,
145
249
"../.." ,
@@ -148,12 +252,13 @@ async function startProdClient(clientOptions) {
148
252
"client.js" ,
149
253
) ;
150
254
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 ) {
153
257
const url = authDisabled
154
258
? `http://127.0.0.1:${ CLIENT_PORT } `
155
259
: `http://127.0.0.1:${ CLIENT_PORT } /?MCP_PROXY_AUTH_TOKEN=${ sessionToken } ` ;
156
260
open ( url ) ;
261
+ console . log ( `\n🔗 Opening browser at: ${ url } \n` ) ;
157
262
}
158
263
159
264
await spawnPromise ( "node" , [ inspectorClientPath ] , {
@@ -224,7 +329,8 @@ async function main() {
224
329
abort . abort ( ) ;
225
330
} ) ;
226
331
227
- let server , serverOk ;
332
+ let server ;
333
+ let getServerStatus = ( ) => ServerStatus . UNKNOWN ;
228
334
229
335
try {
230
336
const serverOptions = {
@@ -242,17 +348,19 @@ async function main() {
242
348
: await startProdServer ( serverOptions ) ;
243
349
244
350
server = result . server ;
245
- serverOk = result . serverOk ;
351
+ getServerStatus = result . getServerStatus ;
246
352
} catch ( error ) { }
247
353
248
- if ( serverOk ) {
354
+ if ( getServerStatus ( ) === ServerStatus . RUNNING ) {
355
+ console . log ( "Server is confirmed running, starting client..." ) ;
249
356
try {
250
357
const clientOptions = {
251
358
CLIENT_PORT ,
252
359
authDisabled,
253
360
sessionToken,
254
361
abort,
255
362
cancelled,
363
+ getServerStatus,
256
364
} ;
257
365
258
366
await ( isDev
@@ -261,6 +369,10 @@ async function main() {
261
369
} catch ( e ) {
262
370
if ( ! cancelled || process . env . DEBUG ) throw e ;
263
371
}
372
+ } else {
373
+ console . log (
374
+ `Server failed to start (status: ${ getServerStatus ( ) } ), not starting client` ,
375
+ ) ;
264
376
}
265
377
266
378
return 0 ;
0 commit comments