Skip to main content
This package provides two sets of hooks: provider hooks for registering your tools with automatic lifecycle management and Zod validation, and client hooks for connecting to MCP servers and calling tools. The hooks handle the boilerplate of tool registration/cleanup, leaving you to focus on tool logic.

Prerequisites

  • React 18+ or React 19 (supports both versions)
  • @mcp-b/global package installed
  • Zod for schema validation
  • MCP-B Extension for testing tools
  • Basic understanding of React hooks and async/await

Installation

pnpm add @mcp-b/react-webmcp @mcp-b/global zod
For client functionality:
pnpm add @mcp-b/transports @modelcontextprotocol/sdk

Features

Provider Hooks (Registering Tools)
  • Type-safe with Zod validation
  • Automatic lifecycle management (StrictMode compatible)
  • Execution state tracking for UI feedback
Client Hooks (Consuming Tools)
  • MCP server connection management
  • Real-time tool list updates
  • Automatic reconnection handling

Provider API (Registering Tools)

Basic Usage

import '@mcp-b/global';
import { useWebMCP } from '@mcp-b/react-webmcp';
import { z } from 'zod';

function PostsPage() {
  const [posts, setPosts] = useState([]);

  const likeTool = useWebMCP({
    name: 'posts_like',
    description: 'Like a post by ID',
    inputSchema: {
      postId: z.string().uuid()
    },
    handler: async ({ postId }) => {
      await api.posts.like(postId);
      return { success: true };
    }
  });

  return (
    <div>
      {likeTool.state.isExecuting && <Spinner />}
      {likeTool.state.error && <Error />}
    </div>
  );
}

Read-Only Context

import { useWebMCPContext } from '@mcp-b/react-webmcp';

function PostDetail() {
  const { postId } = useParams();
  const { data: post } = useQuery(['post', postId], fetchPost);

  useWebMCPContext(
    'context_current_post',
    'Get current post metadata',
    () => ({ postId, title: post?.title, author: post?.author })
  );

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

API Reference

useWebMCP(config)

OptionTypeRequiredDescription
namestringUnique tool identifier
descriptionstringHuman-readable description
inputSchemaRecord<string, ZodType>-Zod validation schema
handler(input) => Promise<TOutput>Tool execution function
annotationsToolAnnotations-Metadata hints (readOnlyHint, idempotentHint, etc.)
formatOutput(output) => string-Custom output formatter
onError(error, input) => void-Error handler
Returns:
{
  state: {
    isExecuting: boolean;
    lastResult: TOutput | null;
    error: Error | null;
  };
  execute: (input) => Promise<TOutput>;
  reset: () => void;
}

useWebMCPContext(name, description, getValue)

Simplified hook for read-only context. Auto-registers and returns current state.

Client API (Consuming Tools)

Basic Usage

import { McpClientProvider, useMcpClient } from '@mcp-b/react-webmcp';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { TabClientTransport } from '@mcp-b/transports';

const client = new Client({ name: 'MyApp', version: '1.0.0' });
const transport = new TabClientTransport('mcp');

function App() {
  return (
    <McpClientProvider client={client} transport={transport}>
      <ToolConsumer />
    </McpClientProvider>
  );
}

function ToolConsumer() {
  const { client, tools, isConnected } = useMcpClient();

  const callTool = async () => {
    const result = await client.callTool({
      name: 'posts_like',
      arguments: { postId: '123' }
    });
    console.log(result.content[0].text);
  };

  return (
    <div>
      <p>Connected: {isConnected}</p>
      <p>Tools: {tools.map(t => t.name).join(', ')}</p>
      <button onClick={callTool} disabled={!isConnected}>
        Call Tool
      </button>
    </div>
  );
}

API Reference

McpClientProvider

<McpClientProvider client={client} transport={transport} opts={opts}>
  {children}
</McpClientProvider>
Available Transports:
  • TabClientTransport - Same-page MCP server
  • ExtensionClientTransport - Chrome extension server
  • InMemoryTransport - Testing

useMcpClient()

Returns:
{
  client: Client;              // MCP SDK client
  tools: Tool[];               // Available tools
  resources: Resource[];       // Available resources
  isConnected: boolean;        // Connection status
  isLoading: boolean;          // Connecting
  error: Error | null;         // Connection error
  capabilities: ServerCapabilities | null;
  reconnect: () => Promise<void>;
}

Complete Example

import '@mcp-b/global';
import { McpClientProvider, useWebMCP, useMcpClient } from '@mcp-b/react-webmcp';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { TabClientTransport } from '@mcp-b/transports';
import { z } from 'zod';

const client = new Client({ name: 'MyApp', version: '1.0.0' });
const transport = new TabClientTransport('mcp');

function App() {
  return (
    <McpClientProvider client={client} transport={transport}>
      <Counter />
    </McpClientProvider>
  );
}

function Counter() {
  const [count, setCount] = useState(0);
  const { client, tools, isConnected } = useMcpClient();

  // Register tool
  useWebMCP({
    name: 'increment',
    description: 'Increment counter',
    inputSchema: { amount: z.number().default(1) },
    handler: async ({ amount }) => {
      setCount(prev => prev + amount);
      return { newValue: count + amount };
    }
  });

  // Call tool
  const callTool = async () => {
    const res = await client.callTool({
      name: 'increment',
      arguments: { amount: 5 }
    });
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={callTool} disabled={!isConnected}>
        Increment
      </button>
    </div>
  );
}

Best Practices

Tool Naming: Use verb-noun format with domain prefix: posts_like, graph_navigate, table_filter Annotations: Set readOnlyHint (true for queries), idempotentHint (true if safe to retry), destructiveHint (for deletions) Error Handling: Throw descriptive errors. Use onError for logging/toasts. Handle connection errors in client components. Performance: Tools auto-dedupe in StrictMode. Use useWebMCPContext for lightweight read-only data.

Migration from Legacy Package

If you’re migrating from an older version of this package (previously named @mcp-b/mcp-react-hooks): Before:
const { registerTool } = useMcpServer();
useEffect(() => {
  const tool = registerTool('my_tool', { description: '...' }, handler);
  return () => tool.remove();
}, []);
After:
useWebMCP({
  name: 'my_tool',
  description: '...',
  handler
});
// Auto-registers and cleans up
Client hooks (McpClientProvider, useMcpClient) remain unchanged.