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

Method start

src/mcp/index.ts:226–275  ·  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

224 * mode — a misbehaving daemon must never block a session from starting.
225 */
226 async start(): Promise<void> {
227 // Long-lived process (direct / proxy / daemon alike): flush buffered
228 // telemetry opportunistically. Fire-and-forget + unref'd — adds nothing
229 // to the handshake path and never keeps the process alive.
230 getTelemetry().startInterval();
231
232 // #1243: the MCP config launches the local binary, so a server left
233 // running drifts behind releases with no signal. Refresh the shared
234 // update-check cache in the background and log ONE stderr notice when a
235 // newer version exists (stderr only — stdout is the protocol channel).
236 // The notice also reaches the agent via the initialize instructions and
237 // codegraph_status. Fire-and-forget: adds nothing to the handshake path.
238 checkForUpdateInBackground();
239
240 // The detached daemon process itself. Checked before the opt-out so the
241 // daemon honors the same env it was spawned with (it never sets NO_DAEMON).
242 if (daemonInternalSet()) {
243 return this.startDaemonProcess();
244 }
245
246 // Direct mode if the user opted out. Setting the env var is sufficient to
247 // get the pre-#411 single-process behavior.
248 if (daemonOptOutSet()) {
249 return this.startDirect('CODEGRAPH_NO_DAEMON set');
250 }
251
252 const root = resolveDaemonRoot(this.projectPath);
253 if (!root) {
254 // No initialized project found — daemon mode has nowhere to put its
255 // socket. The fresh-checkout / outside-project case; behave as before.
256 return this.startDirect('no .codegraph/ root found');
257 }
258
259 try {
260 // Answer the MCP handshake LOCALLY (instant tool registration — no waiting
261 // ~600ms for the daemon to spawn+bind, which produced the cold-start race)
262 // and forward tool CALLS to the shared daemon, connected in the background.
263 // Runs until the host disconnects; the proxy installs its own watchdog and
264 // falls back to an in-process engine if the daemon never comes up.
265 this.mode = 'proxy';
266 await this.runProxyWithLocalHandshake(root);
267 return;
268 } catch (err) {
269 // Belt-and-braces: a throw during proxy SETUP (before the client was served)
270 // is still safe to recover from with a direct-mode session.
271 const msg = err instanceof Error ? err.message : String(err);
272 process.stderr.write(`[CodeGraph MCP] Proxy path failed (${msg}); falling back to direct mode.\n`);
273 return this.startDirect('proxy path threw');
274 }
275 }
276
277 /**
278 * 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