How Environment Variables Work in MCP
MCP servers often need environment variables for API keys, database connections, and runtime configuration. Unlike regular terminal commands, MCP servers are spawned by your client application (Claude Desktop, Cursor, VS Code), which means they do not automatically inherit your shell's environment variables. This is the single most common source of "server starts but tools fail" errors - the server launches fine but cannot authenticate because it does not have the API key you set in your .bashrc.
The MCP configuration supports an "env" field that passes environment variables directly to the server process. This is the primary and most reliable way to configure secrets and API keys for your MCP servers across all clients.
The env Field in MCP Config
Every MCP client that supports stdio transport accepts an "env" field in the server configuration:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}
The env field is a flat key-value object. All values must be strings - not numbers, not booleans, not nested objects. The variables are passed only to that specific server process. They do not affect other servers, the client itself, or any other applications on your system.
Per-Client Environment Variable Handling
Each MCP client handles environment variables differently. Understanding these differences is critical for reliable configuration.
| Client | Inherits Shell Env? | env Field Behavior | Config File |
|---|---|---|---|
| Claude Desktop | Minimal (PATH only) | Merged with minimal system env | claude_desktop_config.json |
| Cursor | More than Claude Desktop | Merged with inherited env | .cursor/mcp.json |
| VS Code | Yes (integrated terminal env) | Merged with VS Code env | .vscode/mcp.json or settings.json |
| Windsurf | Partial | Merged with system env | mcp_config.json |
| Claude Code (CLI) | Yes (full shell env) | Overrides inherited env | .claude/settings.json |
The key takeaway: GUI clients (Claude Desktop, Cursor, Windsurf) do not source your shell profile files (.bashrc, .zshrc, .bash_profile). Environment variables set only in those files will not be available to MCP servers. You must either set them in the "env" field of your MCP config or set them at the OS level (system-wide environment variables).
Claude Desktop
Claude Desktop provides the most restrictive environment. It passes a minimal set of system variables (primarily PATH) plus whatever you specify in the "env" field:
{
"mcpServers": {
"github": {
"command": "/usr/local/bin/npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxxxxxxxxxxxxxxxxxxx",
"GITHUB_API_URL": "https://api.github.com"
}
},
"postgres": {
"command": "/usr/local/bin/npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb"
}
}
}
}
If you need env vars that are set in your shell profile, you must duplicate them in the "env" field. There is no shortcut for this on Claude Desktop.
Cursor
Cursor inherits more of the system environment than Claude Desktop. If an env var is already set at the OS level (not just in your shell profile), Cursor's MCP processes may already have access to it. However, explicitly setting it in the config ensures reliability across different machines and user accounts:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}
VS Code
VS Code MCP configuration supports the "env" field and additionally supports VS Code variable substitution with ${input:variable_name} for interactive secret prompts:
// .vscode/mcp.json
{
"servers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${input:github_token}"
}
}
},
"inputs": [
{
"id": "github_token",
"type": "promptString",
"description": "GitHub Personal Access Token",
"password": true
}
]
}
This prompts you for the token when the server starts, avoiding storing secrets in config files entirely.
CLI Clients
CLI-based MCP clients (like Claude Code) inherit your shell's full environment. You can still use the env field to override or add variables specifically for a server, but most env vars "just work" because the CLI runs in your shell context.
Common Environment Variables
Here are the most commonly used environment variables for popular MCP servers:
API Keys
{
"env": {
"GITHUB_TOKEN": "ghp_xxxx",
"ANTHROPIC_API_KEY": "sk-ant-xxxx",
"OPENAI_API_KEY": "sk-xxxx",
"SLACK_BOT_TOKEN": "xoxb-xxxx",
"GOOGLE_API_KEY": "AIza-xxxx",
"BRAVE_API_KEY": "BSA-xxxx"
}
}
Database Connections
{
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb",
"REDIS_URL": "redis://localhost:6379",
"MONGODB_URI": "mongodb://localhost:27017/mydb",
"SQLITE_PATH": "/path/to/database.db"
}
}
Server Configuration
{
"env": {
"MCP_LOG_LEVEL": "debug",
"MCP_PORT": "3001",
"NODE_ENV": "production",
"PYTHONDONTWRITEBYTECODE": "1",
"MEMORY_FILE_PATH": "/Users/you/memory/claude-memory.json"
}
}
Secrets Manager Integration
Storing API keys directly in MCP config files is convenient for personal use but has security risks, especially in team environments. Here are approaches for integrating with secrets managers:
1Password CLI Integration
1Password's CLI tool (op) can inject secrets at runtime. Create a wrapper script:
#!/bin/bash
# mcp-github-wrapper.sh
export GITHUB_TOKEN=$(op read "op://Development/GitHub/token")
exec npx -y @modelcontextprotocol/server-github "$@"
{
"mcpServers": {
"github": {
"command": "/path/to/mcp-github-wrapper.sh"
}
}
}
AWS Secrets Manager
#!/bin/bash
# mcp-aws-wrapper.sh
export DB_URL=$(aws secretsmanager get-secret-value --secret-id mcp/database --query SecretString --output text)
exec npx -y @modelcontextprotocol/server-postgres "$@"
macOS Keychain
#!/bin/bash
# mcp-keychain-wrapper.sh
export GITHUB_TOKEN=$(security find-generic-password -a "$USER" -s "github-mcp-token" -w)
exec npx -y @modelcontextprotocol/server-github "$@"
Keep Config Out of Version Control
# .gitignore
claude_desktop_config.json
.cursor/mcp.json
.vscode/mcp.json
For comprehensive security guidance, see our MCP Server Security Guide.
Loading .env Files
MCP servers do not automatically load .env files. If your server or project uses a .env file, you need to load it explicitly. Here are the approaches for each language:
Node.js Servers
// Option 1: In your server code, import dotenv at the top
import 'dotenv/config'
// Option 2: Use the --require flag in your MCP config
{
"command": "node",
"args": ["--require", "dotenv/config", "server.js"],
"env": {
"DOTENV_CONFIG_PATH": "/path/to/your/.env"
}
}
// Option 3: Use --env-file flag (Node.js 20.6+)
{
"command": "node",
"args": ["--env-file=.env", "server.js"]
}
Python Servers
# Option 1: In your server code
from dotenv import load_dotenv
load_dotenv("/path/to/your/.env")
# Option 2: Use python-dotenv CLI runner
{
"command": "python3",
"args": ["-m", "dotenv", "-f", "/path/to/.env", "run", "python3", "server.py"]
}
Debugging Missing Environment Variables
If your server is not receiving the right environment variables, follow this debugging workflow:
Step 1: Log the Environment
Add temporary logging to your server to see exactly what variables it receives. The output goes to stderr, which is captured in the client's MCP logs:
// Node.js - add this at the top of your server
console.error('=== MCP Server Environment ===');
console.error('GITHUB_TOKEN:', process.env.GITHUB_TOKEN ? 'SET (length: ' + process.env.GITHUB_TOKEN.length + ')' : 'NOT SET');
console.error('PATH:', process.env.PATH);
console.error('NODE_ENV:', process.env.NODE_ENV);
# Python - add this at the top of your server
import os, sys
print('=== MCP Server Environment ===', file=sys.stderr)
for key in ['GITHUB_TOKEN', 'DATABASE_URL', 'PATH']:
val = os.environ.get(key)
if val:
print(f'{key}: SET (length: {len(val)})', file=sys.stderr)
else:
print(f'{key}: NOT SET', file=sys.stderr)
Check the client's MCP logs for the stderr output. On Claude Desktop, logs are at ~/Library/Logs/Claude/mcp-server-SERVERNAME.log (macOS) or %APPDATA%\Claude\logs\ (Windows).
Step 2: Verify Config Syntax
Common mistakes in the env field that cause silent failures:
// WRONG - using numbers instead of strings
"env": { "PORT": 3000 }
// CORRECT - all values must be strings
"env": { "PORT": "3000" }
// WRONG - nested objects
"env": { "CONFIG": { "key": "value" } }
// CORRECT - flat key-value pairs only
"env": { "CONFIG_KEY": "value" }
// WRONG - boolean value
"env": { "DEBUG": true }
// CORRECT - string representation
"env": { "DEBUG": "true" }
// WRONG - null value
"env": { "OPTIONAL_KEY": null }
// CORRECT - empty string or omit entirely
"env": { "OPTIONAL_KEY": "" }
Step 3: Check PATH Issues
If your server needs specific binaries on the PATH (for example, git, docker, or psql), include PATH in the env field. But be careful - setting PATH in the env field replaces the inherited PATH on most clients. Include all directories you need:
{
"env": {
"PATH": "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:/home/user/.local/bin",
"GITHUB_TOKEN": "ghp_xxxx"
}
}
To find all directories your shell normally has, run echo $PATH in your terminal and copy the full value.
Step 4: Test with MCP Inspector
The MCP Inspector lets you test your server with specific environment variables:
# Set env vars before running the inspector
GITHUB_TOKEN=ghp_xxxx npx @modelcontextprotocol/inspector npx -y @modelcontextprotocol/server-github
If the server works with the Inspector but not with your client, the issue is in how the client passes environment variables - double-check your config file's env field.
Environment Variables for Popular MCP Servers
The official GitHub MCP server is one of the most popular servers and requires specific env vars:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxxx",
"GITHUB_API_URL": "https://api.github.com"
}
}
}
}
Generate a GitHub Personal Access Token at github.com/settings/tokens with the scopes your workflow needs (typically repo, read:org, read:user). For GitHub Enterprise, change GITHUB_API_URL to your enterprise API endpoint.
For a complete list of servers and their required environment variables, browse the MCP servers directory. Each server listing documents the env vars it needs.