You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A single long-lived opencode serve host accumulates one context-mode MCP child process per session/subagent it opens. Children are never reaped while the host stays alive. On this machine, a single opencode host (uptime 1d 18h) has 26 idle context-mode children, all parented to the same opencode PID, totalling ~1.6 GB RSS.
This is a new class of leak, distinct from #103 (orphan-on-host-death), #311 (npm-exec wrapper reparent), #534 (nested pi --help), #559 (upgrade-time leftover), and #562 (Pi subagent DB lock). None of those code paths fires here — the parent is alive, the chain is direct (no npm-exec wrapper), and sibling discovery is hard-coded to Claude plugin paths.
Observed (live snapshot)
26 context-mode children, single opencode parent (PID 23986), ~1.6 GB total RSS
SIGTERM / SIGINT / SIGHUP: only on opencode shutdown.
src/util/sibling-mcp.ts (#559) also misses this. Its POSIX discovery regex is hard-coded to Claude plugin paths:
node.*plugins/(cache|marketplace
OpenCode's child argv is node /home/USER/.npm-global/bin/context-mode. Discovery never matches → /ctx-upgrade from inside opencode would not reap them either. And the killer only fires on upgrade, not at MCP startup.
Run discovery at MCP server startup (not only on /ctx-upgrade).
Kill siblings where ppid === own ppid AND argv matches AND pid !== own pid AND age > N s with no recent activity.
Add a context-mode prune CLI subcommand for manual cleanup.
Composes with COA-1: idle timer handles long-running accumulation, startup sweep handles the case where a session reconnects to a host that already has 25 stale siblings.
COA-5 — North star: port ctx_* tools into the TS plugin, drop the mcp block on opencode
OpenClaw already does this via src/adapters/openclaw/mcp-tools.ts (mcp-bridge pattern). The TS plugin runs in-process and could expose tool registration the same way, eliminating the stdio child entirely on opencode/KiloCode. Largest engineering investment but the cleanest structural fix for any ts-plugin paradigm host.
Immediate user mitigation (no code change)
# Reap idle context-mode MCP children of the running opencode serve
opencode_pid=$(pgrep -fx '.*\.opencode serve.*'| head -1 || pgrep -f 'opencode.*serve'| head -1)
pgrep -P "$opencode_pid" -f 'context-mode'| xargs -r kill -TERM
Suggested fix order
Ship COA-1 + COA-2 as a v1.0.x bugfix (small, mechanical, compose). Track COA-5 as the structural follow-up.
Template fields
Platform: OpenCode
context-mode version: 1.0.131 (latest)
Exact prompt that triggered the bug: N/A — bug is a process-lifecycle accumulation observed over normal multi-session use of opencode over ~2 days, not triggered by any specific prompt. Reproduction is "run opencode for hours, open multiple sessions, observe ps --ppid <opencode-pid>".
Steps to reproduce:
Install: npm install -g context-mode@1.0.131 (or any version ≥ 1.0.19).
Configure opencode with the shipped configs/opencode/opencode.json (mcp.context-mode.type=local + plugin: ["context-mode"]).
Run opencode serve (or opencode interactively) and use it normally across multiple sessions and subagent tasks over a working day.
mcp.type=local causes one stdio child per opencode MCP client (one per session).
Lifecycle guard's ppid + grandparent + stdin-EOF paths all do not fire while opencode stays alive.
sibling-mcp.ts regex is hard-coded to Claude plugin paths; misses the bin/context-mode argv used here.
Verified empirically with pgrep -P and a ps --ppid snapshot (above): 26 children, 1.6 GB RSS, single opencode parent, oldest 1d 18h.
Verified /ctx-upgrade would not help because (a) sibling regex misses these argvs and (b) upgrade is the wrong trigger anyway.
Pre-submission checklist: All four items satisfied (latest version, searched existing issues, repro steps provided, debug script run).
Operating System: Linux (Ubuntu/Debian-class, kernel 6.x)
JS Runtime: node v24.14.1 (mise-managed); bun 1.3.10 available for the opencode host itself.
Debug script output (scripts/ctx-debug.sh)
context-mode diagnostic v2.0.0
─────────────────────────────
25 passed, 4 failed, 1 warnings
Failed (local dev tree only — not relevant to this bug):
✗ build/ directory exists
✗ better-sqlite3 .node binary exists
✗ require('better-sqlite3') succeeds
✗ FTS5 in-memory test
The four failures are because the script was run from the development repo before npm run build. The installed npm-global v1.0.131 used by opencode is fully functional (it's actively running 26 healthy MCP processes that respond to JSON-RPC); these failures only reflect the absence of built artifacts in the local repo and are unrelated to the lifecycle leak described above.
Key fields from the JSON output:
Installed version: 1.0.131 (latest)
OS: Linux
Bash: 5.2.21(1)-release
Node.js: v24.14.1 at /home/USER/.local/share/mise/installs/node/24.14.1/bin/node
Summary
A single long-lived
opencode servehost accumulates onecontext-modeMCP child process per session/subagent it opens. Children are never reaped while the host stays alive. On this machine, a single opencode host (uptime 1d 18h) has 26 idlecontext-modechildren, all parented to the same opencode PID, totalling ~1.6 GB RSS.This is a new class of leak, distinct from #103 (orphan-on-host-death), #311 (npm-exec wrapper reparent), #534 (nested
pi --help), #559 (upgrade-time leftover), and #562 (Pi subagent DB lock). None of those code paths fires here — the parent is alive, the chain is direct (no npm-exec wrapper), and sibling discovery is hard-coded to Claude plugin paths.Observed (live snapshot)
26
context-modechildren, single opencode parent (PID 23986), ~1.6 GB total RSS.opencode serveprocess)Expected
Idle MCP children should not accumulate for the lifetime of a long-running host. One of:
Root cause analysis
Why none of the existing lifecycle paths fire on opencode
OpenCode integrates context-mode through two surfaces simultaneously (per shipped
configs/opencode/opencode.json):{ "mcp": { "context-mode": { "type": "local", "command": ["context-mode"] } }, "plugin": ["context-mode"] }mcppluginbuild/adapters/opencode/plugin.js)OpenCode opens a fresh MCP client per session / subagent task. context-mode's only shutdown paths in
src/lifecycle.ts:npm execwrapper case. Chain here is direct (.opencode serve → node bin/context-mode), no wrapper.src/util/sibling-mcp.ts(#559) also misses this. Its POSIX discovery regex is hard-coded to Claude plugin paths:OpenCode's child argv is
node /home/USER/.npm-global/bin/context-mode. Discovery never matches →/ctx-upgradefrom inside opencode would not reap them either. And the killer only fires on upgrade, not at MCP startup.Why this is not a duplicate of #103, #311, #366, #471, #534, #559, or #562
npm execwrapper meant ppid never changed. Added grandparent-reparent check. opencode does not use an npm-exec wrapper.--help): short-lived nested Pi commands orphaning a server at 100% CPU. Argv-based prevention shipped inpiExtension. Doesn't apply (host is opencode, processes are idle, not 100% CPU)./ctx-upgrade. Sibling discovery is Claude-paths-only and only fires on upgrade.No existing issue covers the "host-alive, MCP children accumulate per session, indefinitely" pattern.
Proposed course of action
COA-1 — Idle self-shutdown timer in
src/lifecycle.ts(recommended first)CONTEXT_MODE_IDLE_TIMEOUT_MS(default e.g. 15 min), callgracefulShutdown().process.stdin.isTTYis true (interactive dev) and when env is0.COA-2 — Generalize
sibling-mcp.tsdiscovery + run at server startupToday's POSIX regex misses
npm-global/bin/context-modeandbun ... server.bundle.mjsshapes. Generalize to also match:Then:
/ctx-upgrade).ppid === own ppidAND argv matches ANDpid !== own pidAND age > N s with no recent activity.context-mode pruneCLI subcommand for manual cleanup.Composes with COA-1: idle timer handles long-running accumulation, startup sweep handles the case where a session reconnects to a host that already has 25 stale siblings.
COA-5 — North star: port
ctx_*tools into the TS plugin, drop themcpblock on opencodeOpenClaw already does this via
src/adapters/openclaw/mcp-tools.ts(mcp-bridge pattern). The TS plugin runs in-process and could expose tool registration the same way, eliminating the stdio child entirely on opencode/KiloCode. Largest engineering investment but the cleanest structural fix for anyts-pluginparadigm host.Immediate user mitigation (no code change)
Suggested fix order
Ship COA-1 + COA-2 as a v1.0.x bugfix (small, mechanical, compose). Track COA-5 as the structural follow-up.
Template fields
Platform: OpenCode
context-mode version: 1.0.131 (latest)
Exact prompt that triggered the bug: N/A — bug is a process-lifecycle accumulation observed over normal multi-session use of opencode over ~2 days, not triggered by any specific prompt. Reproduction is "run opencode for hours, open multiple sessions, observe
ps --ppid <opencode-pid>".Steps to reproduce:
npm install -g context-mode@1.0.131(or any version ≥ 1.0.19).configs/opencode/opencode.json(mcp.context-mode.type=local+plugin: ["context-mode"]).opencode serve(oropencodeinteractively) and use it normally across multiple sessions and subagent tasks over a working day.Full error output: None. There is no error — the symptom is RAM/CPU pressure from accumulating idle MCP children. No crash, no
-32000, no log entry.What I tried before filing:
src/lifecycle.ts,src/util/sibling-mcp.ts,src/adapters/opencode/plugin.ts,configs/opencode/opencode.json,docs/platform-support.md. Confirmed:mcp.type=localcauses one stdio child per opencode MCP client (one per session).sibling-mcp.tsregex is hard-coded to Claude plugin paths; misses thebin/context-modeargv used here.pgrep -Pand aps --ppidsnapshot (above): 26 children, 1.6 GB RSS, single opencode parent, oldest 1d 18h./ctx-upgradewould not help because (a) sibling regex misses these argvs and (b) upgrade is the wrong trigger anyway.Pre-submission checklist: All four items satisfied (latest version, searched existing issues, repro steps provided, debug script run).
Operating System: Linux (Ubuntu/Debian-class, kernel 6.x)
JS Runtime: node v24.14.1 (mise-managed); bun 1.3.10 available for the opencode host itself.
Debug script output (
scripts/ctx-debug.sh)The four failures are because the script was run from the development repo before
npm run build. The installed npm-global v1.0.131 used by opencode is fully functional (it's actively running 26 healthy MCP processes that respond to JSON-RPC); these failures only reflect the absence of built artifacts in the local repo and are unrelated to the lifecycle leak described above.Key fields from the JSON output:
/home/USER/.local/share/mise/installs/node/24.14.1/bin/node/home/USER/.bun/bin/bun