headersHelper in plugin .mcp.json does not receive CLAUDE_PLUGIN_ROOT or CLAUDE_PLUGIN_DATA env vars
Summary
headersHelper in a plugin-provided .mcp.json does not expand ${CLAUDE_PLUGIN_ROOT} in the command string, nor is CLAUDE_PLUGIN_ROOT (or CLAUDE_PLUGIN_DATA) available as an environment variable when the helper command executes.
The only environment variables available to headersHelper are:
CLAUDE_CODE_MCP_SERVER_NAMECLAUDE_CODE_MCP_SERVER_URL
This makes it impossible for plugins to reference bundled authentication scripts in headersHelper, forcing authors to either inline the entire command or use absolute paths (which break on distribution).
Steps to reproduce
- Create a plugin with a script at
scripts/get-token.sh - Configure
.mcp.jsonin the plugin root:
{
"mcpServers": {
"my-server": {
"type": "http",
"url": "https://example.com/api/2.0/mcp/endpoint",
"headersHelper": "${CLAUDE_PLUGIN_ROOT}/scripts/get-token.sh"
}
}
}
- Install the plugin and start Claude Code
- The MCP server fails to connect —
${CLAUDE_PLUGIN_ROOT}is not expanded and the script is not found
Verified behavior
| Approach | Result |
|----------|--------|
| "headersHelper": "${CLAUDE_PLUGIN_ROOT}/scripts/get-token.sh" | ❌ Not expanded, script not found |
| "headersHelper": "$CLAUDE_PLUGIN_ROOT/scripts/get-token.sh" | ❌ Env var not set, script not found |
| "headersHelper": "/absolute/path/to/scripts/get-token.sh" | ✅ Works |
| "headersHelper": "inline command that outputs JSON" | ✅ Works |
| "command": "${CLAUDE_PLUGIN_ROOT}/servers/my-server" (stdio) | ✅ Works (expansion works for command field) |
Expected behavior
Either:
${CLAUDE_PLUGIN_ROOT}should be expanded inheadersHelperstrings (same ascommand,args,env,url,headers), ORCLAUDE_PLUGIN_ROOTandCLAUDE_PLUGIN_DATAshould be set as environment variables when theheadersHelpercommand executes (same as they are for hook processes and MCP server subprocesses)
Documentation inconsistency
The plugins reference states:
"All are substituted inline anywhere they appear in skill content, agent content, hook commands, monitor commands, and MCP or LSP server configs."
This implies headersHelper (which is part of an MCP server config) should support the substitution, but it does not.
The MCP docs env var expansion section lists supported expansion locations as: command, args, env, url, headers — notably headersHelper is missing from this list.
Impact
This blocks plugins from using native HTTP transport with dynamic authentication headers for remote MCP servers. Plugin authors are forced to either:
- Inline the entire auth command (works but less maintainable)
- Use stdio transport with a proxy like
mcp-remote(adds unnecessary dependencies)
This is particularly impactful for MCP servers where tokens must be fetched dynamically.
Related
- #47789 (same issue, closed as stale without fix)
Environment
- Claude Code: v2.1.178
- Platform: macOS (darwin)
- Plugin installed via marketplace
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