You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have searched existing requests and this feature hasn't been requested yet
This is a single feature request (not multiple features)
Problem Statement
Teams using HTTP-based MCP servers in .mcp.json have no clean way to manage per-developer secrets. The config file needs to be committed to the repo so the whole team shares the same MCP server setup, but HTTP servers require auth tokens in headers — putting developers in an impossible position: commit secrets or don't share the config.
For stdio servers, the community workaround is wrapping commands with dotenv-cli to load secrets from a gitignored env file. This works because there's a command to wrap. HTTP servers have no command — just a URL and headers — so there's no place to inject the env file loading.
The current alternatives are all fragile or awkward:
Export vars in ~/.zshrc (not project-scoped, leaks across projects)
Launch with dotenv -e path/to/.env -- claude (easy to forget, breaks if someone just runs claude)
This is a daily friction point for any team using HTTP MCP servers with auth.
Note
This applies specifically to HTTP MCP servers using static Bearer tokens (API keys) — the common pattern for internal and private MCP servers. Servers that support OAuth don't have this problem since Claude Code handles the auth flow interactively. For these internal servers, implementing a full OAuth flow is overkill; they just need a simple way to inject a token without hardcoding it.
Proposed Solution
Add an envFile field to .mcp.json that loads environment variables before any ${VAR} expansion happens:
The path/to/.env file (gitignored) contains the actual secrets in standard dotenv format:
API_TOKEN=sk-abc123
SECRET_KEY=my-secret
The developer experience becomes:
Team commits .mcp.json with ${VAR} references — no secrets, fully shareable
Each developer creates their own path/to/.env with their tokens
Claude Code loads the env file synchronously at startup, before resolving ${VAR} in URLs, headers, args, and env fields
MCP servers connect with the resolved values
This also eliminates the dotenv-cli wrapper hack for stdio servers, giving both transport types a single, consistent way to handle secrets.
Alternative Solutions
1. dotenv-cli wrapper (stdio only)
Works for stdio servers by wrapping the command: npx dotenv-cli -e path/to/.env -- npx @some/server. Not applicable to HTTP servers since there's no command to wrap. Also makes stdio configs verbose and hard to read.
2. Shell profile exports (~/.zshrc)
Each developer adds export API_TOKEN=... to their shell profile. Works reliably but isn't project-scoped — tokens leak across unrelated projects. New developers have to manually figure out which vars to set. Nothing ties the required vars to the project.
Works for both server types since the vars are in the process environment before Claude Code starts. But it's fragile — if anyone runs claude directly, all HTTP MCP servers fail silently. Can't be enforced across a team.
4. SessionStart hook with $CLAUDE_ENV_FILE
A hook that appends path/to/.env contents to $CLAUDE_ENV_FILE at session start. Promising in theory, but has timing issues — MCP servers may initialize before the hook completes. Also $CLAUDE_ENV_FILE can be empty in some contexts (#15840), making this unreliable.
Priority
High - Significant impact on productivity
Feature Category
MCP server integration
Use Case Example
We build a product with two backend services, each exposing an MCP server over HTTP with Bearer token auth. The team has 10+ developers, all using Claude Code Max daily.
Today's painful workflow:
We define both MCP servers in .mcp.json committed to the repo
The HTTP servers need Authorization: Bearer <token> headers
Each developer has their own API token
We tell developers to export tokens in their shell before running Claude — export BACKEND_1_TOKEN=... && export BACKEND_2_TOKEN=... — but people forget, tokens change, and managing 4-5 env vars across multiple MCP servers gets tedious fast
New developers joining the team have to figure out why MCP tools aren't working, discover they need a token, find out how to generate one, and hand-edit the config
With envFile:
.mcp.json is committed with ${BACKEND_1_TOKEN} and ${BACKEND_2_TOKEN} in headers — clean, no secrets
path/to/.env is in .gitignore
Onboarding doc says: "Generate your API tokens, add them to path/to/.env" using path/to/.env.example
Preflight Checklist
Problem Statement
Teams using HTTP-based MCP servers in
.mcp.jsonhave no clean way to manage per-developer secrets. The config file needs to be committed to the repo so the whole team shares the same MCP server setup, but HTTP servers require auth tokens in headers — putting developers in an impossible position: commit secrets or don't share the config.For
stdioservers, the community workaround is wrapping commands withdotenv-clito load secrets from a gitignored env file. This works because there's a command to wrap. HTTP servers have no command — just a URL and headers — so there's no place to inject the env file loading.The current alternatives are all fragile or awkward:
~/.zshrc(not project-scoped, leaks across projects)dotenv -e path/to/.env -- claude(easy to forget, breaks if someone just runsclaude)$CLAUDE_ENV_FILE(timing issues — MCP servers may init before the hook completes; also unreliable per CLAUDE_ENV_FILE not provided to SessionStart hooks #15840).mcp.json(security risk, different per developer, flagged in [BUG] [Privacy Concern] Claude can train on API Secrets if part of MCP configuration #23852)This is a daily friction point for any team using HTTP MCP servers with auth.
Note
This applies specifically to HTTP MCP servers using static Bearer tokens (API keys) — the common pattern for internal and private MCP servers. Servers that support OAuth don't have this problem since Claude Code handles the auth flow interactively. For these internal servers, implementing a full OAuth flow is overkill; they just need a simple way to inject a token without hardcoding it.
Proposed Solution
Add an
envFilefield to.mcp.jsonthat loads environment variables before any${VAR}expansion happens:{ "envFile": "path/to/.env", "mcpServers": { "amazing-software-api": { "type": "http", "url": "http://some-amazing-software.domain/mcp/", "headers": { "Authorization": "Bearer ${API_TOKEN}" } }, "amazing-software-service": { "type": "stdio", "command": "npx", "args": ["-y", "@some/mcp-server"], "env": { "SECRET_KEY": "${SECRET_KEY}" } } } }The
path/to/.envfile (gitignored) contains the actual secrets in standard dotenv format:The developer experience becomes:
.mcp.jsonwith${VAR}references — no secrets, fully shareablepath/to/.envwith their tokens${VAR}in URLs, headers, args, and env fieldsThis also eliminates the
dotenv-cliwrapper hack forstdioservers, giving both transport types a single, consistent way to handle secrets.Alternative Solutions
1.
dotenv-cliwrapper (stdio only)Works for stdio servers by wrapping the command:
npx dotenv-cli -e path/to/.env -- npx @some/server. Not applicable to HTTP servers since there's no command to wrap. Also makes stdio configs verbose and hard to read.2. Shell profile exports (
~/.zshrc)Each developer adds
export API_TOKEN=...to their shell profile. Works reliably but isn't project-scoped — tokens leak across unrelated projects. New developers have to manually figure out which vars to set. Nothing ties the required vars to the project.3. Launcher wrapper (
dotenv -e path/to/.env -- claude)Works for both server types since the vars are in the process environment before Claude Code starts. But it's fragile — if anyone runs
claudedirectly, all HTTP MCP servers fail silently. Can't be enforced across a team.4. SessionStart hook with
$CLAUDE_ENV_FILEA hook that appends
path/to/.envcontents to$CLAUDE_ENV_FILEat session start. Promising in theory, but has timing issues — MCP servers may initialize before the hook completes. Also$CLAUDE_ENV_FILEcan be empty in some contexts (#15840), making this unreliable.Priority
High - Significant impact on productivity
Feature Category
MCP server integration
Use Case Example
We build a product with two backend services, each exposing an MCP server over HTTP with Bearer token auth. The team has 10+ developers, all using Claude Code Max daily.
Today's painful workflow:
.mcp.jsoncommitted to the repoAuthorization: Bearer <token>headersexport BACKEND_1_TOKEN=... && export BACKEND_2_TOKEN=...— but people forget, tokens change, and managing 4-5 env vars across multiple MCP servers gets tedious fastWith
envFile:.mcp.jsonis committed with${BACKEND_1_TOKEN}and${BACKEND_2_TOKEN}in headers — clean, no secretspath/to/.envis in.gitignorepath/to/.env" usingpath/to/.env.examplepath/to/.env:.mcp.jsonmerge cleanly since there are no local editsstdioservers — one consistent approach instead of twoAdditional Context
No response