The first demo of the Playwright MCP Server always goes well: you ask Claude to open a public page, click around, and write a test. Then you point it at your real app — the one behind a login screen — and everything falls apart. The agent logs in on every run. It hits rate limits. It triggers "new device" security emails. It gets stuck on a 2FA prompt it cannot answer.
Authentication is the biggest "Day 2" problem in AI-powered browser testing. The good news: Playwright already has the tools to solve it — storage state, persistent profiles, and isolated sessions. This guide shows you how to wire them into the Playwright MCP Server so Claude stays logged in, safely, across every run.
Why Authentication Breaks AI Browser Agents
A human tester logs in once in the morning and stays logged in all day. An AI agent driving a fresh browser starts every session with no cookies and no local storage. Unless you give it a session, it has to repeat the login flow each time. That causes real problems:
| Problem | What happens |
|---|---|
| Wasted tokens & time | Every run spends several tool calls (and a lot of context) just getting past the login page |
| Rate limiting | Repeated logins from the same IP trip brute-force protection and lock the test account |
| Security alerts | "New sign-in detected" emails and suspicious-login flags on every run |
| MFA / 2FA walls | The agent cannot read an SMS or authenticator app, so it simply stops |
| Credentials in prompts | Teams paste passwords into chat so the agent can log in — a serious security risk |
The fix is the same one Playwright has recommended for regular tests for years: log in once, save the authenticated state, and reuse it.
How Playwright MCP Browser Sessions Work
The official @playwright/mcp server can run the browser in two modes, and they behave very differently when it comes to logins:
| Mode | How it works | Login persists? | Best for |
|---|---|---|---|
| Persistent profile (default) | Browser data is kept in a user-data directory on disk between sessions | Yes, until cookies expire | Local exploration with Claude Code |
Isolated (--isolated) | Each session starts in a clean, in-memory profile | Only if you load a storage state file | Repeatable test generation, CI, teams |
Persistent profiles are convenient, but the state is hidden in a folder you did not create and it drifts over time. For anything you want to be repeatable, isolated mode plus a storage state file is the professional choice. Let us set up both.
Step 1: Save a Storage State File
A storage state file is a JSON snapshot of cookies and local storage for your app. The fastest way to create one is with Codegen — you log in by hand, and Playwright saves the session when you close the window:
# Opens a browser — log in manually, then close the window
npx playwright codegen https://staging.example.com/login \
--save-storage=playwright/.auth/user.json
You can also generate it programmatically with a Playwright setup project, which is the better option for CI because it runs automatically and keeps the file fresh:
import { test as setup, expect } from '@playwright/test'; const authFile = 'playwright/.auth/user.json'; setup('authenticate', async ({ page }) => { await page.goto('/login'); await page.getByLabel('Email').fill(process.env.TEST_USER_EMAIL!); await page.getByLabel('Password').fill(process.env.TEST_USER_PASSWORD!); await page.getByRole('button', { name: 'Sign in' }).click(); await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible(); // Save cookies + localStorage for reuse await page.context().storageState({ path: authFile }); });
Credentials come from environment variables, never from source code. If you are new to login flows in Playwright, the Playwright login test tutorial covers the basics.
Step 2: Load the Session into the Playwright MCP Server
Now point the MCP server at that file. With Claude Code, add the server in isolated mode and pass the storage state:
claude mcp add playwright -- npx @playwright/mcp@latest \ --isolated \ --storage-state=playwright/.auth/user.json
If you configure MCP servers with JSON (Claude Desktop, Cursor, VS Code), the equivalent entry is:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest",
"--isolated",
"--storage-state=playwright/.auth/user.json"
]
}
}
}
Every new browser session now starts already logged in. Ask Claude to open /dashboard and it lands there directly — no login form, no credentials in the prompt. For the rest of the server options, see the Playwright MCP Server setup guide.
Quick local option: if you just want to explore, you can skip the file and use the default persistent profile. Log in once in the browser the agent opens, and the session is remembered next time. Add --user-data-dir=./.mcp-profile to keep that profile inside your project where you can find and delete it.
Step 3: Reuse the Same Session in Generated Tests
The tests Claude writes should use the exact same authentication as the exploration session. Configure a setup project and make your browser projects depend on it:
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'setup', testMatch: /.*\.setup\.ts/ }, { name: 'chromium', use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json', }, dependencies: ['setup'], }, ], });
Now the setup project logs in once per test run, every test starts authenticated, and the MCP server reads the same file. Tell Claude about this in your prompt or project instructions so it does not add login steps to every generated test:
## Testing conventions
- Tests run pre-authenticated via playwright/.auth/user.json.
- Never add login steps to generated tests.
- Never ask for or type real credentials. Use the saved session.
- For logged-out scenarios, use test.use({ storageState: { cookies: [], origins: [] } }).
Handling MFA, 2FA & SSO Logins
Multi-factor authentication is where most AI agent setups get stuck. You have four realistic options, from best to worst:
- Use a dedicated test account without MFA in staging — restricted to non-production data. This is the simplest and most common approach.
- Automate TOTP codes in your setup project. If the account uses an authenticator app, store the TOTP secret as a CI secret and generate the code with a library like
otplib. The agent never sees the secret — only the resulting session. - Log in manually once with Codegen (
--save-storage), completing SSO or MFA yourself, and reuse the file until the session expires. - Bypass at the API level — some apps support a test-only token endpoint. Call it with Playwright's
requestfixture and inject the resulting cookie. Only do this with backend team approval.
Never disable MFA on production accounts to make AI testing easier. Use separate, least-privilege test accounts on a staging environment instead.
Security Best Practices for Auth Files
A storage state file is effectively a logged-in session in a JSON file. Anyone who has it can act as that user until it expires. Treat it like a password:
- Git-ignore it. Add
playwright/.auth/(and any--user-data-dirfolder) to.gitignorebefore your first commit. - Use test accounts only. Never save a session for a real admin or customer account.
- Keep credentials in env vars or a secrets manager. Never paste passwords into a Claude prompt or a CLAUDE.md file.
- Regenerate often. Let the setup project create a fresh file on every CI run instead of caching old sessions.
- Limit what the agent can reach. Point MCP sessions at staging, and use the server's allowed-origins options to keep the agent on your domains.
# Playwright auth sessions — never commit
playwright/.auth/
.mcp-profile/
Troubleshooting: Agent Still Sees the Login Page
| Symptom | Likely cause & fix |
|---|---|
| Redirected to login despite the file | Session expired — regenerate the storage state |
| Works on localhost, fails on staging | Cookies are domain-scoped — save the state against the same domain the agent visits |
| Logged in, but app says "session invalid" | The app keeps auth in sessionStorage, which storage state does not save — restore it with an init script |
Server ignores --storage-state | The flag is used together with --isolated; check the arguments in your MCP config |
| Parallel tests log each other out | The app allows one session per user — use one test account per worker |
Quick Reference
- Save session: codegen --save-storage
- Automate: auth.setup.ts project
- MCP: --isolated --storage-state
- Local: persistent profile
- MFA: test account or TOTP secret
- Git-ignore playwright/.auth/
Frequently Asked Questions
How do I log in with the Playwright MCP Server?
Save an authenticated session to a storage state file (for example with npx playwright codegen --save-storage=playwright/.auth/user.json), then start the server with --isolated --storage-state=playwright/.auth/user.json. Every new browser session begins already logged in, so the AI agent never has to type credentials.
Does Playwright MCP remember logins between sessions?
By default the Playwright MCP Server uses a persistent browser profile, so cookies from a manual login are kept between sessions until they expire. In --isolated mode each session starts clean, and a login only carries over if you load a storage state file.
How do AI agents handle MFA or 2FA in Playwright?
An AI agent cannot read SMS codes or authenticator apps. Use a staging test account without MFA, generate TOTP codes in a Playwright setup project from a stored secret, or complete MFA manually once and save the session with Codegen. Never disable MFA on production accounts.
Is it safe to share a Playwright storage state file?
Treat it like a password. A storage state file contains live session cookies, so anyone with it can act as that user until the session expires. Git-ignore the playwright/.auth/ folder, use least-privilege test accounts, and regenerate the file on each CI run.
Should I paste my password into a Claude prompt so it can log in?
No. Credentials in prompts can end up in logs, history, and shared transcripts. Keep them in environment variables or a secrets manager, let a setup script create the session, and give the agent only the resulting storage state file.
Asim Noaman
Senior QA Automation Engineer & AI Testing Specialist
With years of hands-on experience building test automation frameworks for production applications, Asim specializes in combining traditional QA methodologies with cutting-edge AI tools. He has helped teams adopt Playwright and AI-driven testing workflows to ship faster with fewer bugs.
Complete Course
Take AI Testing Past the Demo — Into Your Real App
Learn how to connect Claude AI to Playwright through the MCP Server and test real, logged-in applications. The course covers sessions, fixtures, and CI pipelines with hands-on projects.
- Playwright MCP Server setup with Claude
- Authentication & storage state patterns
- AI-generated tests for real apps
- CI/CD with GitHub Actions