claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

[BUG] env variable expansion not working in plugin .mcp.json

Open yonijacobi opened this issue 3 months ago β€’ 26 comments

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

  1. install plugin with mcp servers using the builtin /plugin comand
  2. restart claude and run /mcp to verify the mcp servers from the plugin installed
  3. 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

yonijacobi avatar Oct 12 '25 15:10 yonijacobi

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/6204
  2. https://github.com/anthropics/claude-code/issues/3239
  3. 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

github-actions[bot] avatar Oct 12 '25 15:10 github-actions[bot]

πŸ‘Ž

yonijacobi avatar Oct 12 '25 15:10 yonijacobi

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

creatorrr avatar Oct 12 '25 19:10 creatorrr

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

evrimalacan avatar Oct 14 '25 19:10 evrimalacan

Having the same issue 🀚🏻

joelclimbsthings avatar Oct 15 '25 23:10 joelclimbsthings

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}"
      ]
    }
  }
}

vmandela avatar Oct 20 '25 11:10 vmandela

Same issues from notion mcp env variable

"notionApi": {
	"command": "npx",
	"args": ["-y", "@notionhq/notion-mcp-server"],
	"env": {
		"NOTION_TOKEN": "${NOTION_TOKEN}"
	}
}

hongwei0417 avatar Oct 21 '25 14:10 hongwei0417

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)

theagenticguy avatar Oct 21 '25 15:10 theagenticguy

Likewise, glad to know it's not something I was doing wrong on my end.

Manouchehri avatar Oct 28 '25 01:10 Manouchehri

I can confirm this bug as well.

dvic avatar Oct 30 '25 09:10 dvic

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')

theagenticguy avatar Nov 04 '25 14:11 theagenticguy

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

smar-sean-sekora avatar Nov 06 '25 18:11 smar-sean-sekora

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)

neotheprogramist avatar Nov 08 '25 08:11 neotheprogramist

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

evrimalacan avatar Nov 10 '25 08:11 evrimalacan

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

smar-sean-sekora avatar Nov 10 '25 16:11 smar-sean-sekora

Can anyone tell me in which version this issue will be fixed?

caodegao-jk avatar Nov 13 '25 12:11 caodegao-jk

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.

no1187 avatar Nov 13 '25 21:11 no1187

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

evrimalacan avatar Nov 13 '25 21:11 evrimalacan

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

no1187 avatar Nov 13 '25 21:11 no1187

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

theagenticguy avatar Nov 14 '25 00:11 theagenticguy

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

b0dea avatar Nov 17 '25 12:11 b0dea

Same issue.

KylinWu avatar Nov 20 '25 06:11 KylinWu

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

b0dea avatar Nov 20 '25 06:11 b0dea

Surprised this issue is still not fixed. Spent an hour trying to debug this before stumbling upon this open issue.

shukieshah avatar Dec 07 '25 08:12 shukieshah

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 avatar Dec 09 '25 06:12 shukieshah

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

ffriedl89 avatar Dec 18 '25 08:12 ffriedl89

I don't think this is solved either. Encountering the issue on 2.0.73.

TyceHerrman avatar Dec 19 '25 20:12 TyceHerrman

@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?

shukieshah avatar Dec 22 '25 17:12 shukieshah

@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?

yep, "env" gets replaced correctly. However some http MCP servers expect a key in the URL param. not sure if those are expanding yet.

theagenticguy avatar Dec 24 '25 16:12 theagenticguy