Playwright MCP Server — POC with 100+ Test Cases
VentureDive QA Engineering Prepared by: Raheel Imran Date: May 2026
---
Overview
This Proof of Concept (POC) demonstrates the Playwright MCP Server (@playwright/mcp) used as the browser automation engine for a complete test suite. It includes:
- A fully functional demo web application — TaskMaster Pro
- 166 Playwright test cases across 8 feature areas using reusable MCP-style commands
- A
cmdfixture that maps directly to@playwright/mcptool names - A live MCP server demo that connects via
@modelcontextprotocol/sdkand drives the browser using real MCP protocol — no external API calls - MCP server configuration for Claude Desktop and Claude Code
- An interactive HTML test report
---
Architecture
┌──────────────────────────────────────────────────────────────┐
│ Playwright MCP POC │
│ │
│ ┌───────────────────┐ MCP Protocol ┌────────────────┐ │
│ │ MCP Client │◄─────────────────►│ @playwright/mcp│ │
│ │ (run-demo.ts) │ stdio transport │ Server │ │
│ └───────────────────┘ └───────┬────────┘ │
│ │ │
│ Playwright Browser │
│ │ │
│ ┌────────▼─────────┐ │
│ │ TaskMaster Pro │ │
│ │ localhost:3000 │ │
│ └──────────────────┘ │
│ │
│ Playwright Test Runner (166 tests) │
│ └── tests/fixtures/browser-commands.ts ← reusable cmd │
│ ├── auth.spec.ts (25 tests) │
│ ├── navigation.spec.ts (20 tests) │
│ ├── dashboard.spec.ts (12 tests) │
│ ├── tasks.spec.ts (45 tests) │
│ ├── categories.spec.ts (14 tests) │
│ ├── profile.spec.ts (17 tests) │
│ ├── settings.spec.ts (17 tests) │
│ └── ui-components.spec.ts (16 tests) │
└──────────────────────────────────────────────────────────────┘
---
Quick Start
1. Install dependencies
npm install
npx playwright install chromium
2. Start the demo app
npm run dev
# → http://localhost:3000
3. Run all tests and generate HTML report
npm test
npm run test:report # opens playwright-report/index.html
4. Run the MCP server demo (browser driven via MCP protocol)
npm run demo
# Starts @playwright/mcp server, connects as MCP client, runs 10 scenarios
5. Interactive test runner
npm run test:ui
---
npm Scripts
| Script | Description | |--------|-------------| | npm run dev | Start the TaskMaster Pro demo app on port 3000 | | npm test | Run all 100+ tests (Chromium + Firefox) | | npm run test:ui | Open Playwright interactive UI mode | | npm run test:report | Open generated HTML report | | npm run test:headed | Run tests in headed browser | | npm run test:debug | Run tests with Playwright debugger | | npm run demo | Run the MCP server demo (real MCP protocol) | | npm run mcp:start | Start @playwright/mcp standalone server |
---
Reusable Browser Commands
All tests import from tests/fixtures/browser-commands.ts instead of @playwright/test directly. Command names match @playwright/mcp tool names exactly, making the test code portable to any MCP client.
import { test, expect } from './fixtures/browser-commands';
test('example', async ({ cmd }) => {
await cmd.browser_navigate('/login.html');
await cmd.browser_type('email-input', 'testuser@test.com');
await cmd.browser_type('password-input', 'Test@123');
await cmd.browser_click('login-btn');
await cmd.browser_expect_url(/dashboard/);
await cmd.browser_take_screenshot('login-success.png');
});
Full command reference
| Command | MCP Tool | Description | |---------|----------|-------------| | cmd.browser_navigate(url) | browser_navigate | Navigate to URL | | cmd.browser_navigate_back() | browser_navigate_back | Go back in history | | cmd.browser_click(testId) | browser_click | Click element by data-testid | | cmd.browser_type(testId, text) | browser_type | Fill input field | | cmd.browser_clear_and_type(testId, text) | browser_type | Clear then fill | | cmd.browser_select_option(testId, value) | browser_select_option | Select dropdown option | | cmd.browser_check(testId) | browser_click | Check a checkbox | | cmd.browser_uncheck(testId) | browser_click | Uncheck a checkbox | | cmd.browser_take_screenshot(filename?) | browser_take_screenshot | Capture screenshot | | cmd.browser_get_url() | — | Get current URL | | cmd.browser_get_text(testId) | — | Get element text | | cmd.browser_get_value(testId) | — | Get input value | | cmd.browser_is_visible(testId) | browser_snapshot | Returns boolean | | cmd.browser_is_checked(testId) | browser_snapshot | Returns boolean | | cmd.browser_count(testId) | browser_snapshot | Count elements | | cmd.browser_wait_for(testId, state?) | browser_wait_for | Wait for element | | cmd.browser_wait_for_url(pattern) | — | Wait for URL | | cmd.browser_set_storage(key, value) | — (fixture-only) | Set localStorage via page.evaluate | | cmd.browser_evaluate(fn) | — (fixture-only) | Run JS via page.evaluate | | cmd.browser_expect_url(pattern) | — | Assert current URL | | cmd.browser_expect_visible(testId) | — | Assert visible | | cmd.browser_expect_hidden(testId) | — | Assert hidden | | cmd.browser_expect_text(testId, text) | — | Assert text content | | cmd.browser_expect_count(testId, n) | — | Assert element count | | cmd.browser_expect_value(testId, val) | — | Assert input value | | cmd.browser_expect_checked(testId) | — | Assert checkbox state | | cmd.browser_expect_disabled(testId) | — | Assert disabled | | cmd.browser_expect_attr(testId, attr, val) | — | Assert attribute |
---
Demo App — TaskMaster Pro
A fully self-contained web app using localStorage for all data persistence.
| Page | URL | Features | |------|-----|----------| | Login | /login.html | Form validation, password toggle, remember-me | | Register | /register.html | Password strength, validation | | Dashboard | /dashboard.html | Stats, recent tasks, quick-add | | Tasks | /tasks.html | CRUD, filter, search, sort, bulk actions | | Categories | /tasks.html#categories | Create, edit, delete categories | | Profile | /profile.html | Edit name, avatar, change password | | Settings | /settings.html | Theme, notifications, language, export | | 404 | /404.html | Error page |
Default test credentials:
| Email | Password | Role | |-------|----------|------| | testuser@test.com | Test@123 | Test User | | admin@test.com | Admin@123 | Admin User |
Pre-seeded data: 5 tasks (3 active, 2 completed) and 4 categories.
MCP Tools provided by @playwright/mcp
See the full categorized reference in MCP Server Setup → Available MCP tools below. Key tools used in this demo app:
| Tool | Description | |------|-------------| | browser_navigate | Navigate to a URL | | browser_snapshot | Get accessibility tree — returns ARIA refs for click/type targets | | browser_click | Click an element by ARIA ref | | browser_type | Type text into a focused field | | browser_fill_form | Fill multiple form fields in one call | | browser_select_option | Select a dropdown value | | browser_take_screenshot | Capture the current page as an image | | browser_localstorage_set | Write a localStorage key (used for fast auth seeding) | | browser_evaluate | Evaluate JavaScript on the page | | browser_wait_for | Wait for an element or condition | | browser_network_requests | Inspect captured network traffic | | browser_route | Mock API responses for isolated testing | | browser_tabs | List, open, close, or switch tabs | | browser_close | Close the browser |
---
MCP Server Setup
@playwright/mcp exposes browser automation as MCP tools so any MCP-compatible client (Claude Code, Claude Desktop, VS Code, Cursor, Windsurf) can drive a real browser with no screenshots or vision models — just accessibility snapshots.
Claude Code (project-level)
A .mcp.json file is included at the project root. Claude Code picks it up automatically when you open this directory:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
Or install it manually via the CLI:
claude mcp add playwright npx @playwright/mcp@latest
Claude Desktop
Add the following to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
VS Code
One-liner via the VS Code CLI:
code --add-mcp '{"name":"playwright","command":"npx","args":["@playwright/mcp@latest"]}'
Or use the included .vscode/mcp.json — VS Code with the Copilot extension picks it up automatically:
{
"servers": {
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
Cursor / Windsurf
Add the same block under mcpServers in their respective settings files.
Configuration options
Append flags to the args array to customise the server:
| Flag | Effect | |------|--------| | --headless | Run browser without a visible window (default: headed) | | --browser <browser> | Browser to use: chrome, firefox, webkit, msedge (default: Chromium) | | --isolated | Keep browser profile in memory; do not persist to disk | | --port <n> | Expose the server over HTTP/SSE instead of stdio | | --host <host> | Host to bind to (default: localhost; use 0.0.0.0 for all interfaces) | | --vision | Use screenshots instead of accessibility snapshots (requires vision model) | | --caps <caps> | Comma-separated capability subset: tabs,pdf,history,wait,files,install | | --device <device> | Emulate a device, e.g. "iPhone 15" | | --viewport-size <size> | Set viewport, e.g. "1280, 720" | | --user-agent <ua> | Override the browser user-agent string | | --storage-state <path> | Load saved auth state (cookies/localStorage) from a JSON file | | --save-trace | Save a Playwright Trace of the session to --output-dir | | --output-dir <path> | Directory for screenshots, PDFs, and traces | | --proxy-server <proxy> | Route traffic through a proxy, e.g. http://myproxy:3128 | | --no-sandbox | Disable the browser sandbox (useful in some CI environments) | | --extension | Connect to an existing browser tab instead of launching a new browser | | --config <path> | Load advanced settings from a JSON config file |
Example — headless Firefox in isolated mode:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--headless", "--browser=firefox", "--isolated"]
}
}
}
Standalone server (HTTP transport)
For headless/CI environments where stdio is not suitable:
npx @playwright/mcp@latest --port 8931
Then point your MCP client at http://localhost:8931/mcp using the url form:
{
"mcpServers": {
"playwright": {
"url": "http://localhost:8931/mcp"
}
}
}
Available MCP tools
Navigation
| Tool | Description | |------|-------------| | browser_navigate | Navigate to a URL | | browser_navigate_back / browser_navigate_forward | Browser history navigation | | browser_reload | Reload the current page |
Interaction
| Tool | Description | |------|-------------| | browser_click | Click an element by ARIA ref | | browser_check / browser_uncheck | Check or uncheck a checkbox | | browser_type | Type text into a focused field | | browser_press_sequentially | Type text one character at a time (for masked fields) | | browser_fill_form | Fill multiple form fields by their labels in one call | | browser_select_option | Select a dropdown value | | browser_hover | Hover over an element | | browser_drag / browser_drop | Drag-and-drop between elements | | browser_press_key / browser_keydown / browser_keyup | Keyboard key events | | browser_handle_dialog | Accept or dismiss browser dialogs | | browser_file_upload | Upload a file to a file input | | browser_mouse_click_xy / browser_mouse_move_xy / browser_mouse_drag_xy | Pixel-precise mouse control | | browser_mouse_down / browser_mouse_up / browser_mouse_wheel | Low-level mouse events |
Inspection & Assertions
| Tool | Description | |------|-------------| | browser_snapshot | Get the page accessibility tree — primary way to read elements and obtain ARIA refs | | browser_take_screenshot | Capture the current page as an image | | browser_annotate | Capture a screenshot with annotated highlights | | browser_highlight / browser_hide_highlight | Visually highlight an element on the page | | browser_generate_locator | Generate a Playwright locator for a given element | | browser_verify_element_visible | Assert an element is visible | | browser_verify_text_visible | Assert specific text is visible on the page | | browser_verify_list_visible | Assert a list of items is visible | | browser_verify_value | Assert an element's value | | browser_wait_for | Wait for an element or network condition |
Storage
| Tool | Description | |------|-------------| | browser_evaluate | Evaluate a JavaScript expression on the page | | browser_localstorage_get / browser_localstorage_set / browser_localstorage_list | Read/write localStorage | | browser_localstorage_clear / browser_localstorage_delete | Remove localStorage items | | browser_sessionstorage_get / browser_sessionstorage_set / browser_sessionstorage_list | sessionStorage CRUD | | browser_sessionstorage_clear / browser_sessionstorage_delete | Remove sessionStorage items | | browser_cookie_get / browser_cookie_set / browser_cookie_list | Read/write cookies | | browser_cookie_clear / browser_cookie_delete | Remove cookies | | browser_storage_state | Export current cookies and storage to a file | | browser_set_storage_state | Import previously saved storage state |
Network
| Tool | Description | |------|-------------| | browser_network_requests | List all captured network requests | | browser_network_request | Get details for a single network request | | browser_network_clear | Clear the captured request log | | browser_network_state_set | Simulate online/offline/throttled network conditions | | browser_route | Mock requests matching a URL pattern with a custom response | | browser_route_list | List active route mocks | | browser_unroute | Remove a route mock |
Tabs
| Tool | Description | |------|-------------| | browser_tabs | List, create (new), close, or switch to a tab by index |
Console
| Tool | Description | |------|-------------| | browser_console_messages | Get browser console output | | browser_console_clear | Clear the console message log |
Recording & Tracing
| Tool | Description | |------|-------------| | browser_start_tracing / browser_stop_tracing | Record a Playwright Trace | | browser_start_video / browser_stop_video / browser_video_chapter | Record session video | | browser_pdf_save | Save the current page as a PDF |
Misc
| Tool | Description | |------|-------------| | browser_resize | Resize the browser window | | browser_get_config | Get the current server configuration | | browser_run_code_unsafe | Execute an arbitrary Playwright script (RCE-equivalent; trusted clients only) | | browser_close | Close the browser |
How accessibility snapshots work: Instead of returning a screenshot,
browser_snapshotreturns a structured text tree of all visible elements with their roles, labels, and unique[ref=…]identifiers. LLMs reference these ids when callingbrowser_clickorbrowser_type— no vision model required.
Verify the server is working
# Start the demo app first
npm run dev
# Then run the MCP demo (connects via stdio and runs 10 browser scenarios)
npm run demo
---
MCP Demo (demo/)
demo/run-demo.ts connects to @playwright/mcp via real MCP stdio protocol using @modelcontextprotocol/sdk. It runs 10 browser scenarios with no external API dependencies.
npm run demo
╔══════════════════════════════════════════════════════════╗
║ Playwright MCP Server — Direct MCP Protocol Demo ║
╚══════════════════════════════════════════════════════════╝
Target: http://localhost:3000
Transport: stdio (@playwright/mcp subprocess)
Connecting to @playwright/mcp server…
Connected.
Available MCP tools (14):
• browser_navigate
• browser_snapshot
• browser_click
…
S01 ✓ PASS Login page loads via MCP browser_navigate
S02 ✓ PASS Fill login form using browser_type
S03 ✓ PASS Capture login page screenshot
…
---
Test Coverage
| Area | Tests | TC Range | |------|-------|----------| | Authentication | 25 | TC001–TC025 | | Navigation | 20 | TC026–TC045 | | Dashboard | 12 | TC046–TC057 | | Task Management | 45 | TC058–TC102 | | Categories | 14 | TC103–TC116 | | Profile | 17 | TC117–TC133 | | Settings | 17 | TC134–TC150 | | UI Components | 16 | TC151–TC166 | | Total | 166 | |
---
Project Structure
.
├── demo-app/
│ ├── server.js Express static file server (port 3000)
│ └── public/
│ ├── index.html Entry point (redirect)
│ ├── login.html
│ ├── register.html
│ ├── dashboard.html
│ ├── tasks.html Tasks + Categories
│ ├── profile.html
│ ├── settings.html
│ ├── 404.html
│ ├── css/styles.css
│ └── js/utils.js Shared helpers + seed data
├── tests/
│ ├── fixtures/
│ │ └── browser-commands.ts Reusable MCP-style command fixture
│ ├── helpers/
│ │ └── auth.helper.ts Fast localStorage-based auth setup
│ ├── auth.spec.ts TC001–TC025
│ ├── navigation.spec.ts TC026–TC045
│ ├── dashboard.spec.ts TC046–TC057
│ ├── tasks.spec.ts TC058–TC102
│ ├── categories.spec.ts TC103–TC116
│ ├── profile.spec.ts TC117–TC133
│ ├── settings.spec.ts TC134–TC150
│ └── ui-components.spec.ts TC151–TC166
├── demo/
│ ├── scenarios.ts 10 MCP test scenario definitions
│ └── run-demo.ts MCP client demo (real MCP protocol)
├── playwright-report/ Generated HTML report (after npm test)
├── playwright.config.ts
├── package.json
└── tsconfig.json
---
Key Design Decisions
| Decision | Rationale | |----------|-----------| | data-testid attributes on all elements | Tests don't break when CSS or text changes | | Reusable browser-commands.ts fixture | Command names match @playwright/mcp tools — tests are portable | | loginViaStorage() helper | Sets session via localStorage.setItem directly — 10× faster than UI login | | Sequential workers (workers: 1) | Prevents localStorage conflicts between parallel tests | | retries: 1 locally, retries: 2 on CI | Handles transient timing issues without masking real failures | | No external API dependencies | MCP demo uses @playwright/mcp directly via stdio protocol |
---











