@mcp-b/webmcp-local-relay
v2.1.0
Published
Local MCP relay for browser WebMCP tools via embed iframe and localhost WebSocket
Maintainers
Readme
WebMCP Local Relay
Use WebMCP tools from any website, right inside your AI client.
Browser Tab Local Machine
┌──────────────────────┐ ┌──────────────────────┐
│ │ WebSocket │ │
│ Website with ├────────────▶ webmcp-local-relay │
│ WebMCP tools │ localhost │ (MCP server) │
│ │ │ │
└──────────────────────┘ └──────────┬───────────┘
│
stdio │ JSON-RPC
│
┌──────────▼───────────┐
│ │
│ Claude / Cursor / │
│ any MCP client │
│ │
└──────────────────────┘Open a website that has WebMCP tools. Run the relay. The tools show up in your MCP client.
Install
{
"mcpServers": {
"webmcp-local-relay": {
"command": "npx",
"args": ["-y", "@mcp-b/webmcp-local-relay@latest"]
}
}
}Add this to your MCP client config — works with Claude Desktop, Cursor, Windsurf, Claude Code, or anything that speaks MCP.
Use
Once connected, your AI client can see and call tools from any open browser tab that supports WebMCP:
webmcp_list_sources— see which tabs are connectedwebmcp_list_tools— see all available tools- Call any tool directly by name (e.g.,
create_issue,search_docs)
Tools appear and disappear automatically as you open, reload, and close tabs.
For Website Owners
Add one script tag to expose your page's WebMCP tools to the relay:
<script src="https://cdn.jsdelivr.net/npm/@mcp-b/webmcp-local-relay@latest/dist/browser/embed.js"></script>That's it. If your page already registers tools on navigator.modelContext, they'll be picked up automatically.
New to WebMCP? Here's the full setup:
<script src="https://cdn.jsdelivr.net/npm/@mcp-b/global@latest/dist/index.iife.js"></script>
<script>
navigator.modelContext.registerTool({
name: 'get_page_title',
description: 'Get the current page title',
inputSchema: { type: 'object', properties: {} },
execute: async () => ({ content: [{ type: 'text', text: document.title }] }),
});
</script>
<script src="https://cdn.jsdelivr.net/npm/@mcp-b/webmcp-local-relay@latest/dist/browser/embed.js"></script>Custom relay port:
<script
src="https://cdn.jsdelivr.net/npm/@mcp-b/webmcp-local-relay@latest/dist/browser/embed.js"
data-relay-port="9444"
></script>Tool registration references:
Reference
Other Install Methods
The JSON config above works for most clients. Here are additional options:
Claude Desktop (MCPB bundle) — download the .mcpb file from GitHub Releases and double-click to install. No terminal needed.
Direct CLI — run the relay standalone:
npx @mcp-b/webmcp-local-relayExposed Tools
The relay exposes four static management tools that are always available:
| Tool | Description |
|------|-------------|
| webmcp_list_sources | Lists connected browser tabs with metadata (tab ID, origin, URL, title, icon, tool count) |
| webmcp_list_tools | Lists all relayed tools with source info |
| webmcp_call_tool | Invokes a relayed tool by name with JSON arguments — useful for clients that don't support dynamic tool registration |
| webmcp_open_page | Opens a URL in the user's default browser, or refreshes a connected source page by matching origin |
Dynamic tools are registered directly on the MCP server using the original tool name, sanitized to [a-zA-Z0-9_]. When tools from different tabs share a name, a short tab-ID suffix is appended for disambiguation:
- Single provider:
get_issue - Multiple providers with the same name:
search_ed93,search_a1b2
CLI Options
webmcp-local-relay [options]
--host, -H Bind host (default: 127.0.0.1)
--port, -p WebSocket port (default: 9333)
--widget-origin Allowed host page origin(s), comma-separated (default: *)
--allowed-origin Alias for --widget-origin
--ws-origin Alias for --widget-origin
--help, -h Show helpExamples:
# Default: loopback on port 9333
npx @mcp-b/webmcp-local-relay
# Custom port
npx @mcp-b/webmcp-local-relay --port 9444
# Restrict to tools from trusted host pages
npx @mcp-b/webmcp-local-relay --widget-origin https://myapp.comSecurity
- Binds to
127.0.0.1by default (loopback only, not accessible from your network). - The default
allowedOriginsis*, which permits any browser page to connect and register tools. This is convenient for development but means any website open in your browser can expose tools to the relay. --widget-originvalidates the host page origin reported in the browserhellomessage. This is the origin of the page that loadedembed.js(e.g.,https://myapp.com), regardless of whether the widget iframe is served from CDN or self-hosted.- Recommended: Use
--widget-originto restrict which websites can register tools:# Only allow tools from myapp.com webmcp-local-relay --widget-origin https://myapp.com # Allow multiple origins webmcp-local-relay --widget-origin https://app1.com,https://app2.com - Only the host page origin is checked — any local process can connect regardless of origin restrictions.
Architecture
┌──────────────────────────────────────┐
│ MCP Client │
│ (Claude, Cursor, Windsurf, etc.) │
└──────────────────┬───────────────────┘
│ stdio / JSON-RPC
┌──────────────────▼───────────────────┐
│ LocalRelayMcpServer │
│ webmcp_list_sources │
│ webmcp_list_tools │
│ webmcp_call_tool │
│ + dynamic tools from browser │
└──────────────────┬───────────────────┘
│ WebSocket (ws://127.0.0.1:9333)
┌──────────────────▼───────────────────┐
│ RelayBridgeServer │
│ Manages connections, routes calls │
└──────────────────┬───────────────────┘
│ postMessage
┌──────────────────▼───────────────────┐
│ Widget iframe │
│ embed.js injects widget.html │
└──────────────────┬───────────────────┘
│ navigator.modelContext
┌──────────────────▼───────────────────┐
│ Host page │
│ WebMCP runtime + registered tools │
└──────────────────────────────────────┘How it connects: The embed script injects a hidden iframe into the host page. The iframe opens a WebSocket to the relay on localhost. Tools are discovered via navigator.modelContext (or navigator.modelContextTesting as fallback) and forwarded to the relay, which registers them as standard MCP tools over stdio.
If the relay is temporarily unavailable, the widget reconnects automatically using exponential backoff (1.5x multiplier) from 500ms up to 3000ms, stopping after 100 attempts.
Client mode: When a second relay instance starts and the port is already in use (EADDRINUSE), it automatically falls back to client mode. In client mode the relay connects as a WebSocket client to the existing server relay and proxies tool operations through it. If the server relay later stops, the client attempts to promote itself back to server mode. This enables multiple MCP clients to share the same browser connections without manual configuration.
Runtime Compatibility
Supported page runtimes:
@mcp-b/global(recommended)@mcp-b/webmcp-polyfillwithnavigator.modelContextTesting
Runtime dispatch behavior in the browser embed/widget layer:
- Uses
navigator.modelContext.listTools+callToolwhen present. - Falls back to
navigator.modelContextTesting.listTools+executeTool.
WebMCP Standard Status
WebMCP is an emerging web platform proposal. This relay works today with polyfills and will support native browser implementations as they mature.
For Chromium/Chrome Canary native preview testing:
- Open
chrome://flags/#enable-webmcp-testing - Enable WebMCP for testing
- Restart the browser
Troubleshooting
| Problem | Fix |
|---------|-----|
| No sources connected | Ensure the page loaded embed.js and the relay process is running |
| No tools listed | Ensure tools are registered on the page's WebMCP runtime. If tools register after load, confirm your runtime emits tool-change notifications (toolschanged or registerToolsChangedCallback) |
| Tool not found | Tab reloaded or disconnected — call webmcp_list_tools again to refresh |
| Connection blocked | Verify --widget-origin matches your host page's origin (e.g., https://myapp.com), and relay port matches data-relay-port |
Contributing
Project Layout
src/
├── cli.ts CLI entry point
├── cli-utils.ts CLI argument parsing
├── mcpRelayServer.ts MCP server (stdio + dynamic tool sync)
├── bridgeServer.ts WebSocket relay server
├── registry.ts Multi-source aggregation and deduplication
├── naming.ts Tool name sanitization and namespacing
├── schemas.ts Browser <-> relay protocol schemas
├── browser/embed.js Script-tag loader for website owners
├── browser/widget.html Hidden iframe bridge runtime
└── index.ts Public API exportsBuild and Test
From repository root:
pnpm install
pnpm --filter @mcp-b/webmcp-local-relay build
pnpm --filter @mcp-b/webmcp-local-relay test
pnpm --filter @mcp-b/webmcp-local-relay test:e2eBuild MCPB Bundle
pnpm --filter @mcp-b/webmcp-local-relay build:mcpbProduces webmcp-local-relay-<version>.mcpb for distribution via Claude Desktop.
Plugin and Skill Files
| File | Purpose |
|------|---------|
| .claude-plugin/plugin.json | Claude Code plugin definition |
| .claude-plugin/marketplace.json | Plugin marketplace metadata |
| .mcp.json | MCP server configuration |
| skills/webmcp-local-relay/SKILL.md | Claude Code skill documentation |
| manifest.json | MCPB bundle manifest |
References
- MCP Bundle (MCPB) project
- Claude Code plugins
- Claude Code plugin distribution
- Claude Code skills
- Vercel Agent Skills repo
License
MIT
