Getting Started
Prerequisites: Node 18+, any modern bundler (Vite, Next.js, etc.). WebMCP is native in Chrome Canary (origin trial) — other browsers use the production polyfill or the dev shim. See Browser Support.
Install
npm i simple-webmcp
# pnpm add simple-webmcp
# yarn add simple-webmcp
# React is optional peer only if you use simple-webmcp/reactRepo: github.com/emingure/simple-webmcp · npm: simple-webmcp
30-second start
Vanilla — global or manual
import { webmcp } from 'simple-webmcp';
async function addToCart({ productId, quantity }: { productId: string; quantity: number }) {
cart.push({ productId, quantity });
return { ok: true };
}
export const tool = webmcp(addToCart, {
description: 'Add product to shopping cart',
fields: {
productId: { description: 'Product ID' },
quantity: { type: 'integer', minimum: 1 },
},
});
// still callable
await tool({ productId: 'p_1', quantity: 2 });
// global (registers on import, client only)
webmcp.global(addToCart, { description: '...' });
// or
webmcp(addToCart, { description: '..', global: true });
// manual — anywhere
await tool.register(); // => document.modelContext.registerTool(...)
tool.unregister();Browser API is document.modelContext.registerTool — simple-webmcp keeps you on webmcp(fn) while the platform evolves.
React — page / layout scope (1-line optional wrapper)
'use client';
import { useWebMCP, Scope } from 'simple-webmcp/react';
// or useTool alias
import { useTool } from 'simple-webmcp/react';
// 1-line: define + wrap + register while mounted (recommended)
export function Page() {
const searchTool = useWebMCP(searchCustomers, { description: 'Search customers' });
// also: const searchTool = useTool(searchCustomers, { description: '...' });
// searchTool({query:'a'}) still callable
return <SearchUI />;
}
// verbose 2-line still works:
import { webmcp } from 'simple-webmcp';
const searchTool2 = webmcp(searchCustomers, { description: 'Search' });
export function Page2() {
useWebMCP(searchTool2); // mounted = exposed (AbortSignal)
return <SearchUI />;
}
// route-level via layout (Next.js app/layout.tsx naturally gives route scope)
export function Layout({ children }: { children: React.ReactNode }) {
const t = useWebMCP(searchCustomers, { description: 'Search' }); // or pre-wrapped tool
return <Scope tools={[t]}>{children}</Scope>;
}See React guide and Reference — React for useWebMCP options and Scope.
Zod / StandardSchema
import { z } from 'zod';
import 'simple-webmcp/zod'; // enables Zod → JSON conversion (keeps core lean)
webmcp(fn, { description: '…', schema: z.object({ query: z.string().min(1) }) });
webmcp(fn, { fields: { query: z.string().describe('Name or email') } });
schema(whole) establishes contract;fieldspatches it. See Schema.
Browser support (Firefox / Safari)
WebMCP is Chrome-only today (document.modelContext). For production cross-browser, use the real polyfill @mcp-b/webmcp-polyfill. For dev only:
import 'simple-webmcp/dev-polyfill'; // no-op in Chrome with native, in-memory shim elsewhereSee Browser Support & Polyfill.
Hierarchy
schema (whole StandardSchema/JSON) → inferred (runtime best-effort, optional build-time) → fields patch → metadata (name/desc/annotations)Prefer fn({query, limit}) single object param — best inference. strict:true makes low-confidence inference throw. See Schema & Inference.
Hooks (global / scoped / tool)
// global — analytics once
webmcp.configure({ hooks:{ before:[track], after:[trackResult], error:[report] }});
// tool — HITL approval
webmcp(checkout, { hooks:{ before:[async ({input})=>{
const ok = await confirm(`Approve £${total}?`);
if(!ok) return {action:'deny', message:'User declined'};
}]}});
// React scoped — tenant
<WebMCPProvider hooks={{ before:[({input})=>({input:{...input, tenantId}})] }}>
<Scope tools={[tool]}>{children}</Scope>
</WebMCPProvider>Hooks wrap only the agent path — tool({input}) stays pure. See Guide — Hooks and Analytics Step-by-Step.
Next steps
- Schema & Inference — fields, Zod, runtime vs build
- React —
useWebMCP,Scope,WebMCPProvider - Hooks —
before/after/error/denied, HITL demo - Analytics — Overview — PostHog, Sentry, GA4
- Analytics — Step-by-Step — 4-step setup
- Browser Support — Chrome, Firefox, Safari, polyfill
- Reference — Core —
webmcp()options,register()lifecycle - Inspect —
listTools,invokeTool,<Inspector> - Demo — shopping cart + Hooks & HITL log