WebMCP publishes named actions on the page. A browser agent calls an action by name instead of clicking the UI.
You register your TanStack client tools on document.modelContext. The agent finds them there and calls them.
Warning
WebMCP is experimental. Treat it as an extra for browsers that support it. Your page must work without it.
This page sends your tools to WebMCP. To use the WebMCP tools of a page in your own chat, see Page WebMCP Tools in Chat.
npm i @tanstack/ai @tanstack/ai-react zodpnpm add @tanstack/ai @tanstack/ai-react zodyarn add @tanstack/ai @tanstack/ai-react zodbun add @tanstack/ai @tanstack/ai-react zodPut the tool at module scope. This tool opens a help panel:
// tools.ts
import { toolDefinition } from '@tanstack/ai'
import { z } from 'zod'
export const openHelpPanel = toolDefinition({
name: 'open_help_panel',
description: 'Open the help panel for a topic',
inputSchema: z.object({
topic: z.string(),
}),
outputSchema: z.object({
opened: z.boolean(),
}),
}).client((input) => {
const panel = document.querySelector<HTMLElement>('#help-panel')
if (!panel) {
return { opened: false }
}
panel.dataset.topic = input.topic
panel.showPopover()
return { opened: true }
})
export const webMCPTools = [openHelpPanel]Call the register function for your framework. When the component goes away, the function removes the tools.
Call useRegisterWebMCPTools in a component. Keep the tool array and the options stable, for example at module scope.
import { useRegisterWebMCPTools } from '@tanstack/ai-react'
import { webMCPTools } from './tools'
const webMCPOptions = {
onError(error: unknown) {
console.error('WebMCP registration failed', error)
},
}
export function HelpPage() {
useRegisterWebMCPTools(webMCPTools, webMCPOptions)
return (
<section id="help-panel" popover="auto">
<h2>Help</h2>
</section>
)
}That is all. In a browser with WebMCP, an agent on this page can now find and call open_help_panel. In other browsers, the page works the same as before.
During SSR, in an insecure context, or in a browser without WebMCP, the register function adds no tools. It does not throw, so you can call it everywhere.
Each tool must have:
If a tool breaks one of these rules, registration fails and onError gets the error.
If your own code owns the lifecycle, use registerWebMCPTools from @tanstack/ai-client. The signal option is required. The tools stay registered until the signal aborts.
import { registerWebMCPTools } from '@tanstack/ai-client'
import { webMCPTools } from './tools'
const registration = new AbortController()
await registerWebMCPTools(webMCPTools, {
signal: registration.signal,
toolOptions: {
open_help_panel: {
title: 'Open help',
annotations: {
readOnlyHint: false,
untrustedContentHint: false,
},
},
},
})
export function disposeWebMCPTools() {
registration.abort()
}Call disposeWebMCPTools() from the teardown code of your application shell. Do not call it on pagehide, because the browser can restore the page from the back/forward cache.
registerWebMCPTools returns a promise. If registration fails, the promise rejects.
All framework wrappers accept these options. registerWebMCPTools accepts the first two, plus signal.
The annotations are hints for the browser agent:
The annotations do not enforce permissions or approval.
In these frameworks, a new tools or options value replaces the registrations. If the contents do not change, keep both values stable. Stable values prevent extra changes to the WebMCP registry.
WebMCP calls your client handler directly. The handler gets these values:
The two signals have different jobs:
Cancellation is cooperative. Your handler must read abortSignal, or pass it to an API that supports cancellation.
The register function converts each input schema to JSON Schema for WebMCP.
Tool results must be JSON-serializable. If a result cannot be serialized, WebMCP reports the failure to its caller.
A tool with needsApproval: true cannot register with WebMCP. WebMCP annotations cannot enforce the TanStack approval flow.
Do not protect a sensitive action with a browser check only. Your server endpoint must enforce these rules:
Tool names must be unique.
The 1.0 names of the wrapper functions and their option types are in the 1.0 upgrade guide.