Platform: macOS 14.5 (Darwin 23.5.0), ARM64, Node.js v21.7.3
Claude Code: VSCode extension
Summary
When a Claude Code session closes, context-mode MCP server processes become orphaned and enter an infinite error loop consuming 50-60% CPU each. Multiple sessions accumulate zombie processes, leading to sustained >100% CPU usage.
Steps to Reproduce
- Open Claude Code (VSCode extension)
- Use context-mode normally (any tool call triggers it)
- Close the Claude Code session (close panel, close VSCode, or Ctrl+C)
- Open a new Claude Code session
- Repeat steps 2-4
Expected Behavior
Old context-mode processes should terminate when the parent Claude Code session dies.
Actual Behavior
Each closed session leaves behind ~6 orphaned processes. The main MCP server worker enters an infinite error loop:
PID 22095: 50.5% CPU, 53MB RSS, running 1h46m (session closed long ago)
PID 24550: 53.1% CPU, 53MB RSS, running 1h31m
PID 38682: 58.6% CPU, 57MB RSS, running 3m (current session — also high CPU)
Total: ~160% CPU for 3 sessions, doing nothing useful.
Root Cause Analysis
1. Missing stdin end handler in StdioServerTransport
The MCP SDK transport (_a class in server.bundle.mjs) only listens for data and error events:
start() {
this._stdin.on("data", this._ondata);
this._stdin.on("error", this._onerror);
}
No end or close handler. When parent dies and stdin EOF occurs, the transport doesn't initiate shutdown.
2. Watchdog bypassed by npm wrapper layer
Process tree:
[Claude Code (dead)] → start.mjs → npm exec (PPID=1, orphan but alive) → context-mode server
The watchdog (qC()) checks:
var HC = process.ppid; // saved at startup
function qC() {
let t = process.ppid;
return !(t !== HC || t === 0 || t === 1);
}
But the server's direct parent is npm exec (still alive), not Claude Code. So process.ppid never changes → watchdog never triggers.
3. Infinite error loop (CPU profile evidence)
sample profiling of zombie PID 22095 (1 second, 791 samples):
541/791 (68%) → ReportPendingMessages
→ ErrorStackGetter
→ FormatStackTrace (236 samples)
→ CallSitePrototypeToString
→ SerializeCallSiteInfo
→ ArrayPrototypeJoin
The process is stuck in a tight loop: throw error → format stack trace → catch → retry. V8 heap allocation + GC adds additional CPU overhead.
Suggested Fixes
Fix 1: Add stdin end handler (immediate)
start() {
this._stdin.on("data", this._ondata);
this._stdin.on("error", this._onerror);
this._stdin.on("end", () => this.close());
this._stdin.on("close", () => this.close());
}
Fix 2: Fix watchdog to check grandparent or use process group
function isParentAlive() {
// Check if stdin is still readable (most reliable)
if (process.stdin.destroyed || process.stdin.readableEnded) return false;
// Also check ppid chain
const currentPpid = process.ppid;
if (currentPpid === 0 || currentPpid === 1) return false;
if (currentPpid !== savedPpid) return false;
return true;
}
Fix 3: Reduce npm wrapper layer
Instead of npm exec context-mode@latest, start the server directly to eliminate the intermediate process that masks parent death:
// In start.mjs, instead of spawning via npm exec:
import("./server.bundle.mjs"); // Already done when bundle exists
Workaround
Kill orphaned processes manually:
# Kill orphaned context-mode processes (PPID chain leads to init)
ps -eo pid,ppid,command | grep "context-mode" | grep -v grep | while read pid ppid cmd; do
gppid=$(ps -o ppid= -p $ppid 2>/dev/null | tr -d ' ')
[ "$gppid" = "1" ] && kill $pid
done
Environment
- macOS 14.5 (23F79) ARM64
- Node.js v21.7.3
- context-mode v1.0.89
- Claude Code VSCode extension
- Plugin installed as both
local and user scope
Platform: macOS 14.5 (Darwin 23.5.0), ARM64, Node.js v21.7.3
Claude Code: VSCode extension
Summary
When a Claude Code session closes, context-mode MCP server processes become orphaned and enter an infinite error loop consuming 50-60% CPU each. Multiple sessions accumulate zombie processes, leading to sustained >100% CPU usage.
Steps to Reproduce
Expected Behavior
Old context-mode processes should terminate when the parent Claude Code session dies.
Actual Behavior
Each closed session leaves behind ~6 orphaned processes. The main MCP server worker enters an infinite error loop:
Total: ~160% CPU for 3 sessions, doing nothing useful.
Root Cause Analysis
1. Missing
stdinend handler in StdioServerTransportThe MCP SDK transport (
_aclass inserver.bundle.mjs) only listens fordataanderrorevents:No
endorclosehandler. When parent dies and stdin EOF occurs, the transport doesn't initiate shutdown.2. Watchdog bypassed by npm wrapper layer
Process tree:
The watchdog (
qC()) checks:But the server's direct parent is
npm exec(still alive), not Claude Code. Soprocess.ppidnever changes → watchdog never triggers.3. Infinite error loop (CPU profile evidence)
sampleprofiling of zombie PID 22095 (1 second, 791 samples):The process is stuck in a tight loop: throw error → format stack trace → catch → retry. V8 heap allocation + GC adds additional CPU overhead.
Suggested Fixes
Fix 1: Add
stdinend handler (immediate)Fix 2: Fix watchdog to check grandparent or use process group
Fix 3: Reduce npm wrapper layer
Instead of
npm exec context-mode@latest, start the server directly to eliminate the intermediate process that masks parent death:Workaround
Kill orphaned processes manually:
Environment
localanduserscope