page-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@page-mcpsnapshot the login page, fill in the form, then click submit and check the console"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
page-mcp
A CDN-delivered WebMCP tool provider.
Drop one <script> into a web UI and that page's own JS engine starts hosting a
set of devtools tools on document.modelContext:
DOM inspection — a text outline of the page with stable element refs
Interactions — click, fill, type, press, select, hover, scroll, upload
Diagnostics — console/error capture, and a JS REPL
Any agent that can reach the page can discover and call them. There is no server, no LLM and no chat UI in here — this is the tool surface, not an agent.
<script src="https://cdn.jsdelivr.net/npm/page-mcp/dist/page-mcp.js"></script>
<!-- unpkg mirror: https://unpkg.com/page-mcp/dist/page-mcp.js -->npm i page-mcp ships the same single file at
node_modules/page-mcp/dist/page-mcp.js for self-hosting.
Related MCP server: webappmcp
How it is consumed
The premise is that the coding agent owns the browser: it launches Chrome (headless or not), points it at the app it is working on, and drives it over CDP. Under that premise the agent needs no bridge — the WebMCP tools are reachable through the channel it already has.
coding agent (Claude Code / Cursor / DSH / …)
│
│ the browser channel it already has
│ · CDP Runtime.evaluate
│ · chrome-devtools-mcp (list_webmcp_tools / execute_webmcp_tool)
│ · Playwright / Puppeteer
▼
Chrome ── page + <script src="…/page-mcp.js">
│
▼
document.modelContext
├── dev_snapshot, dev_click, dev_fill, …
└── <tools the app registers itself>executeTool calling contract, including the failure shapes
executeTool(tool, "{}") // ✓ the second argument is a JSON STRING
executeTool("name", "{}") // ✗ TypeError: RegisteredTool must be an object
executeTool(tool, {}) // ✗ DOMException UnknownError: Failed to parse input
// arguments — a DOMException, NOT a TypeError, so
// harnesses that classify errors by constructor
// name mis-sort this one as a tool bugThe result is the tool's own raw string, not a CallToolResult wrapper.
Two details worth stating precisely:
CDP
Runtime.evaluateruns in the page's own realm, so thetoolsPermissions-Policy default allowlist ('self') is satisfied — page JS can callgetTools()/executeTool(), and so can an agent evaluating in that realm.Do not depend on Chrome's native WebMCP. It sits behind a flag / origin trial. The bundle carries the polyfill and installs it only when
document.modelContextis absent, so the same file works on stable Chrome with no flag, in headless, and in Firefox/Safari.
A relay or extension is only needed when the agent does not own the browser
— i.e. the tools live in the developer's own everyday Chrome. That is a
secondary case; @mcp-b/webmcp-local-relay covers it.
The app's own tools
The highest-value half. A page can register tools that expose its own domain state, which no external agent can infer from the DOM:
window.pageMcp.register({
name: "vitrine_state",
description: "Read the live state of the 3D viewer…",
annotations: { readOnlyHint: true },
inputSchema: { type: "object", properties: {} },
run: () => JSON.stringify({ /* … */ }),
});This matters most where the DOM is empty. In the bundled demo the app renders
into a WebGL canvas: a snapshot shows three buttons, while vitrine_state
reports the scene graph, renderer stats and materialize progress. Module-scoped
bindings are unreachable from eval, so registering a tool is the only way
to expose them.
Tools
Tool | What it does |
| Text outline of the page; stable refs; |
| Text, attrs, computed style, CSS path for one ref |
| Search by word/phrase, get live refs — no full snapshot needed |
| Geometry + hit-test state; the handoff for a CDP element screenshot |
| Prove clipped text / unclickable / broken-image defects without pixels |
| Real pointer/mouse sequence, then native click |
| Set one field's value, or a whole form in one call (React/Vue-aware) |
| Per-keystroke typing for autocomplete-style inputs |
| Key or key combination |
| Choose an option in a native |
| Pointer over an element |
| Pointer drag, ref or CSS selector — reaches canvases that have no ref |
| Scroll by delta or bring a ref into view |
| Attach files (url or base64) to a file input or dropzone |
| Wait for a selector, text or JS predicate |
| Console + uncaught errors + unhandled rejections |
| fetch / XHR / beacon, with the call site that made each request |
| localStorage / sessionStorage / cookie read-write, for setting up or resetting app state |
| Ordered DOM delta since a token — what your action actually did |
| Long tasks / LCP / CLS / heap + a live frame sample |
| Check several facts in one call; a failure is a real failure |
| JS REPL |
Names are prefixed (data-prefix) so they cannot collide with another browser
tool set in the same agent context. Every tool carries debugging: true, which
a consuming agent can use to filter dev tooling out of an end-user surface.
Boundaries and known limits
style-srcwithout'unsafe-inline'kills inline styles. Measured on Chrome 151:<style>in a shadow root is blocked,el.style.x = …is blocked and a violation is logged, butCSSStyleSheet+adoptedStyleSheetsworks. The page indicator and the click highlight use the latter.script-srcwithout'unsafe-eval'killsdev_eval. It fails with a readable message rather than a crash; the CSP-safe path is to have the app register named tools.dev_snapshotand the act tools are unaffected.Unknown and mistyped arguments are rejected, not ignored.
dev_scroll {delta_y: 200}once scrolled by 0 and reported success. Validation now refuses the call before it runs:invalid arguments — unknown property "delta_y" — accepted: ref, dx, dy, include_snapshot.A third-party wrapper that forwards extra fields will be refused outright rather than silently mis-executing. That is deliberate; opt out per tool withadditionalProperties: true.Some tools cannot exist in the page.
dev_uploadtakes a URL or base64 because the page cannot read a path on the developer's disk. Uploading a host path is a job for the agent's CDP layer (DOM.setFileInputFiles); screenshots are likewise a CDP-side concern (canvas.toDataURLonly works when the app preserved the drawing buffer).debuggingis dropped on Chrome 151 — both native and polyfilledAnnotations are not propagated by the runtime — so we restore them. On Chrome 151, native
getTools()returnsreadOnlyHint: falseeven for a tool that registeredtrue, and the bundled polyfill (@mcp-b/webmcp-polyfill@5.1.0) hard-dropsdebuggingandconsequentialHintentirely. Since we know what we registered,getTools()is wrapped to merge our annotations back in, so a consumer now seesdebugging: trueon both runtimes. Do not "fix" this by switching to the 6.0.0-beta polyfill or the CG's own polyfill: they preserve the keys but changeexecuteToolto take an input object, while native Chrome and chrome-devtools-mcp pass a JSON string. That trades an annotation for an interop break. Measured: docs/research/polyfill-annotation-verification.md.Secure context required.
document.modelContextis[SecureContext].https://andlocalhost/127.0.0.1qualify; plain-HTTP on a LAN IP does not.Dev-only by construction.
dev_evalis arbitrary JS execution in the user's session. The page-corner indicator says so and the script tag can be omitted from production builds.
Development
npm install
npm run build # dist/page-mcp.js — single-file IIFE, polyfill included
node harness/make-demo.mjs # regenerate demo/index.html from the three.js app
npm run serve # http://127.0.0.1:8940/demo/
npm run drive # end-to-end suite, native + polyfill runtimesharness/browser.mjs is the browser tool an agent owns — a CDP attach client,
deliberately not a bridge to this package:
node harness/browser.mjs start http://127.0.0.1:8940/demo/
node harness/browser.mjs tools
node harness/browser.mjs call dev_snapshot
node harness/browser.mjs screenshot /tmp/shot.pngVerified
56/56 end-to-end assertions pass in both runtimes (native flag, and polyfill with no flag): discovery via
getTools(),executeTool()round trips, snapshot of a canvas page, disabled-control diagnosis, app-tool read and mutation, console capture, form pass, eval. Tool count: 24 per runtime.The published demo works over public HTTPS with the polyfill (https://view.pc.randomhash.app/2026-10-03_webmcp-devtools-demo/).
Styling survives a response-header CSP of
default-src 'self'; script-src 'self'; style-src 'self'with 0 violations.
Independently reproduced by other agents on different machines (Chrome 147 + Chrome 151; docs/03, docs/10) — docs/10 records 24/24 tools usable in a third environment. Docs — index · 00 positioning · 01 agent usability test · 02 measurements · 03 external verification · 04 drive rerun
Headless without software GL (
--enable-unsafe-swiftshader --use-angle=swiftshader) will hide any app-registered tools: the app's module aborts on a failed WebGL context and itsregister()calls never run.harness/browser.mjsanddrive.mjspass the flags already.
This server cannot be deployed
Maintenance
Related MCP Connectors
Comment on AI-generated webpages; feedback flows back to your coding agent. Free, MIT, local-first.
Give agents eyes on any web page: structured context, and changes explained in plain language.
Hosted browser for AI agents: screenshots, post-JS DOM, console, WCAG. No install, no API key.
Hosting for AI agents: publish a live website in one tool call, in-built Human in the Loop controls
Related MCP Servers
- AlicenseAqualityNot gradedmaintenanceEnables AI tools to interact with browsers for enhanced frontend development, providing context to LLMs through tools like API call analysis, screenshots, element selection, and documentation ingestion.99 npm10-
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with web applications through DOM inspection, user interaction simulation, and application state management.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI coding agents to see and interact with real Chrome pages through structured DOM inspection, targeted screenshots, recordings, and human-in-the-loop approvals across major coding-agent hosts.MIT
- AlicenseCqualityAmaintenanceEnables AI agents to automate real Chrome via a structured Semantic Action Graph, without screenshots, CDP, or bot detection. It supports clicks, form filling, navigation, and state diffs on strict-CSP sites and modern SPAs.75MIT