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

# Client & KV store reference

> Type signatures for the waniwani() client and the KvStore interface.

This page is the type-level reference for the two main extension points in the SDK: the `KvStore` interface (any flow uses it) and the `waniwani()` client (used by [Platform](/sdk/platform/overview) features).

## `KvStore` interface

```ts theme={null}
export interface KvStore<T = Record<string, unknown>> {
  get(key: string): Promise<T | null>;
  set(key: string, value: T): Promise<void>;
  delete(key: string): Promise<void>;
}
```

That is the entire contract. The engine handles serialization, session-key derivation, expiry semantics, and concurrency. Implement it against any backend with those three operations. See [KV store adapters](/sdk/flows/kv-store) for ready-made recipes.

### Built-in implementations

* **`MemoryKvStore`** from `@waniwani/sdk/mcp`. An in-memory `Map`. Resets on process restart.
* **`WaniwaniKvStore`** from `@waniwani/sdk/mcp`. Persists against `app.waniwani.ai`. Selected automatically when `WANIWANI_API_KEY` is set and no explicit `store` is passed.

### What gets stored

Each session maps to one key. The engine stores:

* The current node (or `END` when complete)
* The merged state object so far
* A pending widget reference, if the current node is a `showWidget` step

Keys are derived from the MCP session identifier (`_meta.waniwani/sessionId` or `Mcp-Session-Id`), so isolating sessions is automatic. Values are JSON-serializable plain objects.

## `waniwani()` client

```ts theme={null}
import { waniwani } from "@waniwani/sdk";

const wani = waniwani({
  apiKey: process.env.WANIWANI_API_KEY,
  apiUrl: "https://app.waniwani.ai",
  tracking: {
    flushIntervalMs: 500,
    maxBatchSize: 50,
    maxRetries: 5,
    shutdownTimeoutMs: 5000,
  },
});
```

All fields are optional. With no arguments, the client reads `WANIWANI_API_KEY` from the environment and targets `https://app.waniwani.ai`.

<ResponseField name="apiKey" type="string">
  Your MCP environment API key. Defaults to `process.env.WANIWANI_API_KEY`. If both are missing, `track()` / `flush()` / `shutdown()` throw at runtime.
</ResponseField>

<ResponseField name="apiUrl" type="string" default="https://app.waniwani.ai">
  Base URL of the Waniwani API. Override for self-hosted deployments or staging backends.
</ResponseField>

<ResponseField name="tracking" type="object">
  Fine-tune the event tracking transport.

  <Expandable title="tracking options">
    <ResponseField name="endpointPath" type="string" default="/api/mcp/events/v2/batch">
      Path appended to `apiUrl` when POSTing batches.
    </ResponseField>

    <ResponseField name="flushIntervalMs" type="number" default={1000}>
      How often the buffered transport flushes events.
    </ResponseField>

    <ResponseField name="maxBatchSize" type="number" default={20}>
      Maximum events per HTTP request.
    </ResponseField>

    <ResponseField name="maxBufferSize" type="number" default={1000}>
      Maximum in-memory buffer size. Oldest events are dropped beyond this.
    </ResponseField>

    <ResponseField name="maxRetries" type="number" default={3}>
      Retry attempts on transient failures.
    </ResponseField>

    <ResponseField name="retryBaseDelayMs" type="number" default={200}>
      Base delay for exponential backoff between retries.
    </ResponseField>

    <ResponseField name="retryMaxDelayMs" type="number" default={2000}>
      Maximum backoff delay between retries.
    </ResponseField>

    <ResponseField name="shutdownTimeoutMs" type="number" default={2000}>
      How long `shutdown()` waits for in-flight events before returning.
    </ResponseField>
  </Expandable>
</ResponseField>

## Graceful shutdown

In Node environments, the SDK attaches handlers for `beforeExit`, `SIGINT`, and `SIGTERM` that flush buffered events automatically. For serverless, edge runtimes, tests, or short-lived scripts, call `shutdown()` explicitly:

```ts theme={null}
const result = await wani.shutdown({ timeoutMs: 5000 });
// => { timedOut: false, pendingEvents: 0 }
```
