[BUG] Environment variables from `env` section not passed to MCP servers
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
- Configure an MCP server in
~/Library/Application Support/Claude/claude_desktop_config.jsonwith 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"
}
}
}
}
- Restart Claude Desktop
- 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-desktopto 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?
17 Comments
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.
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.
Similar issue on Claude Desktop(https://github.com/aaddrick/claude-desktop-debian) too. I am using Ubuntu 24.04.2 LTS.
+1, Claude Desktop for macos
+1, Claude Desktop for Windows. Specifically on any
mcpServersconfig that includes an"env"field.Seems like there's a critical flaw in the MCP runner logic: when an
envblock is provided, it's passed directly as theenvoption tochild_process.spawn()(or equivalent), without merging it withprocess.env. That means only the explicitly defined variables in the config are available in the child process — everything else (likeAPPDATA,PATH,HOME, etc.) is stripped out. This breaks any MCP server that expects standard runtime environment variables.I've verified this by testing:
env: work fine (they inherit the fullprocess.env)envvar defined: crash (missingAPPDATA, which causesnpxand related file operations to fail)For example, with this config:
The MCP server fails with:
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:
which should ensure that injected environment variables are merged with the existing runtime context rather than replacing it outright.
I'm getting this same issue on a locally installed version of Claude Code on Mac.
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!
By the way, I'm assuming the reporter meant to type:
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.envis probably the right behavior (safe and unsurprising) unless someone can identify a hard use case that requires tombstoning one of the onesprocess.envallowlists. I'm mostly acquainted with this from plugins in chef & puppet, and those probably have different needs.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?
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.
I will say that I'm not relying on any standard env variables and the
envblock 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
envFileschema 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.您好,邮件已收到,我会尽快回复您。愿天天开心,幸福相伴!
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
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.
您好,邮件已收到,我会尽快回复您。愿天天开心,幸福相伴!
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.
您好,邮件已收到,我会尽快回复您。愿天天开心,幸福相伴!
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.