Crystallize logo

WebMCP: tools declared by the storefront's own pages

WebMCP lets a web page expose its own functions as tools to the AI agent in the shopper's browser. Where an MCP server serves remote assistants with no browser session, WebMCP tools run in the shopper's tab with their cookies, market and cart, and the shopper watches the page update as the agent works. WebMCP is in early preview in Chromium-based browsers.

warning

Early preview

WebMCP runs in Chromium-based browsers from version 146.0.7672 with the #enable-webmcp-testing flag, under a draft Community Group specification. The declarative attributes are ignored by browsers without WebMCP, so forms keep working everywhere. Imperative tools must check that document.modelContext exists.

How it relates to the MCP server

MCP server

WebMCP

Agent

Remote assistant (Claude, ChatGPT)

The agent in the shopper's browser

Session

None: cartId, OAuth, hand-off links

The shopper's own: cookies, market, cart

Runs

On your server

In the page, as client-side JavaScript

Calls

Your Discovery and Shop API functions

The same functions, through your server actions and routes

Use the same tool names and arguments on both (search_products, add_to_cart, view_cart), so an agent that knows one surface knows the other.

Declarative tools on forms

Self-contained forms become tools with three attributes. The browser builds the input schema from the form's controls and labels:

<search>
  <form action="/en/search" method="get"
        toolname="search_products"
        tooldescription="Search the catalogue by words, brand or spec, and list matching products."
        toolautosubmit>
    <label for="q">Search</label>
    <input id="q" name="q" type="search" toolparamdescription="For example: 18V impact driver over 200 Nm">
    <button type="submit">Search</button>
  </form>
</search>
  • `toolautosubmit` lets the agent submit without waiting for the shopper. Use it for read-only and easily reversed actions: search, filters, add to basket.
  • Never on checkout. Leave toolautosubmit off forms that place an order or pay. The browser then stops for the shopper to review and submit.
  • `toolparamdescription` explains what a value means, with an example. Put it on a <fieldset> to describe a group of radio buttons, such as the variant choice.
  • The form needs real controls. An add-to-basket form needs a variant fieldset with radio buttons and a labelled <input type="number" name="quantity" min="1">. A quantity shown as text between two buttons can't become a parameter (see Accessibility).

When the agent submits, the submit event carries agentInvoked and respondWith(). Return the result as data instead of navigating, and return validation errors as text the agent can act on:

form.addEventListener('submit', (event) => {
  if (!event.agentInvoked) return;
  event.preventDefault();
  const data = new FormData(event.target);
  event.respondWith(addToCart(data.get('sku'), Number(data.get('quantity')), lang).then((count) => `Added. The basket now has ${count} items.`));
});

Style forms while an agent uses them with the :tool-form-active and :tool-submit-active pseudo-classes, so the shopper can see what is happening.

Imperative tools

Use the imperative API for what isn't a single form: filters made of links, the cart drawer, the product on the page. In React, use useWebMCP from the use-webmcp-tool package rather than calling registerTool from components. Without a framework, register with an AbortSignal and abort it when the page changes:

if ('modelContext' in document) {
  const controller = new AbortController();
  await document.modelContext.registerTool(
    {
      name: 'view_cart',
      description: "Show the shopper's basket: lines, quantities and totals.",
      inputSchema: { type: 'object', properties: {} },
      async execute() {
        openCartDrawer();
        return await cartView(lang);
      },
      annotations: { readOnlyHint: true },
    },
    { signal: controller.signal },
  );
  // On navigation or unmount: controller.abort();
}

A tool set that fits a storefront:

Tool

Page

Hint

filter_products, list_filters

Category, brand

readOnlyHint

get_product

Product

readOnlyHint

view_cart

Every page

readOnlyHint

update_cart_line

Every page

Reversible

book_rental

Rental product

consequentialHint

  • Validate inside `execute`. The browser doesn't check arguments against the schema or apply defaults. Return errors as values ({ error: "…" }) the agent can retry from.
  • Return after the UI has updated, so the agent and the shopper see the same state.
  • Pass the execution's `signal` to `fetch`, so a cancelled call stops its requests.

Before you ship

  • WebMCP tools act with the shopper's session, so cross-site scripting matters more. Send the companion security headers and at least a report-only CSP first (see Security headers).
  • Test tools from a script with document.modelContext.getTools() and executeTool(), and by hand with the Chrome flag on.
note

Background

Chrome's Modern Web Guidance has detailed guides for agentic forms and JavaScript tools, which this page follows.