Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.waniwani.ai/llms.txt

Use this file to discover all available pages before exploring further.

withWaniwani(server) is an optional convenience that instruments an MCP server. It activates Platform tracking when an API key is configured, and stays a no-op-but-still-useful wrapper when it isn’t.
Not required for OSS flows. createFlow works without withWaniwani. Use this wrapper when you want auto-tracking, session-id bridging, or widget metadata forwarding on top.

What it does, with WANIWANI_API_KEY

Every tool invocation produces a tool.called event containing name, type, duration, status, input, output, client info, and session correlation. Flow graphs are synced to the dashboard so the funnel view lights up automatically.
import { withWaniwani } from "@waniwani/sdk/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({ name: "my-mcp-app", version: "0.0.1" });

server.registerTool(/* ... */);

withWaniwani(server);

What it does, without an API key

The wrapper still adds two genuinely useful things, even with no key configured:
  1. Transport session-ID bridging. When the MCP transport carries a session ID via the Mcp-Session-Id header but the request _meta doesn’t include one, the wrapper copies it into _meta.waniwani/sessionId so flow nodes and downstream tools can see the correlation.
  2. Widget definition metadata forwarding. Widget keys declared on a tool’s definition _meta (openai/outputTemplate, ui/resourceUri, etc.) are forwarded into each tool result’s _meta, so chat UIs that only see results can still render widgets.
Tracking calls silently no-op when no key is configured. No errors, no thrown exceptions. You can safely wrap your server unconditionally.

When to use it

You wantUse withWaniwani?
Only run OSS flows, no telemetryOptional (still useful for session bridging)
OSS flows + Platform trackingYes
Hosted everything (flow state + tracking + funnel)Yes
Just custom event tracking via waniwani().track()No, call track() directly

Options

import { withWaniwani } from "@waniwani/sdk/mcp";
import { waniwani } from "@waniwani/sdk";

withWaniwani(server, {
  client: waniwani(),
  toolType: (name) => (name.startsWith("get_price") ? "pricing" : "other"),
  metadata: { deployment: "production" },
  flushAfterToolCall: false,
  injectWidgetToken: true,
  onError: (err) => console.error("[waniwani]", err),
});
client
WaniWaniClient
The client used for tracking. Defaults to waniwani(), which picks up WANIWANI_API_KEY from the environment.
toolType
ToolType | (name: string) => ToolType | undefined
Classify tools into dashboard buckets: "pricing", "product_info", "availability", "support", or "other". Accepts a literal or a function that maps a tool name to a type.
metadata
Record<string, unknown>
Extra fields merged into every tool.called event. Use this to tag deployments, tenants, or releases.
flushAfterToolCall
boolean
default:false
Force a flush after every tool call. Turn this on in serverless runtimes where the process may be frozen between invocations. Leave it off for long-running Node processes.
injectWidgetToken
boolean
default:true
Inject a short-lived widget tracking token into tool responses under _meta.waniwani, so browser widgets can post events directly to WaniWani without a proxy route. Without an API key, the token is omitted and only the endpoint metadata is injected.
onError
(error: Error) => void
Callback for non-fatal tracking errors. Tracking failures never block tool execution; use this callback when you want visibility into them.
Call order does not matter. withWaniwani walks existing _registeredTools at call time and intercepts registerTool for any future registrations. It is idempotent: a second call on the same server is a no-op.

Tracked event shape

FieldDescription
nameRegistered tool name
typeCategory derived from the toolType option
durationMsHandler execution time
status"ok" or "error"
errorMessagePresent when the handler threw or returned isError
inputTool arguments
outputHandler response
clientNameMCP client name (for example chatgpt, claude)
clientVersionMCP client version
sessionIdCorrelated from _meta (see Sessions)
Errors (thrown or { isError: true }) are tracked and then re-thrown or returned unchanged, so your tool’s error contract is preserved.

Custom events inside tools

For anything beyond tool.called, call client.track() from inside a handler. Pass meta: extra._meta so the event is linked to the active MCP session.
import { waniwani } from "@waniwani/sdk";

const wani = waniwani();

server.registerTool(
  "get_quote",
  { /* tool config */ },
  async ({ amount, currency }, extra) => {
    await wani.track({
      event: "quote.succeeded",
      properties: { amount, currency },
      meta: extra._meta,
    });

    return { content: [{ type: "text", text: `Quoted ${amount} ${currency}` }] };
  },
);

Session-scoped client in flows

Inside a flow node, ctx.waniwani is a scoped client that auto-attaches session correlation, so you can call ctx.waniwani.track({ event, properties }) without passing meta.