React
@zap-studio/webmcp-react is a separate package with one hook: useWebMCPTool(tool, deps?). It registers a tool with the native WebMCP API on mount, and unregisters it on unmount. It works the same way in Next.js App Router client components and TanStack Start client components — both are React, so no separate adapter is needed per meta-framework.
Installation
npm install @zap-studio/webmcp-react @zap-studio/webmcpImport
import { useWebMCPTool } from "@zap-studio/webmcp-react";Basic Usage
import { useWebMCPTool } from "@zap-studio/webmcp-react";
function LikeButton({ postId }: { postId: string }) {
useWebMCPTool(
{
name: "posts_like",
description: "Like a post by ID",
execute: async ({ id }: { id: string }) => ({ liked: await likePost(id) }),
},
[postId],
);
return <button onClick={() => likePost(postId)}>Like</button>;
}Registration runs in useEffect, so it happens after mount, in the browser only — the hook is safe to render on the server, where it simply registers nothing.
Changing Tools
deps controls when the tool re-registers, exactly like useEffect's dependency array. With the default [], the tool registers once, on mount, using the first render's tool:
useWebMCPTool(
{
name: "posts_like",
description: "Like a post by ID",
execute: async ({ id }: { id: string }) => ({ liked: await likePost(id) }),
},
[],
);Pass the values tool depends on to re-register when they change — for example, when execute closes over a prop:
useWebMCPTool(
{
name: "posts_like",
description: "Like a post by ID",
execute: async () => ({ liked: await likePost(postId) }),
},
[postId],
);Unmount / Cleanup
The tool unregisters automatically when the component unmounts, or right before it re-registers on a deps change — no manual cleanup needed. If registration is still pending when the component unmounts, useWebMCPTool unregisters it as soon as it resolves instead of leaking a dangling tool.
Handling Errors
Registration failures — most commonly an unsupported browser — are caught internally and surfaced through the returned error, not thrown, so a missing agent-callable tool never crashes the component tree:
function LikeButton({ postId }: { postId: string }) {
const { error } = useWebMCPTool(
{
name: "posts_like",
description: "Like a post by ID",
execute: async ({ id }: { id: string }) => ({ liked: await likePost(id) }),
},
[postId],
);
return (
<button onClick={() => likePost(postId)}>Like{error ? " (agent tool unavailable)" : ""}</button>
);
}See Also
- Getting Started —
defineTool,registerTool, and SSR safety - Tool Registry — batch-register tools outside of a component's own lifecycle
- Errors —
WebMCPNotSupportedErrorandhasWebMCPSupport