Google Web MCP: making your website agent-ready

By · · AI Engineering

So I've been messing around with MCP servers since late last year. Backend ones mostly, the kind where you spin up a Python or Node process and let an LLM call tools over stdio. Works well for server-side things. But every time I watched an AI agent try to do something in a browser, I'd cringe a little.

You know the drill. The agent takes a screenshot, squints at it with a vision model, guesses where to click, types something into what it hopes is a search field, and then the site loads a cookie banner that covers everything. Beautiful.

AI trying to navigate a website

Anyway, Google put out something called WebMCP. It's a proposed browser standard, still early, but the idea got me interested enough to write about it. Websites tell AI agents what they can do through structured tool definitions, and agents call those tools directly. No scraping, no pixel-guessing, no praying.

What even is this?

If you've used regular MCP, you already get it. Regular MCP connects models to backend servers. WebMCP does the same thing but in the browser, between an agent and a live web page you have open.

A website registers tools. Could be through HTML attributes on a form, could be through a JavaScript API. Either way, those tools only exist while you're on that tab. Navigate away and they're gone. An AI agent running in the browser — maybe a Chrome extension, maybe some built-in assistant — sees the tools and can call them.

It uses JSON-RPC 2.0 under the hood, JSON Schema for tool definitions. If you've built an MCP server before, you'll recognize the shapes immediately.

Trying it out

Still early preview. You need Chrome Canary 146 or newer.

  1. Grab Chrome Canary if you don't have it
  2. Go to chrome://flags
  3. Search "WebMCP", flip it on
  4. Restart

That's the whole setup. No npm, no server process, nothing to configure.

Two ways to register tools

HTML attributes

This is the lazy version and I mean that as a compliment. You stick some attributes on a regular HTML form and the browser picks it up as a tool.

<form
  toolname="search_products"
  tooldescription="Search the product catalog by keyword"
  action="/api/search"
>
  <label for="query">Search</label>
  <input
    type="text"
    name="query"
    toolparamtitle="Search query"
    toolparamdescription="What to search for, e.g. 'wireless headphones'"
  />
  <input
    type="number"
    name="max_results"
    value="10"
    toolparamtitle="Max results"
    toolparamdescription="Maximum number of results to return"
  />
  <button type="submit">Search</button>
</form>

The form still works fine for regular users. They don't see the toolname or toolparam* attributes at all. Humans get a search box, agents get a callable tool with typed parameters. Same HTML, two audiences.

It just works

JavaScript API

For anything that isn't a form submission you'll need navigator.modelContext. More code, but you get full control over when tools show up and what they actually do.

const modelContext = navigator.modelContext;

modelContext.registerTool({
  name: "add_to_cart",
  description: "Add a product to the shopping cart by name",
  inputSchema: {
    type: "object",
    properties: {
      productName: {
        type: "string",
        description: "Product name, e.g. 'MacBook Pro' or 'AirPods Max'"
      },
      quantity: {
        type: "number",
        description: "How many to add",
        default: 1
      }
    },
    required: ["productName"]
  },
  execute: async ({ productName, quantity = 1 }) => {
    const product = catalog.find(
      p => p.name.toLowerCase() === productName.toLowerCase()
    );

    if (!product) {
      return {
        content: [{ type: "text", text: `Could not find "${productName}"` }]
      };
    }

    cart.add(product, quantity);

    return {
      content: [{
        type: "text",
        text: `Added ${quantity}x ${product.name} to cart. Total: ${cart.total()}`
      }]
    };
  }
});

The execute function runs right there in the page context. It can touch your app state, DOM, API clients, whatever you need. Agent calls the tool, your code runs, result goes back.

To remove a tool while the user is still on the page:

modelContext.unregisterTool("add_to_cart");

Though honestly, most of the time you won't need to call this manually since tools disappear when the tab navigates away.

React integration

In React you probably want tools that show up and disappear based on component state. I played around with a cart component that only exposes checkout when there's actually stuff in it:

import { useEffect } from "react";

function Cart({ items, onCheckout }) {
  useEffect(() => {
    if (items.length === 0) return;

    const modelContext = navigator.modelContext;
    if (!modelContext) return;

    modelContext.registerTool({
      name: "checkout_cart",
      description: `Complete purchase for ${items.length} item(s) in cart`,
      inputSchema: {
        type: "object",
        properties: {
          confirm: {
            type: "boolean",
            description: "Confirm the purchase"
          }
        },
        required: ["confirm"]
      },
      execute: async ({ confirm }) => {
        if (!confirm) {
          return { content: [{ type: "text", text: "Checkout cancelled." }] };
        }
        const result = await onCheckout();
        return {
          content: [{ type: "text", text: `Order placed. ID: ${result.orderId}` }]
        };
      }
    });

    return () => modelContext.unregisterTool("checkout_cart");
  }, [items.length, onCheckout]);

  return (
    <div>
      <h2>Cart ({items.length})</h2>
      {items.map(item => (
        <div key={item.id}>{item.name} - ${item.price}</div>
      ))}
    </div>
  );
}

What I really like here — and I didn't plan this when I started writing the component — is that the security model just falls out naturally. The agent can't check out an empty cart because the tool literally doesn't exist yet. You don't have to write validation for that case. It's just gone.

Vue version

This blog runs on Vue so I'd feel weird not including a Composition API example. Put this in a composable:

import { watch, onUnmounted } from "vue";

