Skip to content

fix(lifecycle): remove stdin listeners causing spurious MCP -32000 errors#255

Open
flightlesstux wants to merge 1 commit intomksglu:mainfrom
flightlesstux:fix/236-lifecycle-stdin
Open

fix(lifecycle): remove stdin listeners causing spurious MCP -32000 errors#255
flightlesstux wants to merge 1 commit intomksglu:mainfrom
flightlesstux:fix/236-lifecycle-stdin

Conversation

@flightlesstux
Copy link
Copy Markdown
Contributor

Summary

Why

The lifecycle guard and the MCP stdio transport both own process.stdin. Any transient pipe event fired onStdinClosegracefulShutdown()process.exit(0) immediately, tearing in-flight JSON-RPC responses mid-flight and producing MCP error -32000: Connection closed. ppid polling every 30s plus SIGTERM/SIGINT/SIGHUP are enough to preserve the orphan protection from #103 without these false positives.

What changed

  • src/lifecycle.ts — stdin listeners + resume() removed; matching removeListener calls removed from the cleanup closure; JSDoc updated (both the file header and the LifecycleGuardOptions.onShutdown field 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 === 42 on 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).
  • Full vitest run — hook/session tests already fail on pristine upstream/main (pre-existing, unrelated to this fix); no new regressions introduced.
  • Manual smoke test: bundled server built with bun run build && bun run bundle, spawned with stdio piped, stdin closed mid-session. The lifecycle guard no longer calls process.exit(0) — the server now winds down via StdioServerTransport's normal EOF handling (clean exit, no -32000 framing corruption).
  • CI on Ubuntu, macOS, and Windows must go green for this PR.

Credit

Co-authored with @ponythewhite whose PR #237 carried the original diff.

Closes #236

…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>
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.

Lifecycle guard stdin listeners cause spurious MCP -32000 Connection closed

1 participant