> ## 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.

# Wrap your MCP server

> Optional convenience wrapper that adds tool tracking, session bridging, and widget metadata forwarding.

`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.

<Note>
  **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.
</Note>

## 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.

```ts theme={null}
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 want                                            | Use `withWaniwani`?                          |
| --------------------------------------------------- | -------------------------------------------- |
| Only run OSS flows, no telemetry                    | Optional (still useful for session bridging) |
| OSS flows + Platform tracking                       | Yes                                          |
| Hosted everything (flow state + tracking + funnel)  | Yes                                          |
| Just custom event tracking via `waniwani().track()` | No, call `track()` directly                  |

## Options

```ts theme={null}
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),
});
```

<ResponseField name="client" type="WaniWaniClient">
  The client used for tracking. Defaults to `waniwani()`, which picks up `WANIWANI_API_KEY` from the environment.
</ResponseField>

<ResponseField name="toolType" type="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.
</ResponseField>

<ResponseField name="metadata" type="Record<string, unknown>">
  Extra fields merged into every `tool.called` event. Use this to tag deployments, tenants, or releases.
</ResponseField>

<ResponseField name="flushAfterToolCall" type="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.
</ResponseField>

<ResponseField name="injectWidgetToken" type="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.
</ResponseField>

<ResponseField name="onError" type="(error: Error) => void">
  Callback for non-fatal tracking errors. Tracking failures never block tool execution; use this callback when you want visibility into them.
</ResponseField>

<Tip>
  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.
</Tip>

## Tracked event shape

| Field           | Description                                                      |
| --------------- | ---------------------------------------------------------------- |
| `name`          | Registered tool name                                             |
| `type`          | Category derived from the `toolType` option                      |
| `durationMs`    | Handler execution time                                           |
| `status`        | `"ok"` or `"error"`                                              |
| `errorMessage`  | Present when the handler threw or returned `isError`             |
| `input`         | Tool arguments                                                   |
| `output`        | Handler response                                                 |
| `clientName`    | MCP client name (for example `chatgpt`, `claude`)                |
| `clientVersion` | MCP client version                                               |
| `sessionId`     | Correlated from `_meta` (see [Sessions](/sdk/tracking/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.

```ts theme={null}
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](/sdk/flows/overview) node, `ctx.waniwani` is a scoped client that auto-attaches session correlation, so you can call `ctx.waniwani.track({ event, properties })` without passing `meta`.
