Skip to main content

The secrets your AI coding agent can read, and how to fence them off

An AI coding agent runs as you. When it reads a file or runs a shell command, it uses your user account, your home directory and every credential that account can reach. That is what makes it useful, and it is also why the question "what secrets can it see?" has a longer answer than most teams expect.

This post is about the secrets that sit on a developer machine, outside the code: .env files, MCP server tokens, shell history and cloud credentials. Secrets committed to the repository are a separate problem, covered in hardcoded secrets in private repos and secrets in git history.

Why the agent's reach matters

Most of the time the agent reads a secret by accident: it opens .env to understand configuration, or prints an environment variable while debugging. The value then sits in the conversation, which may be sent to a model provider, stored in a session log on disk, or pasted into a pull request description.

The worse case is deliberate. An agent that reads untrusted content (an issue, a README in a dependency, a web page, an MCP tool description) can be steered by instructions hidden in it. If the agent can read ~/.aws/credentials and can also make a network request, a prompt injection only has to connect the two. Scoping what the agent can read limits what any injection can steal.

Where the secrets are

.env files in the project

The most common one. Frameworks load .env, .env.local and .env.production from the project root, so they sit exactly where the agent works. They often hold production database URLs and live payment or email provider keys, because someone needed to reproduce a bug once.

MCP server tokens in config files

MCP servers need credentials, and the easy path is to paste a token into the host's config. Claude Code keeps MCP configuration in ~/.claude.json and a project's .mcp.json; Cursor uses ~/.cursor/mcp.json. A literal token in an env block is readable by anything that can read that file, including the agent itself, backups and sync folders:

{
  "mcpServers": {
    "github": {
      "command": "uvx",
      "args": ["mcp-server-github==0.4.2"],
      "env": { "GITHUB_TOKEN": "ghp_live_token_pasted_here" }
    }
  }
}

The fix is to reference a variable the host resolves at launch, so the file holds no secret:

{
  "mcpServers": {
    "github": {
      "command": "uvx",
      "args": ["mcp-server-github==0.4.2"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    }
  }
}

A project .mcp.json is also easy to commit by mistake, which turns a local secret into a repository secret.

Shell history

Every export STRIPE_KEY=..., curl -H "Authorization: Bearer ..." and psql postgres://user:password@... you typed is in ~/.zsh_history or ~/.bash_history. An agent asked to "find the command I used last week" will read it. Both shells can skip commands that start with a space (HIST_IGNORE_SPACE in zsh, HISTCONTROL=ignorespace in bash), which helps only if you remember to use it. Better to keep secrets out of command lines altogether.

Cloud and tool credentials in your home directory

~/.aws/credentials, ~/.config/gcloud/, ~/.azure/, ~/.kube/config, ~/.docker/config.json, ~/.npmrc and ~/.git-credentials all hold long-lived tokens in plain files. None of them belong to the project, and the agent rarely needs any of them to write code.

How to scope what the agent sees

No single setting covers all of this. Layer these.

1. Deny reads in the agent's own settings

Claude Code supports deny rules in its settings file. Its settings documentation shows this exact pattern for .env files, and you can extend it to the paths above:

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(~/.aws/**)",
      "Read(~/.ssh/**)",
      "Read(~/.zsh_history)",
      "Read(~/.bash_history)"
    ]
  }
}

Know the limits. The permissions documentation says Read deny rules also cover file commands it recognises in Bash, such as cat and head, but not a script that opens the file itself or a command that reads files without naming them. For OS-level enforcement it points to the sandbox.

Cursor has .cursorignore. Its documentation says .env* files are ignored by default, and also says terminal and MCP tools cannot be blocked by the file, and that protection is not guaranteed. Treat ignore files as a guard against accidents, not a security boundary.

2. Use a sandbox or a container for real isolation

If a path must be unreachable, enforce it below the agent: the agent's own OS sandbox where one exists, or a dev container that mounts only the project and none of your home directory. A container with no ~/.aws cannot leak ~/.aws, whatever the model is told.

3. Make the secrets that remain short-lived

Swap long-lived keys for short-lived ones: cloud single sign-on sessions instead of static access keys, fine-grained tokens with an expiry instead of classic personal access tokens, and a secrets manager that injects values into one process at run time instead of a .env file on disk. A token that expires in an hour is a much smaller prize.

4. Give MCP servers their own narrow credentials

Each MCP server should get a token scoped to what it does (read-only where possible), referenced through ${VAR}, never pasted in. A filesystem server should be rooted at the project, not your home directory.

Scan for what is already exposed

Settings prevent new exposure. A scan finds what is already there.

Vulkro's secrets detection reads .env* files alongside source and config, plus git history, and tells a provider-format key from a placeholder:

vulkro scan .            # secrets are part of every scan, .env* included
vulkro mcp-audit         # project MCP configs plus the known host config locations

vulkro mcp-audit reads the project's .mcp.json and the known config files for Claude Desktop, Cursor, Windsurf, VS Code, Cline, Gemini and Continue. Rule MCP-004 flags a credential written into an env block instead of a ${VAR} reference, and the finding carries the key name and a coarse length bucket, never the value. MCP-003 flags a filesystem server mounted at / or your home directory.

The opposite direction matters too: code that reads your credential stores. MAL-CRED-001 flags source that opens credential files such as SSH keys or cloud credentials, which is worth running on a cloned repository or a new MCP server before you let an agent execute it. Everything runs on your machine, so no secret leaves it during the scan.

FAQ

Can an AI coding agent read my .env file?

Usually yes, unless you stop it. Agents read files in the project by default, and .env sits in the project root. Add deny rules or ignore entries for .env*, and remember they may not cover every way of reading a file, such as a script the agent runs.

Is it safe to put API keys in an MCP config file?

Not as literal values. Anything that can read the file can read the key, including the agent, backups and sync folders. Reference an environment variable (${GITHUB_TOKEN}) that the host resolves at launch, and give each server a narrow token.

How do I stop an AI coding agent from using my AWS credentials?

Deny reads of ~/.aws/ in the agent's settings, run the agent in a container that does not mount your home directory, and replace static access keys with short-lived single sign-on sessions so anything that leaks expires quickly.

See how Vulkro fits into agent workflows at /ai-agents/, or start a 14-day trial. Related: audit MCP configs and give your agent a scanner.