MCPcopy Index your code
hub / github.com/colbymchenry/codegraph / connectWithHello

Function connectWithHello

src/mcp/proxy.ts:131–166  ·  view source on GitHub ↗
(
  socketPath: string,
  expectedVersion: string = CodeGraphPackageVersion,
)

Source from the content-addressed store, hash-verified

129 * owns the socket. Used by the local-handshake proxy's background connect.
130 */
131export async function connectWithHello(
132 socketPath: string,
133 expectedVersion: string = CodeGraphPackageVersion,
134): Promise<net.Socket | 'version-mismatch' | null> {
135 if (process.platform !== 'win32' && !fs.existsSync(socketPath)) return null;
136 const socket = net.createConnection(socketPath);
137 socket.setEncoding('utf8');
138 // Keep an 'error' listener attached for the socket's ENTIRE life. readHelloLine
139 // attaches its own and then REMOVES it on success (its cleanup()), which left a
140 // window — from here until the caller attaches its onDaemonLost handler — where
141 // a socket 'error' had NO listener. In Node an unhandled socket 'error' is
142 // re-thrown as an uncaughtException, which the global fatal handler turns into
143 // process.exit(1); to an MCP client that surfaces as a bare "Transport closed"
144 // (#974). The window is rarely hit on a healthy FS but is common on flaky
145 // AF_UNIX-over-DrvFs (WSL2 /mnt drives). A no-op guard makes the error
146 // recoverable: the follow-up 'close' drives the caller's normal fallback.
147 socket.on('error', () => { /* absorbed — see #974; 'close' drives the fallback */ });
148 const hello = await readHelloLine(socket).catch(() => null);
149 if (!hello) {
150 socket.destroy();
151 return null; // no daemon yet — caller should keep polling
152 }
153 if (hello.codegraph !== expectedVersion) {
154 // A daemon IS up but it's the wrong version — definitive, not a "not yet".
155 // Don't poll; the caller serves in-process so we never run stale-vs-new.
156 process.stderr.write(
157 `[CodeGraph MCP] Found a daemon on ${socketPath} but version (${hello.codegraph}) ` +
158 `differs from ours (${expectedVersion}); serving this session in-process.\n`
159 );
160 socket.destroy();
161 return 'version-mismatch';
162 }
163 logAttachedDaemon(socketPath, hello);
164 sendClientHello(socket);
165 return socket;
166}
167
168/**
169 * Tell the daemon our pids right after we verify its hello, so its liveness

Callers 2

connectAnyCandidateMethod · 0.90

Calls 6

readHelloLineFunction · 0.85
logAttachedDaemonFunction · 0.85
sendClientHelloFunction · 0.85
onMethod · 0.65
destroyMethod · 0.45
writeMethod · 0.45

Tested by

no test coverage detected