diff --git a/src/mcp/server.ts b/src/mcp/server.ts index e0d4eb794..3724ade7a 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -32,6 +32,26 @@ export async function startMcpServer(engine: BrainEngine) { const transport = new StdioServerTransport(); await server.connect(transport); + + // Exit cleanly when MCP client disconnects (stdin EOF) or on signals. + // Without this, orphaned serve processes accumulate and contend for the + // PGLite write lock, causing ingest jobs (email-sync) to time out. + let shuttingDown = false; + const shutdown = (reason: string, code = 0) => { + if (shuttingDown) return; + shuttingDown = true; + process.stderr.write(`[gbrain-serve] shutdown: ${reason}\n`); + Promise.resolve(engine.disconnect?.()) + .catch(() => {}) + .finally(() => process.exit(code)); + }; + process.stdin.on('end', () => shutdown('stdin end')); + process.stdin.on('close', () => shutdown('stdin close')); + // @ts-ignore — SDK exposes onclose on transport + transport.onclose = () => shutdown('transport close'); + process.on('SIGTERM', () => shutdown('SIGTERM')); + process.on('SIGINT', () => shutdown('SIGINT')); + process.on('SIGHUP', () => shutdown('SIGHUP')); } // Backward compat: used by `gbrain call` command (trusted local path).