Skip to content

Bug: MCP server processes become orphaned when Claude session ends abnormally #53

@shahabzohaib

Description

@shahabzohaib

Description

The MCP server wrapper's signal handling is incomplete, causing mcp-server.js processes to become orphaned when Claude sessions end through terminal closure, crashes, or SIGKILL.

Observed Behavior

After normal usage, multiple orphaned episodic-memory processes accumulate:

$ ps aux | grep episodic-memory
user  87483  node .../episodic-memory/1.0.15/dist/mcp-server.js
user  87466  node .../episodic-memory/1.0.15/cli/mcp-server-wrapper.js
user   8516  node .../episodic-memory/1.0.15/dist/mcp-server.js
user   8515  node .../episodic-memory/1.0.15/cli/mcp-server-wrapper.js
user   7064  node .../episodic-memory/1.0.15/dist/mcp-server.js
user   7062  node .../episodic-memory/1.0.15/cli/mcp-server-wrapper.js
# ... accumulates over time

Each Claude session spawns a new pair of processes. Old ones don't terminate.

Root Cause

In cli/mcp-server-wrapper.js (lines 84-86), only SIGTERM and SIGINT are handled:

process.on('SIGTERM', () => child.kill('SIGTERM'));
process.on('SIGINT', () => child.kill('SIGINT'));

What's missing:

  1. No SIGHUP handler - When terminal windows are closed, the shell sends SIGHUP, not SIGTERM. This signal is ignored.

  2. No stdin close detection - MCP servers communicate via stdio. When the parent Claude process dies unexpectedly, stdin closes, but neither the wrapper nor the server detects this.

  3. No parent process monitoring - If Claude is killed with kill -9, no signal is sent to children. They become orphaned with no way to know their parent died.

Failure scenarios:

Scenario Signal sent Result
Close terminal tab/window SIGHUP Ignored → orphan
Claude crashes None No cleanup → orphan
kill -9 Claude None No cleanup → orphan
Normal /exit SIGTERM Works correctly ✓

Proposed Fix

Add to cli/mcp-server-wrapper.js after line 86:

// Handle terminal hangup (closing terminal window)
process.on('SIGHUP', () => child.kill('SIGHUP'));

// Detect parent process death via stdin close
process.stdin.on('end', () => {
  child.kill();
  process.exit(0);
});

And optionally add to src/mcp-server.ts as a fallback:

// Detect parent death if wrapper fails to forward
process.stdin.on('end', () => process.exit(0));

Environment

  • episodic-memory: 1.0.15
  • Claude Code: 2.1.22
  • OS: macOS (Darwin 25.2.0)
  • Node.js: v24.11.1

Workaround

Users can periodically clean up orphaned processes:

pkill -f "episodic-memory"

The current session will respawn its server when needed.

Impact

Over time, orphaned processes consume memory and system resources. Users may notice system slowdown without understanding the cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions