Desktop app doesn't resolve project settings.local.json env vars for plugin MCP server subprocesses

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 14, 2026

Related prior reports (both closed, but bug not actually fixed)

  • #70486 — closed by a maintainer in favor of tracking in #70485
  • #70485 — later auto-closed by the stale-bot for inactivity ("Please open a

new issue if this is still relevant"), not because it was fixed

This report is fresh, independent confirmation that the bug is still present.

Description

When a plugin-provided MCP server's config references an environment variable
via ${VAR_NAME} templating, the Claude desktop app fails to resolve it from
the project's .claude/settings.local.json env block. The subprocess ends up
receiving the literal unexpanded string ${VAR_NAME} instead of the actual
value, causing auth failures against the downstream API.

Steps to reproduce

  1. Add a secret to .claude/settings.local.json:

``json
{ "env": { "MY_API_KEY": "<value>" } }
``

  1. Use a plugin MCP server whose config references ${MY_API_KEY}.
  2. Call a tool from that MCP server.
  3. Observe a 401/auth error from the downstream API.

Diagnosis

  • Confirmed the Claude Code session's own Bash-tool subprocess correctly

inherits MY_API_KEY from settings.local.json (env | grep MY_API_KEY
shows the real value).

  • Inspected the running plugin MCP server subprocess directly with

ps eww <pid> and found it literally has:
``
MY_API_KEY=${MY_API_KEY}
``
i.e., the template was never substituted for that subprocess's environment.

  • Fully quitting and relaunching the desktop app did not fix it — a freshly

spawned MCP server process (started after full app restart) still showed the
unexpanded placeholder.

  • Workaround: running launchctl setenv MY_API_KEY <value> before launching

the app makes the plugin MCP server pick up the correct value, implying the
substitution is reading from the app's own OS-level process environment
rather than from the project's settings.local.json.

Impact

  • Project-scoped secrets in settings.local.json are silently ignored for

plugin MCP servers, with no error surfaced beyond a generic downstream 401.

  • The launchctl setenv workaround broadcasts the secret to every process in

the user's login session (not just the intended MCP server), which is a
meaningfully worse security posture than a scoped, per-project env var.

Environment

  • Claude desktop app version: 1.30096.1 (macOS)
  • Affected server: a plugin-provided MCP server, but the underlying

substitution mechanism appears to be general to plugin MCP config.

Expected behavior

Plugin MCP server subprocesses should have ${VAR} templates resolved from
the same settings sources (project/user settings.local.json) that the
Bash tool and other in-session subprocesses already use.

View original on GitHub ↗