Using playwright model context protocol on my practicewebsite

Jun 1, 2025 | by Ralph Van Der Horst

Using Playwright Model context protocol on my practicewebsite

Intro – What’s MCP & Why LLMs Need Model Context?

As AI agents evolve, so does their ability to interact with real-world web apps. Traditional automation relies on brittle selectors and hard-coded steps. The Model Context Protocol (MCP) which is an open standard that enables LLMs to interface with the web using structured, semantically rich data.

LLMs don’t “see” pages like humans. They rely on model context, an accessibility based snapshot of a page’s elements, roles, and structure. Instead of fuzzy screenshots, they get clear, machine-readable context. This is where Playwright-MCP shines.

How Playwright-MCP Provides API Tools (navigate, snapshot, click,browse type)

Playwright-MCP acts as a bridge between the browser and the LLM. It extends Playwright with a protocol that:

  • Navigates to any URL
  • Snapshots the page into a structured tree (based on ARIA/accessibility)
  • Allows agents to click, type, hover, or drag using semantic references

For example, instead of page.click("#login-btn"), an agent might request:

{
  "action": "click",
  "ref": {
    "role": "button",
    "name": "Sign in with Microsoft"
  }
}

This creates a more robust, generalizable automation flow.

Behind the Scenes, JSON View of Snapshot -> Mapping to Test Code

When an LLM asks for a snapshot, Playwright-MCP returns a detailed JSON like this:

{
  "role": "form",
  "children": [
    {
      "role": "textbox",
      "name": "Email"
    },
    {
      "role": "textbox",
      "name": "Password"
    },
    {
      "role": "button",
      "name": "Sign in"
    }
  ]
}

From this, the agent generates code like:

await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
await page.getByRole('textbox', { name: 'Password' }).fill('securepass');
await page.getByRole('button', { name: 'Sign in' }).click();

Agent Workflow, Prompt → Actions → Code Generation

The full workflow looks like this:

  1. Prompt The User ask: “Log in to the dashboard using Azure AD.”
  2. Agent uses MCP to:
    • Navigate to /login
    • Request a snapshot
    • Identify the Azure AD login button
    • Execute a click
  3. Optionally, the agent turns its actions into a reusable Playwright test.

Real-World Example Checkbox Testing on my practiceautomatedtesting.com

In this example I used cursor IDE with Playwright-MCP enabled. I prompted:

“Write a test that checks and unchecks checkboxes on practiceautomatedtesting.com.”

Copilot or any other llm will try to do the follwing

  • Launches MCP server
  • Navigates to /webelements
  • Detects the “Check Box” section via snapshot
  • Clicks the link
  • Identifies checkboxes using labels
  • Checks/unchecks them
  • Asserts expected states

This is simplified but based on your chat conversation it will auto generate your testscases and does it pretty well I must say

Playwright MCP - Practiceautomatedtesting

Checkbox Testing on practiceautomatedtesting.com

This is the output the mcp icw llm created

const { test, expect } = require('@playwright/test');

test.describe('Checkbox Section - practiceautomatedtesting.com', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://practiceautomatedtesting.com/webelements');
    // Click the Check Box section
    await page.getByRole('link', { name: /Check Box/i }).click();
    // Wait for checkboxes to be visible
    await expect(page.getByText('Checkbox 1')).toBeVisible();
    await expect(page.getByText('Checkbox 2')).toBeVisible();
  });

  test('should check and uncheck Checkbox 1', async ({ page }) => {
    const checkbox1 = page.getByLabel('Checkbox 1');
    await expect(checkbox1).not.toBeChecked();
    await checkbox1.check();
    await expect(checkbox1).toBeChecked();
    await checkbox1.uncheck();
    await expect(checkbox1).not.toBeChecked();
  });

  test('should check and uncheck Checkbox 2', async ({ page }) => {
    const checkbox2 = page.getByLabel('Checkbox 2');
    await expect(checkbox2).not.toBeChecked();
    await checkbox2.check();
    await expect(checkbox2).toBeChecked();
    await checkbox2.uncheck();
    await expect(checkbox2).not.toBeChecked();
  });

  test('should check both checkboxes and verify', async ({ page }) => {
    const checkbox1 = page.getByLabel('Checkbox 1');
    const checkbox2 = page.getByLabel('Checkbox 2');
    await checkbox1.check();
    await checkbox2.check();
    await expect(checkbox1).toBeChecked();
    await expect(checkbox2).toBeChecked();
  });
});

Wrap-Up, The Benefits, Caveats, Future Directions

Benefits

  • More semantic, reliable test generation
  • Works across different DOM layouts and styling
  • Enables agent-driven workflows and autonomous test writing

Caveats

  • Accessibility structure must be well-formed
  • May require adjustments for dynamic/spa-heavy apps

Future Directions

  • Live agent debugging + replay
  • Multi-modal snapshots (DOM + image)

With Playwright-MCP, building tests isn’t just automation—it’s collaboration between you and your AI assistant.

My Final Thoughts

As someone diving deep into automated testing, integrating Playwright-MCP with tools like Cursor has completely changed how I think about writing tests. What used to take 30 minutes of trial and error now takes a simple prompt and a few seconds of validation.

Working with real examples like practiceautomatedtesting.com, I saw firsthand how model context eliminates the guesswork. The LLM knows exactly what’s on the page, what’s clickable, and what each element means because it sees the page like a screen reader would.

That kind of clarity doesn’t just make testing faster it makes it smarter. And honestly, a little more fun.

This is the future of testing. And it’s already here.

But remember, when using Cursor with Playwright-MCP, it’s crucial to define rules or constraints in your prompts. This helps guide the LLM toward deterministic behavior and prevents it from hallucinating non-existent elements or logic.

For example, specify things like:

  • Only interact with visible elements
  • Use exact role and name combinations
  • Avoid assumptions about navigation or redirects
  • use the right question with proper context

With the right guidance, the AI becomes a powerful, trustworthy partner.

For more details, check out github.com/microsoft/playwright-mcp

by Ralph Van Der Horst

arrow right
back to blog

share this article

Relevant articles

Integrating JIRA with Cucumber and Serenity/JS for Enhanced Test Management

Integrating JIRA with Cucumber and Serenity/JS for Enhanced Test Management

Property Based Testing

Feb 25, 2024

Property Based Testing

Understanding Dynamic Elements DOM

Understanding Dynamic Elements DOM