diff --git a/bin/start.js b/bin/start.js index c958396ff..e6b6878b7 100755 --- a/bin/start.js +++ b/bin/start.js @@ -6,6 +6,7 @@ import { fileURLToPath } from "url"; import { createServer } from "net"; import { execSync } from "child_process"; import { existsSync, readFileSync } from "fs"; +import open from "open"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -169,9 +170,16 @@ function spawnPromise(command, args, options) { async function showSuccessMessage(port) { logDivider(); + // Use BASE_URL if set, otherwise construct from HOST and port + // Default: localhost in development, 127.0.0.1 in production + const defaultHost = + process.env.NODE_ENV === "production" ? "127.0.0.1" : "localhost"; + const host = process.env.HOST || defaultHost; + const baseUrl = process.env.BASE_URL || `http://${host}:${port}`; + const successText = `🎉 MCP Inspector is now running successfully! -📱 Access your application at: http://localhost:${port} +📱 Access your application at: ${baseUrl} 🔧 Unified server ready to handle MCP connections 📊 Monitor your MCP tools and resources 💬 Start chatting with your MCP-enabled AI @@ -181,6 +189,16 @@ Press Ctrl+C to stop the server`; logBox(successText, "🚀 Ready to Go!"); logDivider(); + + // Automatically open the browser + try { + await open(baseUrl); + logSuccess(`🌐 Browser opened at ${baseUrl}`); + } catch (error) { + logWarning( + `Could not open browser automatically. Please visit ${baseUrl} manually.`, + ); + } } async function checkOllamaInstalled() { @@ -356,7 +374,11 @@ async function main() { if (parsingFlags && arg === "--port" && i + 1 < args.length) { const port = args[++i]; envVars.PORT = port; - envVars.BASE_URL = `http://localhost:${port}`; + // Default: localhost in development, 127.0.0.1 in production + const defaultHost = + process.env.NODE_ENV === "production" ? "127.0.0.1" : "localhost"; + const baseHost = process.env.HOST || defaultHost; + envVars.BASE_URL = `http://${baseHost}:${port}`; continue; } @@ -542,7 +564,11 @@ async function main() { // Update environment variables with the final port envVars.PORT = PORT; - envVars.BASE_URL = `http://localhost:${PORT}`; + // Default: localhost in development, 127.0.0.1 in production + const defaultHost = + process.env.NODE_ENV === "production" ? "127.0.0.1" : "localhost"; + const baseHost = process.env.HOST || defaultHost; + envVars.BASE_URL = `http://${baseHost}:${PORT}`; Object.assign(process.env, envVars); } catch (error) { logError(`Port configuration failed: ${error.message}`); @@ -595,20 +621,62 @@ async function main() { await delay(500); } - await spawnPromise("node", [distServerPath], { + // Spawn the server process but don't wait for it to exit + const serverProcess = spawn("node", [distServerPath], { env: { ...process.env, NODE_ENV: "production", PORT: PORT, }, cwd: projectRoot, - signal: abort.signal, - echoOutput: true, + stdio: "inherit", + }); + + // Handle server process errors + serverProcess.on("error", (error) => { + if (!cancelled) { + logError(`Failed to start server: ${error.message}`); + process.exit(1); + } + }); + + // Handle abort signal + abort.signal.addEventListener("abort", () => { + serverProcess.kill("SIGTERM"); }); + // Wait a bit for the server to start up + await delay(2000); + if (!cancelled) { - await showSuccessMessage(PORT); + // Open the browser automatically + // Use BASE_URL if set, otherwise construct from HOST and PORT + // Default: localhost in development, 127.0.0.1 in production + const defaultHost = + process.env.NODE_ENV === "production" ? "127.0.0.1" : "localhost"; + const host = process.env.HOST || defaultHost; + const url = process.env.BASE_URL || `http://${host}:${PORT}`; + + try { + await open(url); + logSuccess(`🌐 Browser opened at ${url}`); + } catch (error) { + logWarning( + `Could not open browser automatically. Please visit ${url} manually.`, + ); + } } + + // Wait for the server process to exit + await new Promise((resolve, reject) => { + serverProcess.on("close", (code) => { + if (code === 0 || cancelled) { + resolve(code); + } else { + reject(new Error(`Server process exited with code ${code}`)); + } + }); + }); } catch (e) { if (!cancelled || process.env.DEBUG) { logDivider(); diff --git a/package-lock.json b/package-lock.json index b9220f9b4..84a93b6bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mcpjam/inspector", - "version": "1.0.1", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mcpjam/inspector", - "version": "1.0.1", + "version": "1.0.3", "license": "Apache-2.0", "dependencies": { "@ai-sdk/anthropic": "^2.0.17", @@ -50,6 +50,7 @@ "lucide-react": "^0.525.0", "next-themes": "^0.4.6", "ollama-ai-provider-v2": "1.3.1", + "open": "^10.2.0", "postcss": "^8.5.6", "posthog-js": "^1.268.8", "posthog-node": "^5.9.2", @@ -11295,6 +11296,21 @@ "dev": true, "license": "MIT" }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -12405,6 +12421,34 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-shell": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/default-shell/-/default-shell-2.2.0.tgz", @@ -12469,6 +12513,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -15547,6 +15603,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -15591,6 +15662,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -15707,6 +15796,21 @@ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", "license": "MIT" }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isbinaryfile": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", @@ -18226,6 +18330,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -20273,6 +20395,18 @@ "url": "https://opencollective.com/express" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -23013,6 +23147,21 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", diff --git a/package.json b/package.json index 0484e79f4..15a1b0d65 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "main": ".vite/build/main.cjs", "bin": { + "inspector": "bin/start.js", "inspector-vite": "bin/start.js" }, "files": [ @@ -102,6 +103,7 @@ "lucide-react": "^0.525.0", "next-themes": "^0.4.6", "ollama-ai-provider-v2": "1.3.1", + "open": "^10.2.0", "postcss": "^8.5.6", "posthog-js": "^1.268.8", "posthog-node": "^5.9.2",