MCP Server August 17, 2026 14 min read

How to Use Playwright MCP Server with Claude Code (2026)

Connect Playwright MCP Server to Claude Code CLI and unlock AI-powered browser automation directly from your terminal. This tutorial walks through installation, configuration, test generation, failure debugging, and advanced custom tools — everything you need to build a modern QA workflow.

Claude Code is Anthropic's terminal-native AI assistant — and when you pair it with the Playwright MCP Server, you get something remarkably powerful: an AI agent that can open a real browser, navigate your application, read the live DOM, and write production-ready Playwright tests — all without leaving your terminal.

This guide covers the complete workflow from first install to advanced custom tools. By the end, you'll have Claude Code generating, running, and debugging Playwright tests against your live application.


What Is the Playwright MCP Server?

The Model Context Protocol (MCP) is an open standard created by Anthropic that lets AI models interact with external tools. Think of it as a USB port for AI — any tool that implements the MCP protocol can plug into Claude and extend what it can do.

The Playwright MCP Server is an official tool built by the Playwright team that exposes browser automation capabilities through this protocol. When connected to Claude Code, it gives the AI direct access to:

  • Browser navigation — go to any URL, click links, fill forms
  • DOM inspection — read the accessibility tree, find elements, check text content
  • Screenshots — capture what the page actually looks like
  • Interaction — click buttons, type into inputs, select dropdowns, hover over menus
  • Page state — read console logs, network requests, cookies, localStorage

Without MCP, Claude Code can write Playwright tests based on your descriptions — but it's guessing at selectors and page structure. With MCP, Claude sees your actual application and writes tests that match the real DOM, real selectors, and real user flows.

Key distinction: MCP is the protocol. Playwright MCP Server is one specific implementation of that protocol. You can have multiple MCP servers connected to Claude Code simultaneously — for example, Playwright for browser access and a custom server for database operations.

Prerequisites

Before starting, make sure you have these three things installed:

  1. Node.js 18 or later — the Playwright MCP Server runs on Node. Check with node --version.
  2. Claude Code CLI — Anthropic's terminal client. Install it with npm install -g @anthropic-ai/claude-code and authenticate with your API key or Claude subscription.
  3. A Playwright project — if you don't have one yet, run npm init playwright@latest to scaffold a new project with TypeScript, test examples, and config.
Terminal — verify all prerequisites
# Node.js 18+ required
node --version
# v20.15.0  ✓

# Claude Code CLI
claude --version
# claude-code 1.x.x  ✓

# Playwright installed in your project
npx playwright --version
# 1.48.0  ✓

If any of these commands fail, install the missing dependency before continuing. The rest of this guide assumes all three are working.

Installing the Playwright MCP Server

The Playwright MCP Server is distributed as the @playwright/mcp npm package. You have three installation approaches:

Method Command Best for
On-demand (npx) npx @playwright/mcp@latest Default. Always runs the latest version, no install step.
Global install npm install -g @playwright/mcp Faster startup (~2s saved). Good if you use MCP daily.
Project dependency npm install --save-dev @playwright/mcp Pin a version for team consistency. Committed to package.json.

For most developers, the npx approach is the right default. It pulls the latest version automatically and requires no setup. If the 2–3 second startup difference matters to your flow, install globally.

Verify the package works by running it directly:

Terminal
# Start the MCP server standalone (for verification only)
npx @playwright/mcp@latest

# Expected output:
# Playwright MCP Server listening on stdio
# Press Ctrl+C to stop

If you see "listening on stdio", the server works. Press Ctrl+C to stop it — Claude Code will manage the server lifecycle automatically.

Configuring Claude Code to Use the MCP Server

Claude Code reads MCP server definitions from a JSON settings file. You can configure it at the project level (recommended) or globally.

Option A: Project-level configuration (recommended)

This scopes the MCP server to your Playwright project. Create or edit .claude/settings.json in your project root:

.claude/settings.json
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest"],
      "env": {}
    }
  }
}

Option B: Global configuration

If you want Playwright MCP available in every Claude Code session regardless of project, add it to your global settings:

~/.claude/settings.json
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest"]
    }
  }
}

Verify the connection

Start a new Claude Code session and check that the MCP server is recognized:

Terminal
# Start Claude Code in your project directory
cd /path/to/your/playwright-project
claude

# Inside Claude Code, check connected MCP servers
/mcp

# Expected: "playwright" listed as a connected server with available tools

If Playwright appears in the /mcp output with tools like browser_navigate, browser_snapshot, and browser_click, your setup is complete.

Common mistake: Editing the settings file while Claude Code is running. Always start a new session after changing MCP configuration. Claude Code reads the config at startup.

Your First AI-Generated Test

