Windows Desktop app-server repeatedly spawns duplicate MCP stdio process pools (183 node.exe / 13GB private memory)

Open 💬 7 comments Opened Jul 7, 2026 by xxH7r
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

Codex Desktop on Windows repeatedly starts duplicate stdio MCP process pools under codex.exe app-server --analytics-default-enabled. The duplicate cmd -> npx/npx.cmd -> node.exe trees are not consistently reaped, causing large memory growth.

This appears related to:

  • #28361
  • #30408
  • #30753
  • #29079

I am filing a separate report because this reproduces on the current Windows Desktop app installed on my machine, and the process count/memory growth is severe enough to affect normal use.

Environment

  • OS: Microsoft Windows 11 Home Chinese, 64-bit
  • OS version/build: 10.0.22621 / build 22621
  • Codex Desktop package: OpenAI.Codex_26.527.3686.0_x64__2p2nqsd0c76g0
  • Codex Desktop version: 26.527.3686.0
  • ~/.codex/version.json: latest_version is 0.142.5
  • App server command observed:
  • codex.exe app-server --analytics-default-enabled

Global MCP servers configured in ~/.codex/config.toml:

  • reactbits: npx -y reactbits-dev-mcp-server
  • tavily: npx.cmd -y tavily-mcp
  • chrome-devtools: npx chrome-devtools-mcp@latest
  • shadcn: npx.cmd -y shadcn@latest mcp
  • figma: HTTP MCP
  • node_repl: bundled Codex node_repl

No secrets are included here.

Observed Behavior

During normal Codex Desktop usage, Task Manager showed a large number of node.exe processes consuming substantial memory.

Read-only process inspection showed:

  • Peak observed: 183 node.exe processes
  • Peak private memory: 13.2 GB
  • Peak working set: 10.92 GB
  • One old Desktop app-server process tree owned about 170 of the node.exe descendants and about 12.24 GB private memory

The duplicated processes were primarily repeated MCP stdio server trees:

  • tavily-mcp
  • reactbits-dev-mcp-server
  • shadcn@latest mcp
  • chrome-devtools-mcp@latest
  • chrome-devtools-mcp watchdog children

Representative command lines:

"node" "...\\npm-cache\\_npx\\...\\chrome-devtools-mcp\\build\\src\\bin\\chrome-devtools-mcp.js"
node.exe ...\\chrome-devtools-mcp\\build\\src\\telemetry\\watchdog\\main.js --parent-pid=...
"node" "...\\npm-cache\\_npx\\...\\shadcn\\dist\\index.js" mcp
"node" "...\\npm-cache\\_npx\\...\\tavily-mcp\\build\\index.js"
"node" "...\\npm-cache\\_npx\\...\\reactbits-dev-mcp-server\\dist\\index.js"

After I only inspected the process tree, without killing any process and without changing config, the old app-server appears to have exited/restarted and the count dropped to:

  • 19 node.exe processes
  • about 1.5 GB private memory

However, shortly afterward, the count started increasing again:

  • 37 node.exe processes
  • 2.89 GB private memory
  • 2.91 GB working set

Breakdown at that point:

chrome-devtools: 12 processes, 1.22 GB private
shadcn:           8 processes, 0.68 GB private
reactbits:        8 processes, 0.49 GB private
tavily:           8 processes, 0.47 GB private
codex-node:       1 process,  0.02 GB private

This suggests the duplicate MCP pools are still being created after the old high-memory app-server tree is cleaned up.

Expected Behavior

Codex Desktop should not create unbounded duplicate MCP stdio process pools.

At most one active MCP process pool should exist per owning active session/thread, and MCP child processes should be reliably cleaned up when the owning session/thread/app-server lifecycle ends.

Actual Behavior

The Desktop app-server creates multiple full MCP stdio process pools. Those pools accumulate under the app-server and can grow to hundreds of node.exe processes and more than 10 GB of memory.

In my case, the leak/duplication was visible without any project dev server being involved. The repeated processes came from Codex MCP startup, not from application code.

Impact

This makes Codex Desktop risky to keep open for long sessions on Windows when several stdio MCP servers are configured. Memory growth can become large enough to degrade the machine and force users to manually kill processes or restart Codex.

Notes

This looks especially close to #28361 and #30753:

  • #28361 describes Windows app-server/MCP children not being reaped and accumulating to hundreds.
  • #30753 describes Windows Desktop creating duplicate MCP pools after RefreshMcpServers on a hidden thread.

The extra data point here is that I observed both behaviors in one session:

  1. a very large old app-server tree with around 170 duplicate node.exe descendants;
  2. automatic cleanup/restart reducing the count;
  3. new duplicate MCP pools appearing again afterward.

