Skip to content

Commit 9fec0aa

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 d06f3b5 commit 9fec0aa

File tree

1 file changed

+175
-133
lines changed

1 file changed

+175
-133
lines changed

client/bin/start.js

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

78229
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-
};
230+
const serverOptions = {
231+
SERVER_PORT,
232+
CLIENT_PORT,
233+
sessionToken,
234+
envVars,
235+
abort,
236+
command,
237+
mcpServerArgs,
238+
};
102239

103-
// For Windows, we need to use stdin: 'ignore' to simulate < NUL
104-
if (isWindows) {
105-
serverOptions.stdin = "ignore";
106-
}
240+
const result = isDev
241+
? await startDevServer(serverOptions)
242+
: await startProdServer(serverOptions);
107243

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-
}
244+
server = result.server;
245+
serverOk = result.serverOk;
154246
} catch (error) {}
155247

156248
if (serverOk) {
157249
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-
}
250+
const clientOptions = {
251+
CLIENT_PORT,
252+
authDisabled,
253+
sessionToken,
254+
abort,
255+
cancelled,
256+
};
212257

213-
await spawnPromise("node", [inspectorClientPath], {
214-
env: { ...process.env, PORT: CLIENT_PORT },
215-
signal: abort.signal,
216-
echoOutput: true,
217-
});
218-
}
258+
await (isDev
259+
? startDevClient(clientOptions)
260+
: startProdClient(clientOptions));
219261
} catch (e) {
220262
if (!cancelled || process.env.DEBUG) throw e;
221263
}

0 commit comments

Comments
 (0)