← Blog

BLOG

Cursor Browser Automation: Wiring the IDE to Real Chromium

Cursor browser automation explained: how to drive hosted Chromium from Cursor via MCP, CDP, and Playwright, plus the trade-offs that matter in production.

10 min readRemote Browser
Ivory computer towers connected across terracotta platforms in a quiet desert landscape
The infrastructure behind your browserREMOTE BROWSER / ILLUSTRATION

Cursor browser automation means letting the Cursor IDE — or an agent running inside it — drive a real Chromium instance instead of guessing at page structure from static files. There are three practical ways to do it: an MCP server that exposes browser tools to the agent, a Playwright or Puppeteer script connected over CDP, or a hosted runtime that gives the agent a persistent, isolated browser session. This guide covers all three, the failure modes you hit when you skip a runtime layer, and the criteria that separate a demo from something you can leave running.

The short version: Cursor is good at writing browser code and bad at being a browser. Pair it with a remote Chromium session over CDP and you get a loop where the agent reads the live DOM, acts, and verifies — without installing Chrome on every developer machine or CI runner.

Why Cursor Needs a Browser It Doesn't Own

Cursor's agent can read your repo, run terminal commands, and edit files. What it cannot do natively is see what a page actually renders. When you ask it to "fix the checkout flow test," it works from source code and your description. If the selector changed because a component library bumped a class name, the agent has no way to know until you run the test and paste back the error.

That feedback loop is the whole problem. Browser automation inside an IDE is valuable precisely because it closes the loop: the agent navigates, reads the accessibility tree or DOM, tries an action, and observes the result. Cursor's own docs describe MCP as the mechanism for adding external tools, and a browser is the most useful external tool an agent can have.

The catch is where that browser lives. Options:

  • Local Chrome via a debugging port. Fast to start, but ties the session to one machine, leaks cookies between projects, and breaks the moment you move to CI.
  • A bundled headless browser inside the agent process. Works for simple fetches, fails on anything with auth, iframes, or anti-bot checks.
  • A remote Chromium session over CDP. The browser runs elsewhere; Cursor connects to a WebSocket endpoint. Sessions are isolated, reproducible, and shareable across machines.

Most teams start with the first and migrate to the third. The migration is the interesting part.

Three Ways to Give Cursor a Browser

1. Browser MCP via a Chrome Extension

The "browser MCP Chrome extension" pattern is the lowest-friction entry point. You install an MCP server (Playwright MCP, Chrome DevTools MCP, or a vendor-specific one), point Cursor at it in ~/.cursor/mcp.json, and the agent gets tools like browser_navigate, browser_click, and browser_snapshot. Some implementations attach to your existing Chrome tab through an extension, which means the agent sees your logged-in session.

This is great for interactive debugging: "open the staging dashboard, log in, and tell me why the export button is disabled." It is poor for anything repeatable. The extension is bound to your desktop, your profile, and your open tabs. Two agents cannot use it at once. There is no audit trail beyond whatever the MCP server logs.

2. Playwright or Puppeteer Over CDP

If you want the agent to write and run code rather than call tools, connect a Playwright script to a remote browser with chromium.connectOverCDP(endpoint). Cursor writes the script, runs it in the terminal, and reads the output. The browser can be local (--remote-debugging-port=9222) or hosted.

This is the most flexible option and the one most production teams converge on, because the same script runs in Cursor, in CI, and in a worker queue. The trade-off is that you now own session lifecycle: who starts the browser, who stops it, what happens when a run hangs.

3. A Hosted Runtime with a Live Viewer

A hosted runtime like Remote Browser gives you a CDP endpoint per session, a live viewer URL you can open in a tab, persistent profiles, and usage metering. Cursor connects the same way it would to a local Chrome, but the browser survives IDE restarts and can be handed to a teammate or a CI job.

The live viewer matters more than it sounds. When an agent gets stuck, you want to watch the page, not read a stack trace. A viewer turns debugging from log archaeology into observation.

Comparison: Which Setup Fits Which Job

ApproachSetup costSession isolationWorks in CIBest for
Chrome extension + MCPLowNone (uses your profile)NoInteractive debugging on your machine
Local Chrome + CDPLowPer-profile, manualFragileSingle-developer scripts
Playwright MCP (headless)MediumPer-processYes, with workAgent-driven test authoring
Hosted Chromium + CDPMediumPer-sessionYesRepeatable runs, shared debugging, scale

The pattern: the further right you go, the more infrastructure you inherit and the more the results hold up when someone other than you runs them.

Connecting Cursor to a Remote Browser

The mechanics are the same whether the browser is local or hosted. You need a CDP WebSocket endpoint, and you pass it to Playwright's connectOverCDP. The Playwright CDP documentation is the authoritative reference for the connection semantics; the Chrome DevTools Protocol spec covers what the endpoint exposes.

Here is a TypeScript example that connects to a hosted session, reuses the existing browser context (important — connectOverCDP attaches to a running browser, it does not create a fresh one), and runs a small verification loop:

import { chromium, type Browser, type Page } from "playwright';

const CDP_ENDPOINT = process.env.BROWSER_CDP_URL!; // wss://... from your session

