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

Function connectWithHello

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

Source from the content-addressed store, hash-verified

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