webmcp-guard
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., "@webmcp-guardNavigate to webmcp.com and list available webmcp tools"
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.
webmcp-guard
A WebMCP-aware drop-in replacement for @playwright/mcp.
Playwright MCP is the de facto browser-automation MCP server wired into
Claude Code, Cursor, VS Code, and friends. It has no awareness of
WebMCP — the emerging W3C
proposal that lets a page register agent-callable tools directly
(document.modelContext.registerTool, with navigator.modelContext as a
legacy alias). Every agent using plain Playwright MCP on a WebMCP-enabled
site falls back to click/type/scrape automation even when the page is
explicitly offering a better, structured interface.
webmcp-guard sits in front of @playwright/mcp as a proxy: it passes its
entire native tool surface through unchanged (zero behavior regression for
sites that don't use WebMCP), detects WebMCP tools after every navigation,
and — because those tools are JavaScript the page controls rather than
code the server ships — exposes them behind a trust layer: session-scoped
provenance tracking that flags when a previously-seen tool's schema changes,
mandatory confirmation before any page-defined tool call runs, clear
webmcp:* namespacing so they're never confused with native tools, and an
audit log. See docs/trust-boundary-design.md
for the full reasoning behind that design.
Validated against the real ecosystem, not just synthetic tests: all 168
live sites in the webmcp.com public directory, plus a
hand-built adversarial test case. That process found and fixed a real
cross-origin tool-invocation vulnerability in this project's own trust
layer. Full methodology, evidence, and honest limitations in
docs/testing-and-validation.md.
Install / config
Not yet published to npm. For now, build from source (see
Install / build (from source) below) and
point your MCP client config at the built CLI directly. Once published,
install will be exactly npm install -g webmcp-guard / npx webmcp-guard,
mirroring @playwright/mcp's own install path — the config shape below is
already written for that end state, it just uses a local path today instead
of a package name.
webmcp-guard is close-to-drop-in with @playwright/mcp: it only
special-cases --browser and --headless/--headed (needed to build its
own browser launch config), and forwards every other flag — including
--allowed-origins, --blocked-origins, --allow-unrestricted-file-access,
--isolated, --caps, etc. — verbatim to the underlying @playwright/mcp
process it spawns. Any existing playwright-mcp config line keeps working
unmodified.
Before — a typical @playwright/mcp entry in an MCP client config
(mcpServers in Claude Code's or Cursor's config file):
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest",
"--browser", "chrome",
"--allowed-origins", "https://example.com",
"--blocked-origins", "https://evil.com"
]
}
}
}After, once published — swap the package name in args, everything
else (all flags, their values, and their order) is unchanged:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": [
"webmcp-guard@latest",
"--browser", "chrome",
"--allowed-origins", "https://example.com",
"--blocked-origins", "https://evil.com"
]
}
}
}Today, from a local build — same flags, command/args point at the
built CLI instead:
{
"mcpServers": {
"playwright": {
"command": "node",
"args": [
"/absolute/path/to/webmcp-guard/packages/webmcp-guard/dist/cli.js",
"--browser", "chrome",
"--allowed-origins", "https://example.com",
"--blocked-origins", "https://evil.com"
]
}
}
}Once published, the entire migration is a one-line package-name swap — no flags need to be added, removed, or reordered.
Related MCP server: mcp-browser
What webmcp:* tools are, and what you'll see
When webmcp-guard navigates to a page that registers WebMCP tools, it lists
them to your harness as new, separate MCP tools named webmcp:<name> (e.g.
webmcp:add_to_cart) — distinct from and never merged into Playwright's
native tool list, so it's always obvious at a glance whether a tool is
server code or something the page itself defined.
Because a webmcp:* tool's implementation is JavaScript the page controls,
every call into one requires explicit confirmation before it runs — this is
on by default and is not something a misconfigured client can silently skip
past. What that confirmation looks like depends on your client:
Claude Code CLI (and any client that declares MCP
elicitationsupport): you'll see an interactive prompt showing the tool name, the page's origin, and — if the tool's schema has changed since webmcp-guard last saw it from that origin — an explicit warning that its definition changed. You accept or decline; declining, cancelling, or letting the prompt time out (30s) all result in the call being denied, never silently allowed.Clients without elicitation support (e.g. Claude Desktop, or a Cursor session hitting its current elicitation-rendering bug): webmcp-guard falls back to relying on your client's own generic tool-approval settings — Claude Code's
settings.jsonask/allow/denyrules, or Cursor's allowlist/auto-run approvals — keyed on thewebmcp:*tool name. You should configure anask(or equivalent) rule forwebmcp:*tools in these clients yourself; webmcp-guard can't force this from the server side, though every fallback-path call is still recorded to its audit log so the reliance is visible after the fact.
See docs/trust-boundary-design.md for why
this two-track design exists rather than relying on elicitation alone.
Architecture
flowchart TD
Harness["Agent Harness\n(Claude Code / Cursor / etc.)"]
Guard["webmcp-guard\npackages/webmcp-guard"]
Trust["@webmcp-guard/trust-layer\nprovenance · confirmation gate\nnamespacing · audit log"]
Adapter["@webmcp-guard/playwright\nspawns & speaks to @playwright/mcp"]
Upstream["@playwright/mcp\n(spawned subprocess, pinned version)"]
Browser["Real Chrome"]
Page["Web page\n(may register document.modelContext tools)"]
Harness -- "MCP over stdio" --> Guard
Guard -- "native tool calls, pass-through" --> Adapter
Guard -- "webmcp:* calls, gated" --> Trust
Trust -- "evaluated call" --> Adapter
Adapter -- "MCP over stdio\n(internal client)" --> Upstream
Upstream --> Browser
Browser --> Page
style Trust fill:#2d5a2d,stroke:#4ade80,color:#fffwebmcp-guard is a proxy, not a fork: it runs @playwright/mcp as an
internal MCP client via server composition (stdio), rather than vendoring
its source. See Decision 1 in research/decision-log.md for why this
replaced the original fork hypothesis.
packages/playwright(@webmcp-guard/playwright) — the adapter that spawns/speaks to@playwright/mcp, passes tool calls through, and runs the WebMCP detection probe via@playwright/mcp's ownbrowser_evaluatetool. Kept Playwright-specific and adapter-agnostic-by-convention so a future non-Playwright backend could reuse the same shape.packages/trust-layer(@webmcp-guard/trust-layer) — provenance store, schema-diffing, confirmation gating, namespacing, and the audit log. Deliberately has no Playwright-specific imports, since it's meant to generalize to other automation backends later.packages/webmcp-guard(webmcp-guard) — the MCP server itself: connects to the adapter on startup, exposes every upstream Playwright tool unchanged plus any detectedwebmcp:*tools gated by the trust layer, and after everybrowser_navigate/tab-switch, runs the detection probe and logs/caches the result.
The trust gate, per webmcp:* call
flowchart TD
Call["Agent calls webmcp:<tool>"] --> Live{"Tool actually present\non the CURRENTLY active page?"}
Live -- No --> Deny1["Denied — stale tool\nfrom a previous origin\n(closes the cross-origin bug)"]
Live -- Yes --> Verdict{"Provenance verdict"}
Verdict -- "new-tool / unchanged" --> Gate
Verdict -- "schema-changed" --> Warn["Confirmation prompt\nshows explicit warning"] --> Gate
Gate{"Client declared\nMCP elicitation?"}
Gate -- Yes --> Elicit["Structured confirmation\nprompt via elicitation"]
Gate -- No --> Fallback["Relies on client's own\ngeneric ask/allow/deny gate"]
Elicit --> Decision{"Confirmed?"}
Decision -- "No / timeout / error" --> Deny2["Denied — fail closed,\nnever silent-allow"]
Decision -- Yes --> Allow["Tool call forwarded\nto the page"]
Fallback --> Allow
Allow --> Audit["Recorded to audit log"]
Deny1 --> Audit
Deny2 --> AuditInstall / build (from source)
npm install
npm run buildRun
node packages/webmcp-guard/dist/cli.js --browser chromeOr wire it into an MCP client config as shown above.
Testing
npm test # builds all packages, runs the trust-layer's unit tests (18 tests)Unit tests cover the trust layer in isolation (provenance/schema-diffing,
confirmation gating, namespacing, audit log). The larger claim — that this
actually works against the real WebMCP ecosystem — is backed by validation
against all 168 live sites in the public
webmcp.com directory plus a hand-built adversarial
test case, documented in full in
docs/testing-and-validation.md. That
process is what found the cross-origin invocation bug shown in the diagram
above — it was not caught by unit tests or the synthetic adversarial test
alone, only by testing against real, unmodified production sites.
Reproducible via the scripts in research/tools/.
Versions pinned
@playwright/mcp@0.0.79@modelcontextprotocol/sdk@^1.30.0
Further reading
docs/trust-boundary-design.md— the full trust-boundary design document: why WebMCP tools are a different trust boundary, the provenance/schema-diff model, the confirmation-gating design, namespacing, the audit log, and what this project is explicitly not.docs/testing-and-validation.md— the consolidated testing report: methodology, every bug found and fixed with evidence, the full 168-site directory crawl results, and honestly-stated limitations.research/README.md— index of the primary-source research this project was built on (WebMCP spec state, MCP protocol details, prior art, upstream internals) and the raw validation data/tools.research/decision-log.md— architecture decisions and the reasoning behind them (fork vs. proxy, detection property names, confirmation mechanism, provenance persistence, etc.).
License
Apache-2.0 — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Find, vet, and run MCP tools through a secure audited gateway with prompt-injection risk scoring
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Give any MCP-compatible AI assistant a builder for live, hosted web tools and workflows.
Render, verify, describe, and safely edit Mermaid diagrams through MCP.
Related MCP Servers
- FlicenseBqualityDmaintenancePlaywright wrapper for MCP that enables LLM-powered clients to control a browser for automation tasks.101-
- FlicenseNot gradedqualityDmaintenanceExposes Playwright browser automation as MCP tools, enabling AI assistants to control a real browser tab-by-tab for form filling, navigation, and more, while preserving the user's active session.-
- AlicenseNot gradedqualityBmaintenanceEnables autonomous web QA by exposing Playwright browser control as MCP tools for navigation, accessibility snapshotting, interaction, and bug detection.2 npmMIT
- FlicenseBqualityCmaintenanceExtends @playwright/mcp with 22 additional tools for web automation, direct uploads, element interactions, and advanced scraping workflows in AI agents.46-