From d48a5d16ff645001530d4b8a0705e22911f3368c Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Wed, 26 Aug 2026 15:23:14 -0500 Subject: [PATCH] fix(core): avoid nested MCP code mode (#45377) --- packages/core/src/mcp/client.ts | 5 ++- .../core/src/plugin/mcp-codemode-exclusion.ts | 2 +- packages/core/test/mcp.test.ts | 36 +++++++++++++++++++ .../plugin/mcp-codemode-exclusion.test.ts | 7 +++- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/packages/core/src/mcp/client.ts b/packages/core/src/mcp/client.ts index 925af3dbe13..2c05360ba01 100644 --- a/packages/core/src/mcp/client.ts +++ b/packages/core/src/mcp/client.ts @@ -207,7 +207,10 @@ export const connect = Effect.fnUntraced(function* ( } if (!URL.canParse(config.url)) return yield* new ConnectError({ server, message: `Invalid MCP URL for "${server}"` }) - return new StreamableHTTPClientTransport(new URL(config.url), { + // Prefer raw tools for our Code Mode without changing the configured URL used for OAuth identity. + const url = new URL(config.url) + if (config.codemode !== false && !url.searchParams.has("codemode")) url.searchParams.set("codemode", "false") + return new StreamableHTTPClientTransport(url, { requestInit: config.headers ? { headers: config.headers } : undefined, authProvider, }) diff --git a/packages/core/src/plugin/mcp-codemode-exclusion.ts b/packages/core/src/plugin/mcp-codemode-exclusion.ts index ee040b888c7..1965c457a65 100644 --- a/packages/core/src/plugin/mcp-codemode-exclusion.ts +++ b/packages/core/src/plugin/mcp-codemode-exclusion.ts @@ -4,7 +4,7 @@ import { define } from "@opencode-ai/plugin/effect/plugin" import { Effect } from "effect" // These servers provide Code Mode, so expose them directly instead of nesting them inside OpenCode Code Mode. -const urls = [/^https:\/\/mcp\.cloudflare\.com\/mcp$/, /^https:\/\/executor\.sh\/[^/]+\/mcp$/] +const urls = [/^https:\/\/executor\.sh\/[^/]+\/mcp$/] export const Plugin = define({ id: "opencode.mcp.codemode.exclusion", diff --git a/packages/core/test/mcp.test.ts b/packages/core/test/mcp.test.ts index 89ae414ed5a..c2b3ed14860 100644 --- a/packages/core/test/mcp.test.ts +++ b/packages/core/test/mcp.test.ts @@ -77,6 +77,7 @@ function resourceServer( templateLists: 0, toolLists: 0, initializations: 0, + urls: [] as string[], } const protocol = new Server( { name: "mcp-resources", version: "1.0.0" }, @@ -145,6 +146,7 @@ function resourceServer( const http = Bun.serve({ port: 0, fetch: async (request) => { + state.urls.push(request.url) const body: unknown = request.method === "POST" ? await request.clone().json() : undefined if (typeof body === "object" && body !== null && "method" in body && body.method === "initialize") { state.initializations += 1 @@ -718,6 +720,40 @@ test("applies configured MCP timeouts to resource operations", async () => { await expect(read).rejects.toThrow("Request timed out") }) +for (const entry of [ + { name: "default", query: "", codemode: undefined, expected: "?codemode=false" }, + { name: "explicit local code mode", query: "", codemode: true, expected: "?codemode=false" }, + { name: "direct tools", query: "", codemode: false, expected: "" }, + { + name: "existing query", + query: "?source=opencode", + codemode: undefined, + expected: "?source=opencode&codemode=false", + }, + { name: "explicit remote code mode", query: "?codemode=true", codemode: undefined, expected: "?codemode=true" }, + { name: "explicit remote opt-out", query: "?codemode=false", codemode: undefined, expected: "?codemode=false" }, + { name: "portal opt-out", query: "?codemode=off", codemode: undefined, expected: "?codemode=off" }, +]) { + testEffect(Layer.empty).live(`remote MCP code mode preference: ${entry.name}`, () => + Effect.gen(function* () { + const server = yield* resourceServer() + const config = new ConfigMCP.Remote({ + type: "remote", + url: server.url + entry.query, + oauth: false, + codemode: entry.codemode, + }) + const connection = yield* connect("resources", config, import.meta.dir) + yield* connection.tools() + expect(server.state.initializations).toBe(1) + expect(server.state.toolLists).toBe(1) + expect(server.state.urls.length).toBeGreaterThanOrEqual(3) + expect(new Set(server.state.urls)).toEqual(new Set([server.url + entry.expected])) + expect(config.url).toBe(server.url + entry.query) + }), + ) +} + test("lists, reads, and reports MCP resource changes", async () => { await Effect.runPromise( Effect.scoped( diff --git a/packages/core/test/plugin/mcp-codemode-exclusion.test.ts b/packages/core/test/plugin/mcp-codemode-exclusion.test.ts index 54249399edf..a8b3fcf1448 100644 --- a/packages/core/test/plugin/mcp-codemode-exclusion.test.ts +++ b/packages/core/test/plugin/mcp-codemode-exclusion.test.ts @@ -21,7 +21,12 @@ it.effect("defaults only known Code Mode MCP servers to direct tools", () => { name: "cloudflare code mode", server: { type: "remote", url: "https://mcp.cloudflare.com/mcp/" }, - codemode: false, + codemode: undefined, + }, + { + name: "cloudflare raw tools", + server: { type: "remote", url: "https://mcp.cloudflare.com/mcp?codemode=false" }, + codemode: undefined, }, { name: "cloudflare docs",