Skip to main content
Glama

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 elicitation support): 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.json ask/allow/deny rules, or Cursor's allowlist/auto-run approvals — keyed on the webmcp:* tool name. You should configure an ask (or equivalent) rule for webmcp:* 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:#fff

webmcp-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 own browser_evaluate tool. 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 detected webmcp:* tools gated by the trust layer, and after every browser_navigate/tab-switch, runs the detection probe and logs/caches the result.

The trust gate, per webmcp:* call

flowchart TD
    Call["Agent calls webmcp:&lt;tool&gt;"] --> 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 --> Audit

Install / build (from source)

npm install
npm run build

Run

node packages/webmcp-guard/dist/cli.js --browser chrome

Or 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.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.

  • A paid remote MCP for AI agent browser DevTools MCP, built to return verdicts, receipts, usage logs,

  • Screenshot, diff, audit and sitemap-capture any web page — 5 MCP tools for AI agents.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SARTHAK2511/webmcp-guard'

If you have feedback or need assistance with the MCP directory API, please join our Discord server