A flow is a single MCP tool. The model calls that tool, the engine drives the graph one step at a time, and the response tells the model whether to ask the user something, render a widget, or stop. Between calls, everything the engine knows about the conversation lives in the KV store, keyed by the MCP session id. This page walks through one full round-trip. If you understand the picture below, debugging “why doesn’t the flow remember what I said?” becomes trivial.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.
The pieces
The KV store is the only place the flow engine looks for state between two tool calls. Your MCP server itself can be either stateful (a long-running process, e.g. stdio or single-instance HTTP) or stateless (a serverless function where every call is a fresh process). The engine doesn’t assume either — it always requires aKvStore. In a stateful runtime, MemoryKvStore is enough, because the process holds the state across calls. In a stateless runtime, you need a real backing store (Redis, Upstash, Cloudflare KV, DynamoDB, or WaniwaniKvStore) so state survives between cold starts and instances.
First call: action: "start"
After this call, the KV holds:
Next call: action: "continue"
Every continue is the same shape: load → apply updates → advance → save → respond.
When the engine reaches END, the response status is "complete" and the KV key is deleted, so a stale continue call can’t accidentally resume a finished session.
What’s in the stored value
The value at a given session key is small and deterministic:_meta.waniwani/sessionId, or Mcp-Session-Id from the transport). One session, one key, one ongoing flow run.
What’s not in the KV store
The KV powers the flow engine only. The following live in a separate pipeline:- Tracking events.
tool.called,quote.succeeded, etc. are emitted bywithWaniwani(server)andclient.track(), batched, and POSTed to the events endpoint. Independent of the KV store. - Funnel analytics and dashboards. Built from tracking events on the server side. Reading them never touches your KV.
- Knowledge base content. Lives in WaniWani’s hosted KB service, unrelated to flow state.
withWaniwani(server) with WANIWANI_API_KEY set. The two systems compose.
Common gotchas
Flow appears to 'forget' between turns
Flow appears to 'forget' between turns
The KV reset between calls. Two common causes:
MemoryKvStorein a serverless runtime. Each invocation is a fresh process, so the in-memoryMapis empty every time. Use a real backend (Redis, Upstash, Cloudflare KV) or setWANIWANI_API_KEYto useWaniwaniKvStore.- Missing session id in
_meta. If the MCP client doesn’t propagate a session id, the engine derives a different key on every call.withWaniwani(server)bridges the transport-level session id into_metafor you. Install it.
Every tool call starts a new flow
Every tool call starts a new flow
Same root cause as above (missing or changing session id), but with a different symptom: the engine never finds a stored value, so it always behaves like
action: "start". Inspect what your transport puts in Mcp-Session-Id and what arrives in _meta on the server. They should be stable across a conversation.State doesn't match the schema after a deploy
State doesn't match the schema after a deploy
You changed the flow’s state schema, then deployed. Sessions that were mid-flow have stored values that no longer match the new schema. The safest fix is to invalidate old sessions: delete the prefix in your KV, or version your flow id (
"onboarding_v2").Concurrent calls on the same session
Concurrent calls on the same session
MCP clients serialize calls within a conversation, so concurrent
continues on one session are rare. If you see racy reads/writes in your KV, the cause is usually two clients sharing one session id by accident (often a misconfigured proxy). Make sure each conversation has a unique session id.Widget never resolves
Widget never resolves
A
showWidget node stores pendingWidget in the KV and returns status: "widget". The engine resumes only when the next continue carries stateUpdates that fill the field declared in showWidget(...). If your display tool never writes that field, the flow stays paused forever. Confirm the display tool’s handler is producing the expected key in stateUpdates.Next
Nodes
The three kinds of nodes and the context they receive.
KV store adapters
Pick a backend or write your own in 10 lines.
Tool contract
Wire-level reference for
FlowToolInput and response statuses.WaniWani Platform
What runs without an API key, what unlocks when you connect the Platform.