MCP App webviews leak Codex Renderer processes on thread reopen (203 widgets → +209/+216 renderers)
Summary
Codex Desktop appears to create an Electron renderer/webview for each hydrated MCP App widget in a thread, and those renderer processes are not reclaimed when the user switches away and reopens the thread.
In one reproducible local thread:
- The rollout contained 203 Atlassian MCP calls with an MCP App resource URI.
- Opening the thread created renderer batches of 161 + 48 = 209 processes.
- Reopening the same thread later created another 161 + 55 = 216 renderer processes.
- The previous renderer processes remained alive.
- A previous app run accumulated more than 1,600
Codex (Renderer)processes. - After a fresh app start, 548 renderer processes had accumulated in 1h 41m.
The created processes are direct children of the Codex desktop main process and run as:
Codex (Renderer) --type=renderer
This appears distinct from the stdio MCP server child-process leak. The leaking processes here are Electron/Chromium renderers associated with MCP App webviews.
Environment
- Codex Desktop:
26.707.51957(build5175) - Bundle ID:
com.openai.codex - Bundled CLI:
codex-cli 0.144.0-alpha.4 - macOS:
26.5 (25F71) - Architecture: Apple Silicon,
arm64
Thread characteristics
- Rollout size: 53,784,301 bytes
- JSONL lines: 2,736
- MCP tool-call completion events: 320
- MCP calls with Atlassian widget resource URIs: 203
- MCP calls in the most recent five turns: 287
- Largest recent turn: 328 items
- Several individual persisted events were 500 KB to 1.1 MB
The 328-item turn is below the desktop client's per-turn item hydration limit, so it is loaded in full.
Reproduction
- Create or open a conversation whose recent turns contain approximately 200 completed MCP App calls with
mcpAppResourceUrivalues. - Switch to the conversation in Codex Desktop.
- Count direct child renderer processes of the Codex main process.
- Switch to another conversation.
- Reopen the widget-heavy conversation.
- Count the renderer processes again.
Observed renderer creation batches:
First open: 161 + 48 = 209
Second open: 161 + 55 = 216
The earlier processes remained alive. The number created per open closely matches the 203 MCP App widgets in the thread.
All 548 renderer children in the later sample were still in the sleeping state. Aggregate RSS is not reported here because Chromium processes share memory, but the busiest renderer alone was approximately 1.5 GB RSS and 28% CPU in that sample.
Desktop bundle observations
Inspection of the installed app.asar shows that the desktop client:
- Resumes with the most recent five turns requested as
itemsView: "full". - On the durable-history path, requests turns as
notLoadedand then eagerly callsthread/items/listfor each recent turn. - Hydrates up to 500 items per turn with five turns processed concurrently.
- Retains the full MCP invocation arguments, result, and
mcpAppResourceUriin renderer state. - Uses
document.createElement("webview")in the MCP App sandbox rendering path.
This means visually collapsed tool cards can still carry the full payload and instantiate widget infrastructure.
Related public source path
The app-server supports Summary and NotLoaded turn views, but the desktop client requests full recent turns. The current app-server implementation also notes that thread/turns/list still replays the entire rollout on every request until turn metadata is indexed separately:
PR #30188 adds groundwork for paginated histories and later SQLite materialization, but this thread is a legacy rollout and the renderer/webview lifecycle issue remains observable in the current desktop build.
Expected behavior
- Collapsed or offscreen MCP tool cards should not instantiate a webview.
- Only an explicitly expanded, visible MCP App should have a live webview.
- Switching away from a thread should destroy its MCP App webviews and reclaim renderer processes.
- Renderer process count should return close to baseline after closing or switching away from a widget-heavy thread.
- Large result payloads should be fetched on demand rather than eagerly copied into renderer state.
- Reopening the same conversation repeatedly should not increase process count.
Suggested regression test
- Build a fixture containing 200 completed MCP App items in one recent turn.
- Open and close the thread ten times.
- Assert that live MCP webviews remain bounded, ideally one active webview.
- Assert that renderer process count returns to baseline after unmount.
- Repeat with 50 MB and 250 MB legacy rollout fixtures.
- Track hydrated bytes, live webview count, destroyed webview count, and resume latency.
Related issues
- #12709
- #18693
- #20435
- #25390
Those reports cover long-thread and renderer slowdown symptoms. This report adds a repeatable near one-to-one correlation between MCP App widget count and newly leaked renderer processes on every thread reopen.