AI AgentsArticle

How to Add WebMCP to Your Website: Complete Developer Guide

Step-by-step WebMCP developer guide — enable Chrome, register imperative tools, annotate HTML forms, integrate with Next.js/React, test with Model Context Tool Inspector, and ship to production.

TMTalal MehmoodFounder & CEO
20 min read
Featured image for How to Add WebMCP to Your Website: Complete Developer Guide
Cover · AI Agents

You read the concept pieces. You understand why agents should call tools instead of guessing which button is "Checkout." Now you need the implementation path — file by file, API by API, with code you can paste into a real project today.

This is that guide. We walk through enabling WebMCP in Chrome, registering your first imperative tool, converting an HTML form with the declarative API, wiring it into a Next.js or React app, testing with Google's inspector extension, and shipping without breaking users on browsers that do not support the API yet.

If WebMCP is new to you, start with our overview: WebMCP Explained — How to Make Your Website Usable by AI Agents. That article covers the why, WebMCP vs server-side MCP, and when implementation is worth the effort. This post assumes you are ready to write code.

What You Are Building

By the end of this tutorial, your site will:

  • Detect whether document.modelContext exists before calling it
  • Register at least one imperative JavaScript tool with JSON Schema inputs
  • Expose at least one HTML form as a declarative WebMCP tool
  • Return human-readable strings agents can relay to users
  • Handle agent-initiated form submission differently from human clicks
  • Pass a basic inspection test in Chrome's Model Context Tool Inspector

We use vanilla JavaScript for clarity, then show how the same patterns map to React and Next.js — the stack we use at Let Start Design for agent-ready client projects.

Prerequisites

  • Chrome 149+ for origin trial support, or Chrome with the local testing flag enabled
  • A site served over HTTPS (or localhost for development)
  • Origin isolation — WebMCP is disabled if document.domain is set or origin-agent-cluster is broken. Standard modern deployments are fine.
  • Basic comfort with JSON Schema and async JavaScript

Official reference: WebMCP documentation on Chrome for Developers.

Step 1: Enable WebMCP in Chrome

For local development, enable the testing flag:

  1. Open chrome://flags/#enable-webmcp-testing
  2. Set Enable WebMCP testing to Enabled
  3. Relaunch Chrome

For staging or production experiments, join the WebMCP origin trial (Chrome 149+). Track API changes on GitHub and the API proposal.

Step 2: Add Feature Detection (Non-Negotiable)

WebMCP is progressive enhancement. Your site must work when the API is absent.

function supportsWebMCP() {
  return typeof document !== 'undefined'
    && 'modelContext' in document
    && typeof document.modelContext.registerTool === 'function';
}

async function initWebMCP() {
  if (!supportsWebMCP()) {
    console.info('[WebMCP] API not available — skipping tool registration.');
    return;
  }
  await registerSiteTools();
}

Call initWebMCP() after your app shell mounts. In Next.js App Router, use a client component — never register tools during SSR, because document does not exist on the server.

Step 3: Install TypeScript Types (Optional but Recommended)

npm install webmcp-types --save-dev

Add a reference in your global types or tsconfig so document.modelContext autocompletes correctly. Validate JSON Schema payloads during development with our free JSON Formatter & Validator — mistyped schemas are the number-one reason tools fail inspection.

Step 4: Register Your First Imperative Tool

Start with one read-only tool. Read-only tools are safer to ship first — they cannot accidentally mutate state while you are learning the API.

async function registerSiteTools() {
  await document.modelContext.registerTool({
    name: 'search_help_articles',
    description:
      'Search the help center by keyword. Use when the user asks how to do something on the site. Returns article titles and URLs.',
    inputSchema: {
      type: 'object',
      properties: {
        query: {
          type: 'string',
          description: 'Search phrase, e.g. "reset password" or "billing invoice"',
        },
        limit: {
          type: 'integer',
          minimum: 1,
          maximum: 10,
          description: 'Max results to return. Defaults to 5.',
        },
      },
      required: ['query'],
    },
    annotations: {
      readOnlyHint: true,
    },
    execute: async ({ query, limit = 5 }, { signal }) => {
      const res = await fetch(
        `/api/help-search?q=${encodeURIComponent(query)}&limit=${limit}`,
        { signal }
      );
      if (!res.ok) throw new Error(`Search failed: ${res.status}`);
      const articles = await res.json();
      if (!articles.length) {
        return `No help articles matched "${query}". Suggest the user contact support.`;
      }
      const lines = articles.map((a) => `- ${a.title}: ${a.url}`);
      return `Found ${articles.length} article(s) for "${query}":\n${lines.join('\n')}`;
    },
  });
}

Patterns that matter here — straight from Google's WebMCP best practices:

  • One job per tool. Do not combine search + delete + export in one callback.
  • Verb-led names. search_help_articles beats helpTool.
  • Positive descriptions. Say what the tool does, not "don't use for weather."
  • Specific return strings. Agents quote your output back to users.
  • Pass signal to fetch. Cancels in-flight requests when the user aborts.
WebMCP implementation workflow from Chrome setup to production monitoring
Image source: Generated for Let Start Design (AI-generated editorial image for this blog post). Not stock photography.

Step 5: Add a Write Tool With Security Annotations

Once read-only tools pass testing, add a tool that changes state — but annotate it honestly.

await document.modelContext.registerTool({
  name: 'add_item_to_quote',
  description:
    'Add a catalog SKU to the user quote cart. Use after confirming product and quantity with the user.',
  inputSchema: {
    type: 'object',
    properties: {
      sku: { type: 'string', description: 'Product SKU, e.g. WP-HOST-01' },
      quantity: { type: 'integer', minimum: 1, maximum: 99 },
    },
    required: ['sku', 'quantity'],
  },
  annotations: {
    readOnlyHint: false,
    consequentialHint: true,
  },
  execute: async ({ sku, quantity }) => {
    const item = await addToQuoteCart(sku, quantity);
    updateQuoteDrawerUI(item);
    return `Added ${quantity}× ${item.name} (${sku}). Quote subtotal: $${item.subtotal}.`;
  },
});

consequentialHint: true tells the browser this action may need explicit user confirmation before execution — similar to how payment flows should never run silently. Read Google's WebMCP security guidance before exposing account, billing, or deletion tools.

Step 6: Declarative API — Annotate an HTML Form

Contact forms, support tickets, and newsletter signups are ideal declarative candidates. You already have the markup — add three attributes.

<form
  id="contact-form"
  toolname="submit_contact_inquiry"
  tooldescription="Send a contact message to the Let Start Design team. Use when the user wants a quote, demo, or general inquiry."
  action="/api/contact"
  method="post"
>
  <label for="name">Name</label>
  <input id="name" name="name" type="text" required
    toolparamdescription="Full name of the person submitting the inquiry." />

  <label for="email">Email</label>
  <input id="email" name="email" type="email" required
    toolparamdescription="Reply-to email address." />

  <label for="service">Service needed</label>
  <select id="service" name="service" required
    toolparamdescription="Primary service the user is interested in.">
    <option value="web-design">Web design</option>
    <option value="wordpress">WordPress development</option>
    <option value="seo">SEO services</option>
    <option value="white-label">White label partnership</option>
  </select>

  <label for="message">Message</label>
  <textarea id="message" name="message" required
    toolparamdescription="Project details, timeline, and budget if known."></textarea>

  <button type="submit">Send message</button>
</form>

Handle agent vs human submission in JavaScript:

document.getElementById('contact-form').addEventListener('submit', async (e) => {
  if (!e.agentInvoked) return; // normal human submit — browser default

  e.preventDefault();
  if (!validateContactForm()) {
    e.respondWith(Promise.resolve('Validation failed: email and message are required.'));
    return;
  }

  const formData = new FormData(e.target);
  e.respondWith(
    fetch('/api/contact', { method: 'POST', body: formData })
      .then((r) => r.json())
      .then((data) => `Inquiry sent. Reference ID: ${data.id}. Team will reply within 1 business day.`)
      .catch(() => 'Submission failed. Ask the user to try again or email hello@letstartdesign.com directly.')
  );
});

Listen for lifecycle events so your UI reflects agent activity:

window.addEventListener('toolactivated', ({ toolName }) => {
  document.body.dataset.activeTool = toolName;
});

window.addEventListener('toolcancel', ({ toolName }) => {
  delete document.body.dataset.activeTool;
  showToast(`Agent cancelled: ${toolName}`);
});

Style agent focus with :tool-form-active and :tool-submit-active pseudo-classes so users see which form the agent is operating — details in the Declarative API docs.

Step 7: Next.js / React Integration

In App Router, isolate WebMCP in a client component:

'use client';

import { useEffect } from 'react';

export function WebMCPProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    initWebMCP().catch(console.error);
  }, []);

  return <>{children}</>;
}

Mount WebMCPProvider in your root layout. For route-specific tools (e.g. catalog page vs checkout), register on mount and unregister on unmount:

useEffect(() => {
  if (!supportsWebMCP()) return;
  const controller = new AbortController();

  document.modelContext.registerTool(catalogTool, { signal: controller.signal });

  return () => controller.abort(); // clean unregister
}, []);

React teams can also try the experimental usewebmcp package — it ties registration to component lifecycle with schema-driven types. Angular has similar experimental support. Our web development team evaluates these bindings per project; vanilla registration stays closest to the spec and ages better while the API is in flux.

Rebuilding a legacy site? If you are migrating platforms while adding WebMCP, coordinate with our SEO-safe migration guide so redirects and agent tools launch together.

Step 8: SPA State — When Tools Change by Route

Single-page apps expose different actions on different views. Do not register checkout tools on the homepage — it confuses agents and wastes context window tokens.

Two approaches:

  • AbortController per route — register tools in useEffect, abort on leave (shown above).
  • Full context refresh — call provideContext({ tools: [...] }) when route state changes (resets all tools at once).

Listen for toolchange on document.modelContext if your agent UI needs to refresh its tool list when the page updates registrations dynamically.

Step 9: Cross-Origin Iframes and Widgets

If tools live inside an embedded widget (booking calendar, payment frame), you need both:

<iframe src="https://partner.example/widget" allow="tools"></iframe>

And on the iframe origin, expose tools explicitly:

await document.modelContext.registerTool(widgetTool, {
  exposedTo: ['https://yourdomain.com'],
});

Then the parent page discovers them with:

const tools = await document.modelContext.getTools({
  fromOrigins: ['https://partner.example'],
});

Treat exposedTo like CORS — only list origins you trust.

Step 10: Test With the Model Context Tool Inspector

Install Chrome's Model Context Tool Inspector extension (linked from the WebMCP docs). It lets you:

  • See registered tools and their JSON Schema on the live page
  • Manually invoke executeTool with test arguments
  • Chat in natural language and verify the agent picks the right tool
  • Read error messages when schema validation fails

Minimum test suite before merge:

  1. Call each tool with only required fields
  2. Call with an invalid enum value — confirm your execute handler or browser rejects it cleanly
  3. Cancel mid-fetch — confirm AbortSignal stops the request
  4. Navigate away in an SPA — confirm tools unregister (no stale tools in inspector)
  5. Submit declarative form as agent — confirm agentInvoked branch runs and UI updates

Google recommends evaluation-driven testing for agent outputs — define expected input/output contracts rather than brittle string matches. That aligns with how we test structured data: clear schemas, observable results. See our guide on schema markup for SEO for the same philosophy applied to search.

Step 11: WordPress and Existing CMS Sites

WordPress sites can add WebMCP without rebuilding the theme:

  • Declarative first — add toolname / tooldescription to Contact Form 7 or native forms in template files.
  • Imperative via child theme — enqueue a small webmcp-tools.js that registers WooCommerce cart/search tools calling existing AJAX handlers.
  • Avoid duplicate logic — tool callbacks should call the same functions your buttons already use.

Our WordPress development team typically ships a must-use plugin with tool definitions separated from theme CSS — so agent support survives theme updates.

Common Errors and Fixes

Tools not appearing in inspector
Check origin isolation, HTTPS, and the Chrome flag. Confirm you called registerTool after hydration, not during SSR.

Schema validation failures
Enums must match exactly. Required arrays must list every mandatory property. Paste your schema into a validator — our JSON Formatter catches trailing commas and type mistakes fast.

Agent picks the wrong tool
Tool overlap. Merge similar tools or tighten descriptions. Google explicitly warns overlapping tools confuse model selection.

UI does not update after tool runs
Your execute callback mutated backend state but not DOM. Agents may call again, thinking the first attempt failed. Always mirror human-click code paths.

Cross-origin iframe tools missing
Missing allow="tools" on iframe or missing exposedTo / fromOrigins pairing.

Production Checklist

  • ☐ Feature detection — site works without WebMCP
  • ☐ Tool strategy doc — one function per tool, no overlap
  • ☐ Read-only tools tested before write tools
  • consequentialHint on purchases, deletes, sends
  • untrustedContentHint on user-generated tool output
  • ☐ SPA routes register/unregister tools correctly
  • ☐ Agent form styling (:tool-form-active) visible to users
  • ☐ Analytics event for agentInvoked submissions
  • ☐ Origin trial token on staging (if applicable)
  • ☐ Internal runbook for support team — what agents can and cannot do

WebMCP and SEO — What Developers Should Know

WebMCP does not replace crawl SEO. Agents still discover your site through search, links, and brand — same as humans. But sites with clean information architecture, fast Core Web Vitals, and valid structured data tend to implement WebMCP more cleanly because they already think in schemas.

Keep publishing helpful content, maintain your XML sitemap, and use FAQ schema where appropriate — our FAQ Schema Generator helps. For the strategic picture on agent-ready sites vs ranking, read WebMCP Explained.

When to Hire Help

This guide covers a single-site implementation. Enterprise scenarios — multi-tenant SaaS, complex auth, PCI-adjacent checkout, white-label partner embeds — need architecture review before tool registration.

Let Start Design implements WebMCP on Next.js, WordPress, and headless commerce stacks: tool mapping workshops, secure annotation review, inspector-based QA, and launch monitoring. View recent work or request a scoping call.

Related Reading

Web development · WordPress development · Free developer tools

Frequently asked questions

06 on file

Enable chrome://flags/#enable-webmcp-testing and relaunch Chrome, or join the WebMCP origin trial for staging and production experiments on Chrome 149+.

No. WebMCP tools run in the browser page. Your execute callbacks can call existing fetch handlers or form endpoints — the same APIs your UI already uses.

Yes. Register tools in a client component useEffect after checking document.modelContext exists. Never register during server-side rendering.

Imperative uses document.modelContext.registerTool() with JavaScript callbacks. Declarative adds toolname and tooldescription attributes to HTML forms — the browser builds the JSON Schema from form fields.

Use Chrome's Model Context Tool Inspector extension to view registered tools, manually execute them, and verify natural-language agent prompts invoke the correct tool with valid arguments.

Yes. Let Start Design adds WebMCP to Next.js, WordPress, and custom web apps — tool strategy, secure annotations, cross-origin iframe setup, and inspector-based QA.

Continue

Adjacent reads

Talal Mehmood portrait

Written by

Talal Mehmood

Founder & CEO

BSCS student from Pakistan. Freelancing since 2018 across web development, marketing, SEO, and finance. Founder of Let Start Design.

View author page
Signal // Leave a noteOpen

Join the conversation

Thoughts, pushback, or a win from applying this — we read every note.

Be constructive. Links welcome when relevant.

Thread

00 comments

  • No comments yet — be the first signal.
Free · 30 min

Ready when you are. Let’s talk.

No pitch deck — just a clear next step for your project.

Book Consultation