English documentation · runtime 7.24.4 · SDK 2.6.7. Content is maintained with runtime development; see each guide's scope and review date.
WebMCP browser bridge
Status: Experimental Scope: public, current-state Last reviewed: 2026-10-10 Owner: AX Code maintainers
The WebMCP bridge lets the agent open pages in an isolated Chrome window, read them, and (optionally) act on them. It is experimental, off by default, and needs Chrome 150 or newer.
WebMCP is a proposed web standard. A page can register structured tools — a name, a description, and an input schema — so an agent can call those actions directly. AX Code consumes tools a page registers, and it can also read and act on the page itself.
Codex and ChatGPT Work document their built-in browser’s version of the same standard as site tools. That page shows how their desktop app turns WebMCP on, how a visitor inspects the tools a site offers, and how a site author registers a tool. The steps below are AX Code’s.
Enable it with Chrome
- Install Google Chrome 150 or newer. Chromium at the same major version also works. AX Code starts its own Chrome window on a fresh profile and leaves an already-open Chrome window alone.
- Start the terminal UI with
ax-code. - Click the WebMCP chip in the sidebar footer, or the same chip on the
Home prompt footer, or run
/webmcp. The click or command is the consent. No browser starts before it. AX Code first checks, without starting a browser, whether a Chrome 150 or newer is installed in the usual places and warns you if not; the check is advice and never blocks the attempt. Your choice is saved to your user config, so the bridge comes back on at the next start without another click. - AX Code opens that isolated window with the WebMCP feature enabled
(
--enable-features=WebMCP). The window holds no logins until you sign in there by hand. - Ask the agent to open a page. With the product default, navigation is unrestricted. A configured or managed origin list narrows it.
- Click the chip again to turn the bridge off. That ends temporary session grants; saved approvals remain until you revoke them in MCP settings.
The chip shows [act] while the interact tier is on. The product default
includes that tier. To turn it off, set interact to false in the entry’s
webmcp profile. An entry you saved earlier keeps the tiers it had.
ax-code mcp webmcp prints a config snippet. It does not install a package,
connect a server, or write a file. Add --interact to print an entry with
the interact tier on, or --executable-path /absolute/path/to/chrome to name
a specific Chrome 150+ binary. When that path is set, AX Code checks the
binary’s major version before launch.
Tools in a Chrome window you opened yourself
The isolated window above already has WebMCP enabled. To try page tools in the Chrome profile you use while building a site, follow Chrome’s WebMCP guide:
- Open
chrome://flags/#enable-webmcp-testing. - Set the flag to Enabled.
- Relaunch Chrome.
That flag applies to the Chrome profile you opened. The AX Code bridge still starts from the chip.
Prompt hint
While the bridge is off, a prompt that names a URL or a local server (for
example localhost:3000) shows a one-line pointer to the chip and /webmcp.
It appears at most once per session and three times in total, and it never
changes what is sent to the agent.
What the agent can do
| Tier | Tools | Approval |
|---|---|---|
| Pages | list, open, navigate, close pages; run tools a page registers through WebMCP | each call unless a supported scope was saved |
| Read | page snapshot, screenshot, console, request metadata | one grant per origin per session, or a saved read approval |
| Interact | click, hover, wait, fill, fill form, press key, answer dialogs | see below |
Page content is untrusted: a page can try to steer the agent. Output is labeled with its origin and size-limited.
Save an approval
Eligible prompts offer Add to WebMCP allowlist with a red background. Select it, review the scope, then choose Add and allow. Saved approvals apply to this project on this machine and survive browser reconnects and AX Code restarts.
- Page listing approves
list_pagesfor the AX Code browser, including titles and URLs from all open origins. It does not approve page content. - Navigation approves opening or navigating to one exact origin.
- Read approves snapshots, screenshots, console and network metadata on one exact origin. Other origins and ports need their own approval.
- Close approves closing any page currently on one exact origin, including pages with unsaved work. It is a separate choice: navigation and read approvals do not grant it. The target origin is checked again before closing.
After the first read approval, AX Code continues that read in the same call after checking the page again. A page or bridge change stops the call and requires a fresh read. It does not replay a failed browser operation.
Navigation restrictions and administrator policy still apply. Saving an
approval does not edit allowedOrigins. Typing, consequential clicks,
dialogs and page-registered tools keep their existing
approvals. Allow once remains temporary; the countdown never saves a
persistent approval.
Select the WebMCP allowlist link beside the WebMCP chip (session
sidebar footer and Home prompt footer), run /webmcp-allowlist, or open
/mcp, select the bridge, and press Ctrl+G. Double click an entry (or
press Enter twice) to revoke it; the first activation marks the row and a
second one within a few seconds confirms, so a single click never revokes.
The clear row revokes all saved approvals for that bridge in this project
the same way. Long lists scroll. Press Escape or click outside the panel to
close it.
The search box also adds approvals. Type an exact https:// origin (or
http://localhost), then select the navigation, read or close row to save
it; a page-listing row is offered until it is saved. The bridge must be
connected: an explicit approval binds to the running bridge identity and is
checked at every call exactly like one saved from a prompt, so managed origin
lists, the read-tier switch and administrator policy still apply.
Turning the WebMCP chip off disconnects the browser and retains saved choices.
The local store is ~/.local/share/ax-code/webmcp-approvals.json by default
(XDG data-directory overrides apply). Approvals are bound to the bridge
identity; changing its launch or browser profile requires fresh approval.
Navigation failures
A navigation error can occur after a page opens or changes. The error reports
a recognized timeout, network or missing-page category when available, without
echoing raw bridge error text. Inspect list_pages before deciding whether to
navigate again. Navigation errors do not remove saved approvals.
Interact tier
Hovering and ordinary clicks run under one grant per origin, good for 20 actions, then the same prompt returns.
- Typing, key presses, dialogs, links, double clicks and clicks whose name sounds consequential (submit, pay, delete, authorize, …) ask every time, showing the target and the full value.
- Fields whose name looks like a credential are labeled and masked. Values shaped like API keys or private keys are refused: type credentials yourself.
- Actions need a fresh snapshot of the same page; a moved page or unknown element is refused. Three refusals in one turn stop further actions.
The name-based checks are heuristics, not a guarantee. The prompt is the control, so read it.
Work with an existing page
Ask AX Code to inspect the page’s capabilities, develop its native tools, or
diagnose a specific behavior. The browser_workflow tool groups these tasks
without requiring you to start a test server for ordinary observations. Enable
the bridge first and identify the intended page; none of these actions opens a
page, signs in, changes the browser profile or schedules future work.
For example: “Inspect the tools on my local app and show which application functions they correspond to,” or “Capture this page before I reproduce the blank results, then compare the errors and point me to candidate source files.”
Inspect native tools and their source
Call status with server, pageId and the exact origin to see the actual
admitted tools and native/snapshot availability. An empty listing does not
establish that inaccessible frames have no tools. Existing connectors may be
better suited to tasks that do not need page context.
{
"action": "inventory",
"server": "webmcp",
"pageId": 1,
"origin": "http://localhost:3000",
"sourceFiles": ["src/tools.ts", "public/search.html"]
}
Keep the returned inventoryId. Pass it as baselineId on a later inventory
call to see added/removed tools and changed schema fields. Files are explicitly
permission-checked, repository-contained and bounded. Static imperative
registrations and quoted HTML form attributes produce source candidates;
computed names, duplicate definitions and unsupported templates remain
unresolved or ambiguous. A matching name and schema corroborate a candidate,
but do not prove that the browser loaded that source revision.
Generate an application integration
Use author to return reviewable integration code:
{
"action": "author",
"name": "find_products",
"description": "Find products matching a query in the current catalog.",
"module": "./src/catalog.js",
"exportName": "findProducts",
"schema": {
"type": "object",
"properties": { "query": { "type": "string" } },
"required": ["query"]
},
"format": "imperative",
"effect": "read"
}
The named function must be a direct export. The generated registration takes
an AbortSignal for component/route cleanup. Set acceptsSignal: true only when
the application function accepts a second {signal} argument and honors it.
format: "declarative" returns a form plus a module binding for human
submission; connect its webmcp-result event to the application’s UI. Primitive
fields are supported; unsupported schema constraints are rejected instead of
silently discarded. Review application validation, authorization and actual
effects before applying either template. Field edits may trigger autosave even
when a form requires human submission.
Check execution and model selection separately
Existing contract steps verify fixed inputs and expected results. Add
{"action":"tool_presence","name":"admin_reset","present":false}
after a route or role change to check that an unavailable operation was
unregistered. present: true can also pin descriptorHash. These are lifecycle
checkpoints, not an event trace. Exported regressions include the same checks.
Use a separate selection evaluation to discover whether a model chooses the right tool and arguments:
{
"action": "selection_eval",
"inventoryId": "<returned inventory UUID>",
"cases": [
{ "task": "Find AX products", "expected": { "name": "find_products", "arguments": { "query": "AX" } } },
{ "task": "Delete every product", "expected": { "name": null, "arguments": {} } }
],
"repeats": 2
}
The selected API model receives only the task and captured catalog, with no execution tools or expected answers. Calls are approved and bounded to 12 cases, three repeats and a two-minute evaluation deadline. The report records model identity, a suite hash, separate exact choice/argument counts, joint correctness and unknown outcomes. Failures stay in the denominator. CLI models are unavailable for this evaluation because they can run their own tools. Selection scores do not prove tool correctness or qualify Arena.
Diagnose before and after an action
Call observe with the same page target, optional sourceFiles, and explicit
assertions using the existing role/name and count/value/checked/disabled
schema. Perform the requested action once through an approved tool or by hand.
Then call {"action":"diagnose","observationId":"<returned UUID>"}.
The result compares assertion outcomes, snapshot hashes, bounded console and
request metadata deltas, semantic inventory changes and source candidates.
Correlation is evidence to investigate, not a proven root cause. Without
assertions the status is observed, not pass.
Baselines are held only in the current runtime session, expire after 30 minutes and are limited to 16 per session. Connection/page-location changes invalidate reuse. The pinned bridge cannot identify a same-URL reload, so these comparisons remain advisory. Only hashes of page locations and snapshots are retained; bounded diagnostic text and tool descriptors are untrusted evidence.
For a reproducible localhost problem, promote takes the observation ID and
an explicit normal manifest containing the local test-server command and
steps. It freezes a new controlled scenario. Observation results are never
copied into acceptance: run a failing control before editing and qualify the
fixed runs through the existing workflow.
Repeat a daily observation task
An on-demand recipe checks an existing page without navigation or scheduling:
{
"action": "recipe",
"server": "webmcp",
"pageId": 1,
"definition": {
"version": 1,
"name": "Preview health",
"origin": "http://localhost:3000",
"assertions": [{ "locator": { "role": "status", "name": "Ready" }, "property": "count", "equals": 1 }]
}
}
The default is snapshot-only. Optional queries contain an exact tool name,
descriptorHash, object input, resultPath and equals. They invoke real
application tools under existing per-call approval; read-only hints do not
certify effects. The result explicitly distinguishes snapshot observation from
application calls with unverified effects, and rechecks snapshots after queries.
Save reviewed definitions, not captured contents or credentials. Definitions
carry no permission and never become Arena receipts. A pass means only the
declared observations matched; missing data cannot satisfy a check.
Register a tool manually
Ask the agent to add a tool that reuses logic your page already has, or register one from the page’s JavaScript. Chrome’s guide covers the imperative API and the declarative form API. A minimal read-only tool looks like this:
if (typeof document.modelContext?.registerTool === "function") {
await document.modelContext.registerTool({
name: "read_heading",
description: "Read the main heading of the current page.",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false,
},
annotations: { readOnlyHint: true },
execute: async () => ({
heading: document.querySelector("h1")?.textContent ?? "",
}),
})
}
A compatible agent can then discover read_heading on that page. OpenAI’s
site tools page walks through the
same idea from the Codex and ChatGPT Work side, including how their built-in
browser lists the tools a site provides.
Limits
No scripts, uploads, downloads, cookies, request bodies, coordinates or
dragging. Administrators can disable the bridge or either tier with the
managed webmcp requirement (allow, allowRead, allowInteract,
allowedOrigins); project and user config cannot loosen it.
Servers you add with ax-code mcp add are a different trust path. See
MCP Integrations.
Debug a localhost page in five steps
For a page that renders wrong, a control that does nothing, or a request that fails:
- Navigate to the page and take a snapshot — the a11y tree is the structural baseline. Navigation and reads use the existing approvals.
- Perform the failing action once, under the existing interaction approvals.
- Read the delta: console errors and network metadata (method, URL, status, type — request and response bodies stay out of scope) around the action. For asynchronous state, wait; never repeat the triggering action.
- Verdict: PASS, FAIL, or BLOCKED with the missing capability named. A tool acknowledgement is not proof of application success.
- If the localhost failure can be expressed as a structured assertion, freeze
it as a
browser_workflowscenario (below), record the failing control, and run the same hash twice after the fix.
Attach evidence to a bug report
For a localhost failure investigated through a frozen scenario, an evidence
bundle makes the report reviewable: the scenario name and hash, the failing
assertion result (without captured page content), the receipt IDs from
browser_workflow inspect, snapshot hashes, the bounded console error and
network metadata deltas, source links with their
explicit/local_map/unresolved labels, and the origin. Include only
runtime-bounded, runtime-redacted output — never request or response bodies,
headers, cookies, storage, page content, or credential-shaped values. Receipt
IDs and copied receipt data are references only; authoritative receipt state
remains runtime-owned.
Reproduce and verify a development change
The browser_workflow tool freezes acceptance steps before you edit a web
application, runs them through the connected WebMCP bridge, and records
structured assertions. Enable the bridge first. The first supported environment
is a disposable HTTP server on 127.0.0.1; each run allocates a different port
and temporary data directory, plus a fresh isolated browser context. Persistent
browser profiles are refused. Empty contexts are retained by the upstream bridge
until disconnect; after 32 runs on one connection, reconnect the bridge before
continuing. The workflow never reuses their cookies or storage.
Ask the agent to freeze a scenario with action: "freeze". For example, a
project-owned test/browser-server.mjs that accepts a port argument can use:
{
"action": "freeze",
"manifest": {
"version": 1,
"name": "Search returns a matching result",
"server": "node test/browser-server.mjs {port}",
"path": "/",
"setup": [],
"reset": [],
"cleanup": [],
"steps": [
{ "action": "fill", "locator": { "role": "textbox", "name": "Search" }, "value": "example" },
{
"action": "assert",
"assertion": {
"locator": { "role": "status", "name": "One result" },
"property": "count",
"equals": 1
}
}
]
}
}
Keep the returned hash. Run with {"action":"run","hash":"<hash>","server":"webmcp"}
(use your connected bridge’s name). Reproduce the failure, make the change,
and run the same hash twice. Each run starts the declared server, opens its
own page, checks the steps, closes the page and stops its server. Lifecycle
commands run in the repository root through the normal shell permissions.
{port} expands to the allocated port; {data} expands to a quoted temporary
directory. Setup, reset and cleanup commands must terminate within 15 seconds;
readiness has a 15-second deadline and the browser run has a 120-second deadline.
Browser actions keep their existing permissions and interaction budgets.
Supported steps are click, hover, fill, structured assertion and page-tool
contract checks. Locators use an exact role and accessible name. Actions with
zero or multiple matches stop as unknown; there is no guessed UID or CSS/script
fallback. Assertions compare count, value, checked or disabled state. An absent
state property is unknown. For asynchronous rendering, add timeoutMs (0-10000,
default 0) to an assert step. The runner polls fresh structured snapshots
until the assertion matches or the deadline expires; it never repeats the
preceding click or fill. The timeout is frozen with the scenario and retained
in its exported test. Scenarios are bounded to 32 steps and 32 KiB.
inspect returns the frozen manifest and runtime receipts. Receipts bind the
scenario to the repository revision/content, operation outcomes, snapshot
hashes and bounded console/network metadata. Changed source, denied operations,
missing evidence, timeouts and incomplete cleanup cannot pass. These results
validate the declared assertions; they do not prove every behavior of the app.
Frozen state and authoritative receipts live in the current runtime session.
After restarting, freeze and qualify again; copied JSON receipts are not
accepted as runtime authority.
Export a regression test
{"action":"export","hash":"<hash>"} returns a standalone Node module using
playwright-core. Save the returned code as a project test and run it with
AX_TEST_WEBMCP_CHROME pointing to Chrome. It starts the same fixture and uses
fresh browser contexts, stable role/name locators and the frozen assertions.
Actually run the exported test before calling it validated. Exported tests are
an independent regression artifact; their output is not an Arena receipt.
Page-tool contract steps use Chrome’s native WebMCP protocol with exact descriptor
hashes and expected output, without evaluating arbitrary page scripts. Chrome
must support that experimental protocol for contract exports.
Develop a page-tool contract and investigate failures
With a page open, {"action":"contracts","server":"webmcp","pageId":1}
returns its registered descriptors and exact descriptor hashes. Freeze a
contract step containing name, descriptorHash, input, resultPath and
equals. Hash changes fail the check. Set expectError: true for negative
inputs: only a confirmed page-tool execution error satisfies it; canceled
calls, permission refusal and missing completion are unknown. Add assertions
after mutations to check the resulting page state as well as the return value.
The template action takes name, a relative application module, an explicit
exportName, and schema. It verifies the local export exists and returns a
registration skeleton. Review it against the application’s input validation,
authorization and business logic before enabling it; the tool cannot establish
those guarantees from an exported function name.
An optional sources list names repository-local files with one-based line
and zero-based column, and optionally a local map file. Failed runs return
bounded diagnostics and source links labelled explicit, local_map or
unresolved. Mapping is advisory, never proof of a root cause or a passing
assertion. Remote maps, paths outside the repository and files over 1 MiB are
not read. Browser network evidence remains metadata only.
Require browser evidence in implement Arena
Supply browserScenario: "<hash>" with mode: "implement". Freeze the scenario
and record a real failing assertion on the current clean base before starting
the Arena. Every candidate receives that frozen contract in its isolated
worktree and must run it twice successfully through its connected isolated
bridge. Browser activation and permissions remain supervised. Missing bridge
access, stale content, unknown results or missing receipts prevent promotion,
even if the repository checks pass. Normal code verification and mutation
checks still run, and no candidate merges automatically.
Choose evidence efficiently
Native WebMCP tools describe application operations; Chrome DevTools MCP supplies
browser inspection and automation. Prefer a page’s registered tool for an
explicit application operation, then verify its result and resulting page state.
Use accessibility snapshots for text and stable element locators. For layout,
canvas or image-only content, take a screenshot: crop to a fresh snapshot uid
or use JPEG with reduced quality to stay within inline limits. Interpreting
pixels requires a vision-capable model. Neither WebMCP nor the documented
DevTools MCP tool list provides a dedicated OCR tool; inferred image text is
advisory and cannot satisfy a structured acceptance assertion.
Narrow console reads with types and pageSize; narrow network metadata with
resourceTypes and pageSize. Workflow receipts retain pre-action baselines
and bounded deltas so errors introduced by the reproduction are easier to find.
Network bodies and arbitrary evaluation remain outside the bridge’s granted
surface. Performance traces, emulation, Lighthouse, screencasts, memory and
extension tools in upstream DevTools MCP are separate capabilities and are not
exposed by this profile.
See Chrome’s WebMCP debugging guide and the upstream DevTools MCP tool reference. Upstream main can differ from AX Code’s pinned bridge; only the local tool schemas describe the supported arguments.