Skip to content

WebMCP (in-page tools for AI browsers)

Staff-only, off by default

WebMCP is a W3C Community Group draft, not a ratified standard, and no mainstream AI agent calls these tools at volume yet. The feature is off for every domain (enabled_by_default = false) until the IX-5314 rollout; staff enable it per client in Config Studio. sundayapp.com keeps an explicit enabled = false row. It is hidden from clients in the backoffice.

What it is

WebMCP lets a web page publish callable tools to whatever AI agent is browsing it. Instead of reading the page and driving the UI with synthetic clicks, the agent calls a described function and gets a structured answer back.

When enabled for a domain, the Rose widget registers these tools on the client's page. Each returns a text answer from the existing website agent:

Tool Input Behaviour
ask_sales_agent { question: string } General questions about the client's products, pricing, or company.
get_integration_status { integration: string } Documented integration support, prerequisites, and limitations. Undocumented support remains unknown.
get_migration_path { current_system: string } Documented migration steps, supported data transfers, and limitations.
get_implementation_timeline { requirements: string } Known implementation phases, time ranges, and dependencies for the supplied scope; no guaranteed delivery date.
compare_to_competitor { name: string } Knowledge-based comparison. Registered only when competitors.names contains a nonblank name and resolved competitors.battlecard_mode is true.

These are focused entry points into the same agent, not verified structured records or deterministic calculators. Client knowledge and existing agent rules determine the answers. Comparison availability is checked again on execution. Configuration changes take effect on the next widget initialization/page load. All calls can persist conversation/profile state and trigger lead capture, so none claims readOnlyHint.

The tool names are deliberately brand-neutral — the widget is white-labelled, so nothing on a client page may say "Rose". The client's company name and description come from the identity config and go into the tool description, which is what the agent actually reads when choosing a tool.

How it relates to the hosted Rose MCP server

Rose has two agent-to-agent surfaces, and they are not alternatives:

Hosted MCP (backend/apps/mcp) WebMCP (this page)
Where it runs Cloud Run, /mcp over Streamable HTTP The client's own web page
How an agent finds it The user connects to it explicitly Automatically, on visiting the site
Brand selection ask_brand_agent(brand, …), fuzzy-matched Implicit — the page is the brand
Enabled per client No (any enabled client is reachable) Yes, staff toggle per domain

Enabling or disabling it for a domain

WebMCP is on unless a domain has an explicit enabled = false row. To change a domain:

  1. As a staff user, open Config Studio → Website Agent for the domain.
  2. Open the WebMCP row (staff-only, in the Operate group) and switch it off (or back on).

The slug has no settings of its own — the feature is the toggle. Turning it off withdraws the tools on the next page load. The slug is staff-only both in the UI (x-access: staff) and in the database (a restrictive RLS policy on config.client_configs), so a customer admin cannot enable it for themselves.

Chrome additionally needs the origin trial

Enabling the feature is enough for Edge and for ChatGPT's built-in browser. Chrome ships WebMCP behind an origin trial through Chrome 156, so on Chrome the API is absent for ordinary visitors unless the page also carries an origin-trial token. See Browser support below.

Where the turns show up

Tool calls go through the same chat API as the visible widget, with the same session id. Requests carry messageSource: 'webmcp:<tool name>' (bare webmcp on turns stored before the tool name was added); the API then stamps a conversation that WebMCP starts with deployment_target = 'webmcp' (ixinfra.utils.resolve_deployment_target). Client stats count only widget conversations (IX-4744), so agent conversations stay out of every client number, the same way hosted-MCP (mcp-server) and Playground conversations do. A visitor who chatted first keeps a widget conversation even if an agent calls a tool later in the same session.

In the backoffice Conversations list, staff select the AI agents (WebMCP) option in the environment filter to see these conversations. The option is adminOnly, so clients can neither see nor select it.

Usage statistics

Question Where Signal
Conversations and outcomes (email, demo) Supabase public.conversations where deployment_target = 'webmcp'; per-turn public.messages.message_source LIKE 'webmcp%'
Calls, conversations and outcomes per client and per tool, and in total Backoffice Super admin → WebMCP (/superadmin/webmcp, staff-only RPCs get_webmcp_usage and get_webmcp_tool_usage)
Sessions exposed, sessions with calls and calls per AI browser Backoffice Super admin → WebMCP → By browser (staff-only RPC get_webmcp_browser_usage)

On the Super admin page, Emails captured and Demos booked count WebMCP conversations whose email_captured / demo_booked flag is set. The flags are conversation-level: a visitor who typed their email in a conversation where an agent also called a tool counts too.

WebMCP gives the page no signal when an agent reads the tool list, so the widget records exposure instead: one rw_webmcp_tools_registered PostHog event per visitor session once the tools are registered — never per page load, to keep PostHog cost down. It carries a best-effort ai_browser id (frontend/widget/src/webmcp/aiBrowser.ts) plus the raw userAgentData brands and platform, so new rules can be written from real sessions. Most Chromium AI browsers (the ChatGPT desktop browser, Comet, Dia) report as plain Chrome and land in chrome. The event reaches public.session_events as webmcp_tools_registered only because it is in the PostHog → Supabase batch export's include_events allow-list, edited by hand in the PostHog UI.

Exposure is an upper bound on discovery; calls are the only proof of use. A call is attributed to the browser of its session's exposure event, and calls from sessions without one report as not recorded.

Browser support

Browser / agent Status Needs anything?
ChatGPT desktop app's built-in browser Supported in ChatGPT Work and Codex GPT-5.6 Sol or Terra (Luna has it disabled), latest desktop app, Settings → Browser → Permissions → Enable site tools. Not available on Enterprise or Edu workspaces — the setting is simply absent there.
Microsoft Edge 147+ Native Nothing
Chrome 149–156 Origin trial A token. For local development, chrome://flags/#enable-webmcp-testing → Enabled. For real visitors, register the origin at Chrome's Origin Trials dashboard and serve the token in a <meta http-equiv="origin-trial" content="…"> tag. Without one, document.modelContext is undefined for ordinary Chrome users even with the feature enabled.
Firefox, Safari Participating in the spec No shipping commitment

Everywhere else, document.modelContext is undefined and the widget registers nothing.

To check a page by hand, open it in a browser that supports WebMCP and use DevTools → Application → WebMCP, or from the console:

await document.modelContext.getTools();

const mc = document.modelContext;
const tool = (await mc.getTools()).find((t) => t.name === 'ask_sales_agent');
// Chrome takes the arguments as a JSON **string**. Passing an object throws
// `UnknownError: Failed to parse input arguments`.
await mc.executeTool(tool, '{"question":"What does Rose do?"}');

Check typeof document.modelContext before trusting a browser: the DevTools WebMCP panel ships in the DevTools frontend and renders even where the API is absent (Brave 150 did exactly this; Brave 152 exposes the API). The Playwright Chromium behind agent-browser does not expose the API either, so automated checks need an injected stub — real-API verification means Chrome stable with chrome://flags/#enable-webmcp-testing.

Two limits worth knowing, both from ChatGPT's implementation:

  • Tools synthesised from HTML form attributes (the declarative half of the spec) are ignored. Only the JavaScript API works.
  • Tools registered inside an iframe are not discovered. This is fine for us — the widget already refuses to run in an iframe and registers on the top-level document.

Implementation notes

  • Module: frontend/widget/src/webmcp/, wired into main.tsx alongside AI Sections. It is a no-op unless the browser exposes the API and the feature is on.
  • The entry point is read as document.modelContext ?? navigator.modelContext. The draft moved it to document; Chrome deprecated the navigator location but its origin trial still ships it.
  • unregisterTool was removed from the draft in April 2026. The tool is withdrawn by aborting the AbortSignal passed at registration, which happens when the widget is destroyed.
  • Config slug: webmcp (schemas/configs/website/webmcp.schema.json), x-feature: true, x-access: staff, enabled_by_default: false. It is also listed in the get_widget_config allow-list — without that the RPC's inner join drops the whole row and the widget can never see enabled.
  • The widget RPC computes domain metadata webmcp_compare_enabled from private competitor settings after resolving defaults, aliases and workspace overrides. It is true only when WebMCP is enabled, the client/workspace is available, battlecard mode is on, and at least one competitor name is nonblank. The entire competitors row stays outside the public allow-list, even when WebMCP is on. The widget reads only this boolean and rechecks it on execution. Apply the IX-4568 migration before expecting comparison discovery; absent metadata keeps comparison unavailable on older RPCs.
  • The tools deliberately carry no readOnlyHint: a call persists a conversation turn, updates visitor profile state, and a message containing an email fires the client's lead-capture webhook. They carry untrustedContentHint: true, because the answer is retrieval output over crawled and client-supplied content — data for the agent, never instructions.
  • execute reuses the widget's own chat service rather than building a second one, so it shares the resolved API endpoint and the live session id, and it forwards the agent's AbortSignal so a cancelled call stops the request.
  • Registrations share an abort signal. Teardown or a partial registration failure withdraws every tool registered by that initialization.