# How to implement WebMCP in any app

> A WebMCP tutorial from a real production rollout: working code, the auth design, tool limits, and the bugs a ChatGPT agent found in our first version.

- **Author**: Rishi Mohan
- **Published**: 2026-09-01
- **Tags**: WebMCP, AI Agents, Engineering, MCP, Developer Tools
- **Read time**: 10 min read
- **URL**: https://orshot.com/blog/webmcp-implementation

---

This tweet kept doing rounds on my timeline last week:

I run Orshot. We spent months connecting it to agents the other way around, through a hosted MCP server that [Claude, ChatGPT, Cursor and Grok](https://orshot.com/agents) all talk to. WebMCP reverses the direction. The agent is already in your browser, it lands on your site, and the site hands it tools.

I implemented WebMCP over a weekend and shipped it to production. A ChatGPT agent has used it, audited it, and found real bugs in my first version.

This is the tutorial I wish existed when I started. I'll use our app as the example, but every step applies to any web app. Most of what ranks for WebMCP right now is theory. This isn't.

**TL;DR** — WebMCP, implemented and shipped. Pick what you came for:

- **The short version**: [22 tools on every page, zero connector setup](#what-is-webmcp) — 6 public tools for anyone plus 16 workspace tools bridged from our hosted MCP server when you are signed in. The login itself is the authorization.
- **Show me the code**: [Both providers, trimmed from the real source](#step-1-expose-public-tools-for-every-visitor) — Registration on document.modelContext, same-origin cookie-less fetches, and the session-to-token bridge that reuses our MCP server verbatim.
- **What broke**: [ChatGPT audited us and found real bugs](#step-4-have-an-agent-audit-your-implementation) — 90 registered tools tripped a config limit that disabled WebMCP entirely, plus validation gaps like open_template({}) navigating to /templates/undefined.
- **Is my site agent-ready?**: [Test with a real runtime, not a checker](#how-to-check-if-your-website-is-ai-agent-ready) — Ask ChatGPT agent mode what tools your page exposes, or run document.modelContext.getTools() in a flagged Chrome. Static scanners cannot see imperative tools.

## What is WebMCP?

WebMCP is a W3C standard, pushed mainly by Google and Microsoft. Your page calls `document.modelContext.registerTool()` with a name, a description, a JSON schema and an execute function.

That's the whole idea. Instead of an agent screenshotting your UI and guessing what "Add to cart" does, your page hands it an instruction manual: here are my tools, here's what they take, here's what they return.

## WebMCP vs MCP vs browser automation

The question I see most on Reddit is some version of "how is this different from Playwright, or from just running an MCP server?" Different layers, different jobs:

|  | Browser automation (Playwright, computer use) | MCP (hosted server) | WebMCP (on-page) |
| :-- | :-: | :-: | :-: |
| How the agent acts | Clicks your human UI, guesses from pixels | Calls your backend from anywhere | Calls tools your page registers |
| Setup for the user | None, but slow and brittle | Connector + OAuth flow | None. Tools appear on page load |
| Auth | Whatever session the browser has | OAuth tokens in the client | Your existing login session |
| Reliability | Breaks when your UI changes | Stable | Stable, and UI-independent |
| Where it shines | Sites that expose nothing | Automation and workflows | In-browser help while you browse |

Two takeaways from shipping all three:

- WebMCP doesn't replace your MCP server. Ours does automation from anywhere; WebMCP serves the person already on the site. Ship both if you can.
- Compared to browser automation, WebMCP is dramatically more token-efficient. The agent reads a tool list once instead of parsing screenshots on every step.

## Who supports WebMCP right now

The consumer side moved fast in the last two weeks:

- **ChatGPT**: agent mode consumes site tools in the in-app and cloud browser (GPT-5.6 Sol and Terra). Click the cursor icon in the in-app browser's URL bar and expand "Available site tools" to see what a page exposes. OpenAI has [docs for site builders](https://learn.chatgpt.com/docs/webmcp "nofollow target=_blank").
- **Chrome**: API behind a flag (`chrome://flags/#enable-webmcp-testing`) with an origin trial running. Gemini in Chrome is the intended consumer, and the Chrome team published [implementation guides](https://developer.chrome.com/docs/ai/webmcp "nofollow target=_blank") this week.
- **Edge**: co-authored the spec with Google; experimental support tracks Chrome.
- **Claude**: not yet. Anthropic's extension still works from screenshots and DOM snapshots. When that changes, WebMCP sites need zero extra work.

The Chrome team shipped guides for the three things you'll actually need: writing secure tools, building the tools themselves, and evaluating whether an agent uses them correctly.

On the consumer side, OpenAI moved WebMCP support from the desktop app's in-app browser into the cloud browser, which is what agent mode uses when it goes off and works on its own.

Two weeks ago none of this existed. That's the whole reason the field is still open.

## Step 1: expose public tools for every visitor

Start with what a logged-out visitor can do. For us that's six tools on every page:

- `search_templates` and `get_template_details` search our [template library](https://orshot.com/templates) and return each template's render parameters
- `open_template` navigates to a template page
- `get_pricing` returns the live [pricing page](https://orshot.com/pricing) as markdown
- `read_page_markdown` fetches any page of ours as markdown, which turns the whole docs site into agent context
- `connect_agent` points the agent at our [hosted MCP server](https://orshot.com/blog/orshot-remote-mcp-server) for everything WebMCP can't do

The provider, trimmed to what matters:

```jsx
// webmcp-provider.jsx (trimmed)
const TOOLS = [
  {
    name: "search_templates",
    description:
      "Search Orshot's library of 2,000+ image, PDF, and video templates.",
    inputSchema: {
      type: "object",
      additionalProperties: false,
      properties: {
        query: { type: "string" },
        category: { type: "string" },
        limit: { type: "number" },
      },
    },
    annotations: { readOnlyHint: true },
    execute: async ({ query, limit } = {}) => {
      const size = Math.min(Math.max(Math.trunc(Number(limit)) || 10, 1), 30);
      const params = new URLSearchParams({ pageSize: String(size) });
      if (query) params.set("search", query);
      const res = await toolFetch(`/api/templates/community?${params}`);
      const data = await res.json();
      return { count: data.templates.length, templates: data.templates.map(compact) };
    },
  },
  // ...5 more tools
];

export function WebMCPProvider() {
  useEffect(() => {
    const modelContext =
      document.modelContext || navigator.modelContext || null;
    if (!modelContext?.registerTool) return; // no-op in normal browsers

    const controller = new AbortController();
    for (const tool of TOOLS) {
      swallowAbort(
        modelContext.registerTool(tool, { signal: controller.signal }),
      );
    }
    return () => controller.abort();
  }, []);
  return null;
}
```

Three things in that snippet I only know because they went wrong:

1. **It's `document.modelContext` now.** The spec moved off `navigator.modelContext` in July 2026. My first provider registered on `navigator` and no runtime ever saw a single tool. If you copied WebMCP example code from a post older than a few weeks, check this first.
2. **OpenAI requires `additionalProperties: false`** on every input schema. It's in their docs, it's easy to miss, and nothing warns you.
3. **`registerTool` returns a Promise** that rejects with an AbortError when its signal fires on unmount. We didn't catch it at first. Every route change dumped a batch of unhandled rejections into error tracking.

Security rules we set for this tier:

- Every tool fetch goes through a `new URL()` same-origin check
- Every request is sent with `credentials: "omit"`, so logged-out tools cannot return logged-in data even if we mess something else up

If you want to skip the typing, hand this to Claude Code, Cursor, Codex or whatever agent already has your repo open. It encodes every gotcha in this post, including the ones that cost me a day.

[Watch the video](https://orshot.com/blog/webmcp-implementation/diagram-how-it-works.mp4)

## Step 2: let the login be the integration

Here's the part I haven't seen any other site ship. If you're signed in, the page bridges tools from our hosted MCP server onto `document.modelContext`. The agent in your browser can render images, list your templates, read your brand kit and prepare social drafts.

No connector. No API key. No OAuth screen. Our funnel data says most people who start an MCP connector flow in ChatGPT never finish it. WebMCP has no flow to abandon.

We didn't rewrite a single tool for this. Our MCP server already answers stateless HTTP requests (that's how ChatGPT's MCP client talks to it), so the bridge discovers and proxies:

```jsx
// webmcp-bridge-provider.jsx (trimmed)
const mint = async () => {
  // Trade the user's session for a short-lived, workspace-scoped token.
  // First-party client, 30-minute expiry, no refresh token, memory only.
  const res = await fetch(`${API_URL}/v1/oauth/browser-agent-token`, {
    method: "POST",
    headers: { Authorization: `Bearer ${sessionToken}` },
    body: JSON.stringify({ client_id: CLIENT_ID, workspace_ids: [workspaceId] }),
  });
  return (await res.json()).access_token;
};

// Discover once per tab, register the curated set, proxy execution
const { tools } = await mcpRequest("tools/list", {}, await bearer());
for (const t of tools.filter((t) => BRIDGED_TOOL_NAMES.has(t.name))) {
  modelContext.registerTool({
    ...t,
    execute: (args) => mcpRequest("tools/call", { name: t.name, arguments: args }, bearer),
  });
}
```

Why this design:

- The MCP server stays the single source of truth. Names, schemas and annotations pass through as-is.
- Tokens are scoped to your active workspace, live only in page memory, and expire after 30 minutes. Logging out kills them.
- There's no consent screen on purpose. Consent screens protect you from third parties. Here the client, the authorization server and the API are all us. "Allow Orshot to access Orshot?" is not a question.

[Watch the video](https://orshot.com/blog/webmcp-implementation/diagram-login-integration.mp4)

## Step 3: register fewer tools than you want to

My first bridge registered all 90 of our MCP tools, several with long descriptions. ChatGPT's response:

> WebMCP is disabled for this page because the site's WebMCP configuration exceeds supported limits.

Not degraded. Off. Including the public six. It also explained a flaky symptom I'd been ignoring, where tools appeared for a second and vanished once the app hydrated.

What works:

- A curated set covering the loop people actually run. For us: find a template, read its parameters, render, publish a draft. 16 workspace tools.
- Descriptions capped at 1,500 characters.
- 22 tools per page, total. Everything else stays on the MCP connector.

## Step 4: have an agent audit your implementation

Before a human reviews your WebMCP, ask ChatGPT agent mode to use it, then to audit it. I asked it to find a LinkedIn carousel template through WebMCP. It did, with our own `search_templates` tool:

![ChatGPT confirming the site supports the WebMCP standard and listing the six public tools it discovered on the templates page](https://orshot.com/blog/webmcp-implementation/chatgpt-tool-discovery.png)

Then I asked for a full audit:

![ChatGPT's audit finding that workspace pages hit WebMCP configuration limits, with the exact error and the list of affected URLs](https://orshot.com/blog/webmcp-implementation/chatgpt-audit-gaps.png)

Besides the tool-count limit, it caught:

- `open_template({})` navigated to `/templates/undefined`. Runtimes don't all enforce your schema's `required` list, so validate inside execute.
- `search_templates({ limit: -1 })` returned a 500. A clamp I should have written on day one.
- `get_template_details` promised parameters in its description and didn't return them. Agents notice when a tool under-delivers its own description, and they say so.

Every fix shipped the same day, and the same prompt re-run confirmed them. Best QA I didn't pay for, and its bug report came with exact URLs and repro steps.

## How to check if your website is AI-agent ready

In order of usefulness:

1. **Ask an agent.** Open your site in ChatGPT agent mode and ask what tools the current page exposes.
2. **Ask the browser.** In Chrome with the flag on, run `await document.modelContext.getTools()` in the console.
3. **Run the ecosystem verifiers.** The [webmcp.com directory](https://webmcp.com "nofollow target=_blank") scans implementations and lists the exact tools a site exposes. The [webmcp-kit](https://github.com/nekuda-ai/webmcp-kit "nofollow target=_blank") plugin ships a headless verification driver.
4. **Test both auth states.** Logged out should never show account tools. Signed in should actually survive registration (see step 3).

One warning: skip the HTML-level "AI readiness checkers". One scored our site 28/100 for having "no navigator.modelContext integration" on the same day ChatGPT was actively calling our 22 tools. Imperative WebMCP registration is invisible to static scanners. They're grading a different thing and selling you the fix.

## Does WebMCP help SEO/GEO discovery?

Not directly. Tools only exist once an agent is already on your page. WebMCP converts agent visits, it doesn't create them.

The second-order effects are real though:

- Agents come back to sites where tasks complete.
- The directories that list real implementations are exactly the kind of source generative engines cite, and the list of production sites is still short enough that shipping gets you on it.
- Pages describing your agent support (ours is [Orshot for browser agents](https://orshot.com/agents/for-browser-agents)) do the discovery work WebMCP itself can't.

**Q: What is WebMCP in one sentence?**

WebMCP is a W3C browser standard that lets a web page register callable, schema-described tools on document.modelContext so the AI agent in a visitor's browser can act on the site directly instead of scraping pixels.

**Q: Do websites need to support WebMCP for agents to use them?**

For WebMCP specifically, yes: the site has to register tools. Agents can still drive non-WebMCP sites through screenshots and DOM automation, just slower and less reliably. That fallback is exactly why adding tools is worth it: your site becomes the one where the agent's task actually completes.

**Q: Why not just publish an API instead?**

An API needs the agent to know it exists, hold credentials, and be set up ahead of time. WebMCP tools are discovered on the page, run in the visitor's existing session, and can reflect the current page state. In practice you want both, and our WebMCP tools are thin wrappers over the same APIs.

**Q: Which browsers and agents support WebMCP today?**

ChatGPT's browser consumes site tools in agent mode (GPT-5.6 Sol and Terra). Chrome ships the API behind a flag with an origin trial running, with Gemini in Chrome as the intended consumer. Edge tracks Chrome. Claude does not consume WebMCP yet.

**Q: How many tools should a page register?**

Fewer than you think. ChatGPT disabled WebMCP entirely when we registered 90 tools with long descriptions. We settled on 22 per page: 6 public plus 16 workspace tools, with descriptions capped at 1,500 characters.

**Q: Do WebMCP tools need their own auth system?**

No, and they should not have one. Reuse your existing session. Our signed-in tools exchange the session for a short-lived, workspace-scoped token from a first-party OAuth client, so agents act with exactly the permissions the logged-in user already has.

**Q: How do I check if my website is AI-agent ready?**

Open it in ChatGPT agent mode and ask what tools the page exposes, or run document.modelContext.getTools() in a WebMCP-enabled Chrome. Runtime checks beat static checkers, which cannot see imperative tool registration at all.

## The bottom line

The whole implementation took about two days, and only because the tools wrap APIs and an MCP server we already had. If you have real user actions and an existing API, WebMCP is a weekend of work. The consuming agents are already here, and the list of production sites is short enough that you'd be early.

If you'd rather use the app we made agent-ready than build one, Orshot generates on-brand images, PDFs and videos from templates, automatically. [Start free and see plans](https://orshot.com/pricing). 30 free credits, no credit card.