From 9fec0aaf9807a7d6e0e3fd0985d6142c2711761a Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Mon, 16 Jun 2025 13:52:57 +0100 Subject: [PATCH] refactor: Extract server and client launch functions for better code organization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- client/bin/start.js | 308 +++++++++++++++++++++++++------------------- 1 file changed, 175 insertions(+), 133 deletions(-) diff --git a/client/bin/start.js b/client/bin/start.js index 2cd1758e..ad75078a 100755 --- a/client/bin/start.js +++ b/client/bin/start.js @@ -12,6 +12,157 @@ function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms, true)); } +async function startDevServer(serverOptions) { + const { SERVER_PORT, CLIENT_PORT, sessionToken, envVars, abort } = + serverOptions; + const serverCommand = "npx"; + const serverArgs = ["tsx", "watch", "--clear-screen=false", "src/index.ts"]; + const isWindows = process.platform === "win32"; + + const spawnOptions = { + cwd: resolve(__dirname, "../..", "server"), + env: { + ...process.env, + PORT: SERVER_PORT, + CLIENT_PORT: CLIENT_PORT, + MCP_PROXY_TOKEN: sessionToken, + MCP_ENV_VARS: JSON.stringify(envVars), + }, + signal: abort.signal, + echoOutput: true, + }; + + // For Windows, we need to use stdin: 'ignore' to simulate < NUL + if (isWindows) { + spawnOptions.stdin = "ignore"; + } + + const server = spawn(serverCommand, serverArgs, spawnOptions); + + // Give server time to start + const serverOk = await Promise.race([ + new Promise((resolve) => { + server.subscribe({ + complete: () => resolve(false), + error: () => resolve(false), + next: () => {}, // We're using echoOutput + }); + }), + delay(3000).then(() => true), + ]); + + return { server, serverOk }; +} + +async function startProdServer(serverOptions) { + const { + SERVER_PORT, + CLIENT_PORT, + sessionToken, + envVars, + abort, + command, + mcpServerArgs, + } = serverOptions; + const inspectorServerPath = resolve( + __dirname, + "../..", + "server", + "build", + "index.js", + ); + + const server = spawnPromise( + "node", + [ + inspectorServerPath, + ...(command ? [`--env`, command] : []), + ...(mcpServerArgs ? [`--args=${mcpServerArgs.join(" ")}`] : []), + ], + { + env: { + ...process.env, + PORT: SERVER_PORT, + CLIENT_PORT: CLIENT_PORT, + MCP_PROXY_TOKEN: sessionToken, + MCP_ENV_VARS: JSON.stringify(envVars), + }, + signal: abort.signal, + echoOutput: true, + }, + ); + + // Make sure server started before starting client + const serverOk = await Promise.race([server, delay(2 * 1000)]); + + return { server, serverOk }; +} + +async function startDevClient(clientOptions) { + const { CLIENT_PORT, authDisabled, sessionToken, abort, cancelled } = + clientOptions; + const clientCommand = "npx"; + const clientArgs = ["vite", "--port", CLIENT_PORT]; + + const client = spawn(clientCommand, clientArgs, { + cwd: resolve(__dirname, ".."), + env: { ...process.env, PORT: CLIENT_PORT }, + signal: abort.signal, + echoOutput: true, + }); + + // Auto-open browser after vite starts + if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") { + const url = authDisabled + ? `http://127.0.0.1:${CLIENT_PORT}` + : `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`; + + // Give vite time to start before opening browser + setTimeout(() => { + open(url); + console.log(`\nšŸ”— Opening browser at: ${url}\n`); + }, 3000); + } + + await new Promise((resolve) => { + client.subscribe({ + complete: resolve, + error: (err) => { + if (!cancelled || process.env.DEBUG) { + console.error("Client error:", err); + } + resolve(null); + }, + next: () => {}, // We're using echoOutput + }); + }); +} + +async function startProdClient(clientOptions) { + const { CLIENT_PORT, authDisabled, sessionToken, abort } = clientOptions; + const inspectorClientPath = resolve( + __dirname, + "../..", + "client", + "bin", + "client.js", + ); + + // Auto-open browser with token + if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") { + const url = authDisabled + ? `http://127.0.0.1:${CLIENT_PORT}` + : `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`; + open(url); + } + + await spawnPromise("node", [inspectorClientPath], { + env: { ...process.env, PORT: CLIENT_PORT }, + signal: abort.signal, + echoOutput: true, + }); +} + async function main() { // Parse command line arguments const args = process.argv.slice(2); @@ -76,146 +227,37 @@ async function main() { let server, serverOk; try { - if (isDev) { - // Development mode - use tsx watch - const serverCommand = "npx"; - const serverArgs = [ - "tsx", - "watch", - "--clear-screen=false", - "src/index.ts", - ]; - const isWindows = process.platform === "win32"; + const serverOptions = { + SERVER_PORT, + CLIENT_PORT, + sessionToken, + envVars, + abort, + command, + mcpServerArgs, + }; - const serverOptions = { - cwd: resolve(__dirname, "../..", "server"), - env: { - ...process.env, - PORT: SERVER_PORT, - CLIENT_PORT: CLIENT_PORT, - MCP_PROXY_TOKEN: sessionToken, - MCP_ENV_VARS: JSON.stringify(envVars), - }, - signal: abort.signal, - echoOutput: true, - }; + const result = isDev + ? await startDevServer(serverOptions) + : await startProdServer(serverOptions); - // For Windows, we need to use stdin: 'ignore' to simulate < NUL - if (isWindows) { - serverOptions.stdin = "ignore"; - } - - server = spawn(serverCommand, serverArgs, serverOptions); - - // Give server time to start - serverOk = await Promise.race([ - new Promise((resolve) => { - server.subscribe({ - complete: () => resolve(false), - error: () => resolve(false), - next: () => {}, // We're using echoOutput - }); - }), - delay(3000).then(() => true), - ]); - } else { - // Production mode - use built files - const inspectorServerPath = resolve( - __dirname, - "../..", - "server", - "build", - "index.js", - ); - - server = spawnPromise( - "node", - [ - inspectorServerPath, - ...(command ? [`--env`, command] : []), - ...(mcpServerArgs ? [`--args=${mcpServerArgs.join(" ")}`] : []), - ], - { - env: { - ...process.env, - PORT: SERVER_PORT, - CLIENT_PORT: CLIENT_PORT, - MCP_PROXY_TOKEN: sessionToken, - MCP_ENV_VARS: JSON.stringify(envVars), - }, - signal: abort.signal, - echoOutput: true, - }, - ); - - // Make sure server started before starting client - serverOk = await Promise.race([server, delay(2 * 1000)]); - } + server = result.server; + serverOk = result.serverOk; } catch (error) {} if (serverOk) { try { - if (isDev) { - // Development mode - use vite - const clientCommand = "npx"; - const clientArgs = ["vite", "--port", CLIENT_PORT]; + const clientOptions = { + CLIENT_PORT, + authDisabled, + sessionToken, + abort, + cancelled, + }; - const client = spawn(clientCommand, clientArgs, { - cwd: resolve(__dirname, ".."), - env: { ...process.env, PORT: CLIENT_PORT }, - signal: abort.signal, - echoOutput: true, - }); - - // Auto-open browser after vite starts - if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") { - const url = authDisabled - ? `http://127.0.0.1:${CLIENT_PORT}` - : `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`; - - // Give vite time to start before opening browser - setTimeout(() => { - open(url); - console.log(`\nšŸ”— Opening browser at: ${url}\n`); - }, 3000); - } - - await new Promise((resolve) => { - client.subscribe({ - complete: resolve, - error: (err) => { - if (!cancelled || process.env.DEBUG) { - console.error("Client error:", err); - } - resolve(null); - }, - next: () => {}, // We're using echoOutput - }); - }); - } else { - // Production mode - use client.js - const inspectorClientPath = resolve( - __dirname, - "../..", - "client", - "bin", - "client.js", - ); - - // Auto-open browser with token - if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") { - const url = authDisabled - ? `http://127.0.0.1:${CLIENT_PORT}` - : `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`; - open(url); - } - - await spawnPromise("node", [inspectorClientPath], { - env: { ...process.env, PORT: CLIENT_PORT }, - signal: abort.signal, - echoOutput: true, - }); - } + await (isDev + ? startDevClient(clientOptions) + : startProdClient(clientOptions)); } catch (e) { if (!cancelled || process.env.DEBUG) throw e; }