Section 1Why testers should care
For years, "AI in testing" meant a tool that recorded clicks or guessed locators. MCP changes the shape of it. Instead of a separate AI product, your everyday assistant — Claude, Copilot in VS Code, Cursor — gets a real browser it can open, read and operate. You describe what you want in plain English; the agent navigates, clicks, fills forms, reads the page and reports back. Then it can turn what it did into a proper Playwright test.
That does not replace an automation engineer. It removes the boring middle: inspecting the DOM for a locator, writing the first rough version of a spec, figuring out why a test broke after a UI change. The engineer still decides what to test, reviews the code and owns the framework.
Section 2MCP in plain words
MCP (Model Context Protocol) is an open standard for connecting AI applications to outside tools and data. Think of it the way you think of a USB port: the AI app is the laptop, an MCP server is a device, and the protocol is the plug shape that lets any device work with any laptop.
Host
The AI app you type into — VS Code with Copilot, Claude Desktop, Claude Code, Cursor.
Client
The part inside the host that speaks MCP and keeps a connection to each server.
Server
A small program that exposes "tools" the model can call. Playwright MCP is one such server.
Tools
Named actions with inputs — navigate, click, type, take a snapshot. The model decides which to call.
Before MCP, every AI tool needed its own custom integration for every service. With MCP, Microsoft wrote one Playwright server and it works in every MCP-capable assistant.
Section 3How Playwright MCP works
The important design choice: the agent reads the page through the accessibility tree, not screenshots. After each action, the server returns a structured text snapshot of the page — roles, names, states — with a short reference (ref) for every element. The model says "click ref e42" and Playwright clicks exactly that element.
Snapshot mode (default)
Fast, cheap in tokens, deterministic. Works with any text model. Mirrors how getByRole sees the page.
Vision mode (opt-in)
Uses screenshots and x/y coordinates. Useful for canvas, charts or badly built UIs with no accessible names.
getByRole does. Good a11y makes both your tests and your AI tooling better.Section 4Setup in 5 minutes
Prerequisite: Node.js 18 or newer. That is it — the server is downloaded on demand with npx.
VS Code (GitHub Copilot agent mode)
Add this to .vscode/mcp.json in your project (or your user settings), then switch Copilot Chat to Agent mode:
{
"servers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
Claude Desktop / Cursor
Same idea, under the mcpServers key of the app's MCP config file:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
Claude Code (terminal)
claude mcp add playwright npx @playwright/mcp@latest
Check it works
Ask the assistant: "Open https://demo.opencart.com and tell me what the main navigation menu contains." A browser window should open and the answer should come back from the live page.
- Browser not installed — run
npx playwright install chromium. - Tools not showing — restart the host app after editing config; check the JSON has no trailing commas.
- Corporate proxy —
npxneeds registry access; set your npm proxy or install the package globally once.
Section 5Tools the agent gets
The server exposes a set of tools. You never call them yourself — the model does — but knowing them helps you write better prompts and read the agent's log.
| Tool | What it does |
|---|---|
browser_navigate | Open a URL |
browser_snapshot | Return the accessibility snapshot with element refs |
browser_click / browser_hover | Click or hover an element by ref |
browser_type / browser_fill_form | Type into one field, or fill several at once |
browser_select_option | Choose from a dropdown |
browser_press_key | Press Enter, Tab, Escape, etc. |
browser_wait_for | Wait for text to appear/disappear or for time |
browser_handle_dialog | Accept or dismiss alerts and confirms |
browser_file_upload | Upload files to a file input |
browser_tabs | List, open, switch and close tabs |
browser_take_screenshot | Capture the page or an element |
browser_console_messages | Read the browser console — great for debugging |
browser_network_requests | List network calls the page made |
browser_evaluate | Run JavaScript on the page |
browser_close | Close the browser |
Tool names evolve between releases — check the server's README for the current list.
Section 6Useful config flags
Pass flags through the args array, e.g. ["@playwright/mcp@latest", "--headless", "--browser=firefox"].
| Flag | Use it when |
|---|---|
--headless | You don't need to watch, or you're on CI / a server |
--browser=firefox|webkit|msedge | Checking behaviour in another engine |
--isolated | Each session should start with a clean profile (no leftover cookies) |
--user-data-dir=<path> | You want to stay logged in between sessions |
--storage-state=<file> | Reuse a saved login state, same as in your test suite |
--viewport-size=1280,720 | Testing a specific screen size |
--caps=vision | Turn on screenshot/coordinate tools |
--save-trace | Keep a Playwright trace of what the agent did — open it in Trace Viewer |
--allowed-origins=<list> | Restrict which sites the agent may visit |
Section 7Real workflows that save time
Workflow 1 — Exploratory testing by conversation
// Prompt
Go to https://demo.opencart.com. Search for "MacBook", open the first result,
add it to the cart and open the cart. Report: product name, unit price,
quantity and total. Flag anything that looks wrong.
The agent performs every step and reports what it saw. You get a quick smoke check of a flow without writing a line of code — useful on a fresh build or a ticket you're picking up for the first time.
Workflow 2 — Explore first, then generate the test
// Prompt, after the exploration above
Now write a Playwright Test in TypeScript for the flow you just did.
Use getByRole / getByLabel locators only, web-first assertions,
and put it in tests/cart.spec.ts.
Because the agent actually walked the page, its locators come from the real accessibility tree instead of guesses. A typical result:
import { test, expect } from '@playwright/test';
test('add MacBook to cart from search', async ({ page }) => {
await page.goto('https://demo.opencart.com/');
await page.getByRole('textbox', { name: 'Search' }).fill('MacBook');
await page.getByRole('button', { name: /search/i }).click();
await page.getByRole('link', { name: 'MacBook', exact: true }).first().click();
await page.getByRole('button', { name: 'Add to Cart' }).click();
await expect(page.getByText(/success: you have added/i)).toBeVisible();
await page.getByRole('link', { name: /shopping cart/i }).first().click();
await expect(page.getByRole('link', { name: 'MacBook', exact: true })).toBeVisible();
});
Workflow 3 — Debug a failing test
tests/checkout.spec.ts fails at the "Place order" step with a timeout.
Open the app, repeat the steps manually, check the console and network
requests, and tell me why. Then propose the smallest fix.
The agent can see what your CI log can't: a cookie banner covering the button, a 500 from an API, a renamed label. Combine with --save-trace to keep evidence.
Workflow 4 — Locator hunting
On the registration page, give me the most stable Playwright locator
for every input and button. Prefer role + accessible name. Output a table.
Workflow 5 — Quick accessibility pass
Take a snapshot of the checkout page and list every interactive element
that has no accessible name, or whose name is unclear.
Section 8Playwright test agents: planner, generator, healer
Newer Playwright releases (1.56+) ship ready-made agent definitions built on top of MCP. Instead of writing prompts from scratch, you get three specialised agents:
🧭 Planner
Explores the app from a seed test and writes a human-readable test plan in Markdown under specs/.
⚙️ Generator
Turns the plan into Playwright test files, checking selectors and assertions live as it writes.
🩹 Healer
Runs failing tests, replays the steps, finds what changed in the UI and patches the test — or marks it skipped if the feature is genuinely broken.
Initialise them for your assistant of choice:
npx playwright init-agents --loop=vscode # or --loop=claude / --loop=opencode
This adds the agent definitions plus a seed test — a small spec that sets up the starting state (base URL, login, fixtures). Every agent starts from it, so get it right first.
Section 9Prompts that work
Be specific about the goal
"Verify a guest can check out with COD" beats "test the checkout".
State your standards
Locator strategy, folder, naming, POM usage, assertion style — say it once, upfront.
Ask for evidence
"Report console errors and failed network calls" turns a click-through into a real check.
Break big flows up
Login, then search, then cart. Long single prompts drift and burn tokens.
Give test data
Provide the user, product and expected values — don't let the agent invent them.
Keep project rules in a file
Put conventions in CLAUDE.md or Copilot instructions so every session follows them.
Section 10Limits & best practices
What it is not good at (yet)
- Not a CI runner. Your regression suite still runs as plain Playwright tests. MCP is for authoring, exploring and debugging.
- Non-deterministic. The same prompt can take a slightly different path. Never treat an agent's click-through as a pass/fail result of record.
- Token cost on big pages. Huge DOMs make huge snapshots. Narrow the scope, or ask for specific sections.
- Captchas, OTP, 2FA block it — use a test environment with these disabled, or a saved storage state.
- Weak accessibility = weak agent. Canvas apps and nameless divs need vision mode or a human.
Safety on real projects
- Point it at dev/QA environments, never production.
- Use test accounts only; don't paste real passwords into prompts.
- Use
--allowed-originsand--isolatedto limit what it can touch. - Check your company's policy before sending internal app content to an AI model — especially in banking, healthcare or government projects.
- Generated code goes through normal code review. No exceptions.
Section 11Interview questions on Playwright MCP
This topic now comes up in SDET interviews, usually as "Have you used AI in your automation work?" Here's how to answer at the level it's asked.
What is MCP and why is it useful for test automation?
Model Context Protocol is an open standard that lets AI assistants call external tools in a uniform way. For automation, the Playwright MCP server gives an assistant a real browser, so it can explore an application, find stable locators, generate test code and debug failures — all from natural-language instructions.
How does Playwright MCP "see" the page?
By default through accessibility snapshots — a structured text view of the page with roles, names and element references. It's faster and more reliable than screenshots and works with text-only models. Vision mode with screenshots can be switched on with --caps=vision when needed.
Can Playwright MCP replace your regression suite?
No. Agent runs are non-deterministic and cost tokens. MCP helps write, maintain and debug tests; the suite itself still runs as normal Playwright tests in CI, giving repeatable results.
Explain the planner, generator and healer agents.
The planner explores the app from a seed test and writes a Markdown test plan. The generator converts that plan into executable Playwright tests, verifying locators live. The healer runs failing tests, works out what changed, and repairs the test or skips it if the feature itself is broken.
What risks do you watch for with AI-generated tests?
- Weak or missing assertions — a test that clicks but proves nothing.
- Hard-coded data and brittle waits.
- Ignoring the framework's POM and utilities.
- A healer "fixing" a test to pass against a real bug.
- Sending sensitive application data to an external model.
Mitigation: code review, clear project instructions, test environments only, and keeping humans responsible for what gets merged.
How did you use it in a real project? (frame your own answer)
Structure: situation (e.g. 40 new screens, tight sprint) → what you did (used MCP to explore and draft specs, refactored into the POM, reviewed every assertion) → result (authoring time cut, flaky locators reduced) → what you'd watch for. Only claim what you actually did — interviewers follow up.
Snapshot mode vs vision mode — when do you pick which?
Snapshot mode by default: cheaper, deterministic, matches role-based locators. Vision mode for canvas, charts, maps, or legacy UIs without accessible names, accepting more cost and less precision.
Section 12 · Hands-onInstall Copilot, Playwright MCP and the agents
Most agent setup failures are version problems. Check these four first.
| Tool | Minimum | Check with |
|---|---|---|
| Node.js | 18+ (20+ recommended) | node --version |
| VS Code | 1.105+ | Help → About |
| GitHub Copilot Chat | Signed in, Agent mode | Chat panel opens |
| Playwright | 1.56+ | npx playwright --version |
Step 1 · Create an empty project
mkdir app-mcp-demo
cd app-mcp-demo
npm init -y
Step 2 · Install GitHub Copilot
Extensions → search GitHub Copilot → Install → restart VS Code → sign in with GitHub. In the chat panel switch to Agent mode and pick a model.
Step 3 · Install Playwright MCP (method A: standalone)
Official repo: github.com/microsoft/playwright-mcp. Click Install Server → VS Code, or run:
code --add-mcp '{"name":"playwright","command":"npx","args":["@playwright/mcp@latest"]}'
Expected output: Added MCP servers: playwright. If code is not found, open the Command Palette → Shell Command: Install 'code' command in PATH, or jump to method B.
Step 4 · Initialise Playwright
npm init playwright@latest
Choose TypeScript, tests folder tests, GitHub Actions workflow: yes, install browsers: yes.
Step 5 · Add the three agents (method B: bundled)
npx playwright init-agents --loop=vscode
--loop=claude, --loop=opencode.Section 13 · Hands-onWhat init-agents creates
Seven new files appear. Know what each one does before you edit anything.
| File | Purpose |
|---|---|
.github/agents/playwright-test-planner.agent.md | Planner agent |
.github/agents/playwright-test-generator.agent.md | Generator agent |
.github/agents/playwright-test-healer.agent.md | Healer agent |
.vscode/mcp.json | Registers the Playwright test MCP server |
specs/README.md | Folder for the Planner's test plans |
tests/seed.spec.ts | Seed test every agent starts from |
.github/workflows/copilot-setup-steps.yml | Cloud Copilot coding-agent setup (ignore for now) |
.vscode/mcp.json (generated)
{
"servers": {
"playwright-test": {
"type": "stdio",
"command": "npx",
"args": ["playwright", "run-test-mcp-server"]
}
},
"inputs": []
}
Open mcp.json and click Start above "playwright-test". It turns into Running | Stop | Restart | N tools. That tool count is your proof the server is installed. Think of mcp.json as the package.json of MCP servers: add Jira or GitHub MCP later and they appear here too.
.agent.md has front matter between --- lines (description, tools, model, mcp-servers) and then a plain-English body. Change the content, never the file name.Section 14 · Hands-onOur three agent files (copy and paste)
Keep the generated front matter. Replace only the body below the closing --- with these AutomateWithUs rules.
🧭 Planner body
.github/agents/playwright-test-planner.agent.md
# Planner (AutomateWithUs rules)
You are the Planner. You explore a running web app and write a numbered
test plan in Markdown. You never write test code.
## Read first
1. AGENTS.md at the project root (if a rule here conflicts, AGENTS.md wins)
2. tests/seed.spec.ts for the base URL and starting state
## Allowed
- Navigate, hover, wait, press keys, switch tabs
- Take accessibility snapshots (your main way of seeing the page)
- Read console and network messages for context
- Create or edit files only inside specs/
## Not allowed
- Clicking destructive actions (delete, remove, cancel order, pay)
- Typing real-looking personal data into forms
- Writing or editing any file outside specs/
- Exploring production URLs: staging, local or public demo sites only
## Output: specs/<feature-name>.md (kebab-case)
# Test Plan: <Feature>
Target: <URL> | Seed: tests/seed.spec.ts | Date: <YYYY-MM-DD>
## Preconditions
## Scenarios
### 1.1 <short title>
- Priority: P0 | P1 | P2
- Tags: @smoke | @regression | @critical
- Steps: numbered, each with "expected: <what the user sees>"
- Assertions: at least one real check, not just "page loaded"
- Edge cases considered
## Not covered (and why)
## Rules
- Numbering is <feature>.<scenario>: 1.1, 1.2, 2.1. The Generator uses these numbers.
- Every scenario is independent and has explicit preconditions.
- If the plan file already exists, ask before overwriting it.
⚙️ Generator body
.github/agents/playwright-test-generator.agent.md
# Generator (AutomateWithUs rules)
You turn ONE scenario from specs/*.md into a runnable Playwright
TypeScript test that follows our framework.
## Read first
1. AGENTS.md
2. tests/seed.spec.ts
3. The plan file and scenario number the user gave you
4. Existing page objects in src/pages/ (reuse before creating)
## Framework rules
- Import test and expect from src/fixtures/base.ts, never from @playwright/test
- Test data comes from tests/data/*.json, never hard-coded in the spec
- One page object class per page in src/pages/, extending BasePage
- Locators are readonly properties set in the constructor
- Page objects contain no expect(); assertions live in the spec
- Spec files are kebab-case and end with .spec.ts
- Ask before creating a new page object
## Locator priority (stop at the first one that is unique)
1. getByRole(role, { name })
2. getByLabel(text)
3. getByPlaceholder(text)
4. getByTestId(id)
5. getByText(text) for static copy only
CSS or XPath only with a code comment explaining why.
If nothing is unique, stop and ask the user.
## Assertions
- Web-first only: toBeVisible, toHaveText, toHaveURL, toHaveCount
- Never page.waitForTimeout or waitForSelector
## Before you finish
- Verify every locator in the live browser before writing it
- Run the test; report pass or fail honestly
🩹 Healer body
.github/agents/playwright-test-healer.agent.md
# Healer (AutomateWithUs rules)
You diagnose a failing test and make the smallest correct fix.
A test that passes for the wrong reason is worse than a red test.
## Read first
AGENTS.md, the failing spec, its page objects, the last error and trace.
## Classify the failure first
A Locator changed (element still there) -> fix the locator
B UI flow changed (element moved) -> update the steps
C Text on screen changed -> update the text after checking
D Real bug in the app -> REPORT it, do not touch the test
E Environment down / seed broken -> REPORT it, do not touch the test
F Flaky timing -> wait for a real state, never a fixed sleep
Check console errors and failed network calls BEFORE assuming A.
## Never
- Weaken an assertion (toHaveText -> toContainText, toHaveCount(6) -> toBeVisible)
- Add test.skip, test.fixme or bigger timeouts without approval
- Use page.waitForTimeout
- Edit page objects, fixtures, test data or playwright.config.ts without approval
- Delete a test, comment out an assertion, or wrap it in try/catch
## Stop and ask when
- The failure looks like category D or E
- The fix needs a page object or fixture change
- You cannot classify the failure with confidence
## Two-attempt limit
After 2 failed fix attempts: stop, report both attempts, ask what next.
## Report format
Category | Root cause | Files changed | Ready to merge / Needs review / Real bug
Section 15 · Hands-onAGENTS.md: the global rulebook
Every agent file says "read AGENTS.md first", but init-agents does not create it. Create it once, by hand, at the project root. Each AI tool auto-loads a different file name:
| Tool | File it reads automatically |
|---|---|
| GitHub Copilot (VS Code) | .github/copilot-instructions.md |
| Claude Code | CLAUDE.md or AGENTS.md |
| Cursor | .cursorrules or .cursor/rules/*.mdc |
| Codex CLI | AGENTS.md |
Keep the real file at .github/copilot-instructions.md and make AGENTS.md a symlink to it. Edit either one and both change.
macOS / Linux / Git Bash
mkdir -p .github
touch .github/copilot-instructions.md
ln -s .github/copilot-instructions.md AGENTS.md
Windows PowerShell (Admin or Developer Mode)
New-Item -ItemType File -Force .github/copilot-instructions.md
New-Item -ItemType SymbolicLink -Path AGENTS.md -Target .github/copilot-instructions.md
Now paste the template into .github/copilot-instructions.md and adjust it to your project:
.github/copilot-instructions.md
# Project rules for AI agents
This is a Playwright + TypeScript automation project.
Follow these rules for every change.
## Stack
- Playwright 1.56+ with TypeScript, Node 20+
- Runner: @playwright/test | Reports: HTML + Allure
- CI: GitHub Actions with sharding
## Folder structure
- src/pages/ page object classes, one file per page
- src/fixtures/ custom fixtures (base.ts)
- src/utils/ pure helpers, no test logic
- tests/ spec files, mirroring the app's URL structure
- tests/data/ JSON test data
- specs/ Markdown test plans from the Planner
## Coding conventions
- Classes PascalCase, methods camelCase, spec files kebab-case.spec.ts
- Import test/expect only from src/fixtures/base.ts
- No hard-coded test data in specs
## Locators (in this order)
getByRole > getByLabel > getByPlaceholder > getByTestId > getByText
CSS/XPath only with a comment saying why.
## Page objects
- Extend BasePage, constructor takes page: Page
- Locators are readonly, set in the constructor
- Methods return Promise<void> or the next page object
- No expect() inside page objects
## Assertions and waits
- Web-first assertions only (expect(locator).toBeVisible())
- No page.waitForTimeout, no waitForSelector
- Custom timeouts only with a comment explaining why
## Adding a new test
- Reuse existing page objects; do not create parallel ones
- Tag every test: @smoke, @regression or @critical
## Forbidden
- Skipping or commenting out failing tests to make CI green
- page.evaluate when an MCP tool can do the job
- Committing .env, credentials, storage-state.json or tokens
- Changing playwright.config.ts or adding npm packages without asking
- page.pause() in committed code
## When unsure
- Ask a question before writing code
- Prefer a small, focused change over a big refactor
- If a needed file does not exist, ask before creating it
.agent.md body → AGENTS.md → the agent's defaults.Section 16 · Hands-onStarter project structure
Agents copy what they see. Give them a small, clean skeleton and they follow it; give them an empty folder and they invent their own.
Terminal
mkdir -p src/pages src/fixtures src/utils tests/data specs
src/fixtures/base.ts
import { test as base, expect } from '@playwright/test';
export const test = base.extend<{}>({});
export { expect };
src/pages/BasePage.ts
import { Page } from '@playwright/test';
export abstract class BasePage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
abstract goto(): Promise<void>;
async waitForReady(): Promise<void> {
await this.page.waitForLoadState('domcontentloaded');
}
}
tests/seed.spec.ts (replace the generated one)
import { test, expect } from '../src/fixtures/base';
test.describe('Seed - environment baseline @smoke', () => {
test('SauceDemo login page loads', async ({ page }) => {
await page.goto('https://www.saucedemo.com');
await expect(page.getByRole('textbox', { name: 'Username' })).toBeVisible();
await expect(page.getByRole('textbox', { name: 'Password' })).toBeVisible();
});
});
tests/data/users.json
{
"standard": { "username": "standard_user", "password": "secret_sauce" },
"locked": { "username": "locked_out_user", "password": "secret_sauce" }
}
These are SauceDemo's public demo users, shown on its own login page.
Verify before using any agent
npx playwright test tests/seed.spec.ts
Final folder tree
app-mcp-demo/
├── .github/
│ ├── agents/ planner, generator, healer .agent.md
│ └── copilot-instructions.md
├── .vscode/mcp.json
├── AGENTS.md -> .github/copilot-instructions.md
├── specs/ test plans (Planner output)
├── src/
│ ├── fixtures/base.ts
│ ├── pages/BasePage.ts
│ └── utils/
├── tests/
│ ├── data/users.json
│ └── seed.spec.ts
└── playwright.config.ts
Section 17 · Hands-onRun Planner → Generator → Healer
The order is fixed: plan → review the plan → generate one scenario → review the code → heal only when a test fails. In Copilot Chat, pick the agent from the agent dropdown first, and check that mcp.json shows Running.
Step 1 · Planner prompt
Explore https://www.saucedemo.com starting from tests/seed.spec.ts.
Cover: login (valid, locked-out, empty fields), add to cart, remove from cart,
and checkout until the overview page. Do not place the order.
Save the plan as specs/saucedemo-checkout.md.
specs/ should change. A bad plan gives bad code.Step 2 · Generator prompt (one scenario at a time)
Generate scenario 1.1 from specs/saucedemo-checkout.md.
Use users.standard from tests/data/users.json.
Create LoginPage and InventoryPage in src/pages/ if they do not exist (ask first).
Save the test as tests/login/standard-user-login.spec.ts and run it.
What a good result looks like
import { test, expect } from '../../src/fixtures/base';
import { LoginPage } from '../../src/pages/LoginPage';
import users from '../data/users.json';
test.describe('Login', () => {
test('standard user lands on inventory @smoke @critical', async ({ page }) => {
const login = new LoginPage(page);
await login.goto();
const inventory = await login.loginAs(users.standard.username, users.standard.password);
await expect(page).toHaveURL(/inventory/);
await expect(inventory.items).toHaveCount(6);
});
});
Step 3 · Healer prompt (only on a red test)
Class exercise: in LoginPage, change the login button's name to a wrong value, run the test so it fails, then ask:
The test tests/login/standard-user-login.spec.ts is failing.
Diagnose it, classify the failure (A to F), and propose the smallest fix.
Do not change any assertion.
Expected: category A (locator drift), a one-line locator fix and a short report. Then try the trap: change the expected count to toHaveCount(7). A well-guarded Healer reports it as a test or data question instead of quietly changing it.
Review checklist for AI-written code
- Imports come from
src/fixtures/base, not@playwright/test - Locators follow the priority list; no unexplained CSS or XPath
- No
waitForTimeout, nowaitForSelector - Assertions check something real (URL, count, text), not just "page loaded"
- No
expect()inside page objects - Test data loaded from JSON; no hard-coded credentials
- Existing page objects reused, not duplicated
- Passes 3 runs in a row:
npx playwright test --repeat-each=3 - You can explain every line. If not, do not merge it.
Learn Playwright properly — then let AI speed you up
AI agents are only as good as the engineer reviewing them. Our live Playwright with TypeScript course gets you to framework level, with real projects and mock interviews included.
See the Playwright course Book a mock interview