BLOG
AI Browser Agent: Runtime, Control, and Production Setup
A practical guide to running an AI browser agent: how control works over CDP, where Playwright or Selenium fit, and what production requires.

An AI browser agent is a model-driven loop that observes a web page, decides on an action, and executes it against a real browser. The model supplies judgment; the browser supplies the execution surface. Most failures in production come from the second half. A local Chrome window that a developer opened by hand works for a demo, but it does not survive concurrency, restarts, or a second machine. This guide covers how an AI browser agent actually controls a browser, where Playwright and Selenium fit, what "invisible browser" and browser MCP setups really mean, and the criteria that separate a working prototype from a runtime you can leave running.
The short version: treat the browser as infrastructure. Give the agent a hosted Chromium session reachable over the Chrome DevTools Protocol (CDP), keep state in persistent profiles, and meter usage per session. Everything below explains why and how.
What an AI browser agent actually is
Strip away the branding and an AI browser agent is three components:
- A perception layer. The agent reads the DOM, an accessibility tree, a screenshot, or a page-to-Markdown conversion. Each has trade-offs: DOM is precise but noisy, screenshots are model-friendly but expensive, accessibility trees are compact but lossy on canvas-heavy sites.
- A decision layer. An LLM picks the next action from a tool schema: click, type, scroll, navigate, extract. This is where prompt design and tool definitions matter.
- An execution layer. Something has to turn "click the third result" into a real input event in a real browser. That is the runtime.
Most public discussion focuses on the decision layer, because that is where the interesting model work happens. But the execution layer determines whether the agent finishes tasks. If the browser dies mid-task, if the session loses its cookies, if two agents share one browser and stomp on each other's tabs, no amount of model quality saves you.
The execution layer is also where the vocabulary gets confusing. "Browser control," "control web browser," "invisible browser," and "browser navigate" all describe the same underlying capability: programmatic access to a browser instance that the agent can drive. The differences are in *where* that browser runs and *how* you connect to it.
How control works: CDP is the common denominator
Chromium exposes the Chrome DevTools Protocol, a JSON-over-WebSocket interface for inspecting and driving the browser. Every serious automation stack speaks it, directly or through a wrapper. Playwright's connectOverCDP and Puppeteer's connect both attach to a remote browser endpoint. Selenium can drive a remote browser through its own WebDriver protocol, and modern Selenium setups can also bridge to CDP for lower-level control.
The practical consequence: if your runtime exposes a CDP endpoint, it works with the tools your team already knows. You do not need a proprietary SDK to get started, and you are not locked into one agent framework.
A minimal connection looks like this:
import { chromium, Browser, Page } from "playwright";
async function runAgentTask(cdpUrl: string, task: string) {
const browser: Browser = await chromium.connectOverCDP(cdpUrl);
const context = browser.contexts()[0] ?? (await browser.newContext());
const page: Page = context.pages()[0] ?? (await context.newPage());
await page.goto("https://example.com", { waitUntil: "domcontentloaded" });
// The agent loop would replace this with model-driven actions.
const title = await page.title();
const links = await page.locator("a").evaluateAll((els) =>
els.slice(0, 10).map((el) => ({
text: (el as HTMLAnchorElement).innerText.trim(),
href: (el as HTMLAnchorElement).href,
})),
);
console.log({ title, links, task });
// Do not call browser.close() on a shared remote session unless you own it.
await browser.close();
}
runAgentTask(process.env.CDP_URL!, "collect the first ten links");Two details matter in production. First, connectOverCDP attaches to an existing browser rather than launching one, so the session lifecycle belongs to whoever created it. Second, browser.close() on a CDP connection detaches the client; whether it tears down the remote browser depends on the runtime. Read the docs for your provider before you rely on either behavior. The Playwright CDP documentation is the authoritative reference for the client side.
Playwright or Selenium: which one for an agent?
Both drive real browsers. The choice depends on what your agent needs to do and what your team already runs.
| Criterion | Playwright | Selenium |
|---|---|---|
| Primary protocol | CDP (Chromium), patched protocols for Firefox/WebKit | WebDriver (W3C), CDP bridging for Chromium |
| Auto-waiting | Built in for most actions | Manual waits or explicit conditions |
| Multi-context isolation | First-class browser contexts | Separate driver instances |
| Remote connection | connectOverCDP, connect | Remote WebDriver, Grid |
| Best fit for agents | DOM extraction, screenshots, tracing | Existing enterprise test grids, cross-browser matrices |
| Language coverage | TypeScript, Python, Java, .NET | Broad, including older enterprise stacks |
For a new AI browser agent, Playwright is usually the shorter path. Its auto-waiting reduces the flaky-action class of failures, and browser contexts give you cheap isolation between tasks. Selenium remains the right answer when you already have a Grid, when you need a browser Playwright does not support well, or when compliance requires a specific vendor.
The runtime question is separate from the framework question. Whether you use Playwright or Selenium, you still need a browser to connect to. That is where hosted Chromium changes the operational picture. See Remote Browser for AI agents for the runtime-layer view.
Local Chrome vs hosted Chromium
The default starting point is a local browser launched by the automation library. It works until it does not. Here is the honest comparison.
| Dimension | Local Chrome | Hosted Chromium |
|---|---|---|
| Setup | Install browsers per machine | Connect to a CDP URL |
| Concurrency | Limited by local CPU and RAM | Scales with sessions, subject to plan limits |
| State persistence | Manual profile directories | Persistent profiles managed by the runtime |
| Debugging | Watch the window, or headless logs | Live viewer, session recordings |
| Isolation | One machine, shared resources | Session-level isolation |
| Failure recovery | Your process restarts it | Runtime handles session lifecycle |
| Cost model | Developer time and hardware | Per browser-hour, see pricing |
The "invisible browser" framing comes from the local case: a headless Chrome process that no one watches. It is invisible in the sense that there is no window, not in the sense that it is undetectable. If you need configurable browser settings such as user agent, locale, timezone, and proxy routing, a hosted runtime exposes those as session parameters rather than environment variables you hand-tune per machine.
Browser MCP and the extension question
A browser MCP server exposes browser actions as tools that an MCP-capable client (an IDE, a coding agent, a chat client) can call. The pattern is useful because it standardizes the tool surface: navigate, click, type, read page, screenshot. If you are wiring an agent into Cursor, Claude Code, or a similar client, a browser MCP server is often the fastest integration path.
There is a common confusion worth clearing up. A "browser MCP Chrome extension" is not the same thing as a browser MCP server. An extension runs inside a browser you already have open and can only act on tabs in that browser. A server connects to a browser instance, local or remote, and can create sessions on demand. For agent workloads that need concurrency, isolation, or a clean profile per task, the server model is the one that scales. Extensions are convenient for interactive, single-user work; they are a poor fit for unattended agents.
The same distinction applies to "browser MCP cursor" setups. Cursor can call an MCP server, and that server can point at a hosted CDP endpoint. The IDE does not need to know where the browser runs. That separation is the point.
Production criteria for an AI browser agent runtime
If you are evaluating runtimes, these are the questions that predict whether the thing holds up.
Session lifecycle. Can you create, inspect, and terminate sessions through an API? Is there a live viewer so a human can watch a stuck agent? Can you record sessions for post-mortems? A runtime without a viewer makes debugging a black box.
Persistent profiles. Many real tasks require login state. Persistent profiles let an agent resume with cookies and local storage intact, which is the difference between "log in every run" and "log in once, then work." Profile management also raises isolation questions: one profile per task, or shared? Shared profiles are faster and riskier.
Isolation. Two agents in one browser will collide on tabs, focus, and storage. Session-level isolation avoids this. Check whether isolation is per session or per browser context, and whether it survives a reconnect.
Proxy and network controls. Some tasks need a specific egress region or a residential route. Configurable proxy settings at the session level are easier to reason about than per-process environment variables.
Usage controls. Browser time is the unit that matters. Understand how sessions are metered, what happens when a session idles, and whether you can cap concurrency. Current rates and limits are on the pricing page; do not plan capacity from blog posts, including this one.
Protocol compatibility. Confirm CDP support and test with your actual client. A runtime that only works with its own SDK is a migration risk.
Observability. Logs, network captures, and console output should be retrievable per session. Agents fail in ways that are obvious in a trace and invisible in a summary.
A production-shaped agent loop
The difference between a demo and a production agent is mostly error handling and state management. A sketch:
- Create a session with the profile and proxy settings the task requires.
- Connect over CDP with Playwright or Puppeteer.
- Run the observe-decide-act loop, capping steps and wall-clock time.
- Persist artifacts (extracted data, screenshots, traces) outside the browser.
- Terminate the session and release the profile.
Step 3 is where most teams under-invest. Cap the loop. Treat a repeated identical action as a signal to stop. Treat a navigation timeout as a recoverable event with a retry budget, not an infinite wait. And log the action sequence, because when an agent fails you will want to replay it.
For teams running many agents, the runtime layer is what makes this repeatable. A hosted session that starts in a known state, exposes a CDP URL, and can be watched live removes a large class of "works on my machine" failures. The documentation covers session creation and connection details; the remote browser online guide covers the no-local-install path.
Common failure modes and what they look like
- Stale selectors. The agent clicks a button that moved. Mitigation: prefer role and text-based locators, and re-read the page after navigation.
- Session death mid-task. The browser process exits and the agent retries against a dead endpoint. Mitigation: health-check the connection and recreate the session on failure.
- Profile bleed. Two tasks share cookies and one logs the other out. Mitigation: one profile per task, or explicit profile assignment.
- Runaway loops. The agent repeats an action because the page state did not change. Mitigation: step caps and no-progress detection.
- Silent timeouts. A navigation hangs and the agent waits. Mitigation: explicit timeouts on every navigation and action.
None of these are model problems. They are runtime and harness problems, which is why the execution layer deserves as much design attention as the prompt.
Where to start
If you are building an AI browser agent today, the pragmatic sequence is: prototype with Playwright against a local browser to validate the task, then move the execution layer to a hosted Chromium session before you add concurrency. Keep the CDP connection as the boundary so you can swap runtimes without rewriting the agent. Use persistent profiles for anything that requires login. Add a live viewer to your debugging workflow early, because you will need it.
The runtime is not the interesting part of an AI browser agent, but it is the part that decides whether the interesting part ships. Treat it accordingly.