Write Playwright tests with Playwright MCP and Claude Code
Posted on Oct 5. Task: Posted on Oct 5 This article was originally published on the Endform blog. By the end of this walkthrough you'll have a single Playwright test that Claude Code wrote against your live app, that you've reviewed in two passes, run repeatedly to check for flakiness, and committed next to the plain-English scenario it came from. The trick that makes it trustworthy: the agent reads locators off the running page through the Playwright MCP server instead of guessing them from your source code. You need four things installed before step 1, so start there. Before you start Confirm all four of these first. Skipping one is the most common reason step 1 fails. Node.js 20 or newer. Check with node --version. The MCP server launches through npx, so Node is required no matter how you installed Claude Code. Node.js 20 or newer. Check with node --version. The MCP server launches through npx, so Node is required no matter how you installed Claude Code. Claude Code, installed and signed in. Check with claude --version. Claude Code, installed and signed in. Check with claude --version. A Playwright project. You need playwright.config.ts at the repo root. No project yet? Run npm init playwright@latest to scaffold one. A Playwright project. You need playwright.config.ts at the repo root. No project yet? Run npm init playwright@latest to scaffold one. An app to test. Local dev server or staging URL, either works. Playwright can start it for you when the config says so. An app to test. Local dev server or staging URL, either works. Playwright can start it for you when the config says so. Got all four? Connect Claude Code to a browser. The problem this setup solves Here's a failure pattern you've probably lived through: Claude Code writes an end-to-end test, it passes locally, you merge, and CI goes red a day later on what looks like flakiness. The test was wrong from the start, it just had no way to show it. The reason is where the locators came from. Reading your components without ever opening the app, an agent spots a button with a class like .btn-primary and writes a selector against it. Then a component library or some runtime logic rewrites that class on its way to the DOM, and by the time the browser renders, the selector points at a hashed string, a restructured node, or nothing. Nobody caught it because nobody ran the test against a real page. CI is the first thing that does. Compare the two ways the same button gets targeted: Anthropic's Model Context Protocol is what closes the gap. The Playwright MCP server hands Claude Code a live browser: it can open the app, walk the accessibility tree, and build locators out of the roles and names the page actually exposes. It's working from a structured snapshot rather than a screenshot, which keeps the locators stable and the token cost low, because the model reasons over text it can already read. Same model, better inputs. Nothing here is a capability upgrade, only an access one. Step 1: Register the Playwright MCP server A single command wires the server into Claude Code: That entry gets saved to your local config, and the server process spins up the next time you open a session. -y skips the install confirmation npx would otherwise wait on, and @latest grabs the newest published build so you're not stuck on a stale cache. By default this is a personal, single-project entry. Working on a team? Add --scope project, which writes the same config to a .mcp.json at the repo root so everyone shares one server without redoing setup: Don't trust it until you've checked it. Run claude mcp list and look for ✔ Connected. Immediately after adding, you may catch a ✘ Failed to connect while npx is still pulling the package in the background; run the command again a few seconds later and it usually clears. If retrying doesn't clear it, the problem is elsewhere. Claude Code runs the server as a subprocess in its own environment, and that subprocess can't always resolve npx the way your interactive shell does. Homebrew-installed Node on macOS is the usual culprit. Give it the absolute binary path instead of trusting PATH: Run claude mcp list once more and wait for ✔ Connected before continuing. Connected only means the process is alive. It doesn't prove Claude Code can actually drive a browser yet. Open a session and call the tool by name, otherwise Claude Code might reach for a Bash command instead of the MCP server:; tools: Claude Code. Human role: The original post does not say how a person stayed involved.; limitations: Prerequisites include Node.js 20+, Claude Code, and a Playwright project. The workflow may fail with long multi-step flows, changing data, deep conditionals, or OAuth/third-party auth. The author notes that MCP tooling changes quickly, so commands may become outdated. Human review is essential for scenario judgment and verifying that tests assert meaningful outcomes..
Tools
Published on Agent Work Atlas:
Overview
Posted on Oct 5
Workflow
Posted on Oct 5 This article was originally published on the Endform blog. By the end of this walkthrough you'll have a single Playwright test that Claude Code wrote against your live app, that you've reviewed in two passes, run repeatedly to check for flakiness, and committed next to the plain-English scenario it came from. The trick that makes it trustworthy: the agent reads locators off the running page through the Playwright MCP server instead of guessing them from your source code. You need four things installed before step 1, so start there. Before you start Confirm all four of these first. Skipping one is the most common reason step 1 fails. Node.js 20 or newer. Check with node --version. The MCP server launches through npx, so Node is required no matter how you installed Claude Code. Node.js 20 or newer. Check with node --version. The MCP server launches through npx, so Node is required no matter how you installed Claude Code. Claude Code, installed and signed in. Check with claude --version. Claude Code, installed and signed in. Check with claude --version. A Playwright project. You need playwright.config.ts at the repo root. No project yet? Run npm init playwright@latest to scaffold one. A Playwright project. You need playwright.config.ts at the repo root. No project yet? Run npm init playwright@latest to scaffold one. An app to test. Local dev server or staging URL, either works. Playwright can start it for you when the config says so. An app to test. Local dev server or staging URL, either works. Playwright can start it for you when the config says so. Got all four? Connect Claude Code to a browser. The problem this setup solves Here's a failure pattern you've probably lived through: Claude Code writes an end-to-end test, it passes locally, you merge, and CI goes red a day later on what looks like flakiness. The test was wrong from the start, it just had no way to show it. The reason is where the locators came from. Reading your components without ever opening the app, an agent spots a button with a class like .btn-primary and writes a selector against it. Then a component library or some runtime logic rewrites that class on its way to the DOM, and by the time the browser renders, the selector points at a hashed string, a restructured node, or nothing. Nobody caught it because nobody ran the test against a real page. CI is the first thing that does. Compare the two ways the same button gets targeted: Anthropic's Model Context Protocol is what closes the gap. The Playwright MCP server hands Claude Code a live browser: it can open the app, walk the accessibility tree, and build locators out of the roles and names the page actually exposes. It's working from a structured snapshot rather than a screenshot, which keeps the locators stable and the token cost low, because the model reasons over text it can already read. Same model, better inputs. Nothing here is a capability upgrade, only an access one. Step 1: Register the Playwright MCP server A single command wires the server into Claude Code: That entry gets saved to your local config, and the server process spins up the next time you open a session. -y skips the install confirmation npx would otherwise wait on, and @latest grabs the newest published build so you're not stuck on a stale cache. By default this is a personal, single-project entry. Working on a team? Add --scope project, which writes the same config to a .mcp.json at the repo root so everyone shares one server without redoing setup: Don't trust it until you've checked it. Run claude mcp list and look for ✔ Connected. Immediately after adding, you may catch a ✘ Failed to connect while npx is still pulling the package in the background; run the command again a few seconds later and it usually clears. If retrying doesn't clear it, the problem is elsewhere. Claude Code runs the server as a subprocess in its own environment, and that subprocess can't always resolve npx the way your interactive shell does. Homebrew-installed Node on macOS is the usual culprit. Give it the absolute binary path instead of trusting PATH: Run claude mcp list once more and wait for ✔ Connected before continuing. Connected only means the process is alive. It doesn't prove Claude Code can actually drive a browser yet. Open a session and call the tool by name, otherwise Claude Code might reach for a Bash command instead of the MCP server:
Reported result
The source describes a workflow where Claude Code generates Playwright tests by reading locators from a live browser via the Playwright MCP server. The author states that this approach prevents flaky tests caused by guessing selectors from source code. The text outlines steps for setup, authentication, prompt construction, generation, review, and verification, but does not provide specific metrics on time saved or success rates.
Human role
The original post does not say how a person stayed involved.
Limitations
Prerequisites include Node.js 20+, Claude Code, and a Playwright project. The workflow may fail with long multi-step flows, changing data, deep conditionals, or OAuth/third-party auth. The author notes that MCP tooling changes quickly, so commands may become outdated. Human review is essential for scenario judgment and verifying that tests assert meaningful outcomes.
Try it
I want to set up a workflow to generate stable Playwright tests using Claude Code and the Playwright MCP server. First, ensure I have Node.js 20+, Claude Code, and a Playwright project installed. Run `claude mcp add playwright -- npx -y @playwright/mcp@latest` to register the server. Verify the connection with `claude mcp list` and confirm it shows 'Connected'. Next, configure authentication by creating a setup test that saves the storage state to `.auth/user.json`. Update the MCP server registration to include `--isolated --storage-state .auth/user.json`. Then, prompt Claude Code to write a test for [replaceable scenario, e.g., 'user logout'] against [replaceable app URL]. Instruct it to explore the app using MCP tools before writing code, prefer `getByRole` locators, and avoid manual waits. Review the generated test for correctness and run it multiple times using `npx playwright test --repeat-each 5` to check for flakiness. Finally, commit the test and its scenario description together. Ensure `.auth/` is added to `.gitignore` to prevent leaking session data.
Source reports are not independently verified. Publication date is the catalog publication date, not the original article date.