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

Resolved 💬 1 comment Opened Jun 24, 2026 by toy-crane Closed Jun 25, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [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. An MCP server spawned 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 is the still-unfixed part of #9427 (closed "completed" 2025-12-19 with no linked fix, while users kept reporting it on 2.0.71/2.0.73) and #40372 (auto-closed as a duplicate). Both are now closed and locked.

Root cause: macOS GUI apps inherit the launchd environment, not the shell's. A variable that lives only in the shell or in .claude/settings.local.json's env block is therefore not present when the desktop app expands ${}. That env block reaches the Bash tool but is not used for .mcp.json ${} expansion in the desktop app.

What Should Happen?

Per the docs (https://code.claude.com/docs/en/mcp#environment-variable-expansion-in-mcp-json), ${VAR} expansion in .mcp.json should behave identically in the desktop app and the CLI. A variable defined in .claude/settings.local.json's env block should be available for ${} expansion regardless of how Claude Code is launched.

Error Messages/Logs

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:

  # 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/...), and that process's own
environment does not contain GOOGLE_MAPS_API_KEY.

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.
  2. Reference it in a project-root .mcp.json:

"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 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.

Workaround: a LaunchAgent (RunAtLoad) that runs launchctl setenv GOOGLE_MAPS_API_KEY "$GOOGLE_MAPS_API_KEY", then fully quit (⌘Q) and reopen the app. This resolves it, confirming the cause is the launch environment, not the key or config syntax.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

N/A (CLI works; desktop app never worked for this)

Claude Code Version

2.1.181 (macOS desktop app)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

_No response_

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