Skip to content

[Bug]: OpenCode 2: a thread loses T3 Code's MCP tools after OpenCode evicts its idle project #16768

Description

@nkoynov

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 in state.mcp (prepareTurn in OpenCode2AdapterV2.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 sends POST /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):

  1. Start an OpenCode thread in a project and let it finish a turn that uses a T3 tool.
  2. Leave that project idle for over an hour, or evict it right away on T3's OpenCode server: DELETE /api/debug/location?location[directory]=<project> (same teardown as the idle eviction).
  3. Send the thread a message asking it to call a T3 tool, e.g. list_scheduled_tasks.

Result: OpenCode's GET /api/mcp?location[directory]=<project> listed linear and t3-code-<id> before the eviction and only linear after it and a full turn, and the model had no T3 tools in any later turn. T3's trace shows the PUT only before the first turn; the turns after an eviction send just the prompt:

07:45:39  PUT  /api/experimental/mcp/t3-code-ccb57895e89db907?location[directory]=…  204
07:45:39  POST /api/session/ses_…/prompt  200
          (Location evicted)
07:46:00  POST /api/session/ses_…/prompt  200
07:46:13  POST /api/session/ses_…/prompt  200

Expected: a thread keeps its T3 tools on every turn. Re-adding the server in prepareTurn when 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).

Activity

  1. juliusmarminge commented on Oct 7, 2026

    @juliusmarminge
    Member

    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) calls client.mcp.add (PUT /api/experimental/mcp/:server) only when state.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 and state.mcp stays 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 treating state.mcp as proof the server is registered. Either:

    1. Call client.mcp.add on 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:
    2. Check GET /api/mcp?location[directory]=… and re-add the server when t3-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:

    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.
  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions