All posts
Best practicesEngineeringEcosystem

WebMCP form coverage: a practical developer checklist

A field-tested checklist to achieve WebMCP form coverage across your site: what to expose as tools, JSON Schemas to use, guardrails, and test flows that agents can run today.

Agent LayerOctober 7, 202611 min read

You searched for WebMCP form coverage because you want agents to complete your forms reliably—sign‑up, cart, checkout, support requests—without scraping your DOM. By the end of this guide you’ll have a concrete checklist, runnable code samples, and a test plan to reach confident form coverage with WebMCP.

TL;DR

  • Map each high‑intent form (search, sign‑in, add‑to‑cart, checkout, contact) to a WebMCP tool with a precise JSON Schema and a clear description of preconditions and effects.
  • Feature‑detect both document.modelContext and navigator.modelContext, and return small, JSON‑serialisable results.
  • Add guardrails for sensitive actions (payments, account changes): require an in‑page confirmation before execute mutates state.
  • Test with an agent that calls page tools today (ChatGPT desktop / ChatGPT Work), and track broader support on the public WebMCP ecosystem status.
  • Use Agent Layer if you want health checks against UI drift, versioned tool configs, analytics on which agents call tools, and an automated audit across your forms.

What “form coverage” means for WebMCP

Form coverage means an AI agent can: discover the action, understand its inputs and constraints, perform it with the user’s current session, and verify the outcome. You don’t need to expose every input field; you need tools that encapsulate the user intent of the form.

A good first pass covers:

  • Search or filter products/content
  • Authentication (sign‑in) and account updates
  • Cart operations (add, update qty, remove)
  • Checkout start and confirm
  • Support/contact or ticket create

With WebMCP, these become page tools with JSON‑Schema inputs rather than a series of DOM clicks. Tools run in the browser tab, with the user’s session—no extra backend needed.

If you’re comparing standards, see what WebMCP is and how it differs from MCP. Short version: WebMCP is in‑page for websites; MCP is backend servers over stdio/HTTP.

Ecosystem reality check

WebMCP is early but usable. Today, we’ve seen real calls from ChatGPT desktop and ChatGPT Work. Other agents and browsers vary: Chrome and Edge are in origin trials; Brave is behind a flag; Firefox and Safari don’t support it. Frameworks like Vercel agent‑browser and Stagehand work today; others are behind flags or not supported. The tracker changes—always verify current status at the public WebMCP support page.

The developer checklist

1) Inventory your intent surfaces

List the flows with user intent that matter for revenue and support:

  • Discovery: search_products, filter_catalog
  • Commerce: add_to_cart, update_cart, start_checkout, confirm_order
  • Account: sign_in, update_profile
  • Support: create_ticket

For each, decide the minimum inputs you actually need. Keep schemas tight.

2) Register tools with feature detection

Always detect both current and older addresses. Keep results small and explicit.

// webmcp-tools.js
(function registerWebMCPTools() {
  const mc = (typeof document !== 'undefined' && document.modelContext) ||
             (typeof navigator !== 'undefined' && navigator.modelContext);
  if (!mc) return; // No WebMCP support; page still works normally

  // 1) Search products
  mc.registerTool({
    name: 'search_products',
    description: 'Use to search the catalog. Requires a non-empty query. Returns up to 10 result summaries with URLs.',
    inputSchema: {
      type: 'object',
      properties: {
        query: { type: 'string', minLength: 1 },
        limit: { type: 'integer', minimum: 1, maximum: 10, default: 5 }
      },
      required: ['query'],
      additionalProperties: false
    },
    execute: async ({ query, limit = 5 }) => {
      // Use your existing in-page search API or fetch; no secrets involved
      const res = await fetch(`/api/search?q=${encodeURIComponent(query)}&limit=${limit}`);
      const data = await res.json();
      // Return a compact summary the agent can reason over
      return {
        ok: true,
        count: Math.min(data.items.length, limit),
        items: data.items.slice(0, limit).map(i => ({
          id: i.id,
          title: i.title,
          price: i.price,
          url: i.url
        }))
      };
    }
  });

  // 2) Add to cart (non-sensitive but stateful)
  mc.registerTool({
    name: 'add_to_cart',
    description: 'Add a product variant to the cart. Preconditions: variant must be in stock. Side effect: mutates cart state.',
    inputSchema: {
      type: 'object',
      properties: {
        variant_id: { type: 'string', minLength: 1 },
        quantity: { type: 'integer', minimum: 1, default: 1 }
      },
      required: ['variant_id'],
      additionalProperties: false
    },
    execute: async ({ variant_id, quantity = 1 }) => {
      const res = await fetch('/cart/add', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ variant_id, quantity })
      });
      if (!res.ok) return { ok: false, error: 'add_failed' };
      const cart = await res.json();
      return { ok: true, cart_total_items: cart.total_items };
    }
  });
})();

