PostHog — WebMCP Analytics
Track every
before → after/error/deniedwithwebmcp.configure. One snippet covers all tools. See Analytics Overview and Step-by-Step.
For hook lifecycle, see Guide — Hooks. For types, see Reference — Hooks.
When to use
PostHog for product analytics — capture webmcp.invoked / succeeded / failed / denied + duration + tenantId for all tools.
External: PostHog JS docs · posthog-js on npm
Step-by-step
Step 1 — Install & init PostHog
npm i posthog-jsIn your app entry (e.g. main.tsx or app/layout.tsx):
import posthog from 'posthog-js';
posthog.init('phc_...', { api_host: 'https://us.i.posthog.com', capture_pageview: true });See PostHog — Install JS.
Step 2 — Configure hooks once (global)
import { webmcp } from 'simple-webmcp';
import posthog from 'posthog-js';
webmcp.configure({
hooks: {
before: [({tool, input, invocationId, metadata})=>{
metadata.start = Date.now();
posthog.capture('webmcp.invoked', { tool: tool.tool.name, invocationId, input });
}],
after: [({tool, invocationId, metadata})=>{
posthog.capture('webmcp.succeeded', {
tool: tool.tool.name,
invocationId,
duration_ms: Date.now()-(metadata.start as number)
});
}],
error: [({tool, error, invocationId})=>{
posthog.capture('webmcp.failed', { tool: tool.tool.name, invocationId, error: String(error) });
}],
denied: [({tool, reason, code, invocationId})=>{
posthog.capture('webmcp.denied', { tool: tool.tool.name, invocationId, reason, code });
}],
}
});Hooks wrap only the agent path — tool({input}) stays pure. invocationId is crypto.randomUUID() per invocation; metadata is the shared bag per call.
Step 3 — Verify in the demo
npm run docs:dev→ open /demo.- Inspect → Invoke
add_to_cartwith{"productId":"keyboard","quantity":1}→ check Hooks & HITL card andconsole [webmcp:hook]. - Toggle Require approval for checkout → Invoke checkout → Deny → verify
webmcp.deniedwithcode:USER_DENIEDin PostHog Activity. - Toggle approval off → Invoke checkout → verify
webmcp.succeededwithduration_ms.
Step 4 — Production notes
PII: Redact
inputbeforecapture— e.g. stripemail/tokeninbeforeorafter. See Hooks — After.Sampling:
if (Math.random() > 0.1) returninside hooks for cost control.Tenant: Use scoped provider to add
tenantId:tsximport { WebMCPProvider } from 'simple-webmcp/react'; <WebMCPProvider hooks={{ before:[({input})=>({input:{...(input as any), tenantId}})] }}> <Scope tools={[tool]}>{children}</Scope> </WebMCPProvider>See React — WebMCPProvider and Analytics Overview.
Cross links
- Step-by-Step: Generic 4-step
- Overview: Analytics
- Guides: Hooks lifecycle · React · Schema · Browser Support
- Reference: Hooks —
BeforeContextetc. - Demo: /demo — live hook log
- External: PostHog JS Docs · posthog-js npm
Troubleshooting
| Symptom | Fix |
|---|---|
| No events in PostHog | Ensure webmcp.configure runs before tool.register() / useWebMCP mount; check posthog.has_opted_in_capturing() |
duration_ms missing | before must set metadata.start; after reads same metadata object per invocation |
input too large | Redact PII and truncate arrays before capture |
See also
- Sentry — errors + performance
- GA4 —
gtagevents - Reference — Hooks