Skip to content

mcp: injected ?codemode= query param breaks strict remote MCP servers (Zoho returns 400, retry fix only covers 404) #47502

Closed

Description

@kzagoris

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

  1. 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"
          }
        }
      }
    }
  2. Start opencode2 and observe the MCP connection attempt.

  3. opencode2 POSTs to https://…/mcp/<token>/message?codemode=false (parameter injected).

  4. 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 …

  5. 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

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:

    1. Non-JSON 401 bodies break OAuth error parsing. Zoho returns 401 with plain-text body Authentication required (plus a correct WWW-Authenticate: Bearer resource_metadata=… header). opencode2's OAuth client fails with Invalid OAuth error response: SyntaxError: JSON Parse error: Unexpected identifier "Authentication" instead of proceeding with the RFC 9728 discovery flow.
    2. 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/*, /register and token requests hit the proxy origin with the wrong paths.
    3. RFC 8707 resource validation compares against the configured URL — including the injected query parameter. The resource advertised 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 the codemode parameter was injected on that particular attempt.
    4. initialize advertises capabilities.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.
    5. A boolean oauth field on a remote server config entry silently drops the entire server. With "oauth": true on the server entry, opencode2 mcp list reports "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.
  • Workaround in place (for reference): a local Node reverse proxy that (a) strips all query params, (b) routes /.well-known/*, /register and token paths to the correct upstream hosts, (c) rewrites plain-text 401 bodies into RFC 6750 JSON, (d) translates the RFC 8707 resource value in both directions, and (e) strips applyDefaults from initialize. 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 the applyDefaults capability) 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's capabilities.elicitation.form.applyDefaults still 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: local mcp-remote fronted by a ~40-line stdio filter that deletes applyDefaults from the initialize message.)

Activity

  1. opencode-agent commented on Sep 5, 2026

    @opencode-agent
    Contributor

    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.

  2. rekram1-node commented on Oct 15, 2026

    @rekram1-node
    Contributor

    Just for context on why this exists:
    #55336 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions