← back to stream

Passing env vars to Cursor MCP servers

#tools#ai#case

Cursor doesn't load .env automatically for MCP servers — something I assumed it would, and every new MCP install I hit the same wall: the server starts with zero env and complains about missing keys. It's a real gap in the product, and there's no one right fix. Four options, each with tradeoffs.

1. Hardcode in mcp.json

The env field on each server takes literal strings — no ${VAR} interpolation, no .env loading. If .cursor/ is in .gitignore (it is by default), the secrets never leave your machine.

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..." }
    }
  }
}

This is what I ended up using for every personal workspace. Simplest, zero indirection, no magic.

2. Inherit from .zshrc / login shell

Cursor launches with your shell's environment, so anything exported in ~/.zshrc or via launchctl setenv is visible to MCP servers. Good when you want one key (OPENAI_API_KEY, ANTHROPIC_API_KEY) available across every project. Bad when you want per-project isolation — any repo can read any key.

3. Shell wrapper in the app bundle

The same trick I use for two Cursor profiles — replace Cursor.app/Contents/MacOS/Cursor with a shell script that sources a profile-scoped env file before execing the real binary:

#!/bin/sh
set -a
. "$HOME/.cursor_work.env"
set +a
exec "/Applications/Cursor.app/Contents/MacOS/Cursor.real" "$@"

Clean isolation between work and personal Cursor profiles — each bundle sources its own env file. Costs a Cursor restart every time you edit the file, and the wrapper has to be restored after Cursor auto-updates.

4. Cursor Secrets

In Settings → Secrets. Only available for Cloud Agent / remote sessions. Local MCP processes don't see them. Nice in theory, not a solution for local dev.

What I actually do

For solo work: hardcode. .cursor/ isn't committed, so there's no leak risk, and hardcoding removes all indirection.

For a team repo where you want to commit mcp.json: commit the template without secrets (.cursor/mcp.json), add .env.cursor.example listing required vars, and document that each developer exports them in .zshrc or their own shell profile. Effectively option 2 with a written contract.

The gap is real — Cursor doesn't ship a native .env loader for MCP — so until it does, pick the least magical option that fits your trust boundary. For me that's hardcode; for a team, it's inherited shell env with a documented list.