Skip to main content
Glama
README.md
# Stick Director

Stick Director is an MCP-powered stick-figure flipbook toy. A user describes
an animation, connects an MCP agent, and watches the story, motion, frames, and
quality review appear in the browser.

This repository contains the complete toy:

- a Cloudflare Worker and MCP endpoint;
- one MCP tool, `flipbook_step`;
- temporary pairing sessions stored in Durable Objects;
- schemas and validation for stories, motion, poses, and review;
- SVG and JPEG renderers; and
- a framework-free browser interface in `site/`.

The Worker does not call a model. It returns structured requests through MCP,
accepts the agent's answers, validates them, and advances the flipbook.

Read the companion article, [You can just delegate
things.](https://oah.ai/posts/if-this-app-only-had-a-brain/), for the idea
behind the toy and its live prototype.

## Try it locally

```bash
npm install
npm run dev       # http://127.0.0.1:8787
```

Open the page and click **Wake it up**. Add the generated MCP URL to an agent,
then send the prompt shown on the page. The browser follows the session as the
agent works and plays the accepted frames when the flipbook is ready.

The generated MCP URL controls one temporary session. Treat it like a password.
The page also receives a read-only viewing URL and keeps the revoke URL in the
browser that created the session. Sessions expire after 24 hours.

## MCP flow

The agent uses the same tool for the entire run:

```text
flipbook_step({ op: "start", goal, frameCount })
        ↓
judgement_required: story_plan | motion_plan | frame_batch | quality_review
        ↓
flipbook_step({ op: "answer", continuation..., answer })
        ↓
validate and advance, or return the next required answer
        ↓
complete: viewerUrl
```

When more than one repair or next step is legal, the Worker can return the
technical routing request `meta.next_judgement`. Its answer selects one legal
contract and the useful context for it; it does not change flipbook state.

An answer call must echo the continuation values from the open request:

```text
flipbook_step({
  op: "answer",
  continuationToken: "<continuation.token>",
  requestId: "<continuation.requestId>",
  requestFingerprint: "<continuation.requestFingerprint>",
  answer: { /* payload matching request.program.outputSchema */ }
})
```

Continue until the result is `complete` or `blocked`. Use `inspect` to read an
existing project when debugging:

```text
flipbook_step({ op: "inspect", continuationToken: "<project id>" })
```

## Local viewer split

The Worker normally serves both the browser UI and API on port 8787. To test the
same UI from a separate static origin:

```bash
npm run viewer    # http://127.0.0.1:8788
```

Keep `npm run dev` running. `site/config.js` points the static viewer back to
the Worker API on port 8787.

## Deploy

Create the KV namespace used by the legacy direct MCP endpoint:

```bash
npx wrangler kv namespace create FLIPBOOKS
```

Put its id in `wrangler.jsonc`. Confirm that the three rate-limit
`namespace_id` values are unused in the Cloudflare account, then deploy:

```bash
npm run deploy
```

Wrangler uploads `site/` as Worker Static Assets. The Durable Object binding
and its `v1` SQLite migration are already declared.

The interactive page creates scoped MCP sessions at `/mcp/s/<capability>`.
The unscoped `/mcp` route remains for direct and legacy clients.

See [INTERNALS.md](./INTERNALS.md) for the implementation map.