With MCP connected, let's generate a real test. This walkthrough uses the Playwright docs site as a target, but substitute your own application URL.

1

Describe the test in plain English

Tell Claude Code what you want to test. Be specific about the page, the user action, and the expected result.

2

Claude navigates and inspects the page

Claude uses the MCP Server to open the URL in a real browser, read the accessibility tree, and understand the page structure before writing any code.

3

Claude writes the test with real selectors

Because Claude can see the actual DOM, it uses getByRole, getByLabel, and getByText selectors that match your real UI — not guesses.

4

Run the test to verify

Claude can run npx playwright test directly and confirm the test passes. If it fails, it debugs immediately.

Here's what the conversation looks like in practice:

Claude Code session
# Your prompt:
Navigate to https://playwright.dev and write a Playwright test that verifies
the "Get Started" button in the hero section links to the /docs/intro page.
Use TypeScript and save it to tests/homepage.spec.ts.

# Claude's workflow (visible in the session):
# 1. browser_navigate → https://playwright.dev
# 2. browser_snapshot → reads the accessibility tree
# 3. Identifies the "Get started" link with role="link"
# 4. Writes the test file
# 5. Runs: npx playwright test tests/homepage.spec.ts
# 6. Reports: 1 passed

The generated test will look something like this:

tests/homepage.spec.ts
import { test, expect } from '@playwright/test';

test('Get Started button links to intro docs', async ({ page }) => {
  await page.goto('https://playwright.dev');

  const getStarted = page.getByRole('link', { name: 'Get started' });
  await expect(getStarted).toBeVisible();
  await getStarted.click();

  await expect(page).toHaveURL(/\/docs\/intro/);
});

Notice the selector quality. Claude used getByRole('link', { name: 'Get started' }) because it read the actual accessibility tree via MCP. Without MCP, it might have guessed .hero-button or a[href="/docs/intro"] — selectors that break when the CSS or HTML changes.

Browser Context: How Claude Sees Your App

Understanding what Claude can and cannot see through MCP is critical for writing effective prompts.

When Claude calls browser_snapshot, it receives the page's accessibility tree — a structured representation of every interactive element, heading, text block, image (with alt text), form field, and ARIA landmark on the page. This is the same tree that screen readers use.

Example: what Claude sees after browser_snapshot
# Simplified accessibility tree output
- navigation "Main"
  - link "Home"
  - link "Docs"
  - link "API"
  - link "Community"
  - link "Get started"
- heading "Playwright" [level=1]
- text "Reliable end-to-end testing for modern web apps"
- link "Get started" [focused]
- link "Star on GitHub"
- heading "Any browser. Any platform." [level=2]
...

Claude can also:

  • Take screenshotsbrowser_screenshot captures a PNG of the visible viewport, useful for visual verification
  • Read console output — see JavaScript errors, warnings, and logs
  • Inspect network requests — verify API calls, check response status codes
  • Execute JavaScript — run arbitrary JS in the page context for advanced inspection

This means Claude's test generation is grounded in reality. It knows exactly which elements exist, what their accessible names are, and how the page is structured — before it writes a single line of test code.

Generating Tests from Live Pages

The most powerful MCP workflow is having Claude navigate your running application and generate tests from the actual UI. Here's a realistic example:

Claude Code prompt
Navigate to http://localhost:3000/login and generate a complete test suite
for the login page. Cover:
1. Successful login with valid credentials
2. Error message for wrong password
3. Required field validation (empty submit)
4. Password visibility toggle
Save to tests/auth/login.spec.ts using Page Object Model.

Claude will navigate to the login page, snapshot the DOM, identify the email input, password input, submit button, error message container, and visibility toggle. Then it generates a full test suite and a Page Object Model class:

pages/LoginPage.ts (generated by Claude)
import { type Page, type Locator } from '@playwright/test';

export class LoginPage {
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;
  readonly togglePassword: Locator;

  constructor(private page: Page) {
    this.emailInput = page.getByLabel('Email');
    this.passwordInput = page.getByLabel('Password');
    this.submitButton = page.getByRole('button', { name: 'Sign in' });
    this.errorMessage = page.getByRole('alert');
    this.togglePassword = page.getByLabel('Show password');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }
}
tests/auth/login.spec.ts (generated by Claude)
import { test, expect } from '@playwright/test';
import { LoginPage } from '../../pages/LoginPage';