async function run() {
  const browser: Browser = await chromium.connectOverCDP(CDP_ENDPOINT, {
    timeout: 30_000,
  });

  // connectOverCDP attaches to existing contexts; do not call newContext()
  const context = browser.contexts()[0];
  const page: Page = context.pages()[0] ?? (await context.newPage());

  page.setDefaultTimeout(15_000);

  await page.goto('https://example.com/dashboard', {
    waitUntil: 'domcontentloaded',
  });

  // Read state the agent can reason about, not just a screenshot
  const heading = await page.getByRole('heading', { level: 1 }).innerText();
  const exportEnabled = await page
    .getByRole('button', { name: /export/i })
    .isEnabled();

  console.log(JSON.stringify({ heading, exportEnabled }));

  // Disconnect without closing the remote browser
  await browser.close();
}

run().catch((err) => {
  console.error(err);
  process.exit(1);
});

Two details that trip people up:

  • `browser.close()` on a CDP connection disconnects; it does not necessarily kill the remote browser. That is usually what you want — the session stays alive for inspection in the viewer. Check your runtime's semantics before relying on it.
  • `context.pages()[0]` may be `undefined` if the session started blank. Always fall back to newPage().

For the full connection setup, including auth headers and endpoint formats, see the documentation.

What "Browser Control" Actually Requires in Production

Once Cursor can drive a browser, the interesting questions are operational. These are the criteria that separate a working demo from a system you can leave running.

Session lifecycle and isolation

Every agent run should get its own browser session. Shared sessions leak state: cookies from run A break run B, a hung tab blocks the next task, and a crash takes down everything. Hosted runtimes handle this by allocating a fresh Chromium per session and tearing it down on stop or timeout. If you self-host, you are building this yourself — container per session, port allocation, cleanup on crash.

Persistent profiles where you need them

Isolation is the default, but some workflows need continuity: a logged-in dashboard, a multi-step onboarding, a session that spans several agent turns. Persistent profiles let you save and reload browser state across sessions. The trade-off is that a persistent profile is a shared secret — treat it like a credential, scope it to one workflow, and rotate it when a team member leaves.

Proxies and configurable browser settings

Many sites behave differently by geography or IP reputation. A runtime that lets you set a proxy per session, or configure browser launch flags, gives you a way to reproduce and fix failures that only appear in one region. Be careful with claims here: "stealth" is a marketing word, and what actually matters is whether the browser presents a consistent, plausible fingerprint and whether your proxy pool is clean. Test against your real targets rather than trusting a feature list.

Live debugging

When an agent fails at step 7 of 12, you need to see the page at step 7. A live viewer, plus the ability to attach a second CDP client to the same session, turns that from a 30-minute investigation into a 30-second one. This is the single feature developers underrate most before they need it.

Usage controls

Browser sessions cost money and time. You want per-session timeouts, a hard cap on concurrent sessions, and visibility into how many browser-hours each workflow consumes. Current rates and limits are on the pricing page; the important design decision is that your agent code should treat "session budget exhausted" as a normal, handled outcome rather than an exception.

Where Cursor Fits and Where It Doesn't

Cursor is excellent at the authoring half of browser automation: writing selectors, generating test scaffolding, refactoring a flaky script, explaining why a locator is ambiguous. It is a poor runtime. The IDE is not where you want a 40-minute scraping job to live, and it is not where you want a scheduled agent to run at 3 a.m.

The productive split:

  • In Cursor: write and iterate on the automation. Use MCP tools or a CDP-connected script to get fast feedback against a live page.
  • Outside Cursor: run the same script against a hosted session in CI or a worker. Same code, different endpoint.

If you keep those two environments aligned — same Playwright version, same browser build, same connection code — you avoid the classic "works on my machine" failure where the agent's local Chrome and the CI browser disagree about a rendering detail.

For teams comparing runtimes, remote browser online covers the hosted-session model in more depth, and remote control browser covers the control-plane side: who can attach, who can watch, and how sessions are handed off.

A Practical Checklist Before You Commit

Before you wire Cursor to a browser and call it done, confirm:

  1. The endpoint is a WebSocket CDP URL, not an HTTP debug port, if the browser is remote. HTTP ports do not traverse most networks cleanly.
  2. Your script handles reconnection. Remote sessions can drop; a retry with backoff around connectOverCDP saves a lot of flaky runs.
  3. You never call `newContext()` after `connectOverCDP`. Attach to the existing context or you will get confusing "target closed" errors.
  4. Session cleanup is explicit. Either your runtime enforces a timeout or your code calls stop in a finally block. Leaked sessions are the most common cause of surprise bills.
  5. You have a viewer URL in your logs. When something fails in CI, the first thing you want is a link to the page.
  6. Secrets live in the runtime, not the prompt. Credentials for logged-in workflows belong in the session profile or a secrets store, never in the text you send to the model.

None of these are exotic. They are the difference between an agent that works when you watch it and one that works when you don't.

Bottom Line

Cursor browser automation is a two-part problem: giving the agent a way to see and act on a live page, and giving that page a home that outlives the IDE session. MCP and Chrome extensions solve the first part quickly. A hosted Chromium session over CDP solves both, and it is the setup that survives contact with CI, teammates, and scheduled runs. Start with the extension if you are exploring; move to a CDP-connected remote session the moment you need the same automation to run somewhere other than your laptop.