[BUG] macOS desktop app does not expand ${VAR} in .mcp.json env (unfixed part of #9427)

Open 💬 0 comments Opened Jun 24, 2026 by toy-crane

Preflight Checklist

  • [x] I have searched existing issues. The exact matches are closed and locked: #9427 (closed as "completed" on 2025-12-19 with no linked fix commit/PR, while users still reported it broken on 2.0.71/2.0.73) and #40372 (auto-closed as a duplicate of #9427). Related: #6204, #14032.
  • [x] This is a single bug report.
  • [x] I am using the latest version of Claude Code.

What's Wrong?

${VAR} expansion in a project-root .mcp.json works in the CLI but not in the macOS desktop app. The MCP server is launched by the desktop app receives the literal string ${GOOGLE_MAPS_API_KEY} instead of the resolved value, so the downstream API rejects it as an invalid key.

This appears to be the still-unfixed part of #9427: macOS GUI apps inherit the launchd environment, not the shell's, so a variable that is only present in the user's shell / settings.local.json env is not available for ${} expansion when the server is spawned by the desktop app.

What Should Happen?

Per the docs, ${VAR} expansion in .mcp.json should behave identically in the desktop app and the CLI. If a variable is defined in .claude/settings.local.json's env block, it should be available for ${} expansion regardless of how Claude Code is launched. (Today, that env block reaches the Bash tool but is not used for .mcp.json ${} expansion in the desktop app.)

Error Messages/Logs

The MCP tool call fails:

Failed to geocode address: API key invalid or required API not enabled.

But the same key works when sent directly:

$ curl "https://maps.googleapis.com/maps/api/geocode/json?address=Fukuoka&key=$GOOGLE_MAPS_API_KEY"
{ "status": "OK", ... }

Inspecting the running MCP child processes (ps eww <pid>) shows two instances with different key values:

# spawned by terminal `claude` CLI  -> correct
GOOGLE_MAPS_API_KEY=AIzaSy...   (length 39)   ✅ works

# spawned by Claude desktop app    -> literal, not expanded
GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}   (length 22)   ❌ "API key invalid"

Process parentage confirms the broken instance is a child of the desktop app
(/Applications/Claude.app/.../claude-code/2.1.181/claude.app...), while the working
instance is a child of the terminal claude CLI. The desktop app's own process
environment (ps eww) does not contain GOOGLE_MAPS_API_KEY, so the ${} is left
as-is.

Steps to Reproduce

  1. Put a secret in .claude/settings.local.json env (and/or a project .env), but do not export it from a shell rc — i.e. it is not in the macOS GUI/launchd environment:

``json
{ "env": { "GOOGLE_MAPS_API_KEY": "<redacted>" } }
``

  1. Reference it in a project-root .mcp.json:

``json
{
"mcpServers": {
"google-maps": {
"command": "npx",
"args": ["-y", "@cablate/mcp-google-map", "--stdio"],
"env": { "GOOGLE_MAPS_API_KEY": "${GOOGLE_MAPS_API_KEY}" }
}
}
}
``

  1. Launch Claude Code via the macOS desktop app (not a terminal) and call any tool from that MCP server.
  2. The call fails with an invalid-key/auth error. ps eww on the spawned server shows the literal ${GOOGLE_MAPS_API_KEY}.
  3. Launch the same project via the terminal claude CLI in a shell that has the variable exported → it works. This is the launch-environment difference.

Workaround

Install a LaunchAgent that runs at login and pushes the secrets into the launchd user domain so GUI apps inherit them:

# ~/.config/.../load.sh (run from a LaunchAgent with RunAtLoad)
launchctl setenv GOOGLE_MAPS_API_KEY "$GOOGLE_MAPS_API_KEY"

After this, fully quit (⌘Q) and reopen the desktop app and the ${} expansion resolves. This confirms the root cause is the launch environment, not the key or the config syntax.

Claude Code Version

2.1.181 (macOS desktop app)

Platform

macOS (Darwin 25.5.0, arm64), shell zsh

View original on GitHub ↗