Repository navigation
V2 session runner does not include MCP tools in LLM requests #45333
Description
Description
When using the V2 interactive session API (POST /api/session/{id}/prompt, used by the
desktop/web app), tool definitions from connected MCP servers are never included in the
outgoing request to the model. Built-in tools (read, glob, grep, bash, edit, etc.) are
included correctly. The MCP server shows as connected and its tools are discoverable, but
the model is never given them, so it can't call them.
The legacy V1 pipeline (POST /session/{id}/message) does include MCP tools correctly, so
this looks specific to the V2 session runner's tool materialization.
Reproduction
- Configure any MCP server with one simple tool, e.g. an
example_pingtool that takes no
arguments and returns a string. - Confirm the server connects (
GET /mcpshows"status": "connected"for it). - Start a session via the interactive UI (or
POST /api/sessionthen
POST /api/session/{id}/prompt) with any agent. - Send a prompt that unambiguously requires the tool, e.g. "Call example_ping and show me
the result. Call the tool, don't explain first." - Inspect the outgoing request to the model provider (e.g. via a local proxy in front of
an OpenAI-compatible endpoint, or any provider request logging you have). - Compare: built-in tools (
read,glob, etc.) are present intools[];example_ping
is absent.
If there's a built-in way to inspect the materialized V2 tool set for a session without an
external proxy, it'd be useful to know for future debugging — I didn't find one and had to
tap the outgoing HTTP request directly.
Expected behavior
The V2 ToolRegistry should materialize a policy-filtered union of built-in tools, MCP
tools, plugin tools, and structured-output tools before the LLM request is built — matching
the intent already documented in the runner's own implementation checklist (see below).
Actual behavior
The V2 interactive request only ever contains the subset of tools that
packages/core/src/tool/registry.ts's ToolRegistry.materialize() currently supports:
built-in ApplicationTools entries plus generic locally-registered tools. Tools from
configured MCP servers are never added to that set, so they never reach the model.
Evidence
Reproduced with two independent, unrelated MCP servers (a stdio-local MCP server we wrote,
and an already-established, previously-working MCP server used for something unrelated to
this bug), ruling out a problem specific to one server or its tool schema:
- Server A: 4 tools expected in the outgoing request → 0/4 present.
- Server B: its tools also absent from the outgoing request.
Reproduced on two builds:
- Global release install,
opencode --version→1.18.18. - This repo's own dev checkout, run directly via
bun run --cwd packages/opencode --conditions=browser src/index.ts serve, at commit
5c76f80b8898fc62c743b5768d6329f5bd8bad2e(git describe→
github-v1.2.25-1646-g5c76f80b88,packages/opencode/package.jsonversion1.18.15).
Confirmed the request that actually reaches the model provider (via a transparent tap in
front of the OpenAI-compatible endpoint) contains e.g.:
Activity
github-actions commentedon Aug 26, 2026
This issue might be a duplicate of existing issues. Please check:
opencode serve(present in the TUI) #38470: MCP tools missing from sessions created viaPOST /api/sessiononopencode serve— very similar symptom: MCP server shows as connected, built-in tools work, but MCP tools never reach the model in API-driven (V2) sessions. That report also narrows the cause to per-session tool materialization in the V2 path.If #45333 is specifically about the
ToolRegistry.materialize()gap rather than the per-request instance lifetime described in #38470, they may be related but distinct root causes — worth cross-referencing either way.Vojtech-Pet commentedon Aug 26, 2026
Thanks for the references — read through both.
#38470: its root cause (confirmed in the thread, with PR #41728) is that
opencode serveruns withinstance: false, so the lazyPlugin/InstanceStatebootstrap that initializes MCP never runs for a barePOST /api/sessionHTTP session — unlike the TUI oropencode run, which both trigger that bootstrap. That's a different mechanism from what I found here: in my reproduction,GET /mcpalready reports the server asconnected(MCP is live and populated) for the exact session that still gets no MCP tools. So this isn't an initialization-timing gap —ToolRegistry.materialize()inpackages/core/src/tool/registry.ts(the V2 registry backing/api/session/{id}/prompt) structurally only sources built-inApplicationToolsand local registrations; there's no MCP merge step in that file at all, regardless of whether MCP itself is already up. PR #41728 also only touchespackages/opencode/src/cli/cmd/serve.ts, so it wouldn't affect this V2 path either way.#33027: same user-visible symptom (connected but not exposed), but no source-level cause was pinned down there (older version, TUI, and the issue itself notes the binary couldn't be inspected directly) — I can't tell from it whether it's the same gap or something else.
So: #38470 looks related but distinct (different code path, already has a proposed fix that doesn't touch V2's
ToolRegistry), and #33027 is inconclusive relative to this one. Happy to have maintainers merge/link these as they see fit — just wanted to share what the V2 trace actually shows in case it's useful for triage.doberkofler commentedon Oct 18, 2026
Confirmed on v2.0.32 (current release)
This still reproduces on the installed release, not only on the older
1.18.xbuilds in the report. I drove the same V2 path described here:
POST /api/sessionfollowed byPOST /api/session/{id}/prompt, with the agentloop waited to idle through
POST /api/experimental/session/{id}/wait.To remove my own MCP server and tool schema from the equation, I used a
dependency-free stdio server with exactly one tool (
example_ping, noarguments, returns
"pong") and a local OpenAI-compatible provider that recordsthe outgoing
tools[]array. The provider calls the tool when it is offered andotherwise answers
NO_PING_TOOL, so the result is visible in the transcript aswell as in the captured request.
Reproduction: https://github.com/doberkofler/opencode-mcp-v2-repro
Measured result:
The
executetool is present. Its Code Mode catalog does not mention the MCPserver either, so the tool is not reachable through that path in this run. The
server is connected and its catalog has been read before the session is
created.
The capture is from a real provider request, not from session metadata. Built-in
tools are present in the same request, which matches your observation that only
the MCP materialization step is missing.
I can contribute this as a regression test if useful. The harness is three
small Node files with no dependencies: MCP server, recording provider, and a
driver that starts
opencode serve --standalone, calls the V2 endpoints, andasserts on the captured
tools[]. It should map naturally onto a test aroundToolRegistry.materialize(); tell me where you want it and I will open a PR.Happy to also re-run it against a candidate build and post the before/after
request capture here.
opencode-agent commentedon Oct 18, 2026
Closing as stale. This describes an old preview of the V2 engine that shipped in the 1.x builds (1.18.x), not the current V2 runner. The code it quotes (
tool/registry.ts,ToolRegistry.materialize(), the runner checklist) has since been removed.Current V2 has sent MCP tools to the model since #34513. By default they are exposed through Code Mode: they appear in the
executetool's catalog rather than as top-level entries in the request'stools[]. Checkingtools[]for an MCP tool name is therefore not a valid test unless that tool hascodemode: falseset.@doberkofler your 2.0.32 repro is a different problem: the startup race tracked in #51812. In your logs the MCP server was started twice under two project instances (the session used the
/varpath, the project used/private/var), and the model request went out about 2ms after the second start, before that server could connect. Please follow #51812 for that.