Developer guide 08

How to Configure Secrets and Environment Variables for AI Coding Agents

Configure secrets for coding agents without leaking keys into prompts, logs, screenshots, MCP config, background agents, or committed files.

How to Configure Secrets and Environment Variables for AI Coding Agents editorial hero image
How to Configure Secrets and Environment Variables for AI Coding Agents

Quick answer

Configure secrets for coding agents without leaking keys into prompts, logs, screenshots, MCP config, background agents, or committed files.

Never put secrets in prompts, committed instruction files, chat transcripts, screenshots, or example config that an agent might copy into a diff. Treat secrets as runtime inputs, not context. The agent can know the name of an environment variable, the service it authenticates to, and the command that needs it. It should not need to see the raw value unless the runtime is injecting that value into the process that actually uses it.

The clean mental model is:

Prompt: tells the agent what to do.
Instruction file: tells the agent how the repo works.
Config file: tells the agent host what tools exist.
Environment variable: gives a process a runtime value.
Secret store: protects and scopes the sensitive value.

When those layers collapse into one place, leaks happen.

What counts as a secret

A secret is any value that lets someone authenticate, impersonate, decrypt, mutate data, bypass approval, or reach a private system.

Common examples:

  • API keys, access tokens, refresh tokens, OAuth client secrets.
  • Database URLs with credentials.
  • Cloud provider keys and service account JSON.
  • GitHub, GitLab, npm, PyPI, Stripe, Slack, Linear, Sentry, Vercel, Firebase, Supabase, OpenAI, Anthropic, or internal platform tokens.
  • Webhook signing secrets.
  • SSH private keys and deploy keys.
  • Session cookies and bearer tokens captured from a browser.
  • Customer exports, production logs, and private test fixtures when they identify real users.

Non-secret configuration still matters, but it can usually live in committed files:

NODE_ENV=development
PUBLIC_APP_URL=http://localhost:3000
FEATURE_SEARCH_V2=true
OPENAI_MODEL=gpt-5.1

The dividing line is simple: if posting the value in a public issue would create work for security, billing, or customer support, treat it as a secret.

Prompts are not secret managers

Do not paste a key into chat because "the agent only needs it once." The transcript may be retained by the tool provider under the applicable product policy, copied into logs, summarized later, or accidentally included in generated docs. More practically, the agent may reuse the value in a command, write it into a .env.example, include it in a screenshot, or paste it into a PR comment while trying to be helpful.

Give the agent handles, not values:

Bad:
Use sk-live-... to call the billing API.

Good:
The billing API key is available as BILLING_API_KEY in the local shell. Run the billing tests only against the sandbox account.

Instruction files should do the same:

Use `STRIPE_SECRET_KEY` for local payment tests. Never read, print, commit, or screenshot `.env*` files.

That tells the agent the contract without turning the instruction file into a credential bundle.

Local .env vs shell env vs MCP env

Local agents usually see secrets through one of three paths:

LocationGood useRisiko
.env.local oder .env.developmentApp runtime secrets for local developmentEasy to read, print, or accidentally commit
Shell environmentOne-off commands and test runsCan leak through process lists, logs, and inherited subprocesses
Agent or MCP config env mappingTool-specific credentialsConfig may get committed or copied between machines

The safest local pattern is:

  1. Keep real values out of prompts and repo instructions.
  2. Put local secret files in .gitignore.
  3. Provide .env.example with names and fake values only.
  4. Deny agent read access to .env* where the host supports it.
  5. Pass only the variables a command or MCP server needs.

Beispiel .env.example:

OPENAI_API_KEY=replace_with_dev_key
GITHUB_TOKEN=replace_with_repo_scoped_token
DATABASE_URL=postgres://user:password@localhost:5432/app_dev

Example prompt:

Run the integration tests. Required variables are already available in the shell. Do not inspect `.env.local`; if a variable is missing, tell me the variable name.

That prompt gives the agent a recovery path without inviting it to open the secret file.

MCP environment variables

