Skip to content

Remote MCP headers: {env:...} interpolation not working #23664

Closed as not planned

Description

@nikf2001

Bug Report

{env:VAR_NAME} syntax in remote MCP server headers is not interpolated — the literal string {env:VAR_NAME} is sent as the header value instead of the resolved environment variable.

Steps to Reproduce

  1. Set an environment variable:

    export MY_TOKEN="actual_token_value"
  2. Configure a remote MCP server in opencode.json:

    {
      "mcp": {
        "my-server": {
          "type": "remote",
          "url": "https://example.com/api/mcp",
          "headers": {
            "Authorization": "Bearer {env:MY_TOKEN}",
            "X-Project-Id": "{env:MY_PROJECT_ID}"
          },
          "oauth": false
        }
      }
    }
  3. Launch OpenCode from a shell where MY_TOKEN is confirmed set (echo $MY_TOKEN shows the value).

Expected Behavior

{env:MY_TOKEN} should resolve to the value of the MY_TOKEN environment variable, resulting in the header Authorization: Bearer actual_token_value.

Actual Behavior

The literal string Bearer {env:MY_TOKEN} is sent as the header value, causing 401 Unauthorized from the remote server.

Confirmed by testing with curl:

# Literal string → 401
curl -H "Authorization: Bearer {env:MY_TOKEN}" https://server/api/mcp
# Resolved value → 200
curl -H "Authorization: Bearer $MY_TOKEN" https://server/api/mcp

Notes

  • {env:...} does work correctly in the environment block of local ("type": "local") MCP servers.
  • The docs show {env:...} being used in remote MCP headers (e.g., the Context7 example), suggesting this is intended to work.
  • Workaround: hardcode the token/values directly in the config.

Environment

  • OpenCode: v0.1.41
  • OS: macOS (darwin)
  • Shell: zsh

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 21, 2026
  2. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  3. rekram1-node commented on Jun 11, 2026

    @rekram1-node
    Contributor

    unable to reproduce on latest

  4. gtso86 commented on Sep 28, 2026

    @gtso86

    Description

    {env:VAR} (and ${VAR}) placeholders in mcp.servers.<name>.headers of a type: remote server are sent to the server as an empty string, even when the variable is exported in the OpenCode process environment (verified via ps eww). This makes Bearer-authenticated remote MCP servers unusable without hardcoding the token in the (often git-tracked) config file. Related: #23664 (Apr 2026) — still reproducible on v2.0.18 (Sep 2026).

    Steps to Reproduce

    1. export N8N_MCP_TOKEN=<valid-token> (confirmed present in the OpenCode process environment).
    2. Configure:
      {
        "mcp": {
          "servers": {
            "n8n": {
              "type": "remote",
              "url": "http://localhost:5678/mcp-server/http",
              "oauth": false,
              "headers": { "Authorization": "Bearer {env:N8N_MCP_TOKEN}" }
            }
          }
        }
      }
    3. opencode mcp list → n8n failed: Error POSTing to endpoint:
      {"message":"Unauthorized: Invalid authorization header format -Malformed Bearer token"}.
      Evidence that the header arrives empty (not literal, not wrong value) Probed the endpoint directly (token values redacted), same body shape the client sends:
    Authorization sent Server response
    Bearer HTTP 200 + tools/list result
    Bearer fromenv (non-empty junk) HTTP 401 {"message":"Unauthorized"}
    Bearer {env:N8N_MCP_TOKEN} (lit) HTTP 401 {"message":"Unauthorized"}
    Bearer (empty) HTTP 401 {"message":"...Malformed Bearer token"}

    OpenCode's failure matches the empty row exactly — and stays identical when the variable is guaranteed non-empty in the parent process, and with ${VAR} syntax too. Only a hardcoded literal token connects.
    Expected Behavior {env:N8N_MCP_TOKEN} resolves to the process environment value, as documented
    (https://opencode.ai/docs/mcp-servers/ Context7 example) and as it already works in the environment block of type: local servers.

    Environment

    • OpenCode: v2.0.18 (standalone binary, Bun runtime)
    • OS: macOS (darwin, arm64)
    • Shell: zsh
    • Server: self-hosted n8n 2.40.7, Streamable HTTP MCP endpoint on loopback

    Impact

    Blocks any setup with Bearer-authenticated remote MCP servers whose tokens live outside the config file — including multi-server setups (e.g. several n8n instances with per-project tokens in .env), where hardcoding N tokens into tracked config is not acceptable.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions