secret-paste
You can request secrets from the user and store them without exposing the value in the conversation.
Call
request_secret({ name, target?, description? })to ask for a named secret;namemust match^[A-Z_][A-Z0-9_]*$.Store the value in
env(default, upserts.env),keychain, orop(1Password).Trigger a native password dialog when available, or a local browser fallback.
Optionally show a description in the prompt.
Receive only metadata such as
{ name, target, stored }, pluspath/readHint/viaon success or a failure reason; the secret value itself is never returned.Requires user interaction on every call.
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-paste ask 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. A native password dialog pops up (pinentry,
zenity, kdialog, or a macOS dialog), you type the value, and it goes straight
to .env, your OS keychain, or 1Password. No browser tab, no "save this
password?" nag. If no dialog tool is available, a local browser page is used
instead. 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": "...", "via": "<backend>" }.
Result otherwise: { "stored": false, "reason": "cancelled" | "timeout" | "<error>", "via": "<backend>" }.
via is one of pinentry, pinentry-mac, zenity, kdialog, osascript,
elicitation (browser page opened through the client), or browser
(browser page opened with xdg-open / open).
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
Native dialog (default)
Backends are tried in order until one is available:
platform | order |
Linux |
|
macOS |
|
Windows | browser |
pinentryis GnuPG's passphrase prompter and the preferred backend. It holds the value in locked memory, draws a native dialog (GNOME/GCR, Qt, GTK, or macOS depending on the installed flavour), and enforces the timeout itself. The server speaks the Assuan protocol to it over stdin/stdout and decodes theDresponse at the byte level. Terminal-only flavours (curses,tty) are skipped because the server has no terminal to give them.SETKEYINFOis deliberately not sent, so pinentry never offers to cache the value in your keyring.zenity --passwordandkdialog --passwordprint the value to stdout, which the server reads as bytes and zeroes after storing.osascriptruns adisplay dialog ... with hidden answerAppleScript.
Set SECRET_PASTE_PROMPT to override the order, e.g. zenity,browser or
browser to force the old behaviour:
claude mcp add --scope user -e SECRET_PASTE_PROMPT=browser secret-paste -- node /path/to/secret-paste/dist/server.jsDialogs are spawned detached from the server's process group and killed on timeout or when the tool call is aborted.
Browser fallback
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: .env upsert, form decoding, dialog backends via fake binaries (node --test)
npm run build # tsc -> dist/Available Tools
1 toolrequest_secretRequest a secret from the userA
Ask the user for an API key or other secret via a native password dialog (pinentry, zenity, kdialog, or a macOS dialog), falling back to a local browser page. The value is stored directly to the chosen target (.env in the working directory, the OS keychain, or 1Password) and never enters the conversation. Afterwards, reference it indirectly (e.g. process.env.NAME); never ask the user to paste secrets in chat.
Inputs: name (required), target, description.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 1 tool
There is only one tool, so there is no possibility of misselection or overlap. Its purpose (securely obtain and store a secret without exposing it in chat) is unmistakable.
The single name request_secret follows a clear verb_noun convention and is self-descriptive. With one tool there is nothing to be inconsistent with.
The server is narrowly scoped to secret ingestion, so one tool is defensible, but it sits at the thin end of the range. A secret-management surface usually warrants at least a companion tool for checking, rotating, or removing stored secrets.
Writing a secret to .env, keychain, or 1Password is covered, but the lifecycle is a dead end: there is no way to verify a secret already exists, rotate it, or delete it. Agents must guess or re-prompt the user, which risks redundant dialogs and stale credentials.
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.3MIT
- 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
- AlicenseAqualityCmaintenanceLets an AI agent request API keys and passwords from a human through an end-to-end encrypted one-time link and pass them to exactly one command or file, without the value ever entering the agent's context. Can also send secrets as self-destructing links.3AGPL 3.0