Security

Environment Variables and Secrets for MCP Servers

Configure env vars and secrets for local and remote MCP — official security guidance, stdio vs HTTP logging, and what never belongs in git or mcplambda.yaml.

10 min read Published July 14, 2026 Updated July 14, 2026

Configuration mistakes are one of the fastest ways to turn a working MCP demo into a security incident.

Rules of thumb

  1. Code reads configuration from the environment
  2. Secrets never live in git, images, client config committed to repos, or tool results
  3. Document required keys in README / .env.example (empty values)
  4. Prefer least privilege upstream credentials (scope minimization)

Local development

PracticeDetail
.env gitignoredReal values only on the machine
.env.example committedNames + dummy placeholders
Host env mapClaude/Cursor stdio configs often pass env — don’t commit those files with secrets

Official local install security: show full commands and consent before one-click local server execution (local MCP compromise).

Stdio logging

From Build an MCP server and stdio transport:

  • MUST NOT write non-MCP messages to stdout
  • Log to stderr or files
  • TypeScript: console.error, not console.log
  • Python: print(..., file=sys.stderr) or logging to stderr

Remote / production

KindExamplesStorage
Non-secret configLOG_LEVEL, NODE_ENVEnv vars OK
SecretsAPI tokens, DB passwordsSecret manager / platform secrets
Client auth materialDeploy API keys, OAuthIssued per user/team; rotatable

On MCPLambda: set env and secrets in dashboard, CLI (-e), or API; secrets overwrite plain env with the same key when configured that way (see deploy/API docs). Do not put secret values in mcplambda.yaml.

Auth tokens vs app secrets

Token typePurpose
MCP client → server authWho may call your MCP endpoint (authorization)
Server → upstream credentialsHow tools call Slack/DB/SaaS

Do not pass client tokens through to upstream APIs (no token passthroughsecurity best practices).

Tool outputs

From tools security: servers MUST sanitize tool outputs. Never return raw secret material in content.

Naming conventions

  • Prefer SERVICE_API_KEY over KEY
  • Fail fast at startup if required vars missing
  • Separate *_STAGING vs prod names if both exist in one process (prefer separate deploys)

Anti-patterns

  • Baking tokens into Docker layers
  • Printing full env on startup
  • One prod secret across all preview deploys
  • Sharing PATs in chat instead of issued remote credentials (team-shared remote)

Checklist

  • .env ignored; example committed
  • Secrets only via platform/secret store in prod
  • Stdio logs to stderr
  • Tool results scrubbed
  • Rotation path documented
  • Offboarding revokes client + upstream credentials

Worked scenario: The committed .env that escaped

A developer commits .env with a prod GitHub PAT so “onboarding is easier.” The token is scraped within days. Every stdio config in the team still embeds the same PAT.

You rotate the token, move secrets to the platform secret store, add .env to gitignore with a committed .env.example, and issue per-user remote credentials for the shared server. mcplambda.yaml stays free of secrets—only build/run/transport.

You also ban startup logs that dump os.environ, and you verify tool results never echo credentials.

Checklist for this topic

  • gitignore real env files; commit .env.example only
  • Runtime secret injection for prod
  • No secrets in mcplambda.yaml or images
  • Separate MCP client auth from upstream API secrets
  • Never log secret values (stdio → stderr for app logs)
  • Rotate on offboarding the same day

Topic-specific failure modes

FailureLikely causeFix
Leaked PATSecrets in git/chatRotate; use secret store
Works for author onlyLaptop-absolute pathsEnv-based config only
Token passthroughForwarding client tokens upstreamMint server-side upstream creds
Preview hits prod dataShared prod secretsEnv-scoped credentials

Securing remote · Team-shared remote · Auth patterns

In practice: secret classes

Label every secret:

  1. Bootstrap — needed to start process (fatal if missing)
  2. Upstream — third-party API keys used inside tools
  3. Edge auth — validates MCP clients

Different rotation and access policies apply. Mixing them in one “SECRETS” bag is how preview environments accidentally share prod upstream keys.

Example README env table

NameRequiredSecret?Description
UPSTREAM_API_BASEyesnoAPI base URL
UPSTREAM_API_KEYyesyesUpstream key
LOG_LEVELnonodefault info

Agents and humans should be able to configure staging from this table alone.

Takeaways

For Environment Variables and Secrets for MCP Servers, remember three things:

  1. Be specific to this problem — the worked scenario “The committed .env that escaped” is the failure mode you should design against, not a generic outage narrative.
  2. Secrets live in the platform secret store, not in git, images, or chat transcripts.
  3. Rotate credentials after any leak path; treat .env commits as incidents.

If you only remember one habit: never put long-lived API keys in MCP tool results or error messages.

Sources

Next steps

Securing remote MCP · Production mcplambda.yaml

FAQs

Frequently Asked Questions

  • Should secrets go in mcplambda.yaml?

    No. mcplambda.yaml is for Git build/run/transport configuration. Inject secrets at deploy time via the platform secret store or deploy flags — never commit production secrets.

  • Can I log env vars for debugging?

    Never log secret values. For stdio servers, also never write application logs to stdout — that corrupts JSON-RPC (official build-server and stdio transport guidance).