My Sister Asked an AI Agent to Book Her Flight. I Watched It Suffer.
Priyanka, my younger sister, needed a flight home for Diwali. She'd read that AI agents can book flights now, so she handed the job to one and went to make chai.
I stayed and watched.
It took a screenshot. Squinted at it. Clicked. Waited. Took another screenshot. Clicked the wrong thing. Tried again.
It worked. Eventually. Painfully.
When Priyanka came back with two cups, she asked, "Done?" I said, "Almost." She sat down, watched for a minute and said something I haven't stopped thinking about: "Why is it using the website like my grandmother does?"
She was more right than she knew.
Why the Agent Looked So Lost
If you've watched a browser-based AI agent fill out a form, move around a SaaS dashboard or book a flight, you've probably noticed two things. It's mind-blowing when it works. It's also painfully slow, fragile and absurdly expensive.
The reason took me a while to accept. Today's browser agents are basically over-engineered web scrapers. They take screenshots, parse deep HTML trees and guess which button to click based on visual heuristics.
The loop Priyanka's agent was stuck in:
- Capture a screenshot or fetch the raw DOM tree.
- Send megabytes of visual or structural data to a multimodal LLM.
- Predict pixel coordinates or CSS selectors to simulate mouse clicks and keystrokes.
- Re-capture the UI to check if it worked, then repeat.
+-----------------------------------------------------------------------+
| Traditional "Browser Use" |
| |
| [LLM] <---> Screenshots / Raw DOM <---> Mouse / Keyboard Emulation |
| * High latency * High token cost * Fragile visual heuristics |
+-----------------------------------------------------------------------+
We built the web for eyes and hands. Agents have neither, so they fake it. That fakery costs you in three ways:
- Slow: every click is a round trip through a big model.
- Expensive: screenshots and raw HTML burn thousands of tokens just to find one input box.
- Fragile: a layout shift, a changed utility CSS class or a dynamic class name breaks the loop. The agent that worked 9 times fails on the 10th.
My first instinct as a developer was, "Fine, just give it a backend MCP server." Those are great for databases and APIs. But they need extra infrastructure, separate API keys or OAuth tokens, and database proxies. They also skip the UI entirely and bypass the session the user is already logged into.
So Priyanka would have watched a screen where nothing happens, and then trusted that her flight got booked. I don't think she'd have liked that. I wouldn't.
Then I Read About WebMCP
I'll be honest: when I first saw the name, I assumed it was another scraping trick with a better vision model bolted on. It isn't.
WebMCP is NOT a scraping framework, a smarter vision model or another backend server you have to deploy and babysit.
WebMCP IS a proposed web standard that lets your website hand AI agents a clean list of structured tools they can call directly, right inside the browser.
It's being incubated at the W3C, with the Chrome and Edge teams co-developing it.
Instead of an agent guessing what a button does, the flight site would say: "Here's a tool called book_flight. Here's the input it needs. Here's what it returns."
The agent calls it. The app runs it. Done.
In human terms: Today, agents use your site like a tourist pointing at a menu in a foreign language. WebMCP hands them a translated menu with prices.
That line made me laugh, because it's Priyanka's grandmother comment, just said more politely.
How It Actually Works
The example that clicked for me was a grocery list, mostly because Priyanka and I share one and she never adds anything to it.
- Your page registers tools with a name, description and JSON Schema.
- The agent discovers them when it visits the page.
- The user asks in plain language: "Add sourdough bread to my grocery list."
-
The agent calls the right tool (
addGroceryItem) with structured data. - Your app runs its normal logic and the item appears on screen, instantly.
The part I underestimated at first: because it runs in the user's active tab, it inherits their logged-in session. No separate auth layer. And the user watches every change happen, so they stay in control.
That fixes both of my complaints about the backend server approach in one move.
What WebMCP changes, compared to the loop above:
- Deterministic execution: agents call strictly typed JavaScript functions instead of simulating click events.
-
Zero infrastructure overhead: tools execute in the active tab's client-side context via the
navigator.modelContextAPI. No external backend MCP server needed. - Massive token savings: agents receive clean JSON schemas instead of full HTML layouts, which cuts token costs dramatically.
TL;DR: WebMCP turns your website into an API for AI agents, without taking the website away from humans.
Under the Hood
WebMCP exposes structured tools inside the browser runtime through client-side JavaScript. It uses the navigator.modelContext object to set up a structured channel between the web page, the browser runtime and the AI agent.
+-----------------------------------------------------------------------+
| WebMCP |
| |
| [LLM Agent] <---> navigator.modelContext <---> Client State (React) |
| * Typed JSON * Zero backend setup * Direct function execution |
+-----------------------------------------------------------------------+
There are two ways to integrate:
- Declarative API: automatically exposes existing standard HTML forms and input elements to the browser's model context, with no custom business logic.
- Imperative API: you explicitly declare tools with JSON Schemas and JavaScript execution handlers.
Here's what an imperative registration looks like inside a React component:
// Example client-side WebMCP Tool Registration
useEffect(() => {
if (typeof navigator !== "undefined" && navigator.modelContext) {
const controller = new AbortController();
navigator.modelContext.registerTool({
name: "add_grocery_item",
title: "Add Grocery Item",
description: "Adds an item to the user's active shopping list",
inputSchema: {
type: "object",
properties: {
itemName: { type: "string" },
quantity: { type: "number" }
},
required: ["itemName"]
},
annotations: {
readOnlyHint: false, // Indicates state mutation
untrustedContentHint: true // Treats output strictly as data to prevent prompt injection
},
execute: async (args) => {
return await addItemToState(args.itemName, args.quantity);
}
}, { signal: controller.signal });
return () => controller.abort(); // Clean up upon unmount
}
}, []);
What each piece does:
-
navigator.modelContext: the browser API registry where client-side scripts bind tools. - Input Schema: JSON Schema definitions for required parameters and string/number constraints.
-
Annotations: security and execution hints.
readOnlyHinttells the model whether an action mutates state.untrustedContentHintmakes sure returned data is treated purely as content, not as prompt instructions. -
Lifecycle cleanup: an
AbortControllersignal insideuseEffectunregisters the tool when the component unmounts.
The annotations were the part I had to read twice. Prompt injection through tool output is a real worry, and seeing it handled at the registration level made me trust the design a bit more.
Side by Side
| Dimension | Visual / DOM Agents | Backend MCP Servers | WebMCP (Client-Side) |
|---|---|---|---|
| Execution Layer | Browser Vision / Pixels | Remote Web Server | Browser DOM / Tab Context |
| Data Format | Unstructured DOM / Images | Structured JSON | Structured JSON Schemas |
| Authentication | Re-uses active session cookies | API Keys / OAuth Tokens | Inherits Active Tab Session Context |
| UI State Visibility | Full human view | Headless (No UI) | Synchronous UI & State Updates |
| Developer Setup | Zero (Scraper-based) | High (Server infrastructure) | Low (Front-end JS hooks) |
Look at the last column. It keeps the good parts of both: the visible UI and session of browser agents, plus the structured data of backend servers.
Where This Gets Interesting
Think about the most complex UI you use. A 3D modeling tool. A photo editor with 28 sliders. A SaaS dashboard with filters buried five menus deep.
-
Kanban and task management: instead of opening cards and picking dropdowns, the agent calls
moveCard,setPriorityandcreateTask. "Move this card to Building and set it to high priority" happens in one shot. - Data dashboards: ask "Compare quarterly revenue by channel" and the agent calls chart-rendering functions directly, no clicks from you.
-
Creative and technical suites: say "make this photo brighter" and the agent calls
setExposure(+0.8). In a web 3D tool, it callsadd3DBlock. That lowers the learning curve for people who aren't experts.
That's the promise: users express intent, the website does the work.
Priyanka's flight booking would have been a single book_flight call. She wouldn't have finished her chai before it was done.
I Tried It That Weekend
WebMCP is early, and part of me is still cautious. It's a proposed standard, and framework support is experimental. But you can play with it right now:
-
Local dev: open
chrome://flags/#enable-webmcp-testing, set it to Enabled and relaunch Chrome. - Production testing: join the WebMCP origin trial (Chrome 149+).
- Debug: install the Model Context Tool Inspector extension to see registered tools, call them manually and validate your schemas.
-
Using a framework? React (
usewebmcp) and Angular both have experimental support.
Here's a first tool, in under 15 lines:
await document.modelContext.registerTool({
name: 'add_grocery_item',
description: 'Add an item to the shopping list.',
inputSchema: {
type: 'object',
properties: { item: { type: 'string' } },
required: ['item'],
},
execute: async ({ item }) => {
addItem(item); // the same function your "Add" button uses
return `Added ${item} to the list.`;
},
});
Notice the trick? You reuse the logic your buttons already call. No rewrite needed.
My advice after going through it: start small. Pick the 5 to 10 actions users repeat most and turn those into tools.
What I Told Priyanka
- Agents today guess. Screenshots and DOM scraping are slow, pricey and fragile.
- WebMCP lets your site declare its abilities as typed, callable tools.
- It runs in the browser, inherits the user's session and keeps the UI visible.
- You can try it today with a Chrome flag and a few lines of JavaScript.
She nodded, then asked if I could make our grocery app do this so she'd finally add things to the list. I said I'd try. She still won't, but at least the agent will.
Stop making agents pretend to be human. Give them a menu.
Two things to confirm before you publish:
-
The two code samples disagree. The React example uses
navigator.modelContext, and the "first tool" example usesdocument.modelContext. I kept both exactly as they were in your raw data. Which one is correct, so I can make them consistent? - The story details are made up (Priyanka, Diwali, the shared grocery list). No technical claims were added. If you'd rather use a different framing, like a teammate or a client, tell me and I'll swap it throughout.
Letβs share some love by sharing it with your friends and all those who need to read this blog. If you have any question, suggestion, feedback or queries you can ask me anytime. I would be happy to help you all.
I love to make new connection and friends. Please be in loop by connecting with me via following links.
Connect with me
- π¦ X / Twitter
- πΈ Instagram
- πΌ LinkedIn



Top comments (0)