Your AI agent has to log in. Keep the password out of its context
Sooner or later an agent that does real work hits a login screen. The CRM wants a password, the supplier portal wants a password and a 2FA code, the staging admin wants both. The easy fix is to put the password in the prompt, or in a file the agent can read. It works on the first try, which is exac

Sooner or later an agent that does real work hits a login screen. The CRM wants a password, the supplier portal wants a password and a 2FA code, the staging admin wants both.
The easy fix is to put the password in the prompt, or in a file the agent can read. It works on the first try, which is exactly why it ends up everywhere. Here is what that costs and a pattern that keeps the secret on your machine instead.
Once the value is part of the conversation, it is copied to every place the conversation goes:
None of this needs an attacker to be clever. It only needs the value to be in the context.
Split the job in two. The model decides which login to use and where. A local tool looks the value up and types it. The model only ever sees a name and a result.
model -> fill_login ( site = " crm " ) # a name, not a value tool -> looks up " crm " in the OS keychain tool -> focuses the field, types it locally tool -> returns " filled " # never echoes the value Enter fullscreen mode Exit fullscreen mode A minimal MCP tool in that shape (TypeScript SDK; getSecret and typeIntoFocusedField are yours to implement):
server. tool ( " fill_login ", { site: z. string (). describe ( " Saved login name, e.g. 'crm' " ) }, async ({ site }) => { const secret = await getSecret ( site ); // OS keychain, see below if (! secret ) return { content: [{ type: " text ", text: `no saved login ' ${ site } '` }] }; await typeIntoFocusedField ( secret. password ); // typed on this machine return { content: [{ type: " text ", text: " filled " }] }; } ); Enter fullscreen mode Exit fullscreen mode The important line is the return value. It says what happened, never what was typed.
Do not invent a secrets file. Every desktop OS has a keychain:
Your tool reads from there at the moment it types. Nothing lands in a dotfile the agent could cat.
We build Auten, an MCP server that gives Claude Code, Codex, Cursor or any MCP client hands on a real screen: your computer (Mac, Windows, Linux) and your Android phone over USB (beta). Logins work this way: passwords stay on your computer and are typed locally, so the model never sees them. Each saved login is pinned to the site or app it was first used on; on a different origin Auten refuses and types nothing. A reader asked us about exactly that gap on dev.to, and we shipped it the same evening. If you want to try it: auten.ai.
How do you handle credentials in your agent setups today? Env vars, a vault, or "it's in the prompt for now"? Tell us in the comments.