---
title: "Building AI-Agent-Ready React Apps with react-webmcp"
date: 2026-02-15T12:00:00.000Z
description: "A hands-on guide to react-webmcp, the React library for the WebMCP standard. Learn how to expose structured tools from your React app so AI agents can interact with your UI reliably."
tags: ["webmcp", "react", "ai-agents", "web-standards", "typescript", "model-context-protocol", "chrome", "developer-tools", "open-source"]
tokens: 2981
content-signal: search=yes, ai-input=yes, ai-train=no
---


![React hooks and components powering WebMCP tool registration for AI agents in the browser](/images/posts/react-webmcp-guide/hero.png)

## TL;DR - Key Takeaways

1. **WebMCP** is a W3C-proposed browser API (`navigator.modelContext`) that lets websites register structured tools for AI agents — no more brittle screen-scraping
2. **react-webmcp** is an open-source React library that wraps both the Imperative and Declarative WebMCP APIs with idiomatic hooks and components
3. **Two lines of code** — `useWebMCPTool` registers a tool on mount and cleans it up on unmount, just like any React hook
4. **Declarative components** — `<WebMCPForm>`, `<WebMCPInput>`, `<WebMCPSelect>` turn existing forms into agent-callable tools with zero imperative JS
5. **Available today** behind a flag in Chrome 146 Canary (146.0.7672.0+); the library is on npm as [`react-webmcp`](https://www.npmjs.com/package/react-webmcp)

---

## The Problem: AI Agents and the Web Don't Speak the Same Language

AI agents today interact with websites the same way a blindfolded person navigates a room — by bumping into things. They take screenshots, parse DOM trees, simulate clicks, and hope the page reacts the way they expect. This approach is:

- **Slow** — multiple round-trips of screenshot-parse-act for a single task
- **Fragile** — a CSS class rename or layout change breaks the entire flow
- **Unreliable** — agents hallucinate button locations or misinterpret form fields

WebMCP fixes this by letting web developers expose their app's capabilities as **structured, schema-driven tools** that agents call directly — like an API, but running in the browser tab alongside the user.

---

## What Is WebMCP?

WebMCP (Model Context Protocol for the Web) is a [W3C-proposed standard](https://webmachinelearning.github.io/webmcp/) co-authored by engineers from Microsoft and Google. It adds a new interface to the browser: `navigator.modelContext`.

Think of it as turning every web page into an MCP server, except the tools execute in client-side JavaScript instead of a backend process. The browser acts as the bridge between the AI agent and your page's tools.

### The Architecture

```mermaid
graph TD
    A["AI Agent<br/>(Gemini, Claude, GPT, Browser Extension)"]
    A -->|"Tool Discovery + Invocation"| B

    subgraph Browser["Browser (Chrome 146+)"]
        B["navigator.modelContext"]
        B -->|"execute(input, client)"| C["Your React App<br/>(reuses existing logic)"]
    end

    B -.- D["registerTool(tool)"]
    B -.- E["unregisterTool(name)"]
    B -.- F["provideContext({ tools })"]
    B -.- G["clearContext()"]
```

WebMCP provides two API modes:

| Mode | How It Works | Best For |
|------|-------------|----------|
| **Imperative** | JavaScript calls to `navigator.modelContext.registerTool()` | Dynamic tools, complex logic, SPAs |
| **Declarative** | HTML attributes (`toolname`, `tooldescription`) on `<form>` elements | Static forms, server-rendered pages, quick adoption |

---

## Why react-webmcp?

The raw `navigator.modelContext` API is straightforward, but using it directly in React means manually managing tool registration, cleanup, event listeners, and feature detection. That's boilerplate you don't need to write.

[`react-webmcp`](https://github.com/tech-sumit/react-webmcp) provides:

- **`useWebMCPTool`** — Register a tool on mount, unregister on unmount. React lifecycle, handled.
- **`useWebMCPContext`** — Register multiple tools at once with `provideContext()`, auto-clear on unmount.
- **`useToolEvent`** — Subscribe to `toolactivated` and `toolcancel` browser events.
- **`<WebMCPForm>`** — Declarative form component that maps props to `toolname` / `tooldescription` HTML attributes.
- **`<WebMCPInput>`, `<WebMCPSelect>`, `<WebMCPTextarea>`** — Form controls with `toolparam*` attribute support.
- **`<WebMCPProvider>`** — Context provider for detecting WebMCP availability in the browser.
- **Full TypeScript support** — Type-augmented `navigator.modelContext` and exported types.

### Installation

```bash
npm install react-webmcp
```

**Requirements:** React 18+ and Chrome 146.0.7672.0+ with the `WebMCP for testing` flag enabled at `chrome://flags/#enable-webmcp-testing`.

---

## Hands-On: Imperative API with Hooks

The imperative API gives you full control. Let's build a todo app that an AI agent can interact with.

### Registering a Single Tool

```tsx
import { useState } from "react";
import { useWebMCPTool } from "react-webmcp";

function TodoApp() {
  const [todos, setTodos] = useState<string[]>([]);

  useWebMCPTool({
    name: "addTodo",
    description: "Add a new item to the todo list",
    inputSchema: {
      type: "object",
      properties: {
        text: { type: "string", description: "The todo item text" },
      },
      required: ["text"],
    },
    execute: ({ text }) => {
      setTodos((prev) => [...prev, text as string]);
      return { content: [{ type: "text", text: `Added todo: ${text}` }] };
    },
  });

  return (
    <ul>
      {todos.map((t, i) => (
        <li key={i}>{t}</li>
      ))}
    </ul>
  );
}
```

That's it. When this component mounts, the `addTodo` tool is registered with the browser. When it unmounts, the tool is removed. An AI agent browsing this page can discover the tool, see its JSON Schema, and call it with structured parameters — no DOM scraping required.

### Registering Multiple Tools at Once

For apps with many tools, `useWebMCPContext` replaces the entire tool set in one call:

```tsx
import { useWebMCPContext } from "react-webmcp";

function ProjectDashboard() {
  useWebMCPContext({
    tools: [
      {
        name: "createTask",
        description: "Create a new task in the project",
        inputSchema: {
          type: "object",
          properties: {
            title: { type: "string", description: "Task title" },
            priority: {
              type: "string",
              enum: ["low", "medium", "high"],
              description: "Task priority level",
            },
          },
          required: ["title"],
        },
        execute: ({ title, priority }) => {
          // your existing createTask logic here
          return { content: [{ type: "text", text: `Created: ${title}` }] };
        },
      },
      {
        name: "listTasks",
        description: "List all tasks with optional status filter",
        inputSchema: {
          type: "object",
          properties: {
            status: {
              type: "string",
              enum: ["open", "in-progress", "done"],
            },
          },
        },
        execute: ({ status }) => {
          // your existing listTasks logic here
          return { content: [{ type: "text", text: "Tasks listed" }] };
        },
      },
    ],
  });

  return <div>{/* Dashboard UI */}</div>;
}
```

### Listening to Agent Events

Know when an agent activates or cancels a tool:

```tsx
import { useToolEvent } from "react-webmcp";

function AgentAwareComponent() {
  useToolEvent("toolactivated", (toolName) => {
    console.log(`Agent activated tool: ${toolName}`);
    // Show a visual indicator, log analytics, etc.
  });

  useToolEvent("toolcancel", (toolName) => {
    console.log(`Agent cancelled tool: ${toolName}`);
  });

  return <div>{/* UI */}</div>;
}
```

You can also filter events for a specific tool:

```tsx
useToolEvent("toolactivated", (toolName) => {
  showNotification("An AI agent is searching flights...");
}, "searchFlights");
```

---

## Hands-On: Declarative API with Components

The declarative API is ideal for forms. Instead of writing JavaScript tool definitions, you annotate your form markup and the browser auto-generates the schema.

### A Restaurant Reservation Form

```tsx
import {
  WebMCPForm,
  WebMCPInput,
  WebMCPSelect,
  useToolEvent,
} from "react-webmcp";

function ReservationForm() {
  useToolEvent("toolactivated", (toolName) => {
    console.log(`Agent activated: ${toolName}`);
  });

  return (
    <WebMCPForm
      toolName="book_table"
      toolDescription="Book a table at the restaurant"
      onSubmit={(e) => {
        e.preventDefault();
        if (e.agentInvoked) {
          // Agent submitted this form — respond programmatically
          e.respondWith(Promise.resolve("Booking confirmed!"));
        } else {
          // Human submitted — standard form handling
          handleHumanSubmission(e);
        }
      }}
    >
      <WebMCPInput
        name="name"
        type="text"
        toolParamDescription="Customer's full name"
        required
      />
      <WebMCPInput
        name="date"
        type="date"
        toolParamDescription="Reservation date (YYYY-MM-DD)"
        required
      />
      <WebMCPSelect
        name="guests"
        toolParamDescription="Number of guests"
      >
        <option value="1">1 Person</option>
        <option value="2">2 People</option>
        <option value="3">3 People</option>
        <option value="4">4 People</option>
      </WebMCPSelect>
      <button type="submit">Book</button>
    </WebMCPForm>
  );
}
```

Under the hood, the browser reads the `toolname`, `tooldescription`, and `toolparamdescription` attributes and generates a JSON Schema for the AI agent. The agent fills in the form fields, the browser fires a `toolactivated` event, and when the form submits, `e.agentInvoked` tells you whether a human or agent triggered it.

### Key Difference: `agentInvoked` and `respondWith`

The `SubmitEvent` is enhanced with two properties:

- **`agentInvoked`** — `true` if an AI agent triggered the form submission
- **`respondWith(promise)`** — lets you return structured data to the agent instead of navigating to a new page

This means the same form works for both humans and agents. Humans see the normal UI flow; agents get a programmatic response.

---

## Detecting WebMCP Availability

Not every browser supports WebMCP yet. Use the provider and hook to detect support and render fallbacks:

```tsx
import { WebMCPProvider, useWebMCPStatus } from "react-webmcp";

function App() {
  return (
    <WebMCPProvider>
      <StatusBanner />
      <TodoApp />
    </WebMCPProvider>
  );
}

function StatusBanner() {
  const { available, testingAvailable } = useWebMCPStatus();

  if (!available) {
    return <p>WebMCP is not supported in this browser.</p>;
  }

  return (
    <p>
      WebMCP is active.
      {testingAvailable && " Inspector API available for debugging."}
    </p>
  );
}
```

You can also use the standalone utility functions:

```tsx
import { isWebMCPAvailable, getModelContext } from "react-webmcp";

if (isWebMCPAvailable()) {
  const ctx = getModelContext();
  // ctx is navigator.modelContext
}
```

---

## Tool Annotations: Hinting Agent Behavior

Annotations provide metadata that helps agents make smarter decisions about when and how to use a tool:

```tsx
useWebMCPTool({
  name: "deleteAccount",
  description: "Permanently delete the user account",
  inputSchema: {
    type: "object",
    properties: {
      confirm: { type: "boolean", description: "Must be true to proceed" },
    },
    required: ["confirm"],
  },
  annotations: {
    readOnlyHint: "false",
    destructiveHint: "true",
    idempotentHint: "false",
  },
  execute: ({ confirm }) => {
    if (!confirm) return "Deletion cancelled.";
    // ...delete logic
    return "Account deleted.";
  },
});
```

| Annotation | Meaning |
|-----------|---------|
| `readOnlyHint` | Tool only reads data, doesn't modify state |
| `destructiveHint` | Tool performs irreversible changes — agent should confirm with user |
| `idempotentHint` | Safe to call multiple times with same result |

These are **hints** — the browser doesn't enforce them, but well-behaved agents use them to decide whether to ask for user confirmation before calling a destructive tool.

---

## Tool Design Best Practices

Google's WebMCP early preview documentation outlines principles that apply directly when designing tools with `react-webmcp`:

### 1. Clear Naming and Descriptions

Use specific verbs that describe exactly what happens. A tool named `create_event` should create an event immediately, not redirect to a form. If it redirects, name it `start_event_creation`.

Describe **what** the tool does and **when** to use it. Prefer positive instructions over negative limitations.

### 2. Accept Raw User Input

Don't force the agent to do math or transformations. If a user says "11:00 to 15:00", your schema should accept `"11:00"` and `"15:00"` as strings, not require the agent to convert to minutes-from-midnight.

### 3. Validate in Code, Not Just Schema

JSON Schema constraints are helpful but not guaranteed. Always validate inside your `execute` function and return **descriptive errors** so the agent can self-correct:

```tsx
execute: ({ email }) => {
  if (!email || !email.includes("@")) {
    return {
      content: [{
        type: "text",
        text: "Error: Invalid email address. Please provide a valid email like user@example.com",
      }],
    };
  }
  // proceed with valid email
}
```

### 4. Atomic and Composable Tools

Avoid overlapping tools with nuanced differences. Combine them into a single tool with input parameters. Each tool should do one thing well. Let the agent compose multiple tool calls to achieve complex tasks.

---

## Testing with the Chrome Extension

![Developer testing workflow with the Model Context Tool Inspector extension](/images/posts/react-webmcp-guide/devtools-testing.png)

The [Model Context Tool Inspector](https://chromewebstore.google.com/detail/model-context-tool-inspec/gbpdfapgefenggkahomfgkhfehlcenpd) Chrome extension is essential for development:

1. **Install the extension** from the Chrome Web Store
2. **Enable the flag** at `chrome://flags/#enable-webmcp-testing`
3. **Open your React app** and click the extension icon
4. **See all registered tools** with their schemas — verify your hooks are registering correctly
5. **Test tools manually** by entering JSON parameters
6. **Test with Gemini** — enter a natural language prompt and see if the agent correctly identifies and invokes your tools

This lets you bypass the non-deterministic nature of LLMs during initial development and test your tool definitions deterministically.

---

## Full Example: Flight Search App

The `react-webmcp` repository includes a complete [flight search demo](https://github.com/tech-sumit/react-webmcp/tree/master/demos/flight-search) that replicates Google's official react-flightsearch demo using hooks. It registers four tools:

| Tool | Description |
|------|------------|
| `searchFlights` | Search for flights between airports |
| `listFlights` | List currently displayed flight results |
| `setFilters` | Apply filters (price range, stops, airlines) |
| `resetFilters` | Clear all active filters |

An agent can orchestrate these tools to handle a request like:

> "Find me the cheapest non-stop flight from London to New York next Monday, returning the following Sunday, for 2 passengers."

The agent calls `searchFlights` with structured parameters, then `setFilters` to narrow results, then `listFlights` to present options — all without scraping a single DOM node.

---

## TypeScript Support

The library ships with full type definitions and augments the global `Navigator` interface:

```typescript
import type {
  WebMCPToolDefinition,
  JSONSchema,
  ToolAnnotations,
  WebMCPFormSubmitEvent,
  ModelContext,
} from "react-webmcp";
```

Your IDE will provide autocomplete for `navigator.modelContext` methods and all hook/component props.

---

## Current Limitations

WebMCP is in early preview. Keep these in mind:

- **Chrome 146+ only** — Firefox, Safari, and Edge are in the W3C working group but haven't shipped implementations
- **Flag-gated** — users must enable `chrome://flags/#enable-webmcp-testing`; not on by default yet
- **Geo-restrictions** — Gemini agent features may require a US-based connection during the preview period
- **JSON-only results** — tool outputs are JSON; rich content types (images, files) are [under discussion](https://github.com/webmachinelearning/webmcp/issues/86)
- **No tool discovery** — agents can only see tools after navigating to a page; there's no equivalent of `robots.txt` for tools yet
- **Max ~50 tools per page** — the spec recommends keeping tool counts manageable to avoid overwhelming agent context windows

---

## What's Next for WebMCP

The spec is actively evolving. Key proposals in flight:

- **Tool categories and filtering** ([Issue #88](https://github.com/webmachinelearning/webmcp/issues/88)) — let agents query specific tool subsets
- **Session and auth context** ([Issue #87](https://github.com/webmachinelearning/webmcp/issues/87)) — standardize how tools interact with authentication
- **Lifecycle events** ([Issue #85](https://github.com/webmachinelearning/webmcp/issues/85)) — `toolwillexecute`, `toolcomplete`, `toolerror` for better observability
- **Remote MCP server bridging** ([Issue #83](https://github.com/webmachinelearning/webmcp/issues/83)) — connect backend MCP servers to browser agents
- **Rich content types** ([Issue #86](https://github.com/webmachinelearning/webmcp/issues/86)) — return images, tables, and HTML from tools
- **File attachments** ([Issue #81](https://github.com/webmachinelearning/webmcp/issues/81)) — upload files via tool interactions
- **WebExtensions API** ([Issue #74](https://github.com/webmachinelearning/webmcp/issues/74)) — let browser extensions consume WebMCP tools

---

## Getting Started

```bash
# Install
npm install react-webmcp

# Enable Chrome flag
# Navigate to chrome://flags/#enable-webmcp-testing → Enabled → Relaunch

# Install the inspector extension
# https://chromewebstore.google.com/detail/model-context-tool-inspec/gbpdfapgefenggkahomfgkhfehlcenpd
```

Explore the [GitHub repository](https://github.com/tech-sumit/react-webmcp) for demos, API reference, and source code. The library is MIT-licensed and contributions are welcome.

WebMCP is where the web meets AI agents. The earlier you start building structured tools into your React apps, the better positioned you'll be when this ships to billions of Chrome users.
