Windows Codex Desktop stdio MCP child process does not inherit user env vars for mcp-remote header substitution

Open 💬 3 comments Opened May 24, 2026 by lwz20210407

Summary

On Windows Codex Desktop, a stdio MCP server launched via npx mcp-remote did not receive a user-level environment variable used in a custom Authorization header. As a result, mcp-remote forwarded the literal placeholder ${AMINER_MCP_AUTH_HEADER} to the remote MCP server instead of a JWT, causing authentication failures.

This looks like a Codex stdio MCP environment propagation/documentation issue rather than a remote MCP server issue: the same token works when connecting with the Python MCP SDK directly, and also works when the stdio server is launched through a PowerShell wrapper that explicitly loads the User environment variable.

Environment

  • OS: Windows
  • App: Codex Desktop
  • Codex CLI shown in local docs/config: 0.133.0
  • MCP server type: stdio wrapper around remote SSE via mcp-remote
  • Remote MCP: https://mcp.aminer.cn/sse
  • Node/npm available through npx

Original config shape

[mcp_servers.AMiner_Scholar_MCP]
command = "npx"
args = [
  "-y",
  "mcp-remote@latest",
  "https://mcp.aminer.cn/sse",
  "--header",
  "Authorization:${AMINER_MCP_AUTH_HEADER}",
  "--transport",
  "sse-only",
  "--silent"
]
enabled = true
startup_timeout_sec = 120

AMINER_MCP_AUTH_HEADER was set as a Windows User environment variable and was visible from normal PowerShell/Python processes.

Expected behavior

Either:

  1. Codex should pass the relevant user/process environment to stdio MCP child processes so that mcp-remote can expand ${AMINER_MCP_AUTH_HEADER} from process.env, or
  2. Codex documentation/config examples should make clear that ${VAR} in args is not expanded by Codex and that stdio MCP child processes may not receive Windows User environment variables unless explicitly provided through MCP config or a shell wrapper.

Actual behavior

The launched mcp-remote process received the literal string as its header value. A process command-line shape check showed the Authorization value had:

  • length: 25
  • contained $
  • contained { and }
  • had 1 JWT segment instead of 3

The remote AMiner MCP tool call returned:

{
  "code": 40308,
  "success": false,
  "msg": "Token Parse Error"
}

Verification that the token/server were not the issue

Using the same token:

  • Python MCP SDK direct SSE connection to https://mcp.aminer.cn/sse succeeded.
  • list_tools returned 16 tools.
  • search_paper returned code: 200 and paper data.
  • Launching the stdio server through a PowerShell wrapper also succeeded.

Workaround that fixed it

[mcp_servers.AMiner_Scholar_MCP]
command = "powershell.exe"
args = [
  "-NoProfile",
  "-ExecutionPolicy",
  "Bypass",
  "-Command",
  "$env:AMINER_MCP_AUTH_HEADER = [Environment]::GetEnvironmentVariable('AMINER_MCP_AUTH_HEADER', 'User'); if (-not $env:AMINER_MCP_AUTH_HEADER) { throw 'AMINER_MCP_AUTH_HEADER missing' }; & npx -y mcp-remote@latest https://mcp.aminer.cn/sse --header 'Authorization:${AMINER_MCP_AUTH_HEADER}' --transport sse-only --silent"
]
enabled = true
startup_timeout_sec = 120

After this change, Codex exposed the AMiner MCP tool and search_paper returned code: 200.

Request

Please consider one of:

  • ensuring stdio MCP servers inherit expected process/user environment variables on Windows Desktop,
  • supporting explicit environment-variable expansion in MCP args,
  • adding a config-supported way to map an existing user env var into the stdio child process without embedding secrets in config.toml, or
  • documenting the expected behavior and recommended Windows workaround.

No secrets are included in this report.

View original on GitHub ↗

3 Comments

jshaofa-ui · 3 months ago

Solution: Windows MCP Child Process Environment Variable Inheritance

Problem

On Windows Codex Desktop, stdio MCP servers do not receive User-level environment variables. ${AMINER_MCP_AUTH_HEADER} is passed literally instead of expanded, causing auth failures.

