Skip to content
Browser testing for agents

How can a coding agent test my web app in a real browser by itself?

Give the agent a browser it can drive: a Playwright MCP server or the Playwright CLI for quick checks, Playwright or Puppeteer tests for checks you keep, or a MAO browser portal wired to the agent when you want to watch it work through the page beside its terminal.

  • Playwright MCP acts on the accessibility tree, not on pixels
  • Playwright and Puppeteer run headless unless you ask otherwise
  • A MAO portal answers only to the agents wired to it
By Updated 10 min read

A coding agent can test your web app by itself once it has a browser it can drive. Add a browser automation server such as Playwright MCP, or the Playwright CLI, for checks the agent makes while it works. Have the agent write and run Playwright or Puppeteer tests for checks you want to keep. In MAO, wire the agent to a browser portal and it drives a live Chromium page with mao portal on the same canvas as its terminal, where you can watch it.

What does an agent need to test a web app by itself?

It needs three things: a way to open and operate a page, a way to read what the page shows, and a way to prove the result. Every option below gives it all three, in different forms.

  • Operate: navigate to a URL, click, fill fields, press keys.
  • Read: the page's structure (the accessibility tree), its text, or a screenshot.
  • Prove: a screenshot or a passing test it can show you.

What changes between the options is where the browser runs, whether you can see it, and whether the check survives the conversation.

How does browser automation over MCP work?

The agent calls browser tools from an MCP server. Playwright MCP is "a Model Context Protocol (MCP) server that provides browser automation capabilities using Playwright", and it works from the page's accessibility tree rather than from pixels.

Add it to Claude Code with one command (it needs Node.js 18 or newer):

claude mcp add playwright npx @playwright/mcp@latest

