Problem
When a Claude Code session ends (exit, crash, terminal close, or kill), the MCP server child processes it spawned are never terminated. They persist as orphan processes indefinitely, accumulating across sessions until the system runs out of memory.
Each session spawns ~10 MCP server processes (playwright-mcp, chrome-devtools-mcp, dokploy-mcp, context7-mcp, sequential-thinking, ollama-mcp, arxiv-mcp-server, etc.). After a few days of normal usage, this leads to dozens of orphaned MCP processes consuming multiple GB of swap/RAM.
Evidence
System with 10 configured MCP servers, after ~3 days of normal usage:
=== Swap consumers by category ===
6517 MB claude (10+ stale sessions)
4023 MB MainThread (orphaned MCP servers: playwright, chrome-devtools, dokploy...)
3000 MB npm (orphaned npm exec MCP starters)
Total orphaned: ~13 GB swap from ~80 processes
OOM kills in 24 hours: 1625
Process tree showing orphaned MCP servers (ppid=1, parent claude dead):
PID=3034 PPID=2927 node playwright-mcp # parent npm dead
PID=3364 PPID=3261 node chrome-devtools-mcp # parent npm dead
PID=3498 PPID=3427 node dokploy-mcp # parent npm dead
PID=58073 PPID=57821 node playwright-mcp # another session's orphans
PID=58305 PPID=58238 node chrome-devtools-mcp
...
(~80 orphaned processes total from ~8 dead sessions)
Root Cause
Claude Code spawns MCP servers as child processes but has no cleanup mechanism when the session ends:
- Normal exit (
/exit, Ctrl+C): No child process cleanup is performed
- Terminal close: Claude receives SIGHUP but doesn't forward it to MCP children
- Kill/crash: Children get reparented to init (ppid=1) and run forever
- Stale sessions: Claude process itself may survive terminal close (zombie in swap) while still holding MCP children alive
The process chain is: claude -> npm exec @playwright/mcp -> node playwright-mcp
When claude dies, both npm exec and node survive as orphans.
Expected Behavior
When a Claude Code session ends (for any reason), all MCP server processes it spawned should be terminated.
Suggested Solutions
1. Process Group cleanup (simplest)
Set MCP server processes in the same process group as the claude session, then killpg() on exit:
# When spawning MCP servers
os.setpgrp() # or use preexec_fn in subprocess
# On session exit
os.killpg(os.getpgrp(), signal.SIGTERM)
2. PR_SET_PDEATHSIG (Linux)
Set child processes to receive SIGTERM when their parent dies:
import ctypes
def set_pdeathsig():
libc = ctypes.CDLL("libc.so.6")
PR_SET_PDEATHSIG = 1
libc.prctl(PR_SET_PDEATHSIG, signal.SIGTERM)
subprocess.Popen(cmd, preexec_fn=set_pdeathsig)
3. Explicit cleanup in session lifecycle
Track spawned MCP PIDs and terminate them in the session end handler:
// On session end
for (const mcpProcess of this.mcpProcesses) {
mcpProcess.kill('SIGTERM');
}
4. PID file / heartbeat
MCP servers periodically check if their parent claude process is still alive:
setInterval(() => {
try { process.kill(parentPid, 0); }
catch { process.exit(0); } // parent dead, self-terminate
}, 30000);
Current Workaround
I've implemented a two-layer workaround:
- SessionEnd hook (
session-cleanup.sh): Kills child processes on clean exit
- systemd timer watchdog: Runs every 30 min, detects orphaned MCP servers (no claude ancestor in process tree) and stale sessions (dead TTY or high swap + idle)
Environment
- Claude Code: latest (claude-code CLI)
- OS: Fedora 43 (Linux 6.18.7)
- 10 MCP servers configured (playwright, chrome-devtools, dokploy, context7, sequential-thinking, ollama, arxiv, scrapling, T4F, voorinfra + more)
- 16 GB RAM, zram swap
Problem
When a Claude Code session ends (exit, crash, terminal close, or
kill), the MCP server child processes it spawned are never terminated. They persist as orphan processes indefinitely, accumulating across sessions until the system runs out of memory.Each session spawns ~10 MCP server processes (playwright-mcp, chrome-devtools-mcp, dokploy-mcp, context7-mcp, sequential-thinking, ollama-mcp, arxiv-mcp-server, etc.). After a few days of normal usage, this leads to dozens of orphaned MCP processes consuming multiple GB of swap/RAM.
Evidence
System with 10 configured MCP servers, after ~3 days of normal usage:
Process tree showing orphaned MCP servers (ppid=1, parent claude dead):
Root Cause
Claude Code spawns MCP servers as child processes but has no cleanup mechanism when the session ends:
/exit, Ctrl+C): No child process cleanup is performedThe process chain is:
claude -> npm exec @playwright/mcp -> node playwright-mcpWhen claude dies, both
npm execandnodesurvive as orphans.Expected Behavior
When a Claude Code session ends (for any reason), all MCP server processes it spawned should be terminated.
Suggested Solutions
1. Process Group cleanup (simplest)
Set MCP server processes in the same process group as the claude session, then
killpg()on exit:2. PR_SET_PDEATHSIG (Linux)
Set child processes to receive SIGTERM when their parent dies:
3. Explicit cleanup in session lifecycle
Track spawned MCP PIDs and terminate them in the session end handler:
4. PID file / heartbeat
MCP servers periodically check if their parent claude process is still alive:
Current Workaround
I've implemented a two-layer workaround:
session-cleanup.sh): Kills child processes on clean exitEnvironment