Skip to content

Commit 31f0715

Browse files
refactor: Extract server and client launch functions for better code organization
- Extracted 4 functions: startDevServer, startProdServer, startDevClient, startProdClient - Eliminated deep nesting in main() function - Each function has a single responsibility - Main function now clearly shows the flow: parse args → start server → start client - No functional changes, purely organizational refactoring 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 07f52f4 commit 31f0715

File tree

1 file changed

+173
-136
lines changed

1 file changed

+173
-136
lines changed

client/bin/start.js

Lines changed: 173 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,152 @@ function delay(ms) {
1212
return new Promise((resolve) => setTimeout(resolve, ms, true));
1313
}
1414

15+
async function startDevServer(serverOptions) {
16+
const { SERVER_PORT, CLIENT_PORT, sessionToken, envVars, abort } = serverOptions;
17+
const serverCommand = "npx";
18+
const serverArgs = [
19+
"tsx",
20+
"watch",
21+
"--clear-screen=false",
22+
"src/index.ts",
23+
];
24+
const isWindows = process.platform === "win32";
25+
26+
const spawnOptions = {
27+
cwd: resolve(__dirname, "../..", "server"),
28+
env: {
29+
...process.env,
30+
PORT: SERVER_PORT,
31+
CLIENT_PORT: CLIENT_PORT,
32+
MCP_PROXY_TOKEN: sessionToken,
33+
MCP_ENV_VARS: JSON.stringify(envVars),
34+
},
35+
signal: abort.signal,
36+
echoOutput: true,
37+
};
38+
39+
// For Windows, we need to use stdin: 'ignore' to simulate < NUL
40+
if (isWindows) {
41+
spawnOptions.stdin = "ignore";
42+
}
43+
44+
const server = spawn(serverCommand, serverArgs, spawnOptions);
45+
46+
// Give server time to start
47+
const serverOk = await Promise.race([
48+
new Promise((resolve) => {
49+
server.subscribe({
50+
complete: () => resolve(false),
51+
error: () => resolve(false),
52+
next: () => {}, // We're using echoOutput
53+
});
54+
}),
55+
delay(3000).then(() => true),
56+
]);
57+
58+
return { server, serverOk };
59+
}
60+
61+
async function startProdServer(serverOptions) {
62+
const { SERVER_PORT, CLIENT_PORT, sessionToken, envVars, abort, command, mcpServerArgs } = serverOptions;
63+
const inspectorServerPath = resolve(
64+
__dirname,
65+
"../..",
66+
"server",
67+
"build",
68+
"index.js",
69+
);
70+
71+
const server = spawnPromise(
72+
"node",
73+
[
74+
inspectorServerPath,
75+
...(command ? [`--env`, command] : []),
76+
...(mcpServerArgs ? [`--args=${mcpServerArgs.join(" ")}`] : []),
77+
],
78+
{
79+
env: {
80+
...process.env,
81+
PORT: SERVER_PORT,
82+
CLIENT_PORT: CLIENT_PORT,
83+
MCP_PROXY_TOKEN: sessionToken,
84+
MCP_ENV_VARS: JSON.stringify(envVars),
85+
},
86+
signal: abort.signal,
87+
echoOutput: true,
88+
},
89+
);
90+
91+
// Make sure server started before starting client
92+
const serverOk = await Promise.race([server, delay(2 * 1000)]);
93+
94+
return { server, serverOk };
95+
}
96+
97+
async function startDevClient(clientOptions) {
98+
const { CLIENT_PORT, authDisabled, sessionToken, abort, cancelled } = clientOptions;
99+
const clientCommand = "npx";
100+
const clientArgs = ["vite", "--port", CLIENT_PORT];
101+
102+
const client = spawn(clientCommand, clientArgs, {
103+
cwd: resolve(__dirname, ".."),
104+
env: { ...process.env, PORT: CLIENT_PORT },
105+
signal: abort.signal,
106+
echoOutput: true,
107+
});
108+
109+
// Auto-open browser after vite starts
110+
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
111+
const url = authDisabled
112+
? `http://127.0.0.1:${CLIENT_PORT}`
113+
: `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
114+
115+
// Give vite time to start before opening browser
116+
setTimeout(() => {
117+
open(url);
118+
console.log(`\n🔗 Opening browser at: ${url}\n`);
119+
}, 3000);
120+
}
121+
122+
await new Promise((resolve) => {
123+
client.subscribe({
124+
complete: resolve,
125+
error: (err) => {
126+
if (!cancelled || process.env.DEBUG) {
127+
console.error("Client error:", err);
128+
}
129+
resolve(null);
130+
},
131+
next: () => {}, // We're using echoOutput
132+
});
133+
});
134+
}
135+
136+
async function startProdClient(clientOptions) {
137+
const { CLIENT_PORT, authDisabled, sessionToken, abort } = clientOptions;
138+
const inspectorClientPath = resolve(
139+
__dirname,
140+
"../..",
141+
"client",
142+
"bin",
143+
"client.js",
144+
);
145+
146+
// Auto-open browser with token
147+
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
148+
const url = authDisabled
149+
? `http://127.0.0.1:${CLIENT_PORT}`
150+
: `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
151+
open(url);
152+
}
153+
154+
await spawnPromise("node", [inspectorClientPath], {
155+
env: { ...process.env, PORT: CLIENT_PORT },
156+
signal: abort.signal,
157+
echoOutput: true,
158+
});
159+
}
160+
15161
async function main() {
16162
// Parse command line arguments
17163
const args = process.argv.slice(2);
@@ -76,146 +222,37 @@ async function main() {
76222
let server, serverOk;
77223

78224
try {
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"),
92-
env: {
93-
...process.env,
94-
PORT: SERVER_PORT,
95-
CLIENT_PORT: CLIENT_PORT,
96-
MCP_PROXY_TOKEN: sessionToken,
97-
MCP_ENV_VARS: JSON.stringify(envVars),
98-
},
99-
signal: abort.signal,
100-
echoOutput: true,
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-
);
150-
151-
// Make sure server started before starting client
152-
serverOk = await Promise.race([server, delay(2 * 1000)]);
153-
}
225+
const serverOptions = {
226+
SERVER_PORT,
227+
CLIENT_PORT,
228+
sessionToken,
229+
envVars,
230+
abort,
231+
command,
232+
mcpServerArgs,
233+
};
234+
235+
const result = isDev
236+
? await startDevServer(serverOptions)
237+
: await startProdServer(serverOptions);
238+
239+
server = result.server;
240+
serverOk = result.serverOk;
154241
} catch (error) {}
155242

156243
if (serverOk) {
157244
try {
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-
});
218-
}
245+
const clientOptions = {
246+
CLIENT_PORT,
247+
authDisabled,
248+
sessionToken,
249+
abort,
250+
cancelled,
251+
};
252+
253+
await (isDev
254+
? startDevClient(clientOptions)
255+
: startProdClient(clientOptions));
219256
} catch (e) {
220257
if (!cancelled || process.env.DEBUG) throw e;
221258
}

0 commit comments

Comments
 (0)