The agent then has tools such as browser_navigate, browser_snapshot (the page's accessibility snapshot), browser_click, browser_type, browser_fill_form and browser_take_screenshot. Its README notes that the agent cannot act on a screenshot; it acts on the snapshot. The browser opens in a visible window by default, and --headless hides it. --device "iPhone 15" emulates a phone.

By default it keeps a persistent profile, so logged-in state carries over between sessions; --isolated keeps the profile in memory and discards it when the browser closes.

The same README suggests that a coding agent may do better with the Playwright CLI and its skills, because CLI calls use fewer tokens. You install it with npm install -g @playwright/cli@latest and give your agent its skills with playwright-cli install --skills. It runs headless unless you open the browser with --headed.

How does an agent write and run its own browser tests?

Ask the agent to write the check as a test and run it. A test stays in the repository, so the next change gets the same check, and CI can run it too.

With Playwright Test, the agent sets up the project with npm init playwright@latest and runs the tests with npx playwright test. Tests run headless by default; --headed shows the browser and --ui opens Playwright's UI mode. npx playwright show-report opens the HTML report. Playwright runs the tests in Chromium, WebKit and Firefox.

A test the agent might write for a checkout page:

import { test, expect } from '@playwright/test';

test('checkout shows the confirmation', async ({ page }) => {
  await page.goto('http://localhost:3000/checkout');
  await page.getByRole('textbox', { name: 'Email' }).fill('test@example.com');
  await page.getByRole('button', { name: 'Pay' }).click();
  await expect(page.getByRole('heading', { name: 'Thank you' })).toBeVisible();
  await page.screenshot({ path: 'checkout.png', fullPage: true });
});

Puppeteer is the other common library: "a JavaScript library which provides a high-level API to control Chrome or Firefox over the DevTools Protocol" or WebDriver BiDi. npm i puppeteer downloads a compatible Chrome during installation, and it runs headless by default. The agent writes a script against it the same way.

How does a MAO browser portal work?

A browser portal is a live Chromium page that sits on the MAO canvas beside your terminals. Wire an agent's terminal to it and that agent drives the page with mao portal; no other agent can.

  1. Choose New portal, place it on the canvas and enter your app's address in its address bar.
  2. Connect the agent's terminal to the portal.
  3. The agent runs mao portal navigate <portal> <url> to open a page.
  4. It inspects the page with info, screenshot or snapshot, and interacts with click, fill, type, key, scroll, text or evaluate.
  5. It runs mao portal close <portal> when it is done.

Every command checks that the wire is still there, so removing the wire takes the browser back. The commands drive a portal that is already open; they do not launch a browser by themselves. The portal needs a Chromium browser, such as Chrome, installed on your machine, and MAO looks for one in Homebrew, /Applications and the usual Windows and Linux install locations.

A few details matter when the agent is testing a form:

  • A password or one-time-code field shows the agent only whether it is filled, in snapshots and in mao portal text and mao portal html.
  • fill checks what the field holds afterwards, and types the value key by key when a field does not accept it at once.
  • A click on an element that is hidden, off screen or covered reports an error instead of success.
  • Only the active tab is kept when the portal is reopened. Browser profile data, popups and downloads are not included.

How do the options compare?

OptionSetupWhat the agent seesCan you watch it liveMobile coverage
Playwright MCPclaude mcp add, Node.js 18+Accessibility snapshot, screenshots on requestYes, the browser is visible by defaultDevice emulation with --device
Playwright testsnpm init playwright@latest in the repoTest results, reports and the screenshots the test savesWith --headed or --ui; headless by defaultDevice emulation: viewport, user agent, touch
Puppeteer scriptsnpm i puppeteer (downloads Chrome)What the script reads from the pageHeadless by defaultDevice metrics and user agent with page.emulate()
MAO browser portalA portal on the canvas, a wire, Chromium installedAccessibility snapshot, page text, screenshots; password fields maskedYes, on the canvas beside the agent's terminalDesktop Chromium; use a device portal for phones
MAO device portalAndroid SDK tools or a phone; Xcode for iOSThe device screen, screenshots and its accessibility treeYes, the screen streams on the canvasAndroid emulator or phone, iOS Simulator

How does the agent test a change to a checkout page?

Here is the whole loop with a MAO browser portal: the agent changes the checkout page, opens it, fills the form, takes a screenshot and tells you what happened.

  1. Start your app's dev server.
  2. Place a browser portal next to the agent's terminal, open the checkout page's local address in it, and connect the agent's terminal to the portal.
  3. Give the agent the task and the check in one prompt (below).
  4. The agent edits the code, then opens the page in the portal with mao portal navigate.
  5. It runs mao portal snapshot to find the form fields and the pay button, fills each field with fill and presses the button with click.
  6. It runs mao portal screenshot on the result, and reads the page with mao portal text if it needs the exact error or confirmation message.
  7. It reports what it changed, what the page showed, and the screenshot. If something broke, it fixes the code and runs the check again.

The prompt:

Move the coupon field above the card details on the checkout page.
Then open the checkout page in the portal you are connected to, fill
the form with test data, pay, and take a screenshot of the result.
Tell me what you changed and whether checkout still reaches the
confirmation screen.

The commands the agent runs to open, read and capture the page:

mao portal navigate <portal> http://localhost:3000/checkout
mao portal snapshot <portal>
mao portal screenshot <portal>

You watch it happen in the portal on the canvas. With Playwright MCP the loop is the same, with browser_navigate, browser_snapshot, browser_fill_form, browser_click and browser_take_screenshot in place of the mao portal commands. Once the flow works, ask the agent to turn it into a Playwright test so the check stays.

When do you need a device portal instead?

Use a device portal when the thing you are testing runs on a phone, not in a desktop browser. Playwright and Puppeteer emulate a phone's screen size, user agent and (for Playwright) touch inside a desktop browser engine; a MAO device portal holds a real Android emulator, an Android phone attached to your computer, or an iOS Simulator.

  • Android: choose New device portal and pick an emulator or attached device. MAO needs the Android SDK tools or an attached device, and finds adb and the emulator through ANDROID_HOME, Homebrew and the SDK's default folders.
  • iOS: on a Mac with Xcode, a device portal shows an existing iOS Simulator. You create and delete simulators in Xcode. Agents can read the app's accessibility tree when they launch the app with mao portal launch.

The wired agent inspects the device with info, screenshot or snapshot, and controls it with click, swipe, type, button, launch, terminate, boot and shutdown. The device screen streams while the portal is on screen; an off-screen or collapsed portal stops streaming.

Frequently asked questions

Yes. Add Playwright MCP with claude mcp add playwright npx @playwright/mcp@latest, ask it to write and run Playwright tests, or, in MAO, connect its terminal to a browser portal so it can drive the page with mao portal.

Mostly the structure. Playwright MCP acts on the page's accessibility snapshot and cannot act on a screenshot, and a MAO browser portal's snapshot reads the accessibility tree; both also take screenshots for you to see.

Yes. Playwright MCP opens a visible browser by default, Playwright Test shows the browser with --headed or --ui, and a MAO browser portal shows the page on the canvas next to the agent's terminal.

In a MAO browser portal, no: a password or one-time-code field shows the agent only whether it is filled. Playwright MCP keeps a persistent profile with your logged-in state by default; --isolated keeps the profile in memory and discards it when the browser closes.

Playwright and Puppeteer can emulate a phone's screen size and user agent in a desktop browser. To test on a real Android emulator, an Android phone or an iOS Simulator, wire the agent to a MAO device portal.

Sources

Checked on 2026-10-28.

Questions? Ask in the Discord or write to hello@miguer.com.

Try it on your own repo

Put your agents on one canvas.

A 7-day trial with every canvas feature. No card.

Download MAO