MCP servers often need credentials because they connect the agent to external tools. That makes MCP configuration one of the highest-risk places in an agent setup.

Codex's MCP configuration supports multiple ways to pass runtime values: env for server-specific variables, env_vars for forwarded variables, bearer_token_env_var for HTTP bearer-token auth, and env_http_headers for HTTP headers whose values come from the environment. Use those mechanisms to reference variables, not to publish raw tokens in a shared config.

For a local stdio MCP server, prefer a pattern like this:

[mcp_servers.internal_docs]
command = "node"
args = ["./tools/mcp/internal-docs-server.js"]
env_vars = ["INTERNAL_DOCS_TOKEN"]
enabled_tools = ["search_docs", "read_doc"]

For an HTTP MCP server, prefer an environment-backed token:

[mcp_servers.issue_tracker]
url = "https://mcp.example.com"
bearer_token_env_var = "ISSUE_TRACKER_MCP_TOKEN"
enabled_tools = ["get_issue", "search_issues"]

Use explicit tool allowlists when the server exposes both read and write actions. A docs search tool and a "delete project" tool should never inherit the same trust just because they come from the same server.

Cursor's MCP docs describe project config in .cursor/mcp.json and global config in ~/.cursor/mcp.json, with environment-variable interpolation such as ${env:API_KEY}. The same rule applies: reference the environment, do not hardcode the value in a committed project file.

Remote and background agents

Remote agents need a different secret plan because they are not running inside your laptop shell. They clone the repo, install dependencies, run commands, and sometimes push branches from a cloud environment.

GitHub Copilot cloud agent has dedicated Agents secrets and variables, separate from Actions, Codespaces, and Dependabot. GitHub's docs say these values are exposed to the cloud agent as environment variables, while values prefixed with COPILOT_MCP_ are only available to MCP configuration. That prefix is useful for separating "the setup script needs this" from "the MCP server needs this."

Good Copilot cloud agent split:

NPM_TOKEN
Used by setup steps to install private packages.

TEST_DATABASE_URL
Used by tests in the agent environment.

COPILOT_MCP_LINEAR_TOKEN
Used only by MCP configuration for Linear.

Cursor background agents run in a remote Ubuntu-based environment by default, can install packages, and can auto-run terminal commands while the agent works. Cursor's docs also describe entering required secrets for the dev environment, stored encrypted at rest and provided in the background-agent environment. That auto-run behavior is exactly why secrets need to be scoped. A token that is safe for a local test may not be safe for an unattended remote process with internet access.

Remote-agent rules:

  • Use development, staging, or sandbox credentials.
  • Prefer repository-scoped or task-scoped tokens.
  • Avoid personal tokens tied to a human's broad account.
  • Do not expose production credentials just so tests can pass.
  • Rotate secrets used by background agents more aggressively than ordinary local dev secrets.
  • Log which secret names were available to the agent, but never the values.

Token scopes and least privilege

The best secret is temporary, narrow, and boring.

For coding agents, least privilege means:

Wrong:
One personal admin token for GitHub, cloud, packages, database, and deploys.

Better:
Separate repo-scoped GitHub token, read-only package token, staging database URL, and preview-deploy token.

Best:
Short-lived credentials minted for the task, with write access only when the task genuinely needs it.

Scope by environment:

NeedPreferVermeiden
Install private packagesRead-only package tokenOrg-wide publish token
Run integration testsStaging or local databaseProduction database
Query issue trackerRead-only tokenToken that can bulk-edit projects
Create PR evidenceRepo-scoped tokenUser token with all repos
Preview deployPreview-only deployment tokenProduction deploy token
MCP docs searchRead-only docs tokenAdmin API token

If a provider supports expiration, use it. If it supports fine-grained repository access, use it. If it supports workload identity or OIDC for CI-style environments, prefer that over storing long-lived cloud keys.

How to test without production keys

Agents are good at finding the path of least resistance. If the only credential that works is production, they will ask for production or fail in confusing ways. Build a testable path instead.

