[BUG] env variable expansion not working in plugin .mcp.json
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?
When including env variables in .mcp.json file inside plugin root folder instead of reading the value of the variables it reads the var string as is ("${VAR}"), with same file in the project root instead of the plugin folder it not happens
What Should Happen?
It should use the envioronment variables like in regular .mcp.json file, as mentioned in the docs
Error Messages/Logs
Steps to Reproduce
- install plugin with mcp servers using the builtin /plugin comand
- restart claude and run /mcp to verify the mcp servers from the plugin installed
- use tool that requires permissions or other usage of env variables
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
No response
Claude Code Version
2.0.14
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
No response
Found 3 possible duplicate issues:
- https://github.com/anthropics/claude-code/issues/6204
- https://github.com/anthropics/claude-code/issues/3239
- https://github.com/anthropics/claude-code/issues/1254
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and π the existing issue instead
- To prevent auto-closure, add a comment or π this comment
π€ Generated with Claude Code
π
I had the same experience. Env var substitution works for the ones defined in local .mcp.json but not when inside a plugin's .mcp.json file
I got very excited about the plugins and quickly created a marketplace with our company's favorite MCPs hoping this will give us an easy way to share MCPs, only to notice the same problem... Each server in the plugin's .mcp.json is throwing authentication errors π
By the way, this issue seems to affect hooks as well: https://github.com/anthropics/claude-code/issues/9447
Having the same issue π€π»
Have the same issue. ${PWD} is passed literally to the mcp invocation without environment variable expansion.
{
"mcpServers": {
"serena": {
"type": "stdio",
"command": "uvx",
"args": [
"--from",
"git+https://github.com/oraios/[email protected]",
"serena",
"start-mcp-server",
"--context",
"ide-assistant",
"--log-level",
"DEBUG",
"--project",
"${PWD}"
]
}
}
}
Same issues from notion mcp env variable
"notionApi": {
"command": "npx",
"args": ["-y", "@notionhq/notion-mcp-server"],
"env": {
"NOTION_TOKEN": "${NOTION_TOKEN}"
}
}
Also have the same issue:
{
"mcpServers": {
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "${CONTEXT7_API_KEY}"
}
},
"deepwiki": {
"type": "http",
"url": "https://mcp.deepwiki.com/mcp"
},
"brave-search": {
"command": "npx",
"args": ["-y", "@brave/brave-search-mcp-server"],
"env": {
"BRAVE_API_KEY": "${BRAVE_API_KEY}"
}
},
"tavily": {
"type": "http",
"url": "https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"
},
"exa": {
"type": "http",
"url": "https://mcp.exa.ai/mcp/?exaApiKey=${EXA_API_KEY}"
}
}
}
2.0.24 (Claude Code)
Likewise, glad to know it's not something I was doing wrong on my end.
I can confirm this bug as well.
Update: environment variables seem to get expanded correctly in the "env" key-value pairs, because Brave API is working. Everywhere else, they are not expanded:
claude --version
>>> 2.0.32 (Claude Code)
β plugin:ai-engineering:context7 - resolve-library-id (MCP)(libraryName: "amazon bedrock agentcore")
βΏ Β Unauthorized. Please check your API key. The API key you provided (possibly incorrect) is: ${CONTEXT7_API_KEY}. API keys should start with 'ctx7sk'
β Context7 Results:
β Failed - Both Context7 instances (essentials and ai-engineering) require API key configuration. The API key should start with 'ctx7sk' but is currently set to a placeholder value ${CONTEXT7_API_KEY}.
Overall Web Search Tool Status:
- Brave Search: β
Working - fully configured and functional
- Tavily: β Needs API key (401 Unauthorized)
- Exa: β Configuration issue (400 Bad Request)
- Context7: β Needs API key (should start with 'ctx7sk')
same issue... it works when adding the MCP configuration to ~/.claude.json, but for the plugin's .mcp.json I need to hard code the value for it to work
I confirm this bug as well. I cannot keep my github token safe in envs and setup Github mcp server in my claude code plugin.
{
"mcpServers": {
"memory": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"env": {}
},
"git": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-server-git"],
"env": {}
},
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp",
"headers": {
"Authorization": "Bearer ${GITHUB_PAT}"
}
}
}
}
2.0.36 (Claude Code)
This issue is killing the whole purpose of plugins. Sad to see it isn't getting any attention. I don't get why anybody would develop plugins if it requires exposing secrets
It would be great if the plugins had a similar feature as Roo Code Marketplace and define values to ask the person installing the plugin to supply
Can anyone tell me in which version this issue will be fixed?
I'm literally so annoyed
I found the issue. The .mcp.json file is using the ${VARIABLE} placeholder format, but according to the Anthropic standard mentioned in the CLAUDE.md, the env object should be empty (env: {}) to automatically load from the .env file.
I'm literally so annoyed
I found the issue. The .mcp.json file is using the ${VARIABLE} placeholder format, but according to the Anthropic standard mentioned in the CLAUDE.md, the env object should be empty (env: {}) to automatically load from the .env file.
not sure I follow
I'm literally so annoyed I found the issue. The .mcp.json file is using the ${VARIABLE} placeholder format, but according to the Anthropic standard mentioned in the CLAUDE.md, the env object should be empty (env: {}) to automatically load from the .env file.
not sure I follow
Sorry that's the error message I'm getting from Claude Code , it's running in loops
I'm literally so annoyed I found the issue. The .mcp.json file is using the ${VARIABLE} placeholder format, but according to the Anthropic standard mentioned in the CLAUDE.md, the env object should be empty (env: {}) to automatically load from the .env file.
not sure I follow
Sorry that's the error message I'm getting from Claude Code , it's running in loops
Claude Code wouldn't be able to fix it, unless it was able to inspect the TypeScript code of Claude Code itself. It's not an LLM problem, it's a problem with parsing ${VARIABLES} in places other than the env: key-object pair
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
Same issue.
Same issue.
Created a fresh issue myself, maybe grabs devs attention... - https://github.com/anthropics/claude-code/issues/11927
Also, maybe there is hope to get this fixed soon, just saw here this comment: https://github.com/anthropics/claude-code/issues/11960#issuecomment-3554770561
Surprised this issue is still not fixed. Spent an hour trying to debug this before stumbling upon this open issue.
SOLVED: Actually NVM, all I needed to do was update Claude Code. Seems like they fixed this in the latest release! :)
Surprised this issue is still not fixed. Spent an hour trying to debug this before stumbling upon this open issue.
@shukieshah Could you please share your version that you are using and how your .mcp.json file looks like inside your plugin?
I still have the same issue and am using version 2.0.71 of claude code.
I don't think this is solved either. Encountering the issue on 2.0.73.
@ffriedl89 I'm currently using Claude 2.0.72 (although latest version seems to be 2.0.75). Here is what my MCP configuration looks like inside my plugin.json file:
{
...
"mcpServers": {
"asymptote-security-mcp": {
"command": "npx",
"args": ["-y", "@asymptote-labs/asymptote-security-mcp"],
"env": {
"ASYMPTOTE_API_KEY": "${ASYMPTOTE_API_KEY}"
}
}
},
"hooks": {
...
}
}
Env var evaluation works fine on my end. Have distributed this plugin to others and it works fine for them as well.
Have you tried providing your MCP server config inline within your plugin.json as per https://code.claude.com/docs/en/mcp#plugin-provided-mcp-servers?
@ffriedl89 I'm currently using Claude 2.0.72 (although latest version seems to be 2.0.75). Here is what my MCP configuration looks like inside my
plugin.jsonfile:{ ... "mcpServers": { "asymptote-security-mcp": { "command": "npx", "args": ["-y", "@asymptote-labs/asymptote-security-mcp"], "env": { "ASYMPTOTE_API_KEY": "${ASYMPTOTE_API_KEY}" } } }, "hooks": { ... } }Env var evaluation works fine on my end. Have distributed this plugin to others and it works fine for them as well.
Have you tried providing your MCP server config inline within your
plugin.jsonas per https://code.claude.com/docs/en/mcp#plugin-provided-mcp-servers?
yep, "env" gets replaced correctly. However some http MCP servers expect a key in the URL param. not sure if those are expanding yet.