stdio MCP servers accumulate under a live app-server on 26.810.52044, after the #18881 / #19753 shutdown fix
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- PR merge metrics
- PR metrics pending
Description
Filing separately from the closed #18881 because the shutdown path that PR #19753
fixed now works on this build, while accumulation during a live session does
not. Cross-referencing #12491 (open, GUI), #20349, #25015, #26984.
Environment
| Codex (ChatGPT.app) | 26.810.52044, bundle 6662 |
| codex-cli | 0.148.0-alpha.9 |
| macOS | 26.6.1 (25G76), arm64 |
| Surface | Codex desktop app, codex ... app-server |
Two stdio MCP servers configured in ~/.codex/config.toml, both launched via a
wrapper script that execs (so the wrapper leaves no process of its own):
[mcp_servers.fin]
command = "/Users/<user>/.ai_ops/bin/mcp-launch.sh"
args = ["fin"] # execs a python stdio server
[mcp_servers.email]
command = "/Users/<user>/.ai_ops/bin/mcp-launch.sh"
args = ["email"] # execs `npx -y @codefuturist/email-mcp stdio`
What happens
A long-lived app-server spawns a fresh stdio MCP server roughly every three
minutes and never closes the previous one. The old servers stay parented to the
app-server with their stdio pipes still held open (lsof shows fds 0/1/2 as
PIPE), at 0.0% CPU and ~1s of accumulated CPU time. They are idle but retained.
This is the same shape as #18881, but that issue was closed by PR #19753
(merged 2026-04-28) and this build postdates it.
What PR #19753 did fix, verified here
Shutdown draining works. When the app-server exits, its MCP children go with it.
Observed directly: app-server PID 43065 was restarted, and all ~33 of its
accumulated MCP children terminated along with it, requiring no manual cleanup.
So this is not a regression of the shutdown path. It is the in-session path,
where servers are replaced but the superseded ones are never shut down.
Measurements
Two independent windows, one app-server each, machine otherwise idle:
| Rate | ~1 new stdio server per 3 minutes, sustained |
| Single app-server (PID 14353), 71 min uptime | 24 fin_mcp children |
| Across both configured servers, ~1 hour after a manual clear from 11 processes | 137 processes |
| RSS at that point | ~5.4 GB |
| Free system memory at that point | 141 MB |
| After killing the superseded children | 4480 MB free |
Reproduced twice, hours apart, across an app-server restart in between.
Reproduction
- Configure one or more stdio MCP servers in
~/.codex/config.toml. - Start the Codex desktop app and leave a project session open.
- Watch the children of the
app-serverprocess:
APPSRV=$(pgrep -f "Resources/codex .*app-server" | head -1)
watch -n 30 "pgrep -P $APPSRV -f mcp | wc -l"
The count climbs monotonically and never decreases while the app-server lives.
Expected
When a stdio MCP server is replaced, the superseded one is shut down and its
process reaped, so the count tracks the number of configured servers rather than
session age.
Notes
Each server is cheap alone (~28 to 90 MB here) and the fault is only visible over
hours. With two servers configured it took roughly one hour to consume several GB
and drive the machine to 141 MB free. #12491 reports the same end state at much
larger scale (1319 processes, 37 GB), which suggests the ceiling is however long
the app stays open.
The three fix directions proposed in #12491 (process groups, startup reaping of
stale trees, heartbeat self-termination) would each cover this case too. The
narrowest fix specific to what is seen here is shutting down the superseded
server at the point of replacement, rather than only at session shutdown, which
is what #19753 addressed.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the app-server's stdio MCP server replacement and lifecycle handling, then compare the in-session path with the shutdown behavior fixed by PR #19753. Reproduce with configured stdio servers and monitor app-server children as described; done means superseded servers are shut down and reaped so the process count tracks configured servers during a live session.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100