Root Cause

On Windows, User environment variables are stored in HKCU\Environment and loaded at login. When Codex Desktop is launched from certain contexts (service, background, or before vars are set), process.env does not include User env vars. The MCP child process inherits this incomplete environment.

Proposed Fix

1. Merge User Environment on Windows:

// src/mcp/env-loader.ts
function loadWindowsUserEnv(): Record<string, string> {
  if (process.platform !== "win32") return {};
  const output = execSync("reg query HKCU\\Environment /s", { encoding: "utf-8" });
  // Parse REG_EXPAND_SZ and REG_SZ values
  // Return merged env
}

function getSpawnEnv(): NodeJS.ProcessEnv {
  return { ...process.env, ...loadWindowsUserEnv() };
}

2. Support env: in MCP Config (Feature):

[mcp_servers.MyServer.env]
AUTH_HEADER = "${env:AUTH_HEADER}"

3. Document the behavior and provide the PowerShell wrapper workaround.

Workaround

command = "powershell.exe"
args = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command",
  "$env:VAR=[Environment]::GetEnvironmentVariable(\"VAR\", \"User\"); & npx mcp-remote@latest ..."]

Full solution: solutions/codex-24362-windows-mcp-env-vars-fix.md

yui-stingray · 3 months ago

I checked current main (9f42c89) and this looks consistent with the current local stdio MCP environment handling, rather than mcp-remote receiving a fully inherited process environment. One small correction to the previous direction: the relevant code path here is in the Rust stdio MCP launcher.

For local stdio MCP servers, Codex does not pass the full ambient environment through. codex-rs/rmcp-client/src/utils.rs:12-23 builds the child env from Codex's default env allowlist, plus variables explicitly listed in the server config env_vars, plus literal env entries. The local launcher then calls .env_clear().envs(envs) before spawning the child (codex-rs/rmcp-client/src/stdio_server_launcher.rs:248-260).

So with the config shown here, AMINER_MCP_AUTH_HEADER is not in the child environment unless it is either listed in env_vars or loaded by a wrapper such as the PowerShell workaround. The config parser supports stdio env_vars (codex-rs/config/src/mcp_types.rs:320-342, :426-435), and there is regression coverage that a whitelisted env_vars value reaches a stdio MCP server (codex-rs/core/tests/suite/rmcp_client.rs:1476-1580). codex doctor also checks named stdio env_vars and reports if one is missing (codex-rs/cli/src/doctor.rs:1463-1501).

A minimal config-side workaround to try, without embedding the secret value in config.toml, would be:

[mcp_servers.AMiner_Scholar_MCP]
command = "npx"
args = ["-y", "mcp-remote@latest", "https://mcp.aminer.cn/sse", "--header", "Authorization:${AMINER_MCP_AUTH_HEADER}", "--transport", "sse-only", "--silent"]
env_vars = ["AMINER_MCP_AUTH_HEADER"]

If that still fails specifically in Windows Desktop, the remaining thing to check is whether the Codex Desktop/app-server process itself can see AMINER_MCP_AUTH_HEADER when it starts. Existing Windows processes generally do not gain newly-created User environment variables automatically, so a full Desktop/app restart after setting the variable may be required. There may also be a docs/config clarity gap here: stdio MCP servers need env_vars when a child process should receive non-default environment variables.

jshaofa-ui · 3 months ago

Solution Proposal: Windows MCP Child Process Environment Variable Inheritance

Problem Analysis

On Windows, stdio MCP servers launched via npx mcp-remote do not receive user-level environment variables used for authentication headers.

Proposed Fix

  1. Environment Block Inheritance: Ensure CreateProcess uses CREATE_UNICODE_ENVIRONMENT and properly inherits user env vars
  2. Explicit Environment Passing: Merge system env + user env + MCP-specific overrides before spawning
  3. Registry Fallback: Check Windows Registry for user-level variables if not found in process env
  4. Clear Error Messages: Surface actionable messages when env vars are missing

Timeline: 3-4 days

Pricing: $2,000 - $3,000

We have deep MCP integration and cross-platform deployment expertise.