* Start the server in HTTP mode. * Each connecting MCP client gets its own Server+Transport pair, * sharing the same module-level project state.
(explicitPort?: number)
| 1885 | * sharing the same module-level project state. |
| 1886 | */ |
| 1887 | async function startHttp(explicitPort?: number): Promise<void> { |
| 1888 | await ensureMcpRuntimeLoaded(); |
| 1889 | |
| 1890 | const serverConfig = await loadServerConfig(); |
| 1891 | await applyServerConfig(serverConfig); |
| 1892 | |
| 1893 | // Port resolution priority: CLI flag > env var > config file > built-in default (3100) |
| 1894 | const portFromEnv = process.env.CODEBASE_CONTEXT_PORT |
| 1895 | ? Number.parseInt(process.env.CODEBASE_CONTEXT_PORT, 10) |
| 1896 | : undefined; |
| 1897 | const resolvedEnvPort = portFromEnv && Number.isFinite(portFromEnv) ? portFromEnv : undefined; |
| 1898 | const port = explicitPort ?? resolvedEnvPort ?? serverConfig?.server?.port ?? 3100; |
| 1899 | const host = serverConfig?.server?.host ?? '127.0.0.1'; |
| 1900 | |
| 1901 | // Validate bootstrap root the same way main() does |
| 1902 | if (primaryRootPath) { |
| 1903 | try { |
| 1904 | const stats = await fs.stat(primaryRootPath); |
| 1905 | if (!stats.isDirectory()) { |
| 1906 | console.error(`ERROR: Root path is not a directory: ${primaryRootPath}`); |
| 1907 | process.exit(1); |
| 1908 | } |
| 1909 | } catch { |
| 1910 | console.error(`ERROR: Root path does not exist: ${primaryRootPath}`); |
| 1911 | process.exit(1); |
| 1912 | } |
| 1913 | } |
| 1914 | |
| 1915 | const handle = await startHttpServer({ |
| 1916 | name: 'codebase-context', |
| 1917 | version: PKG_VERSION, |
| 1918 | port, |
| 1919 | host, |
| 1920 | registerHandlers, |
| 1921 | onSessionReady: (sessionServer) => { |
| 1922 | // Per-session roots change handler |
| 1923 | sessionServer.setNotificationHandler(RootsListChangedNotificationSchema, async () => { |
| 1924 | try { |
| 1925 | await refreshKnownRootsFromClient(); |
| 1926 | } catch { |
| 1927 | /* best-effort */ |
| 1928 | } |
| 1929 | }); |
| 1930 | } |
| 1931 | }); |
| 1932 | |
| 1933 | // Register cleanup — no parent death guard or stdin listeners in HTTP mode |
| 1934 | const stopAllWatchers = () => { |
| 1935 | for (const project of getAllProjects()) { |
| 1936 | project.stopWatcher?.(); |
| 1937 | } |
| 1938 | }; |
| 1939 | |
| 1940 | const shutdown = async () => { |
| 1941 | console.error('[HTTP] Shutting down...'); |
| 1942 | stopAllWatchers(); |
| 1943 | await handle.close(); |
| 1944 | process.exit(0); |
no test coverage detected