Blog · · 3 min read

Build a safe WebMCP tool for a React or Next.js form

Add a small WebMCP tool that prepares a visible form without submitting it, then test registration, validation, cancellation and user confirmation.

The first useful WebMCP feature does not need to make a booking or send a lead straight to your CRM. It can do something smaller: let an agent fill a clearly named form, show the values to the person, and leave the normal submit button in charge of sending it.

That pattern is a good fit for a demo-request form. The example below registers one browser tool that prepares the form. It does not send the request. The visitor reviews the company, email and topic, then chooses whether to submit.

Put the tool beside the real form

In the Next.js App Router, pages and layouts are Server Components by default. Browser APIs and React state belong in a Client Component. Keep data fetching and secrets on the server; use a small client component for tool registration and form state. See the Next.js Server and Client Components guide.

WebMCP is still experimental. Chrome documents experimental React support through the use-webmcp-tool hook, which ties registration to the component lifecycle and checks whether the browser API exists. For a React app, use that integration instead of keeping a second, hand-managed registration lifecycle in an effect. Check the current Chrome WebMCP guide before adopting it.

'use client';

import { useState } from 'react';
import { useWebMCP } from 'use-webmcp-tool';

type DemoRequest = {
  company: string;
  email: string;
  topic: string;
};

const emptyRequest: DemoRequest = { company: '', email: '', topic: '' };

export function DemoRequestForm() {
  const [request, setRequest] = useState(emptyRequest);

  useWebMCP<DemoRequest, string>({
    name: 'prepare_demo_request',
    description: 'Fill the visible demo request form with the visitor’s details.',
    inputSchema: {
      type: 'object',
      properties: {
        company: { type: 'string', description: 'The visitor company name' },
        email: { type: 'string', description: 'The visitor work email' },
        topic: { type: 'string', description: 'What the visitor wants to discuss' },
      },
      required: ['company', 'email', 'topic'],
      additionalProperties: false,
    },
    execute: async (input) => {
      setRequest(input);
      return 'The form is filled in. Ask the visitor to review it and submit it manually.';
    },
  });

  return (
    <form action="/api/demo-requests" method="post">
      <label>
        Company
        <input name="company" required value={request.company}
          onChange={(event) => setRequest({ ...request, company: event.target.value })} />
      </label>
      <label>
        Work email
        <input name="email" type="email" required value={request.email}
          onChange={(event) => setRequest({ ...request, email: event.target.value })} />
      </label>
      <label>
        What would you like to discuss?
        <textarea name="topic" required value={request.topic}
          onChange={(event) => setRequest({ ...request, topic: event.target.value })} />
      </label>
      <button type="submit">Send demo request</button>
    </form>
  );
}

This is a lifecycle example, not a complete production form. Add server-side validation, spam controls, consent language and the normal success and error states. The browser should not be the authority for whether an email is valid or whether a lead can be created.

Why the tool only prepares a draft

The tool name and description say what happens. Its schema lists the expected fields and rejects additional properties. The execute function changes visible form state, then tells the agent to ask the person to review and submit. It never calls the form endpoint.

That boundary matters. A tool invocation is not proof that the person intended to send personal information to your sales team. If you later choose to submit from a tool, treat it as a consequential action, make the action clear in the interface and follow the confirmation behavior expected by the browser and your own product.

The hook unregisters the tool when the component unmounts. That matters on client-side navigation: leaving a tool registered after its form has disappeared creates a confusing or broken action. The hook also feature-detects support, so the form remains usable in browsers without WebMCP. See the use-webmcp-tool package and Chrome's Imperative API guide for lifecycle and API details.

Test it in a supported browser

As of October 2026, Chrome describes WebMCP as an origin-trial feature from Chrome 149, with a flag for local development. Enable the current flag, open the route directly, and inspect the registered tool with Chrome DevTools or an inspector extension. Try a well-formed request, a missing email, an invalid email and an unexpected field.

After each tool call, check the visible controls. Do they show the exact values the agent supplied? Can a person edit them? Does the normal submit button work? If the visitor navigates away, does the tool disappear? If the component remounts, does the page expose one tool rather than duplicates?

Keep a test account and test endpoint. Confirm that a filled form alone creates no CRM record, email or analytics conversion. Then submit it as a person and verify that the existing server-side workflow still behaves correctly.

For background on the browser feature and its current support limits, read WebMCP for website owners.

Sources

Check your site

See what an agent finds on your site, and which of these problems you have.