Playwright MCP: What It Is and How to Use It in Claude Code
Playwright MCP is Microsoft's official Model Context Protocol server for Playwright: it lets an AI agent such as Claude Code, Cursor, or GitHub Copilot open a real browser, click, type, and read the page through Playwright. It works on structured accessibility snapshots rather than screenshots, so the agent reads the page as a tree of roles and names, picks an element reference out of it, and acts on that reference. No vision model sits in the loop.
That design decision is the reason it behaves differently from screenshot-driven browser agents. A snapshot gives the model an exact handle for every interactive element, so a click lands on the element it named instead of on coordinates it estimated. It also means the agent works in the same vocabulary Playwright locators use, which matters the moment you want the session to end in code you can keep.
How to add Playwright MCP to Claude Code
You need Node.js (the Playwright docs ask for version 20 or newer) and the Claude Code CLI. Then it is one command:
claude mcp add playwright npx @playwright/mcp@latest
Start a new Claude Code session and run /mcp. You should see playwright listed as connected, with its browser tools available. Ask for something concrete to check it works: "Open https://demo.playwright.dev/todomvc, add two todos, and tell me what the counter says."
Three variations come up often:
| Goal | Command |
|---|---|
| Pass server flags, such as headless mode | claude mcp add playwright -- npx @playwright/mcp@latest --headless |
| Share the server with everyone on the repo | claude mcp add playwright --scope project npx @playwright/mcp@latest |
| Pin a version instead of tracking latest | claude mcp add playwright npx @playwright/mcp@0.0.83 |
The -- matters in the first row. Without it, Claude Code reads --headless as its own option instead of passing it to the server. The project scope writes the server into a .mcp.json file at the repo root, so teammates get the same browser tools after they approve it.
Cursor, VS Code, Windsurf, and Claude Desktop use the same server through a JSON config instead of a command:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
Free and open sourceCasely writes these cases for youAttach a spec and one file of your team's existing test cases in Claude. Casely copies your columns, names the gaps it found in the spec, and exports a single Excel file your tracker imports in one pass.Read the install docsWhat the server exposes
The default tool set covers the whole interaction surface: browser_navigate, browser_click, browser_type, browser_fill_form, browser_select_option, browser_hover, browser_drag, browser_press_key, browser_file_upload, browser_handle_dialog, browser_wait_for, and browser_snapshot for reading the page back. Inspection comes with it: browser_console_messages, browser_network_requests, browser_evaluate, browser_find.
Everything else is opt in through --caps, which keeps the tool schema small until you need more.
| Capability | Tools it adds | When it earns its place |
|---|---|---|
testing | browser_generate_locator, browser_verify_element_visible, browser_verify_text_visible, browser_verify_value, browser_verify_list_visible | Turning an exploratory walk into assertions and locators you can paste into a spec |
storage | Cookie, localStorage, and sessionStorage tools plus browser_storage_state | Logged-in flows, and saving auth state so the next run skips the login form |
network | browser_route, browser_unroute, browser_route_list, browser_network_state_set | Mocking a backend response or taking the page offline to test an error path |
devtools | browser_start_recording, browser_stop_recording, tracing, video, browser_highlight | Recording a flow you perform by hand and getting Playwright code back |
vision | browser_mouse_click_xy and the other coordinate tools | Canvas, maps, drag handles: anything with no accessibility node to target |
pdf | browser_pdf_save | Checking a generated document rather than a page |
browser_start_recording deserves a callout. You demonstrate a flow in the browser yourself, then browser_stop_recording hands back what you did as Playwright code. That is the shortest path from a manual reproduction to something a runner can execute.
Configuration is CLI flags. --browser picks chrome, msedge, firefox, or webkit. --device "iPhone 15" or --mobile emulates a phone, and the README notes mobile pages are usually lighter, which saves tokens. --test-id-attribute changes what generated locators anchor to, defaulting to data-testid. --codegen sets the language for generated code: TypeScript by default, with Python, Java, and C# available.
How this differs from writing Playwright tests
Both end in the same place, which is why the distinction gets missed. The difference is what each one guarantees.
Playwright Test is the enforcement layer. It has fixtures, retries, sharding across machines, a trace per retry, an HTML report, and a CI exit code. Run it twice on the same commit and it does the same thing twice.
Playwright MCP is an authoring and exploration layer. The agent decides which tool to call next, so the route through the page varies between runs. It has no retries, no sharding, and no report. What it produces is a session, plus whatever locators, assertions, and code you extract from that session before it ends.
Use MCP to find out how the page behaves and to draft the spec. Use the test runner to hold the line afterward. A team that stops at the MCP session has an agent that browsed the app, not a regression suite.
The Playwright team is direct about a related tradeoff: for coding agents, they now point at playwright-cli with skills instead, because CLI invocations avoid loading large tool schemas and verbose accessibility trees into the model's context. MCP stays the better fit for agentic loops that need persistent browser state and iterative reasoning over page structure, such as exploratory automation or self-healing tests. The Playwright CLI vs MCP comparison walks through when each one wins. If you want Playwright to plan, write, and repair whole test files on its own, the built-in Playwright Test Agents are the third option.
Playwright MCP or Chrome DevTools MCP
| Playwright MCP | Chrome DevTools MCP | |
|---|---|---|
| Browsers | Chrome, Edge, Firefox, WebKit | Chrome and Chrome for Testing |
| Strongest at | Driving flows, mocking network, saving auth state, producing locators and code | Performance traces, Lighthouse audits, heap snapshots, deep protocol-level inspection |
| Output you keep | Playwright code, locators, storage state | A diagnosis of what the page did |
| Parallel sessions | Supported with --isolated or separate profiles | One browser per server instance |
The split is clean enough to state in one line each. Reach for Playwright MCP when the goal is a test that will exist tomorrow. Reach for Chrome DevTools MCP when the goal is understanding why a page is slow, leaking, or throwing today, and cross-browser coverage is not part of the question.
Running both is reasonable. They are separate servers with separate tool namespaces, and the cost is context, not conflict.
Where it fails
It is not a security boundary. The project says so directly, and adds that the origin allowlist and blocklist are not security boundaries either and do not affect redirects. File access is restricted to workspace roots unless you pass --allow-unrestricted-file-access, and that flag deserves more thought than it usually gets.
Persistent profiles collide. The default profile is persistent and only one browser instance can hold it at a time, so two MCP clients in the same workspace conflict. Give each one --isolated or its own --user-data-dir.
Generated locators inherit whatever the page gives them. On a UI with no test ids, the agent falls back to roles and accessible names, and those move when someone rewrites a label, and unstable locators are a leading cause of flaky tests. Setting data-testid attributes before an agent starts generating locators is the cheapest stability you will buy all quarter.
Snapshots of heavy pages cost real context. A dense DOM produces a long tree on every action. --mobile and a narrower starting page help. So does turning off capability groups you are not using.
The Docker image runs headless Chromium only. Containerized setups lose the cross-browser advantage that is otherwise the main reason to pick this server over the DevTools one.
Nothing here reviews the test. An agent that walked a flow successfully will happily assert that the flow it just walked works. Assertions written from observed behavior encode the current behavior, bug included, which is the failure mode worth watching for in every AI-authored suite.
What is worth automating
The mechanical half is a good handoff: exploring an unfamiliar screen, finding stable locators, capturing auth state once instead of logging in on every run, and converting a manual reproduction into a first draft spec.
Browser tests sit at the top of the test pyramid, so keep the count small and put most checks lower. Deciding what the assertion should be stays a human call. A generated test tells you the page still does what it did the day it was recorded. Whether that is what the requirement asked for is a separate question, and it is answered upstream, in the test cases, not in the browser.
Frequently asked questions
Is Playwright MCP free? Yes. The server is open source under the Apache 2.0 license and runs locally through npx. You pay only for the model tokens your agent spends reading page snapshots.
Does Playwright MCP need screenshots or a vision model? No. It reads the page as an accessibility tree, so any text model can use it. The vision capability adds coordinate-based tools for canvas-style pages that have no accessibility nodes.
What is the difference between Playwright MCP and Playwright? Playwright is the browser automation library and test runner. Playwright MCP wraps that library as tools an AI agent can call. The tests you keep still run on Playwright Test.
Why does Playwright MCP use so many tokens? Every action returns a fresh snapshot of the page, and dense pages produce long snapshots. Use --mobile, keep capability groups off until you need them, or switch coding-agent work to playwright-cli, which writes snapshots to disk instead of into the model's context.