fix(lifecycle): remove stdin listeners causing spurious MCP -32000 errors#255
Open
flightlesstux wants to merge 1 commit intomksglu:mainfrom
Open
fix(lifecycle): remove stdin listeners causing spurious MCP -32000 errors#255flightlesstux wants to merge 1 commit intomksglu:mainfrom
flightlesstux wants to merge 1 commit intomksglu:mainfrom
Conversation
…rors The lifecycle guard registered end/close/error listeners on process.stdin and called process.stdin.resume(). This conflicted with StdioServerTransport, which owns the same stream via readline. Transient pipe events triggered gracefulShutdown() → process.exit(0), so the MCP client logged "MCP error -32000: Connection closed" multiple times per hour during normal sessions. Remove the stdin-based shutdown path entirely. ppid polling every 30s plus SIGTERM/SIGINT/SIGHUP handlers still guarantee the orphan protection added in mksglu#103. Update tests/lifecycle.test.ts so the integration test asserts the new contract: stdin close must NOT shut the guard down. The previous positive assertion is what made PR mksglu#237 fail CI on Ubuntu/macOS. Supersedes mksglu#237. Closes mksglu#236. Co-authored-by: ponythewhite <ponythewhite@users.noreply.github.com> Co-authored-by: Ercan Ermis <eposta@ercanermis.com>
This was referenced Apr 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
process.stdin.resume()and theend/close/errorlisteners fromsrc/lifecycle.ts(the same diff as PR fix: remove stdin listeners from lifecycle guard to prevent MCP -32000 errors #237).tests/lifecycle.test.tsso the integration test asserts the new contract — stdin close must NOT trigger the lifecycle guard.Why
The lifecycle guard and the MCP stdio transport both own
process.stdin. Any transient pipe event firedonStdinClose→gracefulShutdown()→process.exit(0)immediately, tearing in-flight JSON-RPC responses mid-flight and producingMCP error -32000: Connection closed.ppidpolling every 30s plusSIGTERM/SIGINT/SIGHUPare enough to preserve the orphan protection from #103 without these false positives.What changed
src/lifecycle.ts— stdin listeners +resume()removed; matchingremoveListenercalls removed from the cleanup closure; JSDoc updated (both the file header and theLifecycleGuardOptions.onShutdownfield comment).tests/lifecycle.test.ts— positive test (child exits when stdin is closed) replaced with a negative one (child does NOT exit when stdin is closed (#236)). All other unit tests and the SIGTERM integration test are untouched.Why #237 is superseded
PR #237 by @ponythewhite carried the exact production-code diff but did not update the integration test, which still asserted the old contract (
exit code === 42on stdin close). CI went red on Ubuntu + macOS (Windows skips the integration block for unrelated POSIX-signal reasons). The author has been inactive for 6+ days. This PR keeps their production change verbatim and adds the test update so CI is green.Test plan
bunx vitest run tests/lifecycle.test.ts— 7 tests pass locally (5 unit + 2 integration: negative stdin, SIGTERM).vitest run— hook/session tests already fail on pristineupstream/main(pre-existing, unrelated to this fix); no new regressions introduced.bun run build && bun run bundle, spawned with stdio piped, stdin closed mid-session. The lifecycle guard no longer callsprocess.exit(0)— the server now winds down viaStdioServerTransport's normal EOF handling (clean exit, no-32000framing corruption).Credit
Co-authored with @ponythewhite whose PR #237 carried the original diff.
Closes #236