A healthy repo has:

  • .env.example with every required variable name.
  • A setup command that fails with missing variable names, not vague stack traces.
  • Mock providers for unit tests.
  • Sandbox accounts for payment, email, analytics, and CRM tools.
  • Seeded local data that does not contain real customer records.
  • A way to run "external" tests behind an explicit command such as npm run test:integration.

Example missing-secret error:

Missing required environment variable: STRIPE_SECRET_KEY.
Use a sandbox key. Do not use a production key for local or agent tests.

That error is more useful than letting the agent open .env.local to investigate.

If a key was pasted into a prompt

Treat it as leaked. Do not debate whether the model "probably forgot." The response playbook is:

  1. Revoke or rotate the key at the provider.
  2. Check provider logs for unexpected use after the paste time.
  3. Search the repo for the exact key and nearby partial values.
  4. Search generated files, screenshots, logs, PR comments, issue comments, and terminal output.
  5. If the key was committed, remove it from active history only if your organization requires it, but rotate first.
  6. Replace the workflow with a variable-name based instruction so the leak does not happen again.

Use provider-specific scanning where available, but do not wait for a scanner to prove the leak. Rotation is cheaper than uncertainty.

Secrets in logs, screenshots, and generated files

Agent leaks often do not happen in the obvious prompt. They happen in the work product:

  • A failed test prints the entire environment.
  • A debug script logs request headers.
  • A screenshot captures an admin page with a token visible.
  • A generated README includes a real .env value as an example.
  • A PR comment includes a command with a bearer token.
  • An MCP server logs full tool parameters.

Add defensive defaults:

Never print process.env wholesale.
Never screenshot pages that show tokens, cookies, or customer PII.
Never ask the agent to "show me the .env file."
Never commit .env, .pem, .key, service-account JSON, database dumps, or session exports.
Redact Authorization, Cookie, Set-Cookie, X-API-Key, and webhook-signature headers in logs.

OWASP's MCP security guidance specifically recommends logging MCP tool invocations while redacting secrets and PII. That pairing matters. You want enough audit detail to reconstruct what happened, but not so much that the audit log becomes the leak.

Final safe setup example

For a local coding agent using an MCP docs server and a staging integration test suite:

Repository:
- Commit `.env.example`.
- Ignore `.env*` except examples.
- Add instruction: "Do not read, print, or commit `.env*`."
- Keep MCP config in project only if it contains no raw values.

Local shell:
- Export INTERNAL_DOCS_TOKEN with read-only docs scope.
- Export STAGING_API_KEY with limited test scope.

Agent permissions:
- Read and edit workspace files.
- Deny reads of `.env*`.
- Allow `npm test`, `npm run lint`, and `npm run build`.
- Ask before network calls outside approved domains.

MCP:
- Use `env_vars` or environment interpolation.
- Allow read tools first.
- Keep write tools disabled until a real workflow needs them.

Remote/background agent:
- Configure dedicated agent secrets in the platform UI.
- Use staging credentials.
- Do not reuse a developer's personal token.
- Review branch and logs before merge.

The goal is not to make the agent blind. The goal is to give it the names, scopes, and commands it needs while keeping credential values inside the runtime boundary designed to hold them.

Team checklist

  • Are all real secret values out of prompts, instruction files, and committed config?
  • Does .env.example list names without real values?
  • Are .env*, key files, service-account JSON, dumps, and local exports ignored or denied?
  • Do MCP configs reference environment variables instead of storing tokens directly?
  • Are MCP write tools disabled until explicitly needed?
  • Do remote agents use dedicated agent secrets rather than Actions, Codespaces, or personal developer secrets when the platform provides that separation?
  • Are production credentials unavailable to ordinary agent tasks?
  • Are tokens scoped to one repo, environment, or service where possible?
  • Do logs redact headers, cookies, tokens, PII, and MCP tool parameters that may contain secrets?
  • Is there a rotation playbook for keys pasted into prompts or generated files?

Secrets are not context. They are authority. Give the agent enough authority to do the job, then make sure that authority cannot wander into a transcript, diff, screenshot, or log line.

Quellen überprüft