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

Function connectWithHello

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

Source from the content-addressed store, hash-verified

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

Tested by

no test coverage detected