# webmcp-bridge

A tiny stdio MCP server that lets Claude call the WebMCP tools of whatever page is open in your Chrome. The page registers tools with `document.modelContext.registerTool()`; the bridge reads them over Chrome's DevTools protocol and republishes each one as an MCP tool with the same name, description and input schema. When Claude calls a tool, the bridge runs `document.modelContext.executeTool(tool, input)` in the tab and returns the result as text.

No dependencies: it needs Node 22 or newer (built-in WebSocket client) and a Chrome with WebMCP enabled (Chrome 149+ origin trial, or any recent Chrome with the flag below).

## 1. Launch Chrome with WebMCP and a DevTools port

On macOS, from a terminal:

```bash
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --enable-blink-features=WebMCP --remote-debugging-port=9222 --user-data-dir="$HOME/chrome-webmcp-profile" https://webmcp-audit.com/demo
```

A separate `--user-data-dir` is required: Chrome refuses remote debugging on the default profile, and a throwaway profile keeps your real sign-ins away from the debugging port (anything on your machine that can reach port 9222 can drive that browser).

GUI alternative to the launch flag: open `chrome://flags/#enable-webmcp-testing`, set it to Enabled and relaunch. You still need `--remote-debugging-port=9222 --user-data-dir=...` on the command line for the bridge to connect.

## 2. Register the bridge

Claude Code (replace the path with your checkout):

```bash
claude mcp add webmcp -- node /ABSOLUTE/PATH/TO/webmcp/bridge/webmcp-bridge.js
```

Claude Desktop: add this to `~/Library/Application Support/Claude/claude_desktop_config.json` and restart the app. Use the absolute path to `node` (`which node`), because GUI apps do not see your shell's PATH.

```json
{
  "mcpServers": {
    "webmcp": {
      "command": "/usr/local/bin/node",
      "args": ["/ABSOLUTE/PATH/TO/webmcp/bridge/webmcp-bridge.js"],
      "env": { "WEBMCP_BRIDGE_URL": "https://webmcp-audit.com/demo" }
    }
  }
}
```

Environment variables (all optional):

| Variable | Meaning | Default |
|---|---|---|
| `WEBMCP_BRIDGE_CDP_URL` | DevTools HTTP endpoint | `http://127.0.0.1:9222` |
| `WEBMCP_BRIDGE_URL` | Use the tab whose URL starts with this value instead of the most recently focused tab | unset |
| `WEBMCP_BRIDGE_DEBUG` | `1` for verbose logging on stderr | unset |

The tool list is read from the page on every `tools/list`, so after navigating to another page, ask Claude to list the tools again (in Claude Code: `/mcp`, then reconnect the server, or just ask).

## 3. Walkthrough on the demo page

1. Launch Chrome with the command in step 1. The demo page's status line should read "document.modelContext is exposed here: both tools are registered in this tab." If it says the API is not exposed, the flag is not on: check `chrome://version` shows `--enable-blink-features=WebMCP` in the command line.
2. Start Claude Code in any folder where the bridge is registered and run `/mcp`. The `webmcp` server lists two tools: `search_docs` and `start_audit`.
3. Ask Claude: "Use the search_docs tool with query audit."
4. Claude calls `search_docs` with `{ "query": "audit" }` and the page answers:

```json
{"results":[{"title":"Audit a site","url":"https://webmcp-audit.com/","section":"audit"}]}
```

The call ran inside the browser tab, with the page's own JavaScript, cookies and session. The second tool, `start_audit`, POSTs to the site's `/api/audit` from the page, so calling it really queues an audit.

Without any Claude client, you can also talk to the bridge by hand:

```bash
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"shell","version":"0"}}}' '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_docs","arguments":{"query":"audit"}}}' | node bridge/webmcp-bridge.js
```

## Errors you may see

- "Cannot reach Chrome's DevTools endpoint": Chrome is not running with `--remote-debugging-port=9222`, or it is on another port (set `WEBMCP_BRIDGE_CDP_URL`).
- "DevTools endpoint ... answered HTTP 404": another program already owns port 9222 (on a machine where the main Chrome holds it, this is what you get). Launch Chrome with another port, for example `--remote-debugging-port=9333`, and set `WEBMCP_BRIDGE_CDP_URL=http://127.0.0.1:9333`.
- "No page tab is open": open the page first, or set `WEBMCP_BRIDGE_URL` to pick the right tab.
- "document.modelContext is not available in this tab": the WebMCP flag is off in that Chrome, or the tab is a page without the API (internal pages, the new-tab page).
- "Unknown tool ... Registered tools: ...": the page changed since the last `tools/list`; list again.

## Tests

```bash
node --test bridge/test/*.test.js
```

The tests cover the schema translation (string and object `inputSchema`) and the full list/call path against a fake `document.modelContext` that, like Chrome, rejects anything but the `RegisteredTool` object from `getTools()`. No browser is needed.
