ImplementationTutorialJavaScript

Implementing WebMCP: A Step-by-Step Developer Guide

S
Sarah Mitchell
··15 min read

Prerequisites

Before implementing WebMCP, ensure your environment meets these requirements:

  • Your site is served over HTTPS (WebMCP is only available in secure contexts).
  • Chrome 146+ Canary with the "WebMCP for testing" flag enabled, or the @mcp-b/webmcp-polyfill package for broader browser support.
  • Familiarity with JSON Schema for defining tool input parameters.

Step 1: Feature Detection

Always check for WebMCP availability before attempting to register tools:

if ('modelContext' in navigator) {
  // WebMCP is available
  initializeTools();
} else {
  console.log('WebMCP not supported in this browser');
}

Step 2: Your First Tool

Let us start with a simple product search tool for an e-commerce site:

async function initializeTools() {
  await navigator.modelContext.registerTool({
    name: "search_products",
    description: "Search the product catalog by keyword, category, and price range",
    inputSchema: {
      type: "object",
      properties: {
        query: {
          type: "string",
          description: "Search keywords"
        },
        category: {
          type: "string",
          enum: ["electronics", "clothing", "books", "home"],
          description: "Product category filter"
        },
        maxPrice: {
          type: "number",
          description: "Maximum price in USD"
        }
      },
      required: ["query"]
    },
    execute: async (params) => {
      const response = await fetch(`/api/products?q=${encodeURIComponent(params.query)}&cat=${params.category || ''}&max=${params.maxPrice || ''}`);
      const data = await response.json();
      return {
        count: data.products.length,
        products: data.products.map(p => ({
          name: p.name,
          price: p.price,
          rating: p.rating
        }))
      };
    }
  });
}

Step 3: Declarative Forms

For existing HTML forms, WebMCP offers a zero-JavaScript approach:

<form toolname="contact_support"
      tooldescription="Submit a customer support request"
      method="POST"
      action="/api/support">
  <label>
    Subject
    <input name="subject" type="text" required />
  </label>
  <label>
    Priority
    <select name="priority">
      <option value="low">Low</option>
      <option value="medium">Medium</option>
      <option value="high">High</option>
    </select>
  </label>
  <label>
    Description
    <textarea name="description" required></textarea>
  </label>
  <button type="submit">Submit</button>
</form>

The browser automatically infers the tool schema from form fields. The SubmitEvent.agentInvoked flag lets your server-side code distinguish between human and agent submissions.

Step 4: Multi-Step Workflows

Complex workflows like booking a flight involve multiple sequential tools:

// Step 1: Search
await navigator.modelContext.registerTool({
  name: "search_flights",
  description: "Search available flights",
  inputSchema: { /* ... */ },
  execute: async (params) => {
    const flights = await searchFlights(params);
    return { flights, sessionId: crypto.randomUUID() };
  }
});

// Step 2: Select and book
await navigator.modelContext.registerTool({
  name: "book_flight",
  description: "Book a selected flight. Requires a flight ID from search results.",
  inputSchema: {
    type: "object",
    properties: {
      flightId: { type: "string" },
      passengers: {
        type: "array",
        items: {
          type: "object",
          properties: {
            firstName: { type: "string" },
            lastName: { type: "string" }
          },
          required: ["firstName", "lastName"]
        }
      }
    },
    required: ["flightId", "passengers"]
  },
  execute: async (params, client) => {
    // Request user confirmation for sensitive action
    const confirmed = await client.requestUserInput(
      `Confirm booking flight ${params.flightId} for ${params.passengers.length} passenger(s)?`
    );
    if (!confirmed) return { status: "cancelled" };

    const booking = await createBooking(params);
    return { confirmation: booking.confirmationNumber };
  }
});

Step 5: Error Handling

Robust error handling is essential for production WebMCP tools:

await navigator.modelContext.registerTool({
  name: "update_cart",
  description: "Add or remove items from the shopping cart",
  inputSchema: { /* ... */ },
  execute: async (params) => {
    try {
      const result = await updateCart(params);
      return { success: true, cart: result };
    } catch (error) {
      if (error instanceof AuthError) {
        return {
          success: false,
          error: "authentication_required",
          message: "User must log in to modify cart"
        };
      }
      return {
        success: false,
        error: "internal_error",
        message: "Failed to update cart. Please try again."
      };
    }
  }
});

Step 6: Cleanup and Lifecycle

Always clean up tools when they are no longer relevant:

// Remove a specific tool
navigator.modelContext.unregisterTool("search_flights");

// Clear all tools (e.g., on page transition in an SPA)
navigator.modelContext.clearContext();

// Re-register tools after SPA navigation
window.addEventListener('popstate', () => {
  navigator.modelContext.clearContext();
  registerToolsForCurrentPage();
});

Using the Polyfill

For production use before native browser support, use the MCP-B polyfill:

npm install @mcp-b/webmcp-polyfill
import '@mcp-b/webmcp-polyfill';

// The polyfill auto-detects native support.
// If the browser already has navigator.modelContext,
// the polyfill does nothing.
navigator.modelContext.registerTool({ /* ... */ });

Production Checklist

  • Always validate inputs server-side, even though the schema validates client-side.
  • Use credentials: 'same-origin' on all fetch calls within tool handlers.
  • Only register tools for authenticated users where appropriate.
  • Return structured error objects, not thrown exceptions.
  • Log SubmitEvent.agentInvoked for analytics and audit trails.
  • Test with the Chrome Model Context Tool Inspector extension.

Related Articles

Newsletter

Stay ahead of the curve

Get expert WebMCP insights, implementation guides, and ecosystem updates delivered to your inbox. No spam, unsubscribe anytime.