Notes:

  • Describe when to use the tool, required preconditions, and side effects. Agents choose based on descriptions.
  • Keep return payloads compact and factual. Avoid dumping entire DOM or huge arrays.

3) Add guardrails for sensitive actions

Payment, account changes, and destructive actions should prompt the human for confirmation. Implement a lightweight in‑page check the agent cannot bypass.

// Guarded tool example: confirm order requires explicit user click
(function registerCheckoutGuard() {
  const mc = document.modelContext ?? navigator.modelContext;
  if (!mc) return;

  // Simple in-page confirmation modal
  function confirmAction(message) {
    return new Promise(resolve => {
      const ok = window.confirm(message); // Replace with your modal if you prefer
      resolve(ok);
    });
  }

  mc.registerTool({
    name: 'confirm_order',
    description: 'Create and confirm an order for the current cart. Sensitive: requires explicit user confirmation in page.',
    inputSchema: {
      type: 'object',
      properties: {
        shipping_method: { type: 'string', minLength: 1 },
        payment_method_id: { type: 'string', minLength: 1 }
      },
      required: ['shipping_method', 'payment_method_id'],
      additionalProperties: false
    },
    execute: async ({ shipping_method, payment_method_id }) => {
      const agreed = await confirmAction('Place order now?');
      if (!agreed) return { ok: false, error: 'user_declined' };

      // Never pass raw payment tokens through tool params; use stored IDs only
      const res = await fetch('/checkout/confirm', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ shipping_method, payment_method_id })
      });
      if (!res.ok) return { ok: false, error: 'confirm_failed' };
      const order = await res.json();
      return { ok: true, order_id: order.id, status: order.status };
    }
  });
})();

This pattern keeps secrets and raw payment data out of tool parameters and forces an explicit UX checkpoint before mutation.

4) Express the real schema of your form, not the DOM

Your checkout form might have 20 inputs, but an agent only needs the decision‑bearing fields. Keep schemas stable even if the DOM shuffles. Examples:

  • sign_in: { email, password }
  • update_profile: { display_name, phone? }
  • create_ticket: { subject, body, priority? }

Avoid free‑form additionalProperties: true; it invites guesswork.

5) Return verifiable outcomes

Agents then decide next steps. Good responses:

  • { ok: true, cart_total_items: 3 } after add_to_cart
  • { ok: true, results: [...], count: n } after search_products
  • { ok: true, order_id: 'A123', status: 'placed' } after confirm_order

Bad responses:

  • Huge HTML chunks
  • “Success!” without machine‑readable fields

6) Keep descriptions crisp and directive

Describe: when to use; preconditions; side effects; limits. For example:

  • “Use to start checkout. Preconditions: cart has at least one item. Side effect: navigates to checkout page.”
  • “Use to update quantity of an existing cart line. Requires line_id from a prior add_to_cart or get_cart call.”

7) Test with an agent that actually calls tools

As of now, ChatGPT desktop and ChatGPT Work call page tools in our own testing. Broader support is evolving—track at the ecosystem status page. For a systematic process to validate flows, see WebMCP for testing.

A basic manual test loop:

  • Open your site in the ChatGPT desktop app webview.
  • Ask it to “Search for ‘running shoes’, add the cheapest size 10 men’s to cart, and start checkout.”
  • Watch for tool picks, inputs, and results.
  • Iterate on descriptions and schemas until selections are reliable.

8) Instrument and observe

If you use Agent Layer, you get analytics per tool call (which agent, duration, success/error) and test scenarios that assert “intent X picks tool Y.” This reduces guesswork and catches regressions.

9) Plan for drift

DOMs change; your tools shouldn’t. Bind tools to your app logic, not fragile selectors. If a UI change breaks a binding, Agent Layer’s health checks suggest the new selector and let you roll back a config version.

For a step‑by‑step enablement flow, see How to enable WebMCP on your website.

Example: cart and checkout tool set

Below is a compact set that covers the cart funnel end‑to‑end.

