microsoft / microsoft/apm

[BUG] Codex adapter writes ${VAR} MCP header values into static http_headers

Open
#2,984 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area/mcp-config status/accepted triage/recommended type/bug
Dominant language
Python
Stars
3.9k
Forks
365
Avg merge
1d 17h
Merged PRs (30d)
132

Description

Describe the bug

The Codex client adapter writes an MCP server's headers into ~/.codex/config.toml as http_headers with any ${VAR} placeholder copied verbatim. Codex documents http_headers as a map of header names to static values; environment-sourced values go through env_http_headers (header name → environment variable name) or bearer_token_env_var. The adapter never emits either of those, so a remote MCP server whose header references an environment variable is configured for Codex with the unexpanded placeholder as the literal header value.

The practical case is a local MCP bridge on loopback that requires a bearer token read from the environment (Authorization: "Bearer ${TOKEN}"): the same dependency works for Claude Code and cannot authenticate from Codex.

Source (0.30.0, unchanged on main at 6f3dd393):

  • src/apm_cli/adapters/client/codex.py lines 295–306: each remote["headers"] entry is passed through _resolve_variable_placeholders and stored under remote_config["http_headers"].
  • CodexClientAdapter does not override _supports_runtime_env_substitution (base.py line 319 defaults it to False), so _resolve_variable_placeholders leaves ${VAR} untouched.
  • env_http_headers and bearer_token_env_var appear nowhere under src/apm_cli/adapters/client/.

Codex's reference for streamable HTTP servers (https://learn.chatgpt.com/docs/extend/mcp?surface=cli) lists http_headers — "Map of header names to static values", env_http_headers — "Map of header names to environment variable names (values pulled from the environment)", and bearer_token_env_var — "Environment variable name for a bearer token to send in Authorization".

To Reproduce

With a public package that declares an environment-referencing header — anthropics/claude-plugins-official/external_plugins/context7, whose apm.yml declares:

dependencies:
  mcp:
  - name: context7
    registry: false
    transport: http
    url: https://mcp.context7.com/mcp?client=claude-code-plugin
    headers:
      Authorization: ${CONTEXT7_API_KEY:-}
  1. apm marketplace add anthropics/claude-plugins-official
  2. apm install context7@claude-plugins-official -g -t claude,codex
  3. Inspect ~/.codex/config.toml. Observed:
[mcp_servers.context7]
url = "https://mcp.context7.com/mcp?client=claude-code-plugin"
id = ""

[mcp_servers.context7.http_headers]
Authorization = "${CONTEXT7_API_KEY:-}"

and the lockfile records mcp_target_servers: { claude: [context7], codex: [context7] } with the same header config.

Expected behavior

A header whose value references an environment variable is written in a form Codex resolves at runtime: bearer_token_env_var = "TOKEN" for Authorization: "Bearer ${TOKEN}", and env_http_headers = { "<Header>" = "VAR" } for a value that is exactly ${VAR}. A value mixing literal text and a placeholder in any other shape, which neither Codex field can express, is reported (skipped with a warning) rather than written as a literal.

Environment (please complete the following information):

  • OS: macOS 26.6.2
  • Python Version: Python 3.14.7 (Homebrew apm bundle)
  • APM Version: 0.30.0
  • Codex CLI: codex-cli 0.154.0

Logs

No error or warning is printed; the install reports success for both targets.

Additional context

Not captured: the HTTP request Codex sends. The report rests on the written config.toml, the adapter source, and Codex's documented field semantics. Claude Code's adapter writes the identical placeholder into headers, where Claude Code documents environment-variable expansion, so the same dependency resolves the variable for one target and not the other.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/apm_cli/adapters/client/codex.py lines 295–306 and compare its placeholder handling with base.py line 319. Reproduce the context7 installation and inspect the generated ~/.codex/config.toml. Done means exact environment references use Codex’s bearer_token_env_var or env_http_headers forms, while unsupported mixed values are skipped with a warning rather than written literally.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.