Repository navigation
Description
OpenCode serve memory grows without bound: MCP server processes are never (or incorrectly) reclaimed
Summary
OpenCode serve retains a full set of MCP server child processes for every project directory that was ever touched, and never reclaims them. In one 2.5-hour session of normal use (one or two projects actively used at a time), the serve process tree reached about 9 GB RSS, about 8.3 GB of which was MCP server child processes.
Two defects behind the growth:
1. Locations are never evicted, so their MCP fleets stay resident. The location graph cache is built with idleTimeToLive: Duration.infinity, and the LocationActivity sweep that is supposed to reclaim idle locations rarely evicts in practice.
2. Eviction leaks processes. When a location is evicted, most of its MCP children are killed, but some survive as orphans (PPID 1) and live forever.
Restarting the server releases everything; memory climbs back as more directories are opened.
Environment
- macOS 26, Apple Silicon
- opencode v2.0.26 / v2.0.27
opencode servemanaged by the OpenChamber desktop app; the same setup applies to any long-running serve, e.g.opencode serve --service- Global
opencode.jsonwith 7 local stdio MCP servers:chrome-devtools-mcp,@playwright/mcp,@toolbox-sdk/server --prebuilt=mysql,codegraph,gitea-mcp,mcp-server-tapd, and an Open Design MCP server
With 7 configured MCP servers, one location's fleet is about 1 GB RSS (the @toolbox-sdk/server --prebuilt=mysql entry alone spawns 4 processes, about 550 MB). Visiting 8 directories accumulates about 8 GB.
Root cause
1. Location graphs never expire
packages/core/src/location-services.ts:56:
// Retain healthy graphs. Boot failures, not local filesystem probes,
// decide whether a location (including workspace placement) can retry.
{ idleTimeToLive: Duration.infinity },Reclaiming a location's graph depends entirely on the LocationActivity sweep (packages/core/src/location-activity.ts, default TTL 60 minutes, sweep interval 1 minute). The sweep's conditions make eviction rare:
- Only
SessionEvent.Durabletouches a location's activity timestamp. Booting a directory, connecting its MCP servers, and other work never counts as activity. - Before evicting, the sweep checks the location for active sessions (
execution.active); any active session re-touches the entry. - With
idleTimeToLive: Duration.infinity, there is no backstop: a location that slips past the sweep's conditions is cached forever.
The code comment at packages/web (OpenChamber side) confirms the cost model: "the first directory-scoped read boots that location's whole MCP fleet". Every directory a user merely opens pays a full fleet, and nothing takes it back.
2. Eviction leaks MCP child processes
packages/core/src/mcp/stdio.ts closes the transport through a scope finalizer that signals the child process (SIGTERM, then SIGKILL after a grace period). In practice, eviction killed most of the fleet but leaked at least one process: a codegraph serve --mcp --path <storymaker> child survived its location's eviction with PPID 1 and is still running. Its fleet siblings from the same boot were all terminated correctly.
Evidence
From one running serve, aligned against ~/.local/share/opencode/log/opencode.log (times in UTC):
- 14 distinct directories booted during the day. Each boot spawns that location's full MCP fleet (7 server kinds; mysql toolbox is 4 processes).
- 8 fleets remain attached to the running serve (about 80 child processes, 8.3 GB RSS), matching 6 still-cached locations, most of them idle for hours.
- Eviction does work sometimes:
/Users/jty/binwas evicted on the expected 60-minute cycle several times. Many other locations were never evicted at all. - Orphaned process after eviction:
codegraph serve --mcp --path /Users/jty/project/storymaker(pid 18938) was re-parented to PID 1 when the storymaker location was evicted at 03:30:21Z. Every other process from that fleet died as expected. - Separate issue observed in the same log: the Open Design MCP server exits by itself with code 0 while idle, and OpenCode immediately reconnects. The log accumulated 1,098
MCP server process exitedevents, a spawn loop at a rate of one exit every 1 to 3 minutes.
Impact
- Memory grows linearly with the number of directories visited, with no cap. Restarting serve is the only reliable way to release it.
- mysql toolbox is the heaviest single item: 4 processes and about 550 MB per fleet.
- MCP servers that carry credentials in argv (gitea-mcp in our config) keep their plaintext tokens visible in
psoutput for as long as the processes live. The longer the processes survive, the larger the exposure.
Expected behavior
- Idle locations get reclaimed: a finite
idleTimeToLivefor the location graph cache, or a broader activity definition inLocationActivity(no active sessions and no recent activity expires the location). Either way, an idle location's MCP fleet should shut down and can be re-spawned on the next request. - Eviction kills the whole tree: when a location closes, all of its MCP child processes, including grandchildren, are terminated.
- Optional: reconnect backoff for MCP servers that exit repeatedly, to stop the per-minute respawn loop.
Note for OpenChamber
OpenChamber manages only the opencode serve process itself; the MCP children are not in its registry, so it cannot reclaim them downstream. The fix belongs here. OpenChamber may separately want to surface MCP child memory usage and offer a manual cleanup control.
Activity
opencode-agent commentedon Oct 13, 2026
Thanks for the detailed analysis and measurements. I think this is a duplicate of #51003, which reports the same main problem: a long-running
opencode serve(OpenChamber in that report too) starts a full set of global stdio MCP servers for every directory it loads, and never releases them, so memory grows until the host runs out.Possible duplicates and closely related issues, ordered from most to least likely:
codegraphprocess that survived eviction.The last point in your report, where the Open Design server exits with code 0 while idle and is respawned every 1–3 minutes, doesn't seem to be covered by an existing issue. A separate issue for it (ideally with the server config and a log excerpt) would be welcome.
The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different. Adding your v2.0.26/2.0.27 data to #51003 would also be useful there.
opencode-agent commentedon Oct 14, 2026
Closing this as a duplicate of #51003, since there's been no reply saying how this differs. That issue covers the same problem: a long-running
opencode servestarts a full set of global stdio MCP servers for every directory it loads, never releases them, and memory grows until the host runs out. PR #53046 is open against it. Your v2.0.26/2.0.27 measurements would be useful there.For the other parts of your report:
codegraphprocess left orphaned after eviction is tracked in mcp: orphaned local MCP server processes accumulate after sessions end #50363 and Local MCP disconnect leaves descendant processes running #46253.If this turns out to be a different problem, reply here and it can be reopened.