Let your local Claude Code drive a web page. No backend, no API keys, no agent SDK.
Each page registers tools with WebMCP
(document.modelContext.registerTool(...)), and Claude Code calls them through a small bridge.
claude ──MCP──► webmcp-bridge ──CDP──► Chrome tab ──► document.modelContext tools
You need Node 22+, Chrome and Claude Code. In the folder you run
claude from, add the bridge once:
claude mcp add webmcp -- npx -y @smarchone/webmcp-bridgeThen ask Claude to open one of the live apps:
claude "Open https://smarchone.github.io/webmcp-apps/dino/ with the webmcp tools and play one game"
claude "Open https://smarchone.github.io/webmcp-apps/maps/ with the webmcp tools and plot the IITs in India"Chrome opens the page and Claude starts using its tools. Each page teaches Claude what to do through its own tools, so there's nothing else to install. For better results, each page's install box has an Advanced section with a detailed prompt and a slash command.
The bridge is a plain stdio MCP server, so it also works with other local agents that support MCP (Codex, Gemini CLI, Cursor, VS Code, Claude Desktop and more). Add it to their MCP config:
{ "mcpServers": { "webmcp": { "command": "npx", "args": ["-y", "@smarchone/webmcp-bridge"] } } }| App | What Claude does | Run it |
|---|---|---|
apps/dino/ |
Plays a dino runner from the game's physics and learns from each death | Live |
apps/maps/ |
Analyses data and plots it on the world's countries and states, and India's districts and sub-districts | Live |
editor/ |
Edits Markdown docs with you and answers your comment threads | From a clone |
apps/: everything GitHub Pages publishes (the project page, dino and maps). A workflow in.github/workflows/pages.ymldeploys it on every push to main.bridge/: the WebMCP bridge, published to npm as@smarchone/webmcp-bridge.editor/: the Markdown editor, which still needs its local server:cd editor && npm start, then runclaudethere and use/review-docsor/address-comments.scripts/maps/: rebuilds the map's boundary data.notes/: WebMCP learnings, the API gotchas we hit.
-
Add a page that registers tools:
await document.modelContext.registerTool({ name: 'do_thing', description: 'What the agent can do here', inputSchema: { type: 'object', properties: {} }, async execute(input) { return { ok: true }; }, });
-
Say in the tool descriptions how Claude should use them, so a plain prompt works.
-
Open it with the bridge.
bridge/README.mdcovers how it works.