export function useAgentTool(toolDef, isActive) {
  watch(isActive, (active) => {
    const ctx = navigator.modelContext;
    if (!ctx) return;

    if (active) {
      ctx.registerTool(toolDef);
    } else {
      ctx.unregisterTool(toolDef.name);
    }
  }, { immediate: true });

  onUnmounted(() => {
    const ctx = navigator.modelContext;
    if (ctx) ctx.unregisterTool(toolDef.name);
  });
}

Then use it:

const cartHasItems = computed(() => cart.value.length > 0);

useAgentTool({
  name: "checkout",
  description: "Complete the purchase",
  inputSchema: { type: "object", properties: {} },
  execute: async () => {
    await submitOrder();
    return { content: [{ type: "text", text: "Done." }] };
  }
}, cartHasItems);

Pretty clean. Might turn this into a small package if I end up using it enough.

The problem this is actually solving

So right now, when an AI agent needs to do something on a website, it picks from a few bad options:

Approach How it works What goes wrong
DOM scraping Parse HTML, guess what buttons do Breaks on every UI change
Screenshot + vision Take a picture, ask a vision model Slow, expensive, unreliable
Reverse-engineer APIs Sniff the fetch calls, replay them Fragile, auth headaches, undocumented

WebMCP replaces all of that with a contract. The website says "I have a search_flights tool that takes origin, destination, and date" and the agent just calls it.

That's the difference between an agent that can book a flight and an agent that can probably book a flight if the airline hasn't changed their CSS class names since last Tuesday.

Works on my machine

Where does this actually make sense?

E-commerce is the obvious one. Agent searches products, compares prices, adds stuff to cart, checks out. Through declared tools instead of Playwright scripts that shatter when someone moves a button 10 pixels to the right. If you've ever maintained a browser automation test suite, you know the pain.

Travel booking too. Compare search_flights({ origin: "JFK", destination: "NRT", date: "2026-05-15", max_stops: 1 }) with "click the From field, type JFK, wait for the dropdown to appear, hope it doesn't open a date picker by mistake..." yeah.

I keep thinking about internal tools though. The company admin panel that everyone hates using. If it exposed WebMCP tools for the common stuff — filtering reports, exporting CSVs, toggling feature flags — you could just ask an agent to do it. Nobody needs to build a separate API, you just annotate what's already there.

Same idea works for support pages, documentation sites, really anything where an agent currently has to parse through a bunch of nav menus and sidebars to find one piece of information. Let the site just hand it over.

How this relates to backend MCP

I wrote about building a web scraping MCP server a few days ago, and someone asked me how WebMCP fits in. It doesn't replace backend MCP. They're different layers.

Backend MCP handles databases, file systems, external APIs. WebMCP handles what's happening in the browser tab. An agent could use both at once — pull data from a backend MCP server, then use a WebMCP tool on the page to fill in a form with that data. They're not competing, they're two halves of the same thing.

Google's other MCP stuff

Side note — Google also has 24+ regular backend MCP servers for their cloud services. BigQuery, Maps, Compute Engine, Workspace, bunch of others. Those are normal MCP servers, not WebMCP. Totally separate.

But if you want Google Search in your agent right now, today, without waiting for WebMCP to mature, there are community servers that wire up Gemini's search grounding:

{
  "mcpServers": {
    "google-search": {
      "command": "npx",
      "args": ["-y", "mcp-gemini-google-search"],
      "env": {
        "GEMINI_API_KEY": "your-api-key-here"
      }
    }
  }
}

Stick that in your claude_desktop_config.json and you get a google_search tool. Not related to WebMCP really, but useful if you need web search in your agent workflow now rather than later.

Where this might go

The thing I keep coming back to is that websites could become APIs without anyone actually building an API. Some small business running WordPress adds toolname attributes to their contact form, their booking form, their product search — and now agents can use them. No backend changes. Nobody needs to hire a developer to build a REST API, they just need someone who can add HTML attributes.

And agents stop being so fragile. Right now if a website redesigns, every automation script pointed at it breaks. That's the number one reason browser automation is a mess. With WebMCP, the agent doesn't care what the site looks like. It calls the tool contract. Redesign all you want, as long as search_products still takes a query param and returns results, everything keeps working.

I also wonder about composability. Agent visits five different websites in sequence, discovers the tools on each one, chains them together into a workflow. Search flights here, book a hotel there, grab a restaurant reservation from somewhere else. All typed tool calls instead of clicking around.

Oh and the "AI wrapper" startups. There are a lot of them right now, and most of them are glorified screen scrapers sitting on top of existing websites. If those sites start exposing their capabilities as WebMCP tools... the wrapper doesn't really have a reason to exist anymore. Brutal for them but probably good for everyone else.

My job here is done

One more thing that matters — WebMCP is going through the W3C Web Machine Learning Community Group for standardization. If it actually becomes a web standard, every browser gets it. Not just Chrome. That'd change things pretty significantly. Google says they expect it in stable Chrome around Google I/O 2026, and the Canary preview is up now if you want to poke at it.

If you want to try it

Pick two or three actions people actually do on your site. Add toolname and tooldescription attributes to those forms. For stuff that isn't a form, use navigator.modelContext.registerTool. Test in Chrome Canary.

You don't need to rebuild anything. It sits on top of your existing site.

Wrapping up

Honestly the concept is almost too simple. Websites know what they can do. They just never had a way to tell an AI agent about it. That's it, that's the whole pitch for WebMCP.

The backend MCP world has been moving fast. WebMCP brings the same thinking to the browser. I'm going to try adding these attributes to a project or two this week and see if it actually feels as clean as the examples suggest. Could go either way — things that look neat in blog posts don't always survive contact with real code. But I'm cautiously into it.

If you end up trying it, I'd genuinely like to hear how it goes.