Skip to main content
Glama
launcher.ts5.23 kB
import { type ChildProcess, spawn } from "node:child_process"; import * as http from "node:http"; import * as path from "node:path"; import { fileURLToPath } from "node:url"; import { logger } from "./logger.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); /** * Log only if debug mode is enabled via POLYBRAIN_DEBUG env var * This ensures launcher stdio stays pure for MCP protocol */ function debugLog( level: "debug" | "info" | "warn" | "error", message: string, context?: Record<string, unknown> | Error ): void { if (process.env.POLYBRAIN_DEBUG === "true") { if (context instanceof Error) { if (level === "error") { logger.error(message, context); } else { logger[level](message, { name: context.name, message: context.message, stack: context.stack, }); } } else { logger[level](message, context); } } } export class ServerLauncher { private serverProcess: ChildProcess | null = null; private port: number; constructor(port: number) { this.port = port; } /** * Check if server is already running on the configured port */ async isServerRunning(): Promise<boolean> { return new Promise((resolve) => { const request = http.get(`http://localhost:${this.port}/health`, { timeout: 2000 }, (res) => { resolve(res.statusCode === 200); }); request.on("error", () => { resolve(false); }); request.on("timeout", () => { request.destroy(); resolve(false); }); }); } /** * Start the HTTP server in background */ async startServer(): Promise<void> { try { // Determine the correct path to the server script // Navigate up from dist/bin to dist/ to find index.js const serverScript = path.resolve(__dirname, "..", "index.js"); debugLog("debug", "Starting HTTP server", { script: serverScript, port: this.port }); this.serverProcess = spawn("node", [serverScript], { env: { ...process.env, POLYBRAIN_HTTP_SERVER_ONLY: "true", }, detached: true, stdio: "ignore", // Detach from parent process }); // Don't wait for the process to complete - just let it run in background this.serverProcess.unref(); // Wait for server to be ready let retries = 0; const maxRetries = 30; // 30 seconds while (retries < maxRetries) { const isRunning = await this.isServerRunning(); if (isRunning) { debugLog("info", "HTTP server is ready", { port: this.port }); return; } await new Promise((resolve) => setTimeout(resolve, 1000)); retries++; } throw new Error("Server failed to start within timeout"); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); debugLog( "error", "Failed to start HTTP server", error instanceof Error ? error : new Error(errorMessage) ); throw error; } } /** * Ensure server is running */ async ensureServerRunning(): Promise<void> { try { const isRunning = await this.isServerRunning(); if (!isRunning) { debugLog("info", "Server not running, starting it..."); await this.startServer(); } else { debugLog("debug", "Server already running"); } } catch (error) { debugLog( "error", "Error ensuring server is running", error instanceof Error ? error : new Error(String(error)) ); throw error; } } /** * Stop the server if running */ async stopServer(): Promise<void> { if (this.serverProcess && !this.serverProcess.killed) { logger.info("Stopping server"); this.serverProcess.kill(); } } /** * Kill any server running on the configured port using lsof */ async killServerByPort(): Promise<void> { try { debugLog("debug", "Attempting to kill server on port", { port: this.port }); // Use lsof to find the PID listening on the port const { execSync } = await import("node:child_process"); try { const result = execSync(`lsof -i :${this.port} -t`, { encoding: "utf-8" }).trim(); if (result) { const pids = result.split("\n").filter((pid) => pid.length > 0); for (const pid of pids) { try { execSync(`kill -9 ${pid}`); debugLog("info", "Killed server process", { pid, port: this.port }); } catch (e) { debugLog("debug", "Failed to kill process", { pid, error: String(e) }); } } } else { debugLog("debug", "No process found on port", { port: this.port }); } } catch (e) { // lsof command failed or no process found - that's fine debugLog("debug", "lsof command failed or no process on port", { port: this.port }); } } catch (error) { debugLog( "warn", "Error killing server", error instanceof Error ? error : new Error(String(error)) ); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/danielwpz/polybrain-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server