[Windows Desktop 26.810.41047] Local STDIO MCP process groups accumulate and consume ~7 GB RAM

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

Title: [Windows Desktop 26.810.41047] Local STDIO MCP process groups accumulate and consume ~7 GB RAM

Environment:

  • Windows
  • ChatGPT/Codex desktop app 26.810.41047
  • Local STDIO MCP servers: Playwright, todo-query, node_repl

Actual behavior:
After starting/resuming multiple Codex threads, local MCP process groups accumulate and are not reclaimed when most threads become idle or unloaded.

Observed:

  • 26 identical MCP process groups
  • 183 processes total
  • Approximately 7 GB RAM and 1,200 threads
  • Each group contains:
  • 1 node_repl.exe
  • 2 Python processes for todo-query
  • 2 cmd.exe and 2 node.exe processes for Playwright MCP
  • The app listed only 1 active and approximately 4 idle threads at the time
  • System became noticeably unresponsive

Logs show each thread/start initializing a new MCP manager and launching the configured STDIO servers. Some temporary groups exit gracefully, but many remain attached to the main Codex process.

Expected behavior:
MCP processes belonging to closed, unloaded, or expired thread runtimes should be terminated or reused instead of accumulating.

Workaround:
Restarting the app or disabling local MCP servers releases/prevents the accumulated processes.

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 14 days ago

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

  • #38247
  • #37426
  • #37870

Powered by Codex Action

yzh522163-prog · 11 days ago

Additional reproduction on a newer Windows Desktop build, with a full process-tree breakdown and before/after mitigation.

Environment

  • Codex Desktop: 26.810.7004.0 x64
  • app-server: codex-cli 0.148.0-alpha.9
  • Windows 11
  • Global STDIO MCP servers enabled at the time:
  • bundled node_repl
  • unityMCP
  • comfyui
  • jcodemunch
  • A fifth optional MCP was configured but did not have a persistent process.
  • No project-scoped MCP override and no duplicate config merge were found.

Observed process fan-out

Although only two tasks were intentionally active, the app-server retained/restored approximately 29 execution contexts.

Each retained context started this process set:

| Server | Processes per context |
|---|---:|
| node_repl | 1 |
| unityMCP | 1 Node process |
| comfyui | 1 Node process |
| jcodemunch | 5-process chain (uvx -> uv -> python -> jcodemunch-mcp -> python) |
| Total | 8 |

A process-tree snapshot showed:

  • 29 copies of each logical STDIO MCP group
  • 237 descendants of the single current codex.exe app-server
  • approximately 7.14 GB working set / 7.28 GB private memory
  • approximately 2,100 threads
  • approximately 42,848 handles

At the system level this coincided with:

  • 639 processes
  • 12,331 threads
  • 276,432 handles
  • approximately 115,000 context switches/sec
  • severe system-wide lag

The arithmetic was consistent with the tree: 29 retained contexts * 8 MCP processes = 232, plus a few app/tool helper processes.

These were not leftovers from an older app-server instance. They were descendants of the current app-server and had been started after the current Desktop launch. Some MCP groups remained alive for roughly 68–97 minutes. Counts occasionally dropped as individual contexts were eventually cleaned up, which looks like delayed/partial cleanup rather than permanent OS orphans.

Mitigation and confirming evidence

I changed the four task-specific STDIO MCP servers to enabled = false, kept only the bundled node_repl enabled, and fully restarted Codex Desktop.

After restart:

  • 16 Codex descendants total
  • 13 node_repl.exe processes, stable across repeated samples
  • 0 unityMCP processes
  • 0 comfyui processes
  • 0 jcodemunch / uv / uvx / Python processes
  • all node_repl instances together: about 133 MB working set / 44 MB private memory
  • system process count dropped from 639 to approximately 371

This strongly suggests that per-context STDIO startup is multiplying the cost of every globally enabled server. Disabling the heavy servers removes the severe resource impact, but it does not eliminate the underlying retention: 13 node_repl workers still remained while only a small number of tasks were active.

Expected behavior

  • STDIO MCP process trees should be terminated when their owning task/runtime becomes idle, unloaded, closed, or otherwise disposable.
  • Restored historical tasks should not eagerly start every globally enabled STDIO MCP unless the runtime is actually active.
  • Alternatively, eligible MCP servers should be pooled/shared or the number of retained tool runtimes should be bounded.
  • The UI-visible task count and the number of live tool/MCP runtimes should not diverge enough to create hundreds of background processes.

The configuration-only workaround is effective but removes tools globally. I can provide sanitized PowerShell process-tree collection logic if useful.

yzh522163-prog · 11 days ago

I traced the current main lifecycle for the Windows process accumulation reported here.

The remaining leak is at MCP runtime publication/shutdown boundaries:

  • McpRuntime::publish replaces the published McpConnectionSet, while bindings and prepared calls can still hold the previous set.
  • McpRuntime::shutdown only shut down the latest published set, so a superseded stdio generation could remain alive when an older binding was still in flight.
  • A lazy, dormant connection must not be shut down by awaiting its client: that would start the process during cleanup.

I prepared a focused local patch that:

  1. Identifies connections removed by a refresh but not shared by the successor.
  2. Retires each old connection after its last Arc lease is released, preserving in-flight call semantics.
  3. Tracks retired generations weakly and force-shuts all live retired generations plus the current generation during final session shutdown; cleanup is kept alive if the initiating caller is cancelled.
  4. Cancels dormant lazy startup without polling/starting the child.
  5. Adds regressions for twenty repeated pending-server refreshes and for final session shutdown while an external in-flight call still holds the superseded stdio connection.

This is ownership-based cleanup; it does not scan or kill processes by name/PID. Formatting and diff checks pass locally. Full Rust compilation is currently blocked on my Windows checkout because link.exe/MSVC Build Tools are not installed, so I am not claiming the test suite passed.

The repository's contributing guide says external PRs require maintainer invitation. If this direction matches the intended fix, I can provide the focused branch/PR after an invitation.