test.describe('Login page', () => {
  let loginPage: LoginPage;

  test.beforeEach(async ({ page }) => {
    loginPage = new LoginPage(page);
    await loginPage.goto();
  });

  test('successful login redirects to dashboard', async ({ page }) => {
    await loginPage.login('user@example.com', 'validPassword123');
    await expect(page).toHaveURL('/dashboard');
  });

  test('wrong password shows error message', async () => {
    await loginPage.login('user@example.com', 'wrongPassword');
    await expect(loginPage.errorMessage).toBeVisible();
    await expect(loginPage.errorMessage).toContainText('Invalid credentials');
  });

  test('empty form submission shows validation errors', async () => {
    await loginPage.submitButton.click();
    await expect(loginPage.emailInput).toHaveAttribute('aria-invalid', 'true');
  });

  test('password toggle reveals password text', async () => {
    await loginPage.passwordInput.fill('secret123');
    await loginPage.togglePassword.click();
    await expect(loginPage.passwordInput).toHaveAttribute('type', 'text');
  });
});

Every selector in this code was derived from the live DOM — not guessed from a description. The getByLabel('Email'), getByRole('button', { name: 'Sign in' }), and getByRole('alert') selectors match what actually exists on the page.

Debugging Failures with MCP

This is where the MCP + Claude Code combination truly shines. Instead of staring at a stack trace and manually re-running tests, you hand the failure to Claude and let it investigate with a real browser.

The workflow

1

Paste the failing test and error

Copy the test file and the error output from npx playwright test. Give Claude the full context.

2

Claude navigates to the page

Claude opens the same URL in MCP's browser, snapshots the DOM, and compares the expected selectors against what's actually on the page.

3

Claude identifies the root cause

Maybe the button text changed from "Submit" to "Sign in". Maybe a modal now blocks the element. Maybe the page redirects before the element loads. Claude can see all of this.

4

Claude rewrites and re-runs the test

The fix is applied, the test is executed, and Claude confirms it passes — all in one conversation.

Example prompt for debugging:

Claude Code prompt
This test is failing. Navigate to http://localhost:3000/checkout
and figure out why. Here's the error:

Error: locator.click: Error: strict mode violation:
  getByRole('button', { name: 'Place order' }) resolved to 2 elements

Fix the test in tests/checkout.spec.ts so it targets the correct button.

Claude will navigate to the checkout page, snapshot the DOM, find both "Place order" buttons (perhaps one is in a summary sidebar and one is in a sticky footer), and update the selector to target the correct one — for example, by scoping it to a specific section:

Before (failing)
await page.getByRole('button', { name: 'Place order' }).click();
After (fixed by Claude)
await page.getByRole('region', { name: 'Order summary' })
  .getByRole('button', { name: 'Place order' }).click();

Advanced: Custom MCP Tools for QA

The Playwright MCP Server handles browser automation, but real QA workflows need more — test data setup, database seeding, API mocking, environment resets. You can build custom MCP servers that expose these operations as tools Claude can call.

Why custom tools matter

Imagine telling Claude: "Create a test user with admin permissions, seed 50 products, then test the admin dashboard filtering." Without custom tools, you'd need to set this up manually before running Claude. With a custom MCP server, Claude does it all.

Example: a test data MCP server

mcp-servers/test-data-server.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

const server = new McpServer({ name: 'test-data', version: '1.0.0' });

server.tool(
  'seed_test_user',
  'Create a test user with specified role',
  { email: { type: 'string' }, role: { type: 'string' } },
  async ({ email, role }) => {
    // Insert user into test database
    const user = await db.createUser({ email, role, password: 'TestPass123!' });
    return { content: [{ type: 'text', text: `Created user ${user.id} (${email}, ${role})` }] };
  }
);

server.tool(
  'reset_database',
  'Reset the test database to a clean state',
  {},
  async () => {
    await db.reset();
    return { content: [{ type: 'text', text: 'Database reset complete' }] };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

Register both servers in your Claude Code settings:

.claude/settings.json — multiple MCP servers
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest"]
    },
    "test-data": {
      "command": "npx",
      "args": ["tsx", "mcp-servers/test-data-server.ts"]
    }
  }
}

Now Claude Code can seed data, navigate the browser, and validate the UI — all in a single conversation. This is the foundation of a fully autonomous QA agent.

MCP Server vs Claude Code vs Claude.ai — When to Use Which

These three tools overlap but serve different purposes. Here's when to reach for each:

