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

# Flows Overview

> Multi-step MCP tools for sales funnels, lead generation, booking, and quote apps. Defined as a graph.

<Note>
  **Open source.** Works without an API key. Connect the [Waniwani Platform](/sdk/platform/overview) to add tracking, knowledge base, chat, and hosted state.
</Note>

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 term              | Flow term                |
| ------------------------ | ------------------------ |
| Funnel step / page       | Node                     |
| Drop-off / conversion    | Edge taken vs. abandoned |
| Form field               | Interrupt                |
| Branching question       | Conditional 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

```ts theme={null}
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](/sdk/flows/kv-store).

Register it on your server:

```ts theme={null}
await onboardingFlow.register(server);
```

That is a complete, working flow. See [Register](/sdk/flows/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](/sdk/flows/kv-store) and the [Platform overview](/sdk/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](/sdk/reference/flow-tool-contract) for the full input/output schema.

## What to read next

<CardGroup cols={2}>
  <Card title="Architecture" icon="diagram-project" href="/sdk/flows/architecture">
    How a tool call drives the engine, and why the KV store is required.
  </Card>

  <Card title="State" icon="database" href="/sdk/flows/state">
    Declare the fields the flow collects. Type inference flows from here.
  </Card>

  <Card title="Nodes" icon="circle-nodes" href="/sdk/flows/nodes">
    The three kinds of nodes and the context they receive.
  </Card>

  <Card title="Edges" icon="arrow-right" href="/sdk/flows/edges">
    Direct edges, conditional edges, loops, and compile-time validation.
  </Card>

  <Card title="Interrupts" icon="hand" href="/sdk/flows/interrupts">
    Pausing, resuming, validation, and re-ask on error.
  </Card>

  <Card title="Register" icon="plug" href="/sdk/flows/register">
    Wire the compiled flow onto an MCP server end-to-end.
  </Card>
</CardGroup>
