secret-paste
Stores secrets as 1Password items using the op CLI (op item create), saving to the default vault and allowing retrieval via op item get.
Upserts secrets into a [.env](/mcp/servers/integrations/dotenv) file in the working directory, preserving other lines and ensuring values are readable by dotenv and Node's process.loadEnvFile.
Stores secrets in the Linux Secret Service keychain using secret-tool store (requires libsecret-tools).
Stores secrets in the macOS Keychain using the security add-generic-password command, with the value passed over stdin to avoid appearing in process listings.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@secret-pasteask me for my OpenAI API key and save it to .env"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
secret-paste
An MCP server that lets an agent request an API key or other secret from you without you pasting it into the chat. The value never passes through the conversation, a tool argument, or a tool result. "pbcopy for agents."
The agent calls request_secret. You get a local browser page with a single
password field. The value goes straight from the page to .env, your OS
keychain, or 1Password. The agent only ever sees { name, target, stored }.
This keeps the secret out of the request path, not out of the agent's reach.
Once stored, an agent with shell access can still read it (cat .env,
secret-tool lookup, op item get), and anything it reads lands in the
transcript. Pair this with permission rules that deny reading .env or running
those commands if that matters to you.
Install
cd /path/to/secret-paste
npm install && npm run build
claude mcp add --scope user secret-paste -- node /path/to/secret-paste/dist/server.jsRestart Claude Code (or run /mcp) and confirm secret-paste is connected.
Related MCP server: keyward
Tool
request_secret({ name, target?, description? })
field | notes |
| required, must match |
|
|
| optional, shown on the page |
Result on success: { "name": "...", "target": "...", "stored": true, "path"?: "...", "readHint": "...", "opened": "elicitation" | "browser" }.
Result otherwise: { "stored": false, "reason": "cancelled" | "declined" | "timeout" | "aborted" | "<error>" }.
The value, its length, a prefix, or a hash is never returned, logged, or echoed.
Targets
env: upsertsNAME=valueinto.envin the server's working directory (the directory Claude Code was started in). Other lines are preserved, an existing line for that name is replaced, the file is created with mode 0600 if missing, and.envis appended to.gitignorewhen the directory is a git repo. Values are written unquoted when possible, otherwise wrapped in',`, or"(whichever the value doesn't contain) so dotenv and Node'sprocess.loadEnvFileread them back verbatim. Values that can't be quoted losslessly are rejected with a hint to usekeychainorop.keychain: macOSsecurity add-generic-password(fed over stdin viasecurity -i, so the value never appears inps), or Linuxsecret-tool store(requireslibsecret-tools).op:op item createwith a JSON item template on stdin (1Password CLI), saved to your default vault. Read it back withop item get NAME --fields label=password --reveal.
How it works
A one-shot HTTP server starts on
127.0.0.1on a random port with a random nonce in the path. Requests with the wrong nonce, Host, or Origin are rejected.The URL is handed to Claude Code via MCP URL-mode elicitation, which shows a native "open this link?" prompt. If the client doesn't support that, the server falls back to
xdg-open/open.The page is static HTML with no JavaScript and
Cache-Control: no-store.On submit, the value is parsed at the byte level into a
Buffer, written to the target, then zeroed. It is never held as a JavaScript string.The server shuts down after one submit or cancel, or after 5 minutes.
The tool is tagged _meta["anthropic/requiresUserInteraction"] = true, so
Claude Code prompts for permission on every call, even in auto-accept modes.
How to use it (for Claude)
When you need a credential, call request_secret({ name: "SERVICE_API_KEY" })
and then reference it as process.env.SERVICE_API_KEY (or the readHint in
the result). Never ask the user to paste a secret into chat.
Development
npm test # unit tests for .env upsert and form decoding (node --test)
npm run build # tsc -> dist/This server cannot be deployed
Maintenance
Related MCP Connectors
A secret store for AI agents: the agent never sees the plaintext.
One-time, end-to-end encrypted secrets for agents. 0.005 USDC per secret via x402.
Encrypted secret store and rotation for autonomous agent credentials
Secrets for developers and agents—secure context and workflows without exposing secret values.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables secure credential storage for AI agents by encrypting secrets and providing agent-invisible references, ensuring sensitive data never leaks to the model.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to securely use API keys by storing them in an encrypted vault and injecting them on demand with user approval, without exposing the key values to the model.1MIT
- FlicenseAqualityFmaintenanceEnables AI agents to securely store and retrieve secrets and make authenticated API calls on their behalf, keeping API keys out of conversation context and supporting local file or GCP Secret Manager backends.4-
- AlicenseCqualityCmaintenanceEnables AI agents to securely store and manage encrypted secrets locally while using them indirectly through environment-variable injection or file writes, so plaintext values never enter the agent's context.8MIT