MCP server subprocesses not cleaned up when terminal is closed
Bug Description
When a Claude Code session exits abnormally (e.g., terminal window closed directly instead of using /exit or Ctrl+C), the MCP server subprocesses spawned by Claude Code are not properly terminated. They become orphan processes that persist indefinitely.
Steps to Reproduce
- Start a Claude Code session in a terminal (with MCP servers configured, e.g., lark-mcp, playwright-mcp, firebase-mcp, etc.)
- Close the terminal window directly (click the X button) instead of gracefully exiting Claude Code
- Run
ps aux | grep mcpto check for remaining processes
Expected Behavior
All MCP server subprocesses should be terminated when the parent Claude Code process exits, regardless of how it exits.
Actual Behavior
MCP subprocesses become orphan processes (terminal shows ?? in ps aux output) and continue running indefinitely. Over time, this accumulates many zombie processes consuming system resources.
Example of orphan processes found:
hudequan 79032 0.0 0.0 ?? S node .../lark-mcp mcp ...
hudequan 73931 0.0 0.0 ?? S node .../lark-mcp mcp ...
hudequan 72009 0.0 0.0 ?? S node .../lark-mcp mcp ...
hudequan 87532 0.0 0.1 ?? S node .../chrome-devtools-mcp ...
hudequan 87516 0.0 0.1 ?? S npm exec chrome-devtools-mcp@latest ...
In one session, I found 7 groups of orphan lark-mcp processes plus chrome-devtools-mcp processes — all from previous Claude Code sessions that were not gracefully terminated.
Environment
- OS: macOS (Darwin 24.6.0)
- Claude Code: Latest version
- MCP servers affected: All types (lark-mcp, playwright-mcp, chrome-devtools-mcp, firebase-mcp, figma-developer-mcp, aliyun-sls-mcp, mcp-media-processor)
Suggested Fix
- Use process group signals (e.g.,
SIGHUPpropagation) to ensure child processes are terminated when the parent exits - Register cleanup handlers for
SIGTERM,SIGHUP, andSIGINTsignals - Consider using
prctl(PR_SET_PDEATHSIG)on Linux or equivalent mechanism on macOS to auto-terminate children when parent dies - As a fallback, MCP subprocesses could periodically check if their parent PID is still alive and self-terminate if not
Workaround
Manually kill orphan processes:
ps aux | grep -iE "lark-mcp|playwright-mcp|firebase mcp|chrome-devtools-mcp|figma-developer-mcp|aliyun-sls-mcp|mcp-sever-media-processor" | grep -v grep | awk '$7 == "??" {print $2}' | xargs killThis issue has 3 comments on GitHub. Read the full discussion on GitHub ↗