Skip to content
LogoLogo

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/webmcp

Import

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 StarteddefineTool, registerTool, and SSR safety
  • Tool Registry — batch-register tools outside of a component's own lifecycle
  • ErrorsWebMCPNotSupportedError and hasWebMCPSupport