codex - 💡(How to fix) Fix codex-cli app-server (Linux) leaks chrome-devtools-mcp child trees on JSON-RPC transport close

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Fix Action

Fix / Workaround

Workaround (containment, not a fix)

Code Example

codex app-server PID: 1754 (single instance, uptime ~8 h)
chrome-devtools-mcp wrappers under PID 1754: 4
ages: 11974s, 1236s, 1081s, 1056s   (only the 11974s could plausibly be "active";
                                      the other 3 were spawned during one-shot
                                      `codex exec` runs via codex-cli's bridge mode)
combined RSS: ~1.1 GB

---

1115984  ppid=1754      npm exec chrome-devtools-mcp@latest --no-usage-statistics --no-performance-crux --headless
1116129  ppid=1115984   sh -c chrome-devtools-mcp --no-usage-statistics --no-performance-crux --headless
1116130  ppid=1116129   chrome-devtools-mcp

---

codex app-server --listen unix:///tmp/codex-app-server.sock

---

/usr/local/bin/codex exec -p advisory -C /tmp -s read-only --skip-git-repo-check --ephemeral --json --output-last-message …
RAW_BUFFERClick to expand / collapse

What version of the Codex CLI are you using?

codex-cli 0.132.0 (@openai/[email protected], npm-installed at /usr/lib/node_modules/@openai/codex/)

What subscription do you have?

Pro

What platform is your computer?

Linux 6.8.0-117-generic x86_64 (Ubuntu 24.04, Hostinger VPS, 32 GB RAM, 8 vCPU)

What issue are you seeing?

codex app-server --listen unix://… does not reap chrome-devtools-mcp (stdio MCP) child process trees when the JSON-RPC transport closes. Each orphan tree is npm exec → sh -c → chrome-devtools-mcp ≈ 270 MB RSS. They accumulate indefinitely until OOM or until external intervention.

Related but distinct issues (different surface, same defect class):

  • #17574 — Codex App on macOS / subagent spawn pattern
  • #17832 — Playwright MCP regression after #16895
  • #14548 — Codex spawning too many MCP instances
  • #21008 — Codex Desktop launchd leak
  • #21984 — Eager MCP start per session

This report is specifically about codex-cli's app-server mode on Linux under the claude-bridge.sh peer-agent pattern (Claude Code invoking codex via Unix-socket JSON-RPC), which none of the above cover.

Observed accumulation

Steady-state on my VPS over a ~3-hour window:

codex app-server PID: 1754 (single instance, uptime ~8 h)
chrome-devtools-mcp wrappers under PID 1754: 4
ages: 11974s, 1236s, 1081s, 1056s   (only the 11974s could plausibly be "active";
                                      the other 3 were spawned during one-shot
                                      `codex exec` runs via codex-cli's bridge mode)
combined RSS: ~1.1 GB

Process tree (one of the four):

1115984  ppid=1754      npm exec chrome-devtools-mcp@latest --no-usage-statistics --no-performance-crux --headless
1116129  ppid=1115984   sh -c chrome-devtools-mcp --no-usage-statistics --no-performance-crux --headless
1116130  ppid=1116129   chrome-devtools-mcp

The wrapper PID's parent is the codex app-server daemon. The MCP server never received a graceful close-on-shutdown signal — once the originating JSON-RPC channel closed (single tool call completed), the wrapper + child remained alive.

Steps to reproduce (deterministic)

  1. Start codex app-server:
    codex app-server --listen unix:///tmp/codex-app-server.sock
  2. From another agent (e.g. Claude Code) over the same socket, fire a tools/call for a chrome-devtools-mcp tool method (we use the bridge wrapper claude-bridge.sh):
    /usr/local/bin/codex exec -p advisory -C /tmp -s read-only --skip-git-repo-check --ephemeral --json --output-last-message …
    The bridge spawns a fresh codex worker which talks to the long-lived app-server via the socket.
  3. Inspect ps --ppid <app-server-pid> — the npm exec chrome-devtools-mcp wrapper is alive.
  4. The bridge worker exits, the JSON-RPC channel for the chrome MCP closes from the client side, BUT the wrapper remains alive under app-server.
  5. Repeat. Each invocation adds one orphan.

Expected behavior

When the JSON-RPC transport to a stdio MCP child closes (or when its consuming session shuts down), app-server should:

  1. Send SIGTERM to the wrapper's process group.
  2. Wait a bounded grace (e.g. 5 s).
  3. SIGKILL the process group's surviving descendants (collected before SIGTERM, so reparented chromium survives only briefly).

Actual behavior

Wrapper + descendants stay alive indefinitely. Memory leak scales linearly with tool-call count.

Workaround (containment, not a fix)

I deployed a per-host cron reaper that:

  • snapshots codex app-server PIDs + /proc/<pid>/stat[22] start times,
  • enumerates npm exec chrome-devtools-mcp wrappers parented to the snapshot set,
  • keeps the youngest per app-server plus any wrapper younger than 10 min (grace for in-flight calls),
  • SIGTERMs the rest deepest-first; 5 s; SIGKILLs survivors from the SAVED descendant tree.

Source: happy to paste, but it should not be the fix — app-server knows the exact moment a transport closes; a host-side reaper is guessing.

Suggested fix locations

(I have not read the codex-cli source yet, but the surface area implied by the symptom:)

  • The MCP transport-close handler for stdio servers should track child PIDs and tear the process group down explicitly. node:child_process spawn({ detached: false }) puts children in the parent's PG; process.kill(-pgid, 'SIGTERM') works. For detached: true, the spawn site must store pid and call kill(-pid, …) on transport close.
  • A short post-SIGTERM grace plus SIGKILL covers chromium's slow shutdown.
  • Belt-and-suspenders: on app-server shutdown (SIGTERM to itself), iterate all known MCP child PGs and kill them, since a normal exit() of the daemon does not cascade through.

Cross-references

  • #17574 (Codex App / macOS / subagent): same defect class, different platform.
  • #21008 (Codex Desktop / launchd): same defect class on macOS Desktop.
  • #16895 (the prior Playwright MCP fix) and #17832 (regression after it): suggests a shared lifecycle bug that the Playwright-specific fix didn't generalize to other stdio MCPs.

Environment

  • OS: Ubuntu 24.04, kernel 6.8.0-117-generic
  • Node: bundled with codex-cli (@openai/codex-linux-x64)
  • chrome-devtools-mcp version: latest (npm exec chrome-devtools-mcp@latest)
  • Invocation: codex app-server --listen unix:// + peer-agent bridge over the socket
  • Concurrent CCD (Claude Code) sessions on the same host (7 active) — their own MCP children are correctly scoped and do not leak, only the codex app-server side does

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

When the JSON-RPC transport to a stdio MCP child closes (or when its consuming session shuts down), app-server should:

  1. Send SIGTERM to the wrapper's process group.
  2. Wait a bounded grace (e.g. 5 s).
  3. SIGKILL the process group's surviving descendants (collected before SIGTERM, so reparented chromium survives only briefly).

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

codex - 💡(How to fix) Fix codex-cli app-server (Linux) leaks chrome-devtools-mcp child trees on JSON-RPC transport close