fix: prevent orphaned browser when client disconnects#222
Open
7sorium wants to merge 1 commit into
Open
Conversation
StdioServerTransport only listens for 'data'/'error' on stdin, so when the parent/client process dies (stdin EOF) nothing tears the server down. The process lingers (often reparented to launchd/init) holding its Chromium, which accumulates over time as stale headless browsers. Additionally, the graceful shutdown handler exits without closing the browser, so even SIGINT/SIGTERM left Chromium to be cleaned up only by Playwright's own exit hooks (racy under process.exit). This change: - adds closeBrowser() to toolHandler and calls it from shutdown(), with a 5s timeout so a hung close can't block exit and a re-entrancy guard - watches process.stdin 'end'/'close' in stdio mode to shut down cleanly when the client/parent goes away Verified: launching a browser then closing the server's stdin now reaps both the server process and its Chromium children (no orphans). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Problem
When running in stdio mode, the server can leave orphaned Chromium processes behind:
StdioServerTransportonly attachesdata/errorlisteners tostdin— it never reacts to stdin reaching EOF. So when the MCP client (or parent process) dies, the server keeps running, frequently reparented tolaunchd/init, still holding its launched browser. These accumulate over time as stale headless browsers eating memory.SIGINT/SIGTERMhandler callsprocess.exit(0)without closing the browser, leaving cleanup to Playwright's own exit hooks, which is racy under an immediateprocess.exit.Fix
closeBrowser()totoolHandler.ts— closes the active browser (if connected) and resets state; safe to call when no browser is open.shutdown()before exit, wrapped inPromise.racewith a 5s timeout so a hung close can't block exit, plus a re-entrancy guard for repeated signals.process.stdinend/closein stdio mode so the server shuts down cleanly when the client/parent goes away. (HTTP--portmode is unaffected — these listeners are only registered on the stdio path.)Verification
Launching a browser via
playwright_navigate, then closing the server's stdin:headless_shellchild processes are reaped — no orphans.All 150 existing tests pass; build is clean.
🤖 Generated with Claude Code