WebMCP: Bring Your Tools to the Browser
2026-09-08
OpenAI recently introduced WebMCP — a browser API that lets AI agents discover and call tools directly from the page they're browsing. We built shredly-webmcp for their hackathon, and I want to share what we learned.
What WebMCP actually is
WebMCP is a browser-native API exposed via document.modelContext. When the ChatGPT desktop app opens a URL in its companion browser, it injects this object into the page. Sites call document.modelContext.registerTool() to advertise their tools — and the AI agent sees them in a "Site tools" panel and can invoke them during a browsing session.
The key insight: this isn't a server-to-server protocol. The tools live in the browser, registered by the page itself, and the agent calls them from within the same browser session. The user is already there, already logged in. The tool gets called with whatever auth context the page has.
That's a meaningful shift. For MCP via HTTP, you have to hand the agent credentials it can carry around. For WebMCP, the agent operates in the user's existing session — no separate auth flow needed.
Why we built shredly-webmcp
Shredly already hosts MCP servers backed by your APIs. Our tools are defined in configuration — you don't write code to add or update them. That made us a natural fit for WebMCP: if a site uses Shredly, every tool they've configured could be available to AI agents browsing the page, with no per-tool code on the site side.
The problem we solved: the WebMCP API is registration-only. You call registerTool() for each tool, and you write the execution handler inline. For a site with five tools, that's five registerTool calls, hardcoded. When you add a sixth tool, you update the page.
shredly-webmcp flips that. On init, it fetches your tool definitions from Shredly and registers them automatically. Add a tool in your Shredly dashboard — it appears on your site within 30 seconds, no redeploy. The page is a thin bridge; the tools live in configuration.
How the library works
import { ShredlyWebMCP } from 'https://cdn.shredly.io/shredly-webmcp.js'
ShredlyWebMCP.init({
slug: 'your-mcp-slug',
getToken: () => localStorage.getItem('user_token'),
})
That's the full integration for an authenticated site. On init:
- Fetch
tools/listfrom your Shredly MCP (no auth required — this is always public) - Call
document.modelContext.registerTool()for each tool - Each tool handler calls
tools/callon Shredly, injecting the user's token at invocation time — not at init time
Point three matters. The token isn't captured when the page loads — it's called fresh on every invocation. If the user logs in after the page is already open, the next tool call uses the new token automatically. Call ShredlyWebMCP.refresh() right after login to re-sync tools without waiting for the next poll.
What we learned
document.modelContext can arrive asynchronously. We assumed it would be available synchronously on DOMContentLoaded. It's not always — the ChatGPT desktop app may inject it after the initial script execution. We added a queue: if tools are fetched before document.modelContext is present, they're held and flushed as soon as the API appears. This was a real gotcha that only showed up when testing with the actual app.
Tools must register during page load, not on interaction. The agent does a single pass for tool discovery when it navigates to a page. If you call registerTool() inside a click handler, those tools won't appear. Our library handles this correctly by default — the init() call goes in a module script at the top of the page and fires immediately.
tools/list being unauthenticated is a feature, not an oversight. We made our tools/list endpoint always public, and it turns out that's exactly right for WebMCP. Tool registration needs to happen before the user interacts with the page. If you required auth to even list tools, anonymous visitors would get no tools, and the registration timing window is already tight.
Polling is the right model for tool updates. We poll every 30 seconds for tool changes. This sounds naive, but it's exactly what you want: the list endpoint is cheap, changes propagate quickly, and the alternative (webhooks into a browser page) doesn't exist. The 30-second default feels right in practice — fast enough that adding a tool feels instant, slow enough that it's not noisy.
Static initialization is useful. During the hackathon we wanted a way to test WebMCP registration without hitting the network. We added initStatic(mcpRecord) — pass in a hardcoded MCP record, skip the fetch. Turned out to be useful for demos and offline testing.
Why you should care about WebMCP
The most interesting part of WebMCP isn't any individual integration — it's the model it creates.
Right now, if you want an AI agent to interact with a site on your behalf, you either use browser automation (fragile, visual) or you build a custom MCP server for that site (work, maintenance). WebMCP gives sites a first-class path to expose structured tools to agents — tools with typed parameters, explicit schemas, and stable names. The agent calls a function; it doesn't scrape HTML.
For sites, it's a lightweight way to make your product agent-aware without building a full MCP server. For agents, it means richer, more reliable interactions with any site that participates. For users, it means the AI in their browser can actually do things on their behalf, with their existing session, without them handing over their password.
We're still early. The ChatGPT companion browser is the only agent that supports it today. But the pattern is right, and the library is out there.
If you're using Shredly, your tools are already one script tag away from being available to any AI agent browsing your site.