Coding agents like Claude Code, Cursor or Codex need database passwords and API keys to deploy, migrate and test. The usual ways of handing them over put the value into the agent's context, and from there into session logs on disk and into the requests sent to the model provider. This guide shows where the value leaks, which rules actually keep it out, and how to set that up in a few minutes.
An agent works by running tools and reading their output. That output becomes part of the conversation: it is sent to the model with every following request and written to the session log on your machine. A value that appears there once stays there, long after the task is done. In our own measurement, 68 of 70 deployment secrets ended up in plain text in the session logs of a coding agent within four weeks, and none of them had been typed into the chat.
The pattern is always the same: the agent does nothing wrong, a tool prints the value and the agent reads what tools print.
Run commands such as 1Password's op run or doppler run already follow the first rule: they inject the value into one process instead of printing it, and op run also masks it in the output. Use those instead of read or get commands. What they do not cover is the moment the value is missing: the agent cannot ask you for it without the chat, and nothing binds the value to the command it was meant for.
vault-ask is a small open source command line tool and MCP server for Node.js 20 or newer, with no dependencies.
npm install -g @erseni/vault-ask The agent requests the value for exactly one command:
vault-ask DB_PASSWORD -- psql -h db.example.com -U app vault-ask prints a link. You open it in your browser, see the command the value is meant for and enter the value. It arrives end-to-end encrypted, goes only into the environment of that command, and the agent sees nothing but a line like "DB_PASSWORD received, 32 characters, passed to the command".
For tools that read a file, the value goes into a .env file or a single file with permissions 600:
vault-ask STRIPE_KEY SENTRY_DSN --env-file .env vault-ask TLS_KEY --file certs/server.key In Claude Code, add the MCP server once. From then on the agent asks for secrets on its own instead of asking you to paste them:
claude mcp add vault-ask -- npx -y @erseni/vault-ask mcp In Cursor or any other MCP client, add the server to the MCP configuration (for Cursor in ~/.cursor/mcp.json):
{ "mcpServers": { "vault-ask": { "command": "npx", "args": ["-y", "@erseni/vault-ask", "mcp"] } } } A value written to a file can still be read later. For Claude Code, vault-ask therefore ships vault-ask-guard, a PreToolUse hook that refuses to read the files vault-ask wrote and refuses commands like env or echo as a target. Add it to .claude/settings.json:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash|Read|Edit|MultiEdit|Write|Grep|Glob|NotebookEdit|mcp__vault-ask__run_with_secret|mcp__vault-ask__send_secret", "hooks": [{ "type": "command", "command": "vault-ask-guard" }] } ] } } These rules catch accidents, and they make a manipulated agent visible, because it would have to ask you again for every new command. They cannot stop an agent that runs under your own operating system user with a shell and deliberately reads the memory or environment of a process it started. Complete protection needs privilege separation: the secret is held by a service under another user and the agent only holds a reference to it.