Browser Execution for AI Agents: Playwright, MCP and a Safe Executor
Browser execution for AI agents: a browser as runtime next to MCP and code mode, Playwright under the hood, secured with profiles, domain limits and policies.
This article is published in English.
Sources: Why Browser Execution Is Becoming the Missing Capability in Agent Architecture?
Production agents are already fluent with machines. They hit APIs, connect to MCP servers, run code, query the web, open files and issue shell commands. Yet a surprising share of real software exposes some of its key operations only through a browser UI, and that's the gap browser execution is meant to fill.
The newest agent products keep a browser or a cloud computer on hand for the agent. It's tempting to wave this off as one more demo of an AI clicking buttons. A more useful reading is architectural: the browser isn't a new kind of agent, it's a new execution environment.
The difference matters. If a system already offers MCP servers, skills, code running, programmatic tool calls, sandboxes, search and direct backend hooks, a browser shouldn't replace any of those. It extends the system into territory they can't cover reliably: stateful, JavaScript-heavy, authenticated applications built for humans.
Search looks things up. APIs and MCP converse with systems. Code transforms data and coordinates steps. A browser works the interface that was built for people.
The interface that gets forgotten
Agent architectures usually begin with structured capabilities. Where a service ships a tidy REST API, an SDK or an MCP server, that's nearly always the best interface for an agent: explicit contract, typed inputs and outputs, failures that are easier to reason about and a clearer security boundary.
Code execution comes next. Rather than make the model call twenty tools one at a time, let it write a small program that calls them, aggregates the results locally, filters data and returns one compact answer. That suits large tool catalogs, repeated calls and data-heavy workflows. A sandbox adds another surface with Python, shell, files and perhaps Kubernetes or GitHub tooling inside a controlled environment. By then the agent can do plenty, but it still assumes everything useful is exposed through machine-readable interfaces.
Not so. Plenty of tools sit behind screens only: internal admin panels, old vendor portals, reporting dashboards, partner systems, job boards, analytics products, SaaS settings pages, or apps that fetch real data only once several filters are set. The data can be public while the useful state is produced by JavaScript. A person finds the task trivial, yet no API is documented. Occasionally an internal API exists but is private, unstable or intentionally unsupported.
That's the real use case: not letting the AI browse the internet in the abstract, but using the browser as the fallback interface when the application itself is part of the task.
Why MCP doesn't already cover it
Whenever browser execution comes up, so does this question, and it deserves a straight answer. MCP never requires the target system to have an API; all it specifies is how an agent talks to a capability. Suppose the target application offers nothing but a UI for people. The code behind an MCP tool can then drive a browser itself. The limit isn't MCP. It's the lack of a stable, structured interface behind the target.
The target may expose nothing structured. Whatever sits behind MCP can be a browser, a sandbox, a workflow engine or something else entirely. Trouble starts when the app has no public API, SDK, database integration or supported programmatic path to the operation you need. MCP can still publish a tool named browser_execute, but something behind it has to operate the system, and that's browser execution. They aren't rivals: MCP can be the protocol exposing the browser capability, and the browser is how the human-facing app gets touched when nothing better exists.
Authentication may be tied to the browser. MCP has an authorization model of its own, OAuth flows included, but it covers only the hop between MCP client and MCP server. It leaves the login rules of the system the tool must finally operate untouched. Many internal portals, vendor apps and older SaaS products allow access only via an interactive browser flow with SSO, MFA, session cookies or other state created at a human login, and no service credential or delegated token exists. An MCP tool can still expose the capability, with an authenticated browser profile behind it. The user signs in via the browser, session state is kept under controlled access, and later runs act within that identity. The limit is the target app's authentication model, not MCP's.
Some tasks are UI workflows and not single calls. Consider "pick Retail, Europe and the last 90 days, then compute average conversion per country." That may need a series of filters, a render step and extraction of the resulting state, with no endpoint returning the answer directly. A browser can operate the interface and read what the app produces.
Structured APIs, called directly or through MCP, stay the first choice whenever they exist. They're cheaper, more deterministic and easier to secure. The browser handles the part that lives only behind the human-facing interface.
Search isn't browsing
In many agent stacks, web search, HTTP fetching, browser automation and computer use get lumped together as a single fuzzy "web" skill, though they are different tools.
Asked what changed in the latest MCP specification, web search probably suffices. Given the URL of an ordinary public article, direct retrieval may be enough. Asked to work with Salesforce, use its API or MCP. Asked to compute statistics over JSON, use code.
Now picture another request: open a public dashboard, choose Ukraine, set the year to 2026, select Enterprise, step through every category and average each metric. A search engine can't perform that interaction, and a plain HTTP request may return only the empty shell of the page. The interesting data appears after the browser runs JavaScript, sends client-side requests, updates app state and renders the result.
Or a job board: pick AI Engineering, Remote and Germany, then count how many vacancies mention RAG in the description. That isn't really a search query. It's a UI workflow followed by extraction and computation. This is where a real browser gives the agent something new, a stateful web execution surface.
The browser is a runtime
A managed browser such as Amazon Bedrock AgentCore Browser is infrastructure: an isolated remote browser session that can be controlled programmatically. The LLM doesn't magically talk to it. You define the contract between agent and environment.
AgentCore Browser exposes sessions through a WebSocket automation endpoint, and libraries like Playwright can connect to the remote Chromium over the Chrome DevTools Protocol. AWS adds separate OS-level browser actions for interactions CDP can't handle cleanly. The browser is a remote runtime and not an intelligence layer. Seen that way, integration gets simpler: the agent needs a programmable capability, and the browser runtime is where it executes.
Let the agent write the browser code
A browser can reach an agent in a few ways. One option is a high-level tool like browser_task(task="find the latest invoice"), with a browser-specific agent inside that reads the task, explores the site, picks what to click and reports back. It can help for unfamiliar sites where the workflow has to be discovered, but it's a poor starting point.
If the main model is a capable coder, a cleaner first design is to have it generate the browser program and pass it to a Browser Executor. A signature as small as browser_execute(code, profile_id, allowed_domains) is enough:
browser_execute(
code=generated_playwright_code,
profile_id="vendor-portal-user-42",
allowed_domains=["portal.example.com"]
)
On receiving the call, the executor checks the request, then spins up (or reuses) an isolated session and attaches Playwright to the remote browser. It executes the program and hands back structured data. The main agent does its thinking a single time. A second LLM re-interpreting the same intent only makes sense when the task really needs exploration. For a predictable dashboard, catalog or portal, this beats a nested browser agent on price and on how easy it is to observe.
Why code mode fits
This matters most where programmatic tool calling is already in use. The wasteful pattern is easy to spot: the model calls click, gets a result, calls screenshot, reasons again, calls type, gets another result. Work a plain script would orchestrate in milliseconds becomes dozens of model turns.
The better pattern matches tool aggregation: let the model write one program that performs many browser operations inside the execution environment and returns a single compact result.
page = await browser.page()
await page.goto("https://jobs.example.com")
await page.get_by_label("Category").select_option("AI Engineering")
await page.get_by_label("Location").select_option("Germany")
await page.get_by_label("Remote").check()
await page.get_by_role("button", name="Apply").click()
jobs = await page.locator(".job-card").all_inner_texts()
rag_jobs = [job for job in jobs if "RAG" in job.upper()]
return {
"total_jobs": len(jobs),
"jobs_mentioning_rag": len(rag_jobs)
}
The model writes one program, the executor performs dozens or hundreds of deterministic DOM operations, and the agent receives a small JSON object in place of a long trail of clicks and screenshots.
It can go further. A capability-aware code runtime can give the generated program a handful of controlled interfaces together: MCP clients, plain HTTP, files, browser control and local number crunching. A single program might pull accounts from Salesforce over MCP, open a legacy portal, read a metric for each account, aggregate and return one answer.
customers = await mcp.salesforce.search_accounts(segment="enterprise")
results = []
for customer in customers:
await browser.goto(f"https://legacy.example.com/customer/{customer.id}")
metric = await browser.locator("#health-score").inner_text()
results.append({
"customer": customer.name,
"health_score": metric,
})
return summarize(results)
That isn't an AI clicking around. It's programmatic orchestration across machine interfaces and human interfaces together.
A worked flow
Take a request that sounds simple: go to a public analytics dashboard, select Retail, pick Europe, restrict to the past 90 days and compare how conversion and retention differ between countries.
The agent shouldn't jump straight to a browser. A capability router asks first whether a cheaper, more structured route works. If search can answer, search. If the page exposes a stable API, call it. If direct HTTP retrieval holds the data, don't start Chromium.
If the values only appear after client-side interaction, the router picks browser execution. The main agent generates a Playwright program, the executor runs it in the managed session, waits until the page stops changing, reads the rendered numbers and returns them as structured data. Code mode then computes the comparison, and the agent writes the answer. The user needn't know a browser was opened. In many products this should be an invisible backend capability, like a database query or a code sandbox.
Automation first, computer use second
Browser automation and computer use are sometimes treated as one thing, but they belong at different levels of the stack. Playwright and CDP work on browser structure: pages, selectors, DOM elements, forms, network requests, downloads and state. As a result they run quicker and cost less, and they are simpler to test and to fence in.
Computer use is visual. The model watches the screen and emits mouse or keyboard actions. It earns its place when the DOM can't handle the interface reliably: canvas apps, native dialogs, context menus, odd drag-and-drop flows. AWS draws the same line in AgentCore Browser, where the WebSocket automation path is built on CDP and the InvokeBrowser API gives OS-level control for what CDP can't do. So use structured automation first and fall back to visual control only when needed. The lower in the stack the agent goes, the more flexible it becomes and the more expensive, stateful and risky each action is.
After a fallback to computer use, the executor should run a closed verification loop, not a chain of blind actions. A click that returns without error proves nothing about the operation. The page might still be loading, a modal might have swallowed the action, or another element might have taken the click. After any consequential step, the runtime should look at the new state and confirm the expected change happened before moving on. It should also spot repeated actions that accomplish nothing. If the same step repeats with no change, retrying again is wrong; the answer is to replan or drop back to a more structured strategy.
This loop leans on three guardrails: capping how many screenshots are kept, catching repeated actions that change nothing, and applying permission tiers action by action.
Authentication makes it real work
Public dashboards suit a proof of concept, but the enterprise value shows up with authenticated applications. Playwright already has a strong notion of browser contexts and reusable authentication state, and AgentCore Browser adds managed profiles that persist cookies and local storage across sessions. A user signs in once, the profile is saved, and a later session restores that state. That serves internal portals, customer systems, legacy SaaS and vendor apps where an API integration would otherwise need custom reverse engineering or be impossible.
It also changes how identity is modeled. A browser profile is effectively a credentialed execution identity and not a generic convenience object. The profile that can reach a finance portal shouldn't automatically be available to every agent or workflow. In production, bind profiles to tenant, user, purpose and policy scope, and give the agent a grant to a specific profile and not blanket access to every session the user owns.
Security matters more than the demo
Browser execution is powerful because it acts through the same interface a human uses, which also means it can make mistakes with a human's privileges.
The first boundary is the executor. Generated browser code should run in isolation with explicit domain restrictions, timeouts, resource limits and controlled access to profiles. The model shouldn't get host environment variables, arbitrary credentials or a general network path just because it can run Playwright.
The second is action policy. Extracting metrics from a page is a different act from posting, messaging, editing account permissions, erasing data or approving a payment. Classify those side effects outside the model and gate them at the execution layer.
The third is prompt injection. Once an agent reads arbitrary pages, everything rendered becomes untrusted input. Text inside a dashboard, email, banner or document can look like an agent command. The executor must never take on-screen content as user authorization. OpenAI's computer-use guidance says the same: treat third-party content as untrusted and ask for confirmation right before risky actions such as posting, sending, deleting, changing permissions or moving money. That's an execution-policy concern, not just a prompt-engineering one. The browser may observe broadly, but it should change the world only through explicit policy.
Observability follows. A production executor should record the requested task, the profile used, domains accessed, the program executed, files downloaded, external side effects attempted and where human approval was needed. AgentCore Browser can be watched live, and custom browsers support recording and replaying a session, which gives auditing a solid base.
How it sits beside MCP, skills and sandboxes
Nothing here replaces anything else. MCP and APIs stay preferred for structured business operations. Skills package repeatable procedures, context and operational knowledge. Code mode stays the efficient layer for repeated calls, aggregation and transformation. Sandboxes stay the controlled home for files, shell, Python, testing and heavy compute. Browser execution fills a separate gap: operating a human-facing interface when no machine-facing equivalent is dependable enough.
Keep routing conservative. Can an API do the job? Use it. Can direct retrieval do it? Don't open a browser. Does the site itself have to be operated to get the result? Then browser execution is right. Can structured automation not complete the interaction? Computer use is the last resort.
MCP as the contract
The branches rarely stay apart in practice. Code execution, browser execution and even tool discovery usually end up behind an MCP server. MCP isn't just the API branch; it's the contract through which an agent gets any capability, whether a REST call, a sandboxed script or a remote Chromium sits underneath.
Packaging works well as one dedicated MCP server exposing a handful of tools, such as execute_code, browser_execute and find_tool, in place of one tool per underlying action. Tool count stays at three or four and not thirty. A model has no use for separate click, screenshot and select_option tools, any more than it needs restart, scale and rollback as separate tools when one operation with a plan will do.
Portability is another reason. One server can sit behind a custom agent harness or equally plug into Claude, ChatGPT or any MCP-compatible client, so the same capability can be tested through a familiar interface with no second integration.
Keep the executor separate
From the agent's side, the browser can look like part of code mode, because one generated script may use browser.goto() and browser.locator() next to MCP clients, files and compute APIs inside a single logical runtime. In deployment, the Browser Executor is still better as a separate component. Browsers bring their own lifecycle and risk: sessions, cookies, local storage, downloaded files, network rules, logged-in profiles, page state, recordings and side effects outside your system. A code sandbox deals in CPU, memory, files, packages and processes. Splitting them lets each runtime enforce the security model that suits it. Code mode can offer a restricted browser client while the executor owns the remote session, so the programming model stays unified and the infrastructure stays divided.
Start boring
Skip the universal autonomous browser agent at the start. The first useful build is intentionally dull: one isolated remote Chromium session, a small browser_execute contract, Playwright for automation, a strict domain allowlist and structured results. Test it on a public JavaScript-heavy dashboard or a sample app where you can show that the browser beats search and HTTP retrieval.
After that works, connect it to the programmatic pattern already used for code mode, so the main agent generates a browser program and doesn't call low-level click tools over and over. Then add authenticated profiles for controlled internal apps, then action policies and approval gates before any workflow can create external side effects. Add visual computer use only for interfaces Playwright can't handle reliably, and consider a specialized browser-agent loop that observes, reasons, retries and explores only when a task is too ambiguous for the main agent to produce a stable program. That order keeps the architecture understandable and pushes the costliest, least deterministic behavior to the edge instead of making it the default.
The bigger shift
The interesting part isn't that an LLM can click a button. Browser automation has existed for years. What's new is that an agent can decide when the browser is the right surface, write the program that drives it, combine the result with MCP calls and code execution and carry on, with no developer building a bespoke integration for each human-facing app.
Machine interfaces are still better: more reliable, more secure and easier to govern. But the world is full of systems that don't offer the needed operation through a clean one, and workflows used to end with a person having to open the site and finish the job. Browser execution moves that boundary. It makes the human UI another programmable surface, to be used carefully, isolated aggressively and preferred only when structured interfaces fall short.
The next generation of agent platforms is best understood not as bigger tool collections or armies of browser bots, but as systems with several execution environments and a router that knows which one should handle each part of a task. The aim isn't to make agents click more. It's to let them reach the parts of software that APIs still don't expose.