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.

Open source. Works without an API key. Connect the WaniWani Platform to add tracking, knowledge base, chat, and hosted state.
A flow is a graph of nodes that compiles into a single MCP tool. Each tool call runs the engine one step at a time: it asks a question, validates the answer, branches on state, then persists progress server-side under the MCP session id. The model never has to carry a token between calls.

Flows are MCP funnels

Most apps people build with flows are funnels: a sales funnel, a lead generation flow, a booking flow, an insurance quote flow, a pricing quote flow. The two vocabularies map cleanly:
Funnel termFlow term
Funnel step / pageNode
Drop-off / conversionEdge taken vs. abandoned
Form fieldInterrupt
Branching questionConditional edge
Funnel state (lead data)Flow state (typed)
If you’re building an MCP funnel, you’re building a flow. The rest of this page is the graph engine that powers it. Flows exist because multi-step conversations need explicit state. Whether your MCP server runs as a long-running process or a serverless function, anything longer than a single-shot Q&A (onboarding, quoting, qualification, triage, lead capture, booking) otherwise becomes a hand-rolled state machine serialized through the model.

What you declare

  1. State. The fields the flow collects, as a map of Zod schemas. State is type-inferred.
  2. Nodes. Handlers that return state updates, an interrupt signal, or a widget signal.
  3. Edges. How nodes connect. Direct or conditional.
You call .compile() on the graph to get a RegisteredFlow, then register it on your MCP server.

Minimal flow

import { createFlow, END, MemoryKvStore, START } from "@waniwani/sdk/mcp";
import { z } from "zod";

export const onboardingFlow = createFlow({
  id: "onboarding",
  title: "User Onboarding",
  description: "Use when a new user wants to get started.",
  state: {
    email: z.string().describe("Work email"),
    useCase: z.string().describe("What they want to build"),
  },
})
  .addNode({
    id: "ask_email",
    run: ({ interrupt }) =>
      interrupt({ email: { question: "What is your work email?" } }),
  })
  .addNode({
    id: "ask_use_case",
    run: ({ interrupt }) =>
      interrupt({
        useCase: {
          question: "What do you want to build?",
          suggestions: ["Analytics", "Support", "Lead capture"],
        },
      }),
  })
  .addEdge(START, "ask_email")
  .addEdge("ask_email", "ask_use_case")
  .addEdge("ask_use_case", END)
  .compile({ store: new MemoryKvStore() });
MemoryKvStore is fine for development and tests. For production, plug in a real backend (Redis, Upstash, Cloudflare KV, DynamoDB) or set WANIWANI_API_KEY to use hosted state. See KV store adapters. Register it on your server:
await onboardingFlow.register(server);
That is a complete, working flow. See Register for full server wiring.

Execution model

Every tool call drives the engine until it hits one of three outcomes:
  • Interrupt. Pause and ask the user one or more questions. The response carries status: "interrupt".
  • Widget. Pause and delegate rendering to a display tool. The response carries status: "widget".
  • Complete. The graph reached END. The response carries status: "complete" and server-side state is deleted.
Between those pause points, the engine auto-advances through action nodes (nodes that return a plain state update). No round-trip to the model. A single tool call can run several action nodes before hitting the next interrupt. State persists between calls in the flow store, keyed by the session id extracted from _meta. You pick the store at .compile() time. See KV store adapters and the Platform overview for the hosted option.

Tool contract

A compiled flow registers itself as a single MCP tool. The model calls it with { action: "start" | "continue", intent?, stateUpdates? } and receives one of four response statuses: interrupt, widget, complete, or error. You don’t usually parse responses yourself; the protocol is documented in the generated tool description so the model knows how to call it. See Flow tool contract for the full input/output schema.

Architecture

How a tool call drives the engine, and why the KV store is required.

State

Declare the fields the flow collects. Type inference flows from here.

Nodes

The three kinds of nodes and the context they receive.

Edges

Direct edges, conditional edges, loops, and compile-time validation.

Interrupts

Pausing, resuming, validation, and re-ask on error.

Register

Wire the compiled flow onto an MCP server end-to-end.