[BUG] Codex adapter writes ${VAR} MCP header values into static http_headers
Nobody has claimed this yet.
- 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.pylines 295–306: eachremote["headers"]entry is passed through_resolve_variable_placeholdersand stored underremote_config["http_headers"].CodexClientAdapterdoes not override_supports_runtime_env_substitution(base.pyline 319 defaults it toFalse), so_resolve_variable_placeholdersleaves${VAR}untouched.env_http_headersandbearer_token_env_varappear nowhere undersrc/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:-}
apm marketplace add anthropics/claude-plugins-officialapm install context7@claude-plugins-official -g -t claude,codex- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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