- Claude Code
- Codex
- Cursor
- OpenAI
Claude asked for my API key. Is it safe to paste it?
No, not into the chat. A key you paste there becomes part of the conversation: the model provider gets it with every later request, the transcript keeps it on disk, and the agent can copy it into a file or a commit. Instead, let the agent ask for the key with a tool call. With offprompt, you type the key on a page in your browser, it is written straight to the project's .env.local, and the agent only learns that the key exists.
What happens to a key you paste into the chat
An agent such as Claude Code, Codex or Cursor works by sending the whole conversation to a model on every turn. Once a key is in the conversation, it goes along each time:
- The model provider receives it, with that request and every later one in the session, and keeps it for as long as its data policy keeps requests.
- The transcript keeps it. Coding agents save each session on your disk so you can resume it, and a hosted agent keeps it in your account. A summary made when the conversation is compacted can repeat it.
- The agent can echo it. Asked to make something work, it may write the key into a config file, a test, a script or a log line, and from there into a commit.
None of this can be taken back. A key that has been in a chat is best treated as exposed, and replaced.
The fix: the agent asks, and you type the key somewhere else
offprompt is an MCP server, a tool the agent can call. When a task needs a key the project does not have, the agent calls offprompt's collect_secret tool with the key's name, why it needs it, and the file it goes in:
{
"secrets": [{ "name": "OPENAI_API_KEY", "provider": "openai" }],
"reason": "Each new support ticket gets a short summary from **OpenAI**, written by `lib/summarise.ts`.",
"sink": { "kind": "dotenv", "path": ".env.local" }
}- A page opens in your browser, served from
127.0.0.1on your own machine. It says which agent is asking, what for, and which file it will write. - You paste the key. The page checks it against what offprompt knows of the provider, such as an OpenAI project key starting with
sk-proj-, and links to where the key is made. - offprompt writes the key to
.env.local, and answers the agent with the key's name and a fingerprint of four emoji, which your page shows too:
From then on the project reads the key by name, as process.env.OPENAI_API_KEY. The agent never had the value, so it cannot repeat it.
When the agent runs in a cloud sandbox, such as Claude Code on the web or Codex cloud, it cannot open a page on your screen. It shows you a link instead, through a Cloudflare quick tunnel. You open it in your own browser, which encrypts the key to one only the sandbox holds before sending it, so the tunnel carries only ciphertext. See Cloud sandboxes.
Set it up in 30 seconds
In the project's folder, with Node 22 or later, run npx offprompt init. It finds Claude Code, Codex, Cursor and Pi on your machine and declares offprompt in each one's project config. Commit what it adds, and everyone on the project gets it.
To add it by hand instead, start it from npm in each agent's config:
{
"mcpServers": {
"offprompt": {
"command": "npx",
"args": ["-y", "offprompt", "mcp"],
"timeout": 330000
}
}
}{
"mcpServers": {
"offprompt": {
"command": "npx",
"args": ["-y", "offprompt", "mcp"]
}
}
}[mcp_servers.offprompt]
command = "npx"
args = ["-y", "offprompt", "mcp"]
tool_timeout_sec = 330Claude Code asks once before it starts a server a project declares, and Codex reads a project's config only in a project you trust. The timeouts give you time to type: Codex otherwise ends a tool call after 60 seconds. On Linux, init also passes Codex the variables offprompt needs to open your browser. Other agents, such as Cline, are in Agents.
Then ask for the task, not the key: “Send a welcome email with Resend when someone signs up.” The agent calls offprompt when it reaches the step that needs the key.
Keep the agent out of the file afterwards
offprompt keeps the key out of the conversation, but the file is on disk and the agent could still open it. Every result offprompt returns tells the agent not to and lists the keys the file holds, so it has no reason to. To make sure, close its file tools to the file:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./.env.local)"]
}
}In Cursor, list .env* in .cursorignore. Neither covers every shell command, so a script that prints the environment can still show the value. How to keep secrets out of your AI agent's context goes through each way in.
If you already pasted one: rotate it
- Make a new key at the provider, with no more access than the task needs.
- Ask the agent to use the new key with offprompt, so it is written without entering the chat.
- Delete or expire the old key, and check the provider's logs for requests you did not make.
- Search the project, and its git history, for the old value, in case the agent wrote it anywhere.
Guides for one provider at a time
Each sets up a key with only the access it needs, has the agent ask for it, and uses it in working code.
- Send email with Resend without giving your agent the API keyVerify a domain, make a Resend key that can only send from it, and let your coding agent ask for RESEND_API_KEY with offprompt, so the key never enters the chat.
- Resend
- Take Stripe payments in test mode without your agent seeing the secret keyBuild Stripe Checkout with a coding agent while the secret key and webhook signing secret stay out of the chat: test keys, restricted keys and offprompt.
- Stripe
- Use Supabase with a coding agent without leaking the service_role keySupabase's service_role and secret keys bypass Row Level Security. Keep them out of the agent's chat and the browser, and use them only in server code.
- Supabase
FAQ
Does the model provider see my key when the agent uses offprompt?
No. The value goes from your browser to offprompt, running on your machine, and into the file. It is never in a prompt, a tool call's arguments or its result, so nothing sent to the model provider carries it. The provider sees the key's name, the file and a four-emoji fingerprint. If the agent later opens the file, the value enters its context after all: every result tells it not to, and a deny rule in Claude Code or .cursorignore in Cursor closes its file tools to the file.
Does it work when the agent runs in a cloud sandbox?
Yes. From Claude Code on the web, a Cursor Cloud Agent, Codex cloud, a codespace or an SSH session, the agent shows you a link to offprompt's page through a Cloudflare quick tunnel. Your browser encrypts the values to a key made in the sandbox for that one request before they leave it, so the tunnel carries only ciphertext. The link is good for 30 minutes and one write; open it yourself and don't pass it on.
I already pasted a key into the chat. What should I do?
Rotate it. Make a new key at the provider, let the agent ask for it with offprompt, then delete or expire the old one. Deleting the conversation does not reach the copies already sent to the model provider or written into files by the agent. The provider guides below say where each key is rotated.
Does offprompt keep my keys or send them anywhere?
No. offprompt runs on your machine as a process your agent starts. It writes the values to the file the agent named, keeps none of them once they are written, collects no telemetry and has no server of ours to send anything to. It is open source under the MIT license.
Which agents does it work with?
npx offprompt init sets it up for Claude Code, Codex, Cursor and Pi. Any other agent that speaks MCP, such as Cline, Windsurf, Gemini CLI or VS Code, can start it with npx -y offprompt mcp; Cline also needs the project as its cwd.
Will the key end up in git?
Not unless the file is tracked. offprompt's page says whether the file is gitignored, and when the file is tracked by git it holds back the write until you tick an override. Most frameworks keep .env.local out of git by default; check that your .gitignore lists it.
Try it
In a project's folder, run the command below, then ask the agent for something that needs a key.
npx offprompt init