fix: kill orphaned MCP child processes and expose OPENCODE_PID on shu…#15516
fix: kill orphaned MCP child processes and expose OPENCODE_PID on shu…#15516nexxeln merged 1 commit intoanomalyco:devfrom
Conversation
|
The following comment was made by an LLM, it may be inaccurate: Based on my search, I found the following potentially related PRs: Related PRs (not duplicates)
These PRs address related issues with process cleanup and MCP server management, but none appear to be duplicates of PR #15516. The current PR is uniquely focused on:
No duplicate PRs found |
64e0d60 to
0cfcf0e
Compare
|
One more note about this one, the reason I didn't add OPENCODE_SESSION_ID to be exported is because the point specifically where MCP services are initiated is BEFORE a session has been initiated. Setting/exporting SessionID for use with the MCP services would be a major overhaul compared to how it's done today, and not add much to any value in my opinion. I can see the point of setting OPENCODE_SESSION_ID as an environment variable once a session is established so that it can be used in the session or for agents or other use cases, but that is solving a different problem and different area of the code than this PR, so to keep it clean this was focused on the MCP services. |
Issue for this PR
Closes #6633
Related: #7261, #14237, #6279, #15117
Type of change
What does this PR do?
When opencode exits, MCP servers that spawn child processes (e.g. chrome-devtools-mcp spawning Chrome) leave those grandchild processes orphaned. The MCP SDK's
StdioClientTransport.close()only signals the direct child — it has no way to reach processes further down the tree. These orphans accumulate across sessions and cause a 3-5 second shutdown delay because the SDK times out waiting on a server that is blocked waiting on its own children.Additionally, concurrent opencode sessions sharing the same MCP server config clash on shared state. With chrome-devtools-mcp for example, each new session tries to take over the same browser instance, killing the previous session's MCP service in the process.
Before calling
client.close(), we now walk the full descendant tree of each local MCP server transport usingpgrep -Pand SIGTERM every descendant. With its children already gone, the MCP server exits immediately and the SDK close completes without hitting its timeout. Skipped on Windows wherepgrepis not available.Also sets
OPENCODE_PIDinprocess.envat startup alongside the existingOPENCODE=1. MCP server configs can reference{env:OPENCODE_PID}to keep each session's MCP services fully independent — each session gets its own isolated browser instance and they no longer interfere with each other.Example config:
Changes:
mcp/index.ts:descendants()walks the process tree viapgrep -PBFS; dispose handler SIGTERMs all descendants before SDK closeindex.ts,e2e-local.ts: setprocess.env.OPENCODE_PID = String(process.pid)How did you verify your code works?
Started opencode with chrome-devtools-mcp configured with
--userDataDir={env:HOME}/.cache/chrome-devtools-mcp/{env:OPENCODE_PID}. Confirmed Chrome launched. Exited opencode — exit was immediate (previously 3-5 seconds).pgrep -fa chrome-devtools-mcpreturned nothing. Ran two concurrent sessions and confirmed each used an isolated profile directory with no SingletonLock conflicts.Screenshots / recordings
N/A — terminal process, no UI changes.
Checklist