Playwright MCP Server Claude Code (CLI) Claude.ai (Web)
What it is Browser automation bridge Terminal AI assistant Web chat interface
Browser access Yes — real Chromium/Firefox/WebKit Only via MCP Server No
File system access No Yes — read/write project files No
Run tests No (it's a tool, not a runner) Yes — runs npx playwright test No
Best for Giving AI eyes on your app Full test generation + debugging workflow Quick questions, learning concepts
Ideal workflow Connected to Claude Code as a tool Primary interface for QA automation Planning, code review, documentation

The recommended setup for 2026: Use Claude Code as your primary interface with the Playwright MCP Server connected. This gives you browser access + file system access + terminal execution in a single workflow. Use Claude.ai for planning, conceptual discussions, and code reviews where you don't need live browser interaction.

Pro tip: You can also connect the Playwright MCP Server to Claude Desktop (the GUI app) for a more visual experience. See our MCP Server setup guide for Claude Desktop configuration.

Troubleshooting Common Issues

If your setup isn't working, find your symptom below. These are the most frequently reported issues with the Playwright MCP + Claude Code combination.

1. MCP server not appearing in /mcp output

Cause: JSON syntax error in .claude/settings.json, or the file is in the wrong location.
Fix: Validate your JSON at jsonlint.com. The file must be at <project-root>/.claude/settings.json for project-level or ~/.claude/settings.json for global. Start a new Claude Code session after any config change.

2. "spawn npx ENOENT" error

Cause: Node.js / npx is not on the PATH that Claude Code inherits.
Fix: On macOS/Linux, make sure Node is in your shell profile (~/.zshrc or ~/.bashrc). On Windows, use the full path: "command": "C:\\Program Files\\nodejs\\npx.cmd".

Windows fix
{
  "mcpServers": {
    "playwright": {
      "command": "C:\\Program Files\\nodejs\\npx.cmd",
      "args": ["@playwright/mcp@latest"]
    }
  }
}

3. Timeout when Claude tries to navigate

Cause: The target URL is unreachable — your dev server isn't running, or there's a network/proxy issue.
Fix: Start your dev server first (npm run dev). Verify you can open the URL in a regular browser. If you're behind a corporate proxy, add proxy environment variables to the MCP config:

Config with proxy
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest"],
      "env": {
        "HTTP_PROXY": "http://proxy.company.com:8080",
        "HTTPS_PROXY": "http://proxy.company.com:8080"
      }
    }
  }
}

4. "Browser executable not found"

Cause: Playwright browsers aren't installed on this machine.
Fix: Run npx playwright install chromium (or npx playwright install for all browsers). On Linux, also run npx playwright install --with-deps to install system-level dependencies.

5. "Permission denied" on macOS

Cause: macOS Gatekeeper blocking the Chromium binary that Playwright downloads.
Fix: Run xattr -cr ~/Library/Caches/ms-playwright to remove the quarantine flag from all Playwright browser binaries. Alternatively, open System Settings → Privacy & Security and approve the blocked application.

macOS permission fix
# Remove quarantine flag from Playwright browsers
xattr -cr ~/Library/Caches/ms-playwright

# Verify Chromium launches
npx playwright open https://example.com

6. MCP server crashes mid-session

Cause: Memory exhaustion (common with many open tabs/contexts) or a version incompatibility.
Fix: Update to the latest version: npx @playwright/mcp@latest. If you installed globally, run npm update -g @playwright/mcp. Start a new Claude Code session — MCP servers are restarted with each session.

7. Claude says "I don't have browser access"

Cause: The MCP server is configured but Claude doesn't know it can use browser tools.
Fix: Run /mcp to confirm the server is connected. If it is, explicitly ask Claude to use it: "Use the Playwright MCP Server to navigate to [URL]." Claude sometimes needs a nudge to invoke MCP tools versus writing code directly.

Frequently Asked Questions

Can Claude Code generate Playwright tests without MCP Server?

Yes, but the quality is lower. Without MCP, Claude generates tests based solely on your descriptions and any code you paste. With MCP, Claude navigates your live app, reads the actual DOM, and writes tests with selectors that match real elements — producing significantly more reliable tests.

Is Playwright MCP Server free to use with Claude Code?

The Playwright MCP Server (@playwright/mcp) is completely free and open-source. Claude Code requires an Anthropic API key or a Claude Pro/Team subscription. There is no additional charge for using MCP Server features.

How do I debug a failing test with Claude Code and MCP?

Paste the failing test file and the error output into Claude Code. Ask Claude to navigate to the same page via MCP, inspect the DOM, and find the root cause. Claude will identify the issue (wrong selector, timing, changed UI) and rewrite the test — then run it to confirm the fix.

Does MCP Server work with Claude Code in VS Code terminal?

Yes. Claude Code runs in any terminal, including VS Code's integrated terminal. Add MCP Server config to your project's .claude/settings.json and it will be available in every Claude Code session started from that project directory.

Can I create custom MCP tools for my QA workflow?

Yes. Build custom MCP servers using the @modelcontextprotocol/sdk package to expose project-specific tools — database seeding, test user creation, API mocking, environment resets. Register them alongside Playwright in your .claude/settings.json and Claude can use all tools in a single session.


Asim Noaman - Playwright and Claude AI course instructor

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.

Udemy Instructor Published course author
Playwright + AI Expert Specialized in AI-powered QA
Production Experience Enterprise-grade frameworks
Connect on LinkedIn