View original on GitHub ↗

7 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #30753
  • #30408

Powered by Codex Action

xxH7r · 1 month ago

Warning: a user posted codex_mcp_fix.zip in this thread. My browser/Windows security flagged it as malicious. Please do not download or run it. Maintainers may want to remove that comment/attachment.

mayank6091 · 1 month ago

@xxH7r This feels like one of those issues where process metrics alone won't explain the root cause. For long-running agent workflows, it's useful to capture an execution timeline that correlates MCP server lifecycle events, tool invocations, and process creation. That makes it much easier to identify whether duplicate pools are caused by orchestration retries, MCP reconnects, or process management itself.

ken-jo · 1 month ago

I reproduced the same process-pool accumulation on a newer Windows Codex Desktop build.

Environment

  • Codex Desktop package: OpenAI.Codex_26.707.3748.0_x64__2p2nqsd0c76g0
  • Bundled process: codex.exe app-server
  • App-server uptime at inspection: about 40 hours

Process tree before cleanup

The single long-lived app-server owned repeated, thread-shaped pools:

  • 29 × context-mode/start.mjs
  • 30 × bundled Sites mcp/server.mjs
  • 30 × Data Analytics mcp/server.cjs --stdio
  • 30 × node_repl.exe
  • 35 × codex-windows-sandbox-setup.exe observed globally

The three Node MCP groups were direct children of the same app-server PID. Their creation times appeared in repeated clusters from July 11 through July 13, consistent with new thread/task startup.

Memory snapshot:

  • 99 × node.exe globally: ~14.4 GB aggregate working set
  • 89 of those Node processes were direct app-server children
  • context-mode/start.mjs alone: ~8.7 GB aggregate working set, ~2.45 GB private working set, ~4.57 GB private commit
  • app-server codex.exe: ~1.27 GB working set

This was process-count accumulation rather than a clearly demonstrated single-process heap leak: process age did not positively correlate with private working set.

Disk activity

Before cleanup, Task Manager showed sustained disk writes around 50 MB/s. Local Codex state at the time was:

  • ~/.codex: 10.24 GiB total
  • 6.55 GiB of files modified within the previous hour
  • logs_2.sqlite: 3.53 GiB
  • context-mode storage: 3.20 GiB
  • sessions: 2.08 GiB

Cleanup result

I retained the four newest active MCP/REPL pools and terminated 130 older managed child processes, then removed 22 accumulated sandbox-setup helpers.

Immediately afterward:

  • global node.exe count: 99 → 29
  • aggregate Node working set: ~14.4 GB → ~5.28 GB
  • free physical RAM: 31.2 GB → 33.4 GB
  • disk write counter snapshot: ~50 MB/s observed before cleanup → 0.0 MB/s after cleanup

The remaining global Node count included unrelated local development processes, so the reduction is not based on killing all Node processes indiscriminately.

This strongly matches the per-thread MCP pool retention described here. It also indicates that the problem remains reproducible on Windows package 26.707.3748.0, after the earlier explicit MCP shutdown work in #19753.

wherewolf87 · 1 month ago

Reproduced on the newer Windows package 26.707.8479.0. I filed #32797 with a sanitized current-version process snapshot.

Key measurements from one Codex Desktop app-server tree:

  • 193 total descendants
  • 83 node.exe
  • 3 node_repl.exe
  • 54 cmd.exe launcher wrappers
  • Node family: 7.742 GiB working set and 6.837 GiB private memory
  • three creation batches of 26, 28, and 32 Node-family processes within about three and a half minutes of app-server startup

The processes were attributed through the full parent chain to codex.exe app-server. Seven Claude Code-owned Node processes and 19 already-orphaned/unattributable Node processes were explicitly excluded. This confirms current-build recurrence of Codex-owned MCP/Node process-batch accumulation and is not an artifact of counting every machine-wide Node process. Process evidence alone does not identify the internal manager, refresh, or session event that owned each batch.

Follow-up from the same app-server at 2026-07-13T09:50:42.6070046-04:00: the retained tree had grown to 147 node.exe, 5 node_repl.exe, 94 cmd.exe, 13.902 GiB Node-family working set, and 12.084 GiB private memory. Two additional batches of 33 Node-family processes each began at 09:42 and 09:43. Their specific internal trigger remains unverified.

lixunqi12 · 1 month ago

Still reproduces on 26.707.9981 and, after today's auto-update, on 26.715.3651 (Windows 11 Pro, Store package OpenAI.Codex_26.715.3651.0_x64__2p2nqsd0c76g0). Adding some evidence that narrows the root cause to the client side.

Setup

6 stdio MCP servers in ~/.codex/config.toml (npx-based: @mskalski/lightroom-mcp, @playwright/mcp; uvx-based: paper-search-mcp, photoshop-mcp-server; one plain node script; bundled node_repl), 3–4 conversations open in the Desktop app.

Measured behavior

  • The app-server re-spawns the full set of configured servers roughly every 60–90 s per conversation (≈ one new set every ~15 s with 4 conversations). 90-second sample: +20 processes. After ~5 h unattended: 172 node.exe + 104 python/uv/uvx processes, ≈ 11 GB working set, 877 total system processes.
  • Abandoned sets are never killed and their pipe handles are never closed: the orphaned cmd → npx → node wrapper chains stay alive indefinitely, i.e. the stdio servers never receive stdin EOF, so a spec-compliant server cannot exit on its own.
  • uvx-based (python) servers accumulate identically to npx-based ones, so it is not npm-specific.

The servers themselves are healthy

Manual stdio probe (spawn the exact configured command, write an MCP initialize request, time the response):

  • @mskalski/lightroom-mcp: valid initialize response in 1.27 s — even with its backend app (Lightroom) closed
  • local plain-node server: valid response in 0.58 s
  • npx package resolution itself: ~1 s (warm cache), measured from wrapper spawn → server process spawn timestamps

So the app-server discards connections to servers that complete a valid handshake within ~1 s, respawns the whole pool, and leaks the old one.

Control group

Same machine, same server binaries/configs attached to Claude Code: exactly one set per session, stable for hours, zero accumulation. This rules out the servers, npx/uvx, and the OS — the lifecycle management difference is in the Codex client.

Workaround

Scheduled reaper task (every 10 min). Reliable kill criterion under the bug: any same-command MCP process owned by the app-server older than ~10 minutes is necessarily abandoned, because the respawn loop keeps replacing the live set — survivors automatically equal (open conversations) × (one set). Plus unconditional reaping of processes whose parent chain is dead (with PID-reuse check: parent creation time later than child ⇒ real parent is gone).

Happy to provide the probe script or raw process listings if useful.

abangkis · 15 days ago

Independent reproduction on Windows build 26.803.10989.0

I can reproduce this on a newer Windows Codex Desktop build, using two native Rust stdio MCP servers without npm/cmd wrapper chains.

Environment
  • Windows 11 x64
  • Codex Desktop package: OpenAI.Codex_26.803.10989.0_x64
  • Codex CLI: 0.147.0-alpha.6.6
  • One long-lived Desktop app-server: codex.exe app-server --analytics-default-enabled
  • Project configuration registers two Rust stdio MCP servers:
  • one read-only proxy
  • one human-gated registration server
Measured process tree

Snapshot captured on 2026-08-13 at 12:09:53 UTC+07:00:

  • One app-server held 104 direct child processes.
  • The two custom MCP servers had accumulated into 22 live processes:
  • 11 × mcp-proxy
  • 11 × registration-mcp
  • They were created in matching timestamp pairs across multiple waves.
  • Every retained process still had the same live app-server as its direct parent; these were not Windows orphans.
  • The complete set of direct app-server children included:
  • 52 × node.exe MCP processes
  • 26 × node_repl.exe
  • 22 × the custom Rust MCP processes
  • 4 other app-server helper processes

Aggregate resource use of all 104 direct children:

  • approximately 2.8 GB working set
  • approximately 2.8 GB private memory
  • 19,470 handles

The 22 Rust MCP processes alone used:

  • 137.5 MB working set
  • 20.8 MB private memory
  • 2,551 handles
  • zero CPU delta during a three-second observation
  • no TCP connections; they were blocked waiting for stdio input
Useful lifecycle evidence

Both Rust servers use a bounded read_line() loop and return normally as soon as stdin reaches EOF:

Earlier generations nevertheless remain alive indefinitely. This strongly suggests that the app-server retains their stdio writer/transport instead of closing the previous generation.

The processes are also not children of the supervised application. Shutting down that application correctly leaves these Codex-owned MCP processes untouched, confirming that their lifetime is controlled by the Desktop app-server.

Expected behavior

When an MCP connection, thread, agent, or replacement generation is no longer active, Codex should close its stdio transport and wait for or terminate that specific child. Repeated generations should not accumulate beneath one app-server.

This appears consistent with #30408, but adds:

  • reproduction on the newer Windows package 26.803.10989.0;
  • native single-process Rust MCP servers without npm/cmd wrappers;
  • direct evidence that EOF-based shutdown is implemented but never triggered; and
  • measured handle growth in addition to process and memory counts.