Repository navigation
mcp: injected ?codemode= query param breaks strict remote MCP servers (Zoho returns 400, retry fix only covers 404) #47502
Description
Summary
opencode2 injects a ?codemode=true|false query parameter into the configured URL of every remote MCP server. Servers that reject unexpected query parameters fail with an HTTP 400 — and the existing mitigation (PR #45563, which retries without the injected parameter) only triggers on 404, so these servers remain broken. Zoho's MCP gateway is one such server.
While working around this with a local stripping proxy, several further opencode2-interop problems surfaced for strict/older remote MCP servers (details in Additional Context).
Environment
- opencode version: 0.0.0-beta-19151 (
opencode2, beta channel) - OS: Windows 11 (Windows_NT 10.0.26200, win32 x64)
- Terminal: Unavailable (not captured)
- Shell: PowerShell 7 / Windows PowerShell 5.1
- Install/channel: beta (
@opencode-ai/cli@beta) - Active plugins: none (
"plugin": []in global config)
Reproduction
-
Configure a remote MCP server whose gateway rejects unknown query parameters, e.g. a Zoho MCP endpoint:
// opencode.json { "mcp": { "servers": { "zoho-email": { "type": "remote", "url": "https://email-<id>.zohomcp.eu/mcp/<token>/message" } } } }
-
Start opencode2 and observe the MCP connection attempt.
-
opencode2 POSTs to
https://…/mcp/<token>/message?codemode=false(parameter injected). -
Zoho's gateway responds:
HTTP 400 {"status":"failure","data":{"error_code":"INVALID_INPUT","message":"either the request body or parameters is in wrong format"}}
The TUI shows:
Streamable HTTP error: Error POSTing to endpoint: … INVALID_INPUT … -
The same endpoint works when called without the query string (curl / v1
opencode/ Codex CLI).
Verified independently: sending the exact same initialize request to the Zoho URL with ?codemode=true appended produces the 400, while the clean URL does not.
Expected Behavior
Either opencode2 should not inject codemode into the user-configured URL by default, or the fallback introduced in PR #45563 should cover other HTTP error statuses (at minimum 400), not just 404. Ideally: on connection failure where the configured URL without the injected parameter differs, retry once with the original URL regardless of the specific 4xx status.
Actual Behavior
- Initial connection POST includes
?codemode=false|true. - Zoho returns 400 with
INVALID_INPUT. - PR fix(mcp): retry initial 404 without injected codemode #45563's retry only fires on 404, so no fallback happens and the server stays in
failedstate.
Additional Context
-
Same root cause as mcp: Figma Desktop fails with Error POSTing — ?codemode= causes 404 #45499 (Figma Desktop, 404 variant), fixed by fix(mcp): retry initial 404 without injected codemode #45563. This report is the 400 variant, which the fix does not cover. Reproduced and confirmed on
0.0.0-beta-19151. -
While building a local reverse proxy to work around this, further opencode2 ↔ strict-server interop issues surfaced, each of which independently breaks the connection; listing them here since they may share fix sites:
- Non-JSON 401 bodies break OAuth error parsing. Zoho returns
401with plain-text bodyAuthentication required(plus a correctWWW-Authenticate: Bearer resource_metadata=…header). opencode2's OAuth client fails withInvalid OAuth error response: SyntaxError: JSON Parse error: Unexpected identifier "Authentication"instead of proceeding with the RFC 9728 discovery flow. - OAuth discovery/registration/token endpoints are resolved against the configured origin rather than using the absolute URLs from the RFC 9728/8414 metadata (closely related to Remote MCP OAuth: resource_metadata URL from WWW-Authenticate is ignored (origin-only RFC 9728 lookup) — breaks AWS Bedrock AgentCore runtimes #44790). Behind any proxy/indirection,
/.well-known/*,/registerand token requests hit the proxy origin with the wrong paths. - RFC 8707
resourcevalidation compares against the configured URL — including the injected query parameter. Theresourceadvertised in the protected-resource metadata (the server's own absolute URL) is rejected unless it matches the URL currently in use, and the expected value is inconsistent: on some connect attempts it is the clean configured URL (http://127.0.0.1:18773/), on others it includes the injected parameter (http://127.0.0.1:18773/?codemode=false) —Protected resource https://… does not match expected http://127.0.0.1:18773/?codemode=false (or origin). A static rewrite cannot satisfy both; the effective resource value depends on whether thecodemodeparameter was injected on that particular attempt. initializeadvertisescapabilities.elicitation.form.applyDefaults, which older Java MCP SDK servers (strict Jackson mapping, e.g. Zoho's gateway) reject with-32603 Unrecognized field "applyDefaults". Codex CLI and v1 opencode connect to the same server fine because they don't send this field.- A boolean
oauthfield on a remote server config entry silently drops the entire server. With"oauth": trueon the server entry,opencode2 mcp listreports "No MCP servers configured" (that server missing, others listed); removing the field makes it load. Expected: either accept the documented boolean or fail loudly with a schema error.
- Non-JSON 401 bodies break OAuth error parsing. Zoho returns
-
Workaround in place (for reference): a local Node reverse proxy that (a) strips all query params, (b) routes
/.well-known/*,/registerand token paths to the correct upstream hosts, (c) rewrites plain-text 401 bodies into RFC 6750 JSON, (d) translates the RFC 8707resourcevalue in both directions, and (e) stripsapplyDefaultsfrominitialize. With that, the server connects and all tools work — which confirms every failure above is client-side request-shaping, not the server. -
A simpler end-user workaround also works and narrows the bug further: Zoho's own recommended integration path,
npx mcp-remote <url> --transport http-only(configured as a local stdio server). On its own, mcp-remote connects to the same Zoho server fine — its own initialize (without theapplyDefaultscapability) is accepted, and its OAuth client tolerates Zoho's plain-text 401. However, when driven by opencode2 the same failure returns: mcp-remote forwards the connecting client's initialize capabilities upstream, so opencode2'scapabilities.elicitation.form.applyDefaultsstill reaches Zoho's Java MCP SDK and the connection fails with the same -32603. This makes item 4 (the initialize capability) a self-contained minimal repro: any client that adds this field to a session bridged through mcp-remote will fail against Java-SDK servers, while removing it fixes the connection immediately. (Working opencode2 config: localmcp-remotefronted by a ~40-line stdio filter that deletesapplyDefaultsfrom the initialize message.)
Activity
opencode-agent commentedon Sep 5, 2026
Fixed the HTTP 400 query-parameter fallback in #47507, now merged into
v2. Initialization retries once with the original configured URL on HTTP 400 or 404. The additional interoperability reports in this issue are outside that change.rekram1-node commentedon Oct 15, 2026
Just for context on why this exists:
#55336 (comment)