Repository navigation
[Bug]: OpenCode 2: a thread loses T3 Code's MCP tools after OpenCode evicts its idle project #16768
Copy link
Copy link
Open
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Description
Activity
Note
Grok responding on behalf of Julius.
Triage: confirmed bug in the OpenCode 2 adapter (the trigger is upstream).
Root cause (
apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.ts):ThreadState.mcp(L399-402) caches "T3's MCP server as registered for this thread."prepareTurn(L3241) callsclient.mcp.add(PUT /api/experimental/mcp/:server) only whenstate.mcp === undefined(L3264-3285). Once that succeeds,state.mcp = wanted.- After that, the cache is cleared only when the directory or credential changes (L3254-3262) or when the server reconnects (L2843-2844, "A restarted server forgot T3's MCP servers"). Every later turn sends only the prompt.
- OpenCode keeps runtime-added servers in the Location's in-memory state and evicts a Location after it has been idle for 60 minutes (core: 60m idle location eviction interrupts a running session and rejects pending questions anomalyco/opencode#51343). The eviction drops
t3-code-<id>, but the server process keeps running, so no reconnect fires andstate.mcpstays stale. The thread then runs without T3 tools, and nothing warns about it.t3OrchestrationSystemPrompt(state.mcp !== undefined)also keeps telling the model the tools are there.
Proposed fix: in
prepareTurn, stop treatingstate.mcpas proof the server is registered. Either:- Call
client.mcp.addon every turn. It's idempotent: OpenCode leaves an unchanged config connected. This costs one PUT per turn and also covers any other way OpenCode can lose runtime servers. Or: - Check
GET /api/mcp?location[directory]=…and re-add the server whent3-code-<id>is missing.
Option 1 is the simplest and most robust. Add a test that removes or evicts the server between turns and checks the PUT is sent again.
Related:
- fix(server): OpenCode 2 threads get T3 Code's MCP tools #16142 (merged) added the per-turn registration that has this caching gap.
- [Bug]: OpenCode 2: a thread's first turn doesn't see T3 Code's MCP tools (prompt is sent before OpenCode registers them) #16141 (open) is the first turn not seeing the tools because the prompt goes out before registration finishes. That's a separate timing issue in the same code path, so whoever fixes one should look at both.
- feat(mcp): shared MCP servers reach every agent session #16466 (open, shared MCP servers via T3's proxy) doesn't touch this path, so it's not a duplicate or a fix.
- Upstream core: 60m idle location eviction interrupts a running session and rejects pending questions anomalyco/opencode#51343 is the eviction itself. T3 should be robust to it either way.
Workaround:
- Restarting T3's OpenCode server (a reconnect) makes the next turn re-add the server.
- Or use an OpenCode plugin that re-adds T3's server configs when a Location boots, as the reporter does.
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 7, 2026
Metadata
Metadata
Assignees
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
The OpenCode 2 adapter adds the thread's MCP server (
t3-code-<id>) once, on the first turn after it loads the thread, and remembers that instate.mcp(prepareTurninOpenCode2AdapterV2.ts). It only forgets it when the OpenCode server reconnects.OpenCode 2 keeps MCP servers added at runtime (
PUT /api/experimental/mcp/:server) in the project Location's in-memory state, and evicts a Location after 60 minutes without activity (packages/core/src/location-activity.ts). The eviction drops the server. T3's next turn on that thread, whether a user message, a scheduled task or a PR-watch wake, only sendsPOST /session/:id/prompt, so the thread runs without T3's tools and nothing tells the model or the user.Repro (nightly 20261007.2761, OpenCode 2.0.24):
DELETE /api/debug/location?location[directory]=<project>(same teardown as the idle eviction).list_scheduled_tasks.Result: OpenCode's
GET /api/mcp?location[directory]=<project>listedlinearandt3-code-<id>before the eviction and onlylinearafter it and a full turn, and the model had no T3 tools in any later turn. T3's trace shows thePUTonly before the first turn; the turns after an eviction send just the prompt:Expected: a thread keeps its T3 tools on every turn. Re-adding the server in
prepareTurnwhen it is missing from the Location (or adding it on every turn; OpenCode leaves an unchanged config connected) would cover eviction, and any other way OpenCode can lose runtime servers.Threads that wake on schedules or PR watches are idle for hours between runs, so they hit this regularly. We work around it with an OpenCode plugin that remembers T3's server configs per directory and adds them back when the Location boots again.
Related on the OpenCode side: anomalyco/opencode#51343 (idle eviction).
Found and written with Claude Opus 5.5 in T3 Code (OpenCode 2 + cursor-opencode-provider).