Skip to content

service: MCP subprocesses are not reaped when a client disconnects #52020

Closed as not planned

Description

@z291173301

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; opencode shim at C:\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:

  1. Start the desktop app; it starts the background service (main.log: v2 CLI background service ready { port: '49374' }).
  2. 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 s and +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
  3. The two sets correspond to the two Electron client processes holding connections to the service port. Client-side Get-NetTCPConnection -RemotePort 49374 -State Established shows PID 27008 (OpenCode.exe main, 2 sockets) and PID 24172 (OpenCode.exe --type=utility, 1 socket). The service is PID 24836 on 127.0.0.1:49374.
  4. Now churn the client connections — let the --type=utility process be respawned, reopen windows, or lose and regain connectivity. Each new client process gets a fresh fleet; the previous fleet is not reaped.
  5. After several desktop restarts against a service that was never restarted, the service accumulates 28 MCP children totalling ~1732 MB, with everything-search.exe and markitdown-mcp.exe at 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.
  6. opencode service restart drops 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

  1. z291173301 commented on Sep 29, 2026

    @z291173301
    Author

    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.

  2. github-actions commented on Sep 29, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • Please use the Bug Report issue template and include its required Description field. The current report has a detailed Summary, but no Description field.

    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.

  3. github-actions commented on Sep 29, 2026

    @github-actions
    Contributor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions