[BUG] Environment variables from `env` section not passed to MCP servers

Resolved 💬 17 comments Opened May 23, 2025 by sspaeti Closed Jan 19, 2026

Environment

  • Platform (select one):
  • [ ] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [x] Other: Claude Desktop + Claude Code MCP integration
  • Claude CLI version: 1.0.2 (Claude Code) (Claude Desktop: Version 0.9.3)
  • Operating System: macOS
  • Terminal: Ghostty

Bug Description

Environment variables defined in the env section of claude_desktop_config.json are not being passed correctly to MCP servers, causing servers that require API keys or tokens to fail during initialization. The MCP server crashes because it receives an invalid/empty token instead of the configured environment variable value.

Steps to Reproduce

  1. Configure an MCP server in ~/Library/Application Support/Claude/claude_desktop_config.json with environment variables:
{
  "mcpServers": {
    "mcp-server-motherduck": {
      "command": "uvx",
      "args": [
        "mcp-server-motherduck",
        "--db-path",
        "md:",
        "--motherduck-token",
        "MOTHERDUCK_TOKEN"
      ],
      "env": {
        "MOTHERDUCK_TOKEN": "actual_token_value"
      }
    }
  }
}
  1. Restart Claude Desktop
  2. Try to use the MCP server (or import it to Claude Code using claude mcp add-from-claude-desktop)

Expected Behavior

The environment variable MOTHERDUCK_TOKEN should be available to the MCP server process with the configured value, and the server should initialize successfully.

Actual Behavior

The MCP server crashes during initialization with authentication errors, indicating it's not receiving the environment variable properly.

Claude Desktop logs (~/Library/Logs/Claude/mcp*.log):

2025-05-23T06:36:31.993Z [info] [mcp-server-motherduck] Client transport closed
2025-05-23T06:36:31.993Z [info] [mcp-server-motherduck] Server transport closed unexpectedly, this is likely due to the process exiting early. If you are developing this MCP server you can add output to stderr (i.e. `console.error('...')` in JavaScript, `print('...', file=sys.stderr)` in python) and it will appear in this log.
2025-05-23T06:36:31.993Z [error] [mcp-server-motherduck] Server disconnected. For troubleshooting guidance, please visit our [debugging documentation](https://modelcontextprotocol.io/docs/tools/debugging)
2025-05-23T06:36:31.993Z [info] [mcp-server-motherduck] Client transport closed

Additional Context

The MCP server works perfectly when run directly from command line:

❯ uvx mcp-server-motherduck --db-path md: --motherduck-token $MOTHERDUCK_TOKEN
[mcp_server_motherduck] INFO - 🦆 MotherDuck MCP Server v0.5
[mcp_server_motherduck] INFO - Ready to execute SQL queries via DuckDB/MotherDuck
[mcp_server_motherduck] INFO - Waiting for client connection...
[mcp_server_motherduck] INFO - Starting MotherDuck MCP Server
[mcp_server_motherduck] INFO - Using MotherDuck token to connect to database `md:`
[mcp_server_motherduck] INFO - Database client initialized in `motherduck` mode
[mcp_server_motherduck] INFO - 🔌 Connecting to motherduck database
[mcp_server_motherduck] INFO - ✅ Successfully connected to motherduck database

Workaround that works:
Hardcoding the token directly in the args array works fine:

{
  "args": [
    "mcp-server-motherduck",
    "--db-path",
    "md:",
    "--motherduck-token",
    "hardcoded_token_here"
  ]
}

Additional notes:

  • I used claude mcp add-from-claude-desktop to import the configuration to Claude Code
  • The issue affects both Claude Desktop and Claude Code when using the same configuration
  • I've tried different variations of the env variable syntax but none work
  • Not sure if I'm doing something wrong with the configuration format - or even if this is the correct place to report?

View original on GitHub ↗

17 Comments

shalalalaw · 1 year ago

Similar issue on Claude Desktop for Windows and similar logs. However, my error logs are giving me additional context that may help diagnose the source of the issue.

2025-06-03T18:48:16.428Z [memory] [info] Message from client: {"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}
npm ERR! code ENOENT
npm ERR! syscall lstat
npm ERR! path C:\Users\shalalalaw\AppData\Local\AnthropicClaude\app-0.9.4\${APPDATA}
npm ERR! errno -4058
npm ERR! enoent ENOENT: no such file or directory, lstat 'C:\Users\shalalalaw\AppData\Local\AnthropicClaude\app-0.9.4\${APPDATA}'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

It looks like the APPDATA path is not being read as a variable, but only when an env argument is passed. Additionally, it's somehow seems to be treating APPDATA as a variable for a folder name instead of a full path.

---
Edit to add that I also have Claude Code running, but it is not experiencing the same issues.

yinebebt · 1 year ago

Similar issue on Claude Desktop(https://github.com/aaddrick/claude-desktop-debian) too. I am using Ubuntu 24.04.2 LTS.

lyzno1 · 12 months ago

+1, Claude Desktop for macos

ain3sh · 12 months ago

+1, Claude Desktop for Windows. Specifically on any mcpServers config that includes an "env" field.

Seems like there's a critical flaw in the MCP runner logic: when an env block is provided, it's passed directly as the env option to child_process.spawn() (or equivalent), without merging it with process.env. That means only the explicitly defined variables in the config are available in the child process — everything else (like APPDATA, PATH, HOME, etc.) is stripped out. This breaks any MCP server that expects standard runtime environment variables.

I've verified this by testing:

  • MCP servers without env: work fine (they inherit the full process.env)
  • MCP servers with even one env var defined: crash (missing APPDATA, which causes npx and related file operations to fail)

For example, with this config:

"memory": {
            "command": "npx",
            "args": [
                "-y",
                "@modelcontextprotocol/server-memory"
            ],
            "env": {
                "MEMORY_FILE_PATH": "C:/Users/user/AppData/Roaming/Claude/memory.json"
            }
        },

The MCP server fails with:

npm ERR! enoent: no such file or directory, lstat 'C:\Users\user\AppData\Local\AnthropicClaude\app-0.12.28\${APPDATA}'

This seems to suggest that APPDATA is missing from the environment entirely, so ${APPDATA} is interpreted literally and path expansion fails.

Proposed fix in the MCP spawning logic:

// instead of:
spawn(command, args, { env: config.env || process.env })
// use:
spawn(command, args, { env: { ...process.env, ...(config.env || {}) } })

which should ensure that injected environment variables are merged with the existing runtime context rather than replacing it outright.

JohnYangSam · 11 months ago

I'm getting this same issue on a locally installed version of Claude Code on Mac.

josephholsten · 11 months ago

I'm not sure of how Claude desktop is evaling these mcpServer commands, but I have to strongly recommend against full shell expansion in the eval. This has led to uncountable security holes over the years.

"But Joseph, no one asked for shell expansion, they just want environment variables!"

Ah yes, this is how it begins. To those who have not screamed at the POSIX Shell Command Language spec, it seems like the simplest thing to just take the environment variables' keys, prepend a $, then substitute that value for another. Well, okay, until you realize that you could be inserting more $ into the mix. So that's got to have validation and a test cases to make sure someone doesn't "optimize" away the safety.

But then someone's going to want to have exciting environment variable names. Why not $$? Okay, so not we've got to add escaping.

But hey, remember how shell does ${HOME} and that seems reasonable too right? Oh wait, that also lets us have parameter expansion with really useful features like defaults such as ${MEMORY_FILE_PATH:=$HOME/.claude/memory.json} or throwing errors on absence like ${MEMORY_FILE_PATH:?Required environment variable MEMORY_FILE_PATH was empty or unset}

Look, I'm not actually concerned about having to implement 80% of Korn Shell '93 in this file. But I am saying that you could use literally any scripting language to emit this JSON. I swear, even system sh on Solaris 10 would give you the power you seek without us ever having to argue about double escaping edge conditions.

Let's say no to expansion, just let strings be strings. Don't make another person become a bitter, sad, specification-reading shell of a human being. Choose life!

josephholsten · 11 months ago

By the way, I'm assuming the reporter meant to type:

{
  "mcpServers": {
    "mcp-server-motherduck": {
      "command": "uvx",
      "args": [
        "mcp-server-motherduck",
        "--db-path",
        "md:",
        "--motherduck-token",
        "$MOTHERDUCK_TOKEN"
      ],
      "env": {
        "MOTHERDUCK_TOKEN": "actual_token_value"
      }
    }
  }
}

because no one in their right mind wants to do a pure string replace on any instance of the env var key.

@ain3sh seems to be having a different issue than what I'm arguing against, merging config.env & process.env is probably the right behavior (safe and unsurprising) unless someone can identify a hard use case that requires tombstoning one of the ones process.env allowlists. I'm mostly acquainted with this from plugins in chef & puppet, and those probably have different needs.

rovamaesn · 11 months ago

Hi, I'm encountering the same issue on windows. The environment variables I’ve configured are not being passed to the MCP server correctly. Are there any updates on this issue?

zendesk-berengamble · 10 months ago

I'm also experiencing this issue. My MCP server requires auth and it's not passing the env vars through so the MCP server can't communicate with the upstream API.

kamranayub · 8 months ago

I will say that I'm not relying on any standard env variables and the env block passes my API key info to the MCP server just fine with Claude Desktop on Windows. One thing I did have to fix though was that I'm using fnm for managing Node and I had to set a global default alias in my PATH so that Claude would see the global installation.

I was actually hoping to find that Claude supported the envFile schema config option like in VS Code but nope, which is disappointing 😢 This can be worked around with more secure secrets management but is just annoying for basic local dev.

wiesel07 · 8 months ago

您好,邮件已收到,我会尽快回复您。愿天天开心,幸福相伴!

b0dea · 8 months ago

Is there any update on this one? My plugins keep not working now because 'env' vars passed through .claude/settings.json are not being correctly fetched now. As a consequence some MCPs do not work at all and some restart themselves resulting in 2 instances (eg Serena). Thanks

github-actions[bot] · 7 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

wiesel07 · 7 months ago

您好,邮件已收到,我会尽快回复您。愿天天开心,幸福相伴!

github-actions[bot] · 5 months ago

This issue has been automatically closed due to 60 days of inactivity. If you're still experiencing this issue, please open a new issue with updated information.

wiesel07 · 5 months ago

您好,邮件已收到,我会尽快回复您。愿天天开心,幸福相伴!

github-actions[bot] · 5 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.