Skip to content

fix: dispose LSP children on MCP stdio close (#47) - #48

Open
Fortune0001 wants to merge 1 commit into
ktnyt:mainfrom
Fortune0001:fix/issue-47-stdio-close-handler
Open

fix: dispose LSP children on MCP stdio close (#47)#48
Fortune0001 wants to merge 1 commit into
ktnyt:mainfrom
Fortune0001:fix/issue-47-stdio-close-handler

Conversation

@Fortune0001

Copy link
Copy Markdown

Fixes #47.

Summary

Adds process.stdin close and end handlers in index.ts that mirror the existing SIGTERM handler. Without these, when the parent MCP client (e.g. claude -p) exits and closes the stdio transport, cclsp keeps running and its spawned LSP children (pylsp, typescript-language-server) become orphaned. On Windows this accumulates to OOM-class memory pressure across long-running parent sessions.

Change

 process.on('SIGTERM', () => {
   lspClient.dispose();
   process.exit(0);
 });

+process.stdin.on('close', () => {
+  lspClient.dispose();
+  process.exit(0);
+});
+
+process.stdin.on('end', () => {
+  lspClient.dispose();
+  process.exit(0);
+});

lspClient.dispose() already cleans up LSP children correctly (delegates to ServerManager.dispose() in src/lsp/). The only missing piece was invoking it on the stdio-close path.

Test plan

  • bun test — 218 pass, 9 skip, 0 fail
  • Manual smoke: with the patched build, fire many claude -p calls in a loop in a Python-heavy workspace, then enumerate orphan python.exe processes whose command line contains pylsp — should be 0 (was ~100 before the patch).

Happy to iterate if you'd prefer a different shape (e.g., transport.onclose instead of process.stdin.on('close')) — both achieve the same thing and I went with the process.stdin form to mirror the surrounding signal handlers. Let me know.

cclsp only handled SIGINT/SIGTERM, but the MCP protocol's canonical shutdown
signal on a stdio transport is parent-stdin close, not POSIX signals. Without
a stdin close/end handler the server kept running after the parent client
disconnected, leaving spawned LSP children (pylsp.exe, typescript-language-server)
orphaned. On Windows this accumulated to OOM-class memory pressure across
long-running parent sessions (e.g. eval harnesses firing many claude -p calls).

Add process.stdin 'close' and 'end' handlers that mirror the existing SIGTERM
handler -- both invoke lspClient.dispose() and exit cleanly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LSP children orphaned on MCP stdio disconnect (Windows; missing stdin close handler in index.ts)

1 participant