React
tsx
'use client';
import { webmcp } from 'simple-webmcp';
import { useWebMCP, Scope } from 'simple-webmcp/react';useWebMCP — now optional wrapper
useWebMCP accepts either a wrapped WebMCPTool or a raw function — raw is auto-wrapped and visible for the component's lifetime. This is the 1-line ergonomic requested:
tsx
// 1-line: wrap + register while mounted, still callable
export function Page() {
const searchTool = useWebMCP(search, { description: 'Search customers' });
// searchTool({query:'alice'}) still works
// searchTool.registered, searchTool.status also available
return null;
}
// equivalent verbose (still supported):
const tool = webmcp(search, { description: 'Search' });
const { supported, registered, error, status } = useWebMCP(tool);
// alias — same as useWebMCP(fn, opts)
import { useTool } from 'simple-webmcp/react';
const tool2 = useTool(search, { description: 'Search' });- Registers via
registry.register(contract, {signal})(asyncPromise<void>per WebMCP spec). - Unregisters on unmount via
AbortSignal— mirrors spec. - Deduped for StrictMode double-mount.
enabled:false→ inert.- For
useWebMCP(tool)(already wrapped) returns{supported,registered,error,status}for backward compat; foruseWebMCP(fn, opts)returnsWebMCPTool & statusso you get callable + state in one.
Scope
tsx
export function Layout({ children }: { children: React.ReactNode }) {
return <Scope tools={[searchTool, updateTool]}>{children}</Scope>;
}Mounted = exposed. In Next.js app/layout.tsx this naturally gives route-level scope. Scope is in simple-webmcp/react, not next — React subtree, not route API.
Patterns
- Page-level tool:
useWebMCPin page component. - Shared tools:
Scopein layout. - Global:
webmcp.global(fn, opts)(orwebmcp(fn,{global:true})) — no hook needed, but prefer Scoped for least privilege.