Repository navigation
service: MCP subprocesses are not reaped when a client disconnects #52020
Description
Summary
MCP subprocess fleets created for a client are never shut down when that client disconnects. A long-lived background service that outlives several desktop app restarts accumulates orphaned MCP processes (observed up to 28 processes / ~1.7 GB instead of the steady-state 19 / ~1.2 GB), and nothing reclaims them.
Environment
- opencode version: opencode v2.0.19
- OS: Windows 11 25H2 (10.0.26200), win32 x64
- Terminal: Windows Terminal,
TERM=xterm-256color - Shell: PowerShell 7.6.6
- Install/channel: desktop app present alongside CLI;
opencodeshim atC:\Users\...\AppData\Roaming\npm\opencode.cmd - Active plugins:
D:/@Data/MCP Server/opencode-dcp/node_modules/@tarquinen/opencode-dcp
Reproduction
Not deterministic on a fresh service. It requires a service that outlives its clients plus connection churn. Observed state was:
- Start the desktop app; it starts the background service (
main.log:v2 CLI background service ready { port: '49374' }). - The service spawns two complete sets of all 9 configured local MCP servers within 0.4 s of startup — even though there is only one service process. Measured creation offsets from service start were
+3.5 sand+3.7 s:- set 1: github 21096, embyfin 22640, rapidocr 22424, playwright 24496, filesystem 26812, everything 16440, markitdown 1940, context7 27352, docs 9664
- set 2: github 26080, embyfin 4420, rapidocr 23556, playwright 9592, filesystem 3720, everything 5376, markitdown 21636, context7 25536, docs 24308
- The two sets correspond to the two Electron client processes holding connections to the service port. Client-side
Get-NetTCPConnection -RemotePort 49374 -State Establishedshows PID 27008 (OpenCode.exemain, 2 sockets) and PID 24172 (OpenCode.exe --type=utility, 1 socket). The service is PID 24836 on127.0.0.1:49374. - Now churn the client connections — let the
--type=utilityprocess be respawned, reopen windows, or lose and regain connectivity. Each new client process gets a fresh fleet; the previous fleet is not reaped. - After several desktop restarts against a service that was never restarted, the service accumulates 28 MCP children totalling ~1732 MB, with
everything-search.exeandmarkitdown-mcp.exeat 6 copies each and the rest at 3. The orphans carry creation timestamps from an earlier boot of the desktop app (13:06:36) than the surviving clients. opencode service restartdrops this back to 19 children / ~1196 MB immediately.
Expected Behavior
When a client disconnects, or when its last connection closes, the MCP subprocesses spawned for that client should be terminated. Alternatively, fleets should be shared/reference-counted across clients, or reaped on an idle timeout. Currently no code path reclaims them, so the service's memory footprint grows monotonically with connection churn for as long as the service lives.
Actual Behavior
Orphaned MCP subprocesses persist for the entire lifetime of the background service. Steady state is 19 processes / ~1.2 GB, but a service that survives multiple desktop restarts reached 28 / ~1.7 GB with no upper bound observed.
Per-process working set at steady state (2 copies of each unless noted):
| MCP server | working set | notes |
|---|---|---|
| docs-server-mcp | 207 MB | heaviest, plus a short-lived 3rd worker that respawns (bounded) |
| playwright-mcp | 88 MB | |
| rapidocr-mcp | 73 MB | |
| context7-mcp | 70 MB | |
| filesystem-mcp | 38 MB | |
| embyfin-mcp | 22 MB | |
| github-mcp | 21 MB | |
| everything-mcp | 9 MB | |
| markitdown-mcp | 9 MB |
Additional Context
Isolation test — CLI calls are not the trigger. Running opencode mcp list three times plus opencode service status produced identical child-process counts before and after every call (total=19), and the count was still 19 after 25 s idle. Repeated CLI invocations reuse the existing service and do not add fleets.
Service discovery is a separate, intermittent problem. In the same session, opencode mcp list run from C:\Windows\System32 twice returned No MCP servers configured, and a third run from the same directory with OPENCODE_LOG_LEVEL=DEBUG returned all 11 servers as connected. ~/.config/opencode/service.json contains only {"password": "..."} with no port or pid, and opencode service get returns {}, so the port must be re-derived by the client. When discovery fails the CLI repeatedly spawns serve --service instances that bind nothing, have zero children, and exit on their own; if the existing service already holds the port, the spawn cannot become ready and the CLI fails with Error: Timed out waiting for the background service to start. I suspect this discovery race is what produces the connection churn that exposes the leak. Happy to file that separately if useful.
Environment notes: OPENCODE_DISABLE_PROJECT_CONFIG=1 is set. ~/.local/share/opencode/log/ is empty (no opencode.log), so no server-side lifecycle logs were available for forensics. Desktop logs are under %APPDATA%\ai.opencode.desktop\logs\<timestamp>\.
Activity
z291173301 commentedon Sep 29, 2026
Found a likely trigger for the churn that produces these orphans and filed it separately: #52021 (opencode mcp list intermittently fails with Timed out waiting for the background service to start, and repeated serve --service spawns that bind no port). Following that discovery race as the root cause of the unreaped fleets.
github-actions commentedon Sep 29, 2026
This issue doesn't fully meet our contributing guidelines.
What needs to be fixed:
Please edit this issue to address the above within 2 hours, or it will be automatically closed.
This issue might be a duplicate of existing issues. Please check:
If you believe this was flagged incorrectly, please let a maintainer know.
github-actions commentedon Sep 29, 2026
This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.
Feel free to open a new issue that follows our issue templates.