MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / start

Method start

src/mcp/index.ts:267–316  ·  view source on GitHub ↗

* Start the MCP server. * * Decision order: * 1. `CODEGRAPH_NO_DAEMON=1` → direct mode (unchanged pre-#411 behavior). * 2. `CODEGRAPH_DAEMON_INTERNAL=1` → we ARE the detached daemon; listen. * 3. No `.codegraph/` reachable → direct mode (the daemon's lockfile and * socke

()

Source from the content-addressed store, hash-verified

265 * mode — a misbehaving daemon must never block a session from starting.
266 */
267 async start(): Promise<void> {
268 // Long-lived process (direct / proxy / daemon alike): flush buffered
269 // telemetry opportunistically. Fire-and-forget + unref'd — adds nothing
270 // to the handshake path and never keeps the process alive.
271 getTelemetry().startInterval();
272
273 // #1243: the MCP config launches the local binary, so a server left
274 // running drifts behind releases with no signal. Refresh the shared
275 // update-check cache in the background and log ONE stderr notice when a
276 // newer version exists (stderr only — stdout is the protocol channel).
277 // The notice also reaches the agent via the initialize instructions and
278 // codegraph_status. Fire-and-forget: adds nothing to the handshake path.
279 checkForUpdateInBackground();
280
281 // The detached daemon process itself. Checked before the opt-out so the
282 // daemon honors the same env it was spawned with (it never sets NO_DAEMON).
283 if (daemonInternalSet()) {
284 return this.startDaemonProcess();
285 }
286
287 // Direct mode if the user opted out. Setting the env var is sufficient to
288 // get the pre-#411 single-process behavior.
289 if (daemonOptOutSet()) {
290 return this.startDirect('CODEGRAPH_NO_DAEMON set');
291 }
292
293 const root = resolveDaemonRoot(this.projectPath);
294 if (!root) {
295 // No initialized project found — daemon mode has nowhere to put its
296 // socket. The fresh-checkout / outside-project case; behave as before.
297 return this.startDirect('no .codegraph/ root found');
298 }
299
300 try {
301 // Answer the MCP handshake LOCALLY (instant tool registration — no waiting
302 // ~600ms for the daemon to spawn+bind, which produced the cold-start race)
303 // and forward tool CALLS to the shared daemon, connected in the background.
304 // Runs until the host disconnects; the proxy installs its own watchdog and
305 // falls back to an in-process engine if the daemon never comes up.
306 this.mode = 'proxy';
307 await this.runProxyWithLocalHandshake(root);
308 return;
309 } catch (err) {
310 // Belt-and-braces: a throw during proxy SETUP (before the client was served)
311 // is still safe to recover from with a direct-mode session.
312 const msg = err instanceof Error ? err.message : String(err);
313 process.stderr.write(`[CodeGraph MCP] Proxy path failed (${msg}); falling back to direct mode.\n`);
314 return this.startDirect('proxy path threw');
315 }
316 }
317
318 /**
319 * Stop the server. In daemon mode this triggers graceful shutdown of every

Callers 1

mainFunction · 0.95

Calls 10

startDaemonProcessMethod · 0.95
startDirectMethod · 0.95
getTelemetryFunction · 0.90
daemonInternalSetFunction · 0.85
daemonOptOutSetFunction · 0.85
resolveDaemonRootFunction · 0.85
startIntervalMethod · 0.80
writeMethod · 0.45

Tested by

no test coverage detected