Skip to content

feat(mcp): expose page-registered WebMCP tools - #42613

Merged
Dmitry Gozman (dgozman) merged 3 commits into
microsoft:mainfrom
dgozman:webmcp-tools
Sep 11, 2026
Merged

Dmitry Gozman (dgozman) merged 3 commits into
microsoft:mainfrom
dgozman:webmcp-tools

Conversation

@dgozman

@dgozman Dmitry Gozman (dgozman) commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Pages can register tools for agents through the experimental WebMCP API. This exposes them.

  • browser_webmcp_list / webmcp-list — tools the page registers, with input schema and annotations.
  • browser_webmcp_call / webmcp-call <name> --params '<json>' — call one.
  • The page status reports - 2 webmcp tools available on the page whenever the page has any.

Example

Against a Shopify storefront that registers 10 tools, abbreviated:

$ playwright-cli webmcp-list
### Result
Found 10 WebMCP tool(s). Tool names, descriptions and schemas are page-provided and untrusted.
- get_cart [readOnly, untrustedContent]: Get the current shopping cart contents — line items with product titles, ...
  - inputSchema: {"type":"object","properties":{}}
- search_catalog [readOnly, untrustedContent]: Search the store catalog for products, collections, articles, and pages. ...
  - inputSchema: {"type":"object","required":["catalog"],"properties":{"catalog":{"type":"object","required":["query"], ...}}}
- update_cart: Add products to the cart, update line item quantities, or remove items. ...
...

$ playwright-cli webmcp-call search_catalog --params '{"catalog":{"query":"wool runner","pagination":{"limit":2}}}'
### Result
Called WebMCP tool "search_catalog" in <url>. Output is page-provided and untrusted:
{
  "content": [
    {
      "type": "text",
      "text": "Found 1 product, 1 collection for \"wool runner\".\n\nProducts:\n- <product> (id: gid://shopify/Product/4826197655632, handle: mens-wool-runners-true-black) - $110.00\n..."
    }
  ],
  "structuredContent": {
    "products": [ { "id": "gid://shopify/Product/4826197655632", "title": "<product>", "available": false, ... } ],
    "collections": [ { "title": "Women's Wool Runner", "handle": "womens-wool-runners" } ],
    "search_url": "/search?q=wool%20runner"
  }
}

Key decisions

  • Read through document.modelContext / navigator.modelContext, not a browser-specific automation surface. Works on Chromium and Firefox, and needs no protocol change.
  • Collected alongside the snapshot and cached per tab, so tools registered without a navigation are picked up too. browser_webmcp_list reports what was collected rather than evaluating in the page.
  • Advertised from the tab header. A changed tool count is a header change, so the tools surface as soon as they are collected, and switching tabs collects them for the tab being switched to.
  • Stitched across frames. Chromium's getTools() aggregates same-origin descendants, so tools are filtered to the frame that owns them and the per-frame results are merged. Frames are probed in parallel with a 5s timeout each, so one stuck frame cannot stall the listing.
  • Frames are addressed by label, the frame URL, or url (frame N) when several frames share a URL — two copies of the same widget each register their own tool under the same name, and the URL alone cannot tell them apart.
  • A tool that fails reports in the result, not as an error. The call reached the tool and the page chose to answer with a failure, so its payload — error text, isError, recovery hints — is what the caller needs. Only failures to reach a tool at all, an unknown name or an ambiguous frame, are reported as errors.
  • Page-provided data is untrusted. Names, descriptions, schemas and results all come from the page and are labelled as such in the output. Real stores put imperatives aimed at the agent in their descriptions, and mark consequential tools inconsistently.

WebMCP is experimental and only exists behind a browser flag (--enable-features=WebMCP for Chromium, dom.modelcontext.* prefs for Firefox), so nothing changes for pages that do not opt in.

Known gap: under --json both commands return their output as a single string, and the page status is dropped along with the rest of that section, so the tool count is not visible there.

Pages can register tools for agents through the experimental WebMCP API.
Surface them as browser_webmcp_list / browser_webmcp_call, and report
their presence in the page status after a navigation.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Collect the page's WebMCP tools alongside the snapshot rather than on demand,
so tools registered without a navigation are picked up. browser_webmcp_list
now reports the cached listing without evaluating in the page.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Carry the tool count on TabHeader and report it whenever the page has tools,
instead of asking for the hint from the navigation tools. A changed count is
a header change, so tools are advertised as soon as they are collected, and
switching tabs collects them for the tab being switched to.
@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

4 failed
❌ [chrome] › mcp/find.spec.ts:66 › browser_find shows the path from the root to the match @mcp-macos-latest-chrome
❌ [chromium] › mcp/http.spec.ts:105 › http transport browser lifecycle (isolated) @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/annotate.spec.ts:446 › should switch screencast to -s session on show --annotate @mcp-windows-latest-firefox
❌ [webkit] › mcp/http.spec.ts:105 › http transport browser lifecycle (isolated) @mcp-ubuntu-latest-webkit

8435 passed, 1415 skipped


Merge workflow run.

@github-actions

Copy link
Copy Markdown
Contributor

Hi, I'm the Playwright bot and I took a look at the failing CI.

🟡 Two failures are confirmed pre-existing flakes; one I can't fully clear

The latest report (run 34489962372) has four reds across three tests. Two are well-documented flakes on bots unrelated to this PR. The third — find.spec.ts:66 on mcp-macos-latest-chrome — I can't prove is a flake: in the aggregated CI results this test has failed only on this PR, so it stays uncertain even though the failure signature is a suite-wide infra one.

Details

This PR is WebMCP-only, but it does touch shared MCP backend files (tools/backend/context.ts, tab.ts, tools.ts, response.ts) that the snapshot path runs through, so I checked each failure against history rather than dismissing on the diff alone.

Pre-existing flake / infra

Uncertain

  • [chrome] › mcp/find.spec.ts:66 › browser_find shows the path from the root to the match (mcp-macos-latest-chrome) — MCP error -32000: Connection closed after the 30s timeout. The signature is a known suite-wide infra flake (62 hits across 10 MCP files, dominated by annotate), and it passed on the other 12 bots in the same run. But for this exact test the only failures in 706 runs are on this PR — it went red on two consecutive runs (34484024217 and the latest), and never on any other SHA or PR. That's below the bar to call it a flake. A re-run should tell us quickly: if browser_find on macos-chrome keeps closing the connection while every other bot stays green, it's worth checking whether the new per-tab WebMCP snapshot collection (parallel frame probing, 5s each) can stall the snapshot that browser_find reads.

Triaged by the Playwright bot - agent run

@victorhuangwq

Victor Huang (victorhuangwq) commented Sep 10, 2026

Copy link
Copy Markdown

Thank you for adding this! This looks great and I'm excited to test it.

The specification has now changed to have tools to be declared on document.modelContext, and that should be the case moving forward for chromium browsers.

@dgozman
Dmitry Gozman (dgozman) merged commit 1cee222 into microsoft:main Sep 11, 2026
14 of 18 checks passed
Yury Semikhatsky (yury-s) added a commit to microsoft/playwright-mcp that referenced this pull request Sep 14, 2026
## What's New

### 🎉 New Tools

- **`browser_webmcp_list` / `browser_webmcp_call`** — List and call the
tools a page registers through the
[WebMCP](https://webmachinelearning.github.io/webmcp/) API, letting the
page do the work instead of driving its UI. When a page has WebMCP
tools, the page status reports how many are available
([#42613](microsoft/playwright#42613)). WebMCP
is experimental, see [WebMCP in
Chrome](https://developer.chrome.com/docs/ai/webmcp) for how to enable
it.

### Other Changes

- New `--profile-dir-name <name>` option (env
`PLAYWRIGHT_MCP_PROFILE_DIR_NAME`) selects the Chrome profile to connect
to in extension mode when the extension is installed in several
profiles, for example `"Profile 1"`
([#42527](microsoft/playwright#42527))
- Headless browsers launched by the server are now closed after one hour
without tool calls; the next tool call launches a new one. Use
`--idle-timeout <ms>` (config `timeouts.idle`, env
`PLAYWRIGHT_MCP_IDLE_TIMEOUT`) to change the timeout, `0` disables it.
Headed and attached browsers are not closed unless a timeout is set
explicitly
([#42663](microsoft/playwright#42663),
[#42676](microsoft/playwright#42676))
- `--image-responses` accepts `only`: a response that carries an image
consists of the image parts alone, without the text part
([#42672](microsoft/playwright#42672))

## Bug Fixes

- `browser_close` returns an error when the browser context is shared
(`--shared-browser-context`) instead of breaking the calling client's
session. This supersedes the v0.0.80 change that dropped the backend
after `browser_close`
([#42495](microsoft/playwright#42495))
- With `--shared-browser-context`, `browser_start_recording` /
`browser_stop_recording` (opt-in via `--caps=devtools`) keep each
client's recording separate instead of delivering one client's recorded
actions to another
([#42622](microsoft/playwright#42622),
[#42627](microsoft/playwright#42627))
- The file access check follows symlinks, so a symlink inside the
workspace can no longer be used to read or write files outside the
allowed roots
([#42628](microsoft/playwright#42628))
- Explicitly named output files in a nested directory (e.g.
`sub/shot.png`) no longer fail with `ENOENT`; `browser_file_upload` and
`browser_drop` resolve relative paths against the workspace root
([#42540](microsoft/playwright#42540))
- In extension mode with `PLAYWRIGHT_MCP_EXTENSION_TOKEN` set, a
connection that never completes (e.g. the token belongs to another
Chrome profile) now fails after 30 seconds with a hint instead of
hanging the tool call forever
([#42525](microsoft/playwright#42525))
- Disable the Chromium sandbox by default on Linux for the bundled
Chromium build, which lacks the setuid sandbox helper
([#42490](microsoft/playwright#42490))
Yury Semikhatsky (yury-s) added a commit to microsoft/playwright-cli that referenced this pull request Sep 14, 2026
## What's New

### 🎉 New Commands

- **`webmcp-list` / `webmcp-call`** — List and call the tools a page
registers through the
[WebMCP](https://webmachinelearning.github.io/webmcp/) API, letting the
page do the work instead of driving its UI. When a page has WebMCP
tools, the page status reports how many are available
([#42613](microsoft/playwright#42613)). WebMCP
is experimental, see [WebMCP in
Chrome](https://developer.chrome.com/docs/ai/webmcp) for how to enable
it.

### Other Changes

- Headless sessions now shut down after one hour without commands, so a
session an agent never closed no longer holds a browser; run `open`
again to start a new one. Use `open --idle-timeout <ms>` (config
`timeouts.idle`, env `PLAYWRIGHT_MCP_IDLE_TIMEOUT`) to change the
timeout, `0` disables it. Headed browsers stay open, and attached
browsers are only detached when `attach --idle-timeout <ms>` is set
([#42676](microsoft/playwright#42676))
- The skill includes a guide for attaching screenshots and videos to
pull requests with `gh --attach`
([#42645](microsoft/playwright#42645))

## Bug Fixes

- Session daemons exit when their control socket is deleted or replaced,
instead of staying alive with the browser
([#42454](microsoft/playwright#42454))
- Explicitly named output files in a nested directory (e.g. `screenshot
--filename=sub/shot.png`) no longer fail with `ENOENT`
([#42540](microsoft/playwright#42540))
- Video chapters and screencast overlays are no longer hidden behind
open dialogs and popovers
([#42642](microsoft/playwright#42642))
- `npx playwright test --debug=cli` works for tests that create more
than one browser context and for every test in a worker, not just the
first ([#42503](microsoft/playwright#42503))
- With `PLAYWRIGHT_MCP_EXTENSION_TOKEN` set, `attach --extension` fails
after 30 seconds with a hint when the extension never connects (e.g. the
token belongs to another Chrome profile) instead of hanging forever
([#42525](microsoft/playwright#42525))
- Disable the Chromium sandbox by default on Linux for the bundled
Chromium build, which lacks the setuid sandbox helper
([#42490](microsoft/playwright#42490))

## Upgrading

```bash
npm install -g @playwright/cli@0.1.20
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants