Repository navigation
MCP servers spawned 8-10x per instance on Windows, consuming ~3.5 GB RAM on startup #26336
Description
Description
On Windows, the OpenCode desktop app spawns 8-10 duplicate instances of every local MCP server immediately on startup, instead of one. This causes ~3.5 GB of MCP-related process memory at idle, with 82+ child processes under opencode-cli.exe.
Environment
- OpenCode version: 1.3.13 (desktop)
- OS: Windows 11
- RAM: 32 GB total
- Local MCPs configured: 9 enabled, 4 disabled
- System idle RAM: ~41% (12.8 GB used)
- OpenCode idle RAM: ~70% (21.8 GB used) — 9 GB attributed to OpenCode
Evidence
After a fresh restart with no sessions active, process tree under opencode-cli.exe:
MCP Server Instances Process Type
───────────────────── ───────── ────────────
zai-mcp-server 10x cmd.exe -> node.exe (via npx)
exa-search 9x cmd.exe -> node.exe (via npx)
replicant 9x cmd.exe -> node.exe (via npx)
chrome-mcp 9x node.exe
cdp-browser 9x node.exe
edge-tts 8x uvx.exe
windows-mcp 9x uvx.exe
flaui-mcp 9x dotnet.exe
playwright (DISABLED) 7x cmd.exe -> node.exe (via npx)
Total: 82 direct children, ~3465 MB RAM
Key observations:
- Disabled MCPs still spawn (
playwrightisenabled: falsebut spawns 7 instances) - Each MCP is spawned via a separate
cmd.exewrapper (fromnpx/uvx) - The multiplier varies between 8-10 but is consistently much greater than 1
Root Cause Analysis
Bug 1: descendants() is a no-op on Windows
In packages/opencode/src/mcp/index.ts, the cleanup function that kills child processes during instance disposal:
const descendants = Effect.fnUntraced(
function* (pid: number) {
if (process.platform === "win32") return [] as number[] // <- Returns empty on Windows
// Uses pgrep -P (Unix-only command)
const pids: number[] = []
// ...
},
)On Windows, this returns [], so the finalizer never kills descendant processes (cmd.exe, node.exe, uvx.exe, dotnet.exe). When instances are reloaded or disposed, old MCP processes are left as orphans. client.close() only closes the MCP protocol connection, not the OS processes.
Fix: Use taskkill /T /F /PID <pid> on Windows to kill the entire process tree, or implement Windows-native descendant enumeration via WMI (Win32_Process parent-child traversal).
Bug 2: ScopedCache race condition or multi-initialization
The MCP layer creates state via InstanceState.make() which uses Effect's ScopedCache, keyed by instance directory. The InstanceStore in packages/opencode/src/project/instance-store.ts has proper deduplication with deferred entries.
However, on the desktop app, the initial burst of connections (WebSocket, HTTP polling, UI initialization) appears to trigger multiple concurrent ScopedCache lookups before the first one completes, or the MCP layer is rebuilt multiple times during startup. This needs investigation in the Effect ScopedCache behavior under concurrent access or the desktop app's initialization sequence.
Bug 3: enabled: false MCPs are not respected
MCP servers with enabled: false in config are still spawned. The create() function checks mcp.enabled === false and returns DISABLED_RESULT, but the process tree shows disabled MCPs still spawning. This suggests the config check may be bypassed in certain initialization paths.
Impact
- Memory: ~3.5 GB wasted on duplicate MCP processes at idle
- Performance: 82+ background processes competing for CPU and I/O
- Startup time: Each duplicate MCP must be independently initialized via
npx/uvx/dotnet run - Stability: Orphan processes accumulate across restarts (due to Bug 1), progressively consuming more RAM
Activity
github-actions commentedon May 8, 2026
This issue might be a duplicate of existing issues. Please check:
descendants()no-op / orphan process cleanup problem described hereIf the core issue (the Windows
descendants()no-op and the over-spawning on startup) is already tracked in one of those, consider closing this as a duplicate or linking them. If this report adds new context (especially the disabled MCP still spawning and the 8–10x multiplier data), it may be worth keeping open separately.github-actions commentedon Jul 9, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.