(function cartAndCheckoutTools() {
  const mc = document.modelContext ?? navigator.modelContext;
  if (!mc) return;

  mc.registerTool({
    name: 'get_cart',
    description: 'Read current cart summary. No side effects.',
    inputSchema: { type: 'object', properties: {}, additionalProperties: false },
    execute: async () => {
      const res = await fetch('/cart');
      const cart = await res.json();
      return {
        ok: true,
        total_items: cart.total_items,
        lines: cart.lines.map(l => ({ line_id: l.id, title: l.title, qty: l.quantity, price: l.price }))
      };
    }
  });

  mc.registerTool({
    name: 'update_cart_line',
    description: 'Change quantity of a cart line. Requires a valid line id. Side effect: mutates cart.',
    inputSchema: {
      type: 'object',
      properties: {
        line_id: { type: 'string', minLength: 1 },
        quantity: { type: 'integer', minimum: 0 }
      },
      required: ['line_id', 'quantity'],
      additionalProperties: false
    },
    execute: async ({ line_id, quantity }) => {
      const res = await fetch('/cart/update', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ line_id, quantity })
      });
      if (!res.ok) return { ok: false, error: 'update_failed' };
      const cart = await res.json();
      return { ok: true, total_items: cart.total_items };
    }
  });

  mc.registerTool({
    name: 'start_checkout',
    description: 'Begin checkout for the current cart. Preconditions: cart has at least one item. Side effect: may navigate.',
    inputSchema: { type: 'object', properties: {}, additionalProperties: false },
    execute: async () => {
      // Optionally navigate, but also return a machine-readable state
      window.location.assign('/checkout');
      return { ok: true, phase: 'checkout_started' };
    }
  });
})();

Flow of an agent calling a guarded action

The diagram shows how an agent discovers and calls a guarded tool with human confirmation.

Agent discovers tools, calls a guarded tool, user confirms, and the page returns a result

Coverage checklist by funnel stage

Stage Tool name Must accept Must return Guardrail
Discovery search_products query, optional limit items[] {id,title,price,url}, count None
Cart add_to_cart variant_id, optional quantity cart_total_items None
Cart update_cart_line line_id, quantity total_items None
Checkout start_checkout none phase: 'checkout_started' None
Checkout confirm_order shipping_method, payment_method_id order_id, status Explicit user confirmation
Account sign_in email, password ok, user_id or error Consider confirmation if risky

For commerce specifics (e.g., Shopify sections and events), see the practical setups in WebMCP for Shopify and the broader strategy in Agentic commerce for Shopify.

What you can do without Agent Layer

You can hand‑roll everything above: register tools, maintain schemas, and test in agents that support WebMCP. This works, especially for a small surface area.

Where people add Agent Layer is when the surface grows:

  • Health checks catch when a UI change breaks a selector and propose a fix.
  • Guardrails are standardized for sensitive flows.
  • Versioned configs: ship, roll back, and A/B test tool definitions.
  • Analytics: see which agents called what, how often, with which errors.
  • Test scenarios: encode “intent → expected tool” and run them.

Common pitfalls

  • Skipping feature detection: only checking document.modelContext and missing agents that still look at navigator.modelContext.
  • Vague descriptions: “Search the site” without limits or preconditions → agents guess.
  • Oversharing: returning entire cart objects or HTML; keep results small and stable.
  • No guardrails on sensitive actions: letting an agent confirm an order without a user click.
  • Schema drift: tool schemas changing every time the DOM changes; keep schemas stable and decoupled from markup.
  • Hidden required fields: form requires an anti‑bot token or hidden checkbox not represented in the tool → calls fail.
  • Relying on scraping as a fallback: some agents will try, but it’s brittle. See why in Why DOM scraping and vision models are dead for agentic web workflows.
  • Testing in the wrong environment: using an agent or browser that doesn’t support WebMCP yet. Check the ecosystem status first.

What breaks in production

  • UI drift: class or structure changes break bindings that tools rely on. Health checks or alerts catch this early.
  • State assumptions: agents call start_checkout with an empty cart. State preconditions should be checked in execute with clear errors.
  • Rate limiting and bot challenges: CAPTCHAs or strict WAF rules can block in‑page calls, especially during checkout. Prefer first‑party in‑page APIs and keep flows human‑initiated when required.
  • Partial rollouts: staging and prod diverge; a tool works in staging but not prod. Version configs and test both.
  • Multistep ambiguity: multiple tools that could satisfy an intent (e.g., apply_coupon, update_cart_line, start_checkout) without clear descriptions. Disambiguate with “Use when …”.

Try it

Start by running the free agent‑readiness audit on your site. It flags missing WebMCP tools, unlabeled inputs, and other blockers, and suggests page‑by‑page tool definitions. Then test your high‑intent forms with an agent that calls tools today, and watch the ecosystem tracker as support broadens. When you’re ready to harden things—guardrails, drift protection, versioned configs—Agent Layer takes over the maintenance load.

Get started

Don't Be Invisible to Tomorrow's Buyers.

Make your platform executable for every AI assistant your customers already use. No engineering, no lock-in, no breakage.