Skip to main content
Glama
JRTrippett

StateForge

by JRTrippett
README.md
# MCP StateForge

![StateForge demo](docs/demo.gif)

Build, debug, and deploy stateless MCP servers against the **2026-07-28** specification.

``` bash
npm create stateforge@latest my-server
cd my-server && npm run dev
```

Scaffolds a TypeScript + Express MCP server with a pluggable `StateStore`, a working multi-turn example built on explicit state handles, a visual inspector, and deploy configs for Vercel and Docker.

------------------------------------------------------------------------

## Why this exists

The 2026-07-28 revision removed protocol-level sessions. No `initialize` handshake, no `Mcp-Session-Id`, no held-open bidirectional stream. Every request is self-describing and can land on any instance behind a plain load balancer.

That is a large win for scaling and a real problem for anything multi-turn. The pattern that replaces sessions is the **explicit state handle**: a tool mints `cart_7f3a9c2e5b1d`, returns it, and every later tool takes it as an argument. The model can see the handle, hold several at once, and thread them between tools.

StateForge is that pattern, written out properly — with the storage abstraction, the handle validation, the multi-round-trip confirmation flow, and the debugging surface that a stateless server otherwise has no way to give you.

------------------------------------------------------------------------

## Repository layout

```         
packages/create-stateforge/   The CLI. Zero runtime dependencies.
templates/default/            What gets scaffolded. A complete, working server.
```

`templates/default` is deliberately **not** a pnpm workspace member: it is copied verbatim into a user's project, so its dependencies must resolve on their own.

------------------------------------------------------------------------

## The CLI

```         
create-stateforge [directory] [options]

  --name <name>          npm package name (default: the directory name)
  --store <memory|redis> State backend to configure (default: memory)
  --pm <npm|pnpm|yarn|bun>
                         Package manager to install with (default: detected)
  --no-install           Skip dependency installation
  --no-git               Skip `git init`
  -y, --yes              Accept every default, ask nothing
```

Interactive by default; falls back to defaults automatically when stdin is not a TTY, so it works unattended in CI.

It has no runtime dependencies. A `create-*` package is the first thing a user runs, and every dependency is install latency they pay before seeing anything.

------------------------------------------------------------------------

## What gets generated

A server whose whole point is that it holds nothing between requests:

-   **`StateStore`** — `get` / `set` / `update` / `delete` / `list` / `clear`. In-memory implementation for local work (per-key locking, TTL, versioning); Redis implementation for production (Lua compare-and-swap, `SCAN` rather than `KEYS`). Swapping backends is one environment variable.
-   **Explicit handles** — `mintHandle('cart')` produces `cart_<12 hex>`; `assertHandle` validates shape before the value is ever used as a store key.
-   **A shopping cart example** — `create_cart`, `add_item`, `set_item_quantity`, `remove_item`, `view_cart`, `abandon_cart`, `checkout`. Every mutating tool requires the handle.
-   **Multi-round-trip checkout** — returns `input_required`, the client answers and retries, HMAC-signed `requestState` carries the quoted total so a cart that moved mid-flow gets re-quoted instead of charged.
-   **The inspector** — live `tools/list`, schema-generated call forms, and a state browser where records are editable JSON. Single HTML file, no build step.
-   **Deployment** — `api/mcp.ts` (four lines, because `handler.fetch` is already web-standard), `vercel.json`, and a multi-stage Dockerfile that runs unprivileged with a healthcheck.
-   **Tests** — domain, store, and end-to-end JSON-RPC through the real handler.

Full details in [`templates/default/README.md`](templates/default/README.md).

------------------------------------------------------------------------

## Developing StateForge

``` bash
pnpm install                 # workspace packages
pnpm template:install        # the template's own dependencies
pnpm typecheck               # CLI + template
pnpm test                    # the template's test suite
pnpm --filter create-stateforge build
```

`build` compiles the CLI and copies `templates/default` into the package as `template/`, so the published tarball is self-contained.

To try the CLI against the working tree:

``` bash
pnpm --filter create-stateforge dev -- /tmp/try-it --yes --no-install
```

### Bumping the protocol revision

`MCP_PROTOCOL_VERSION` in `packages/create-stateforge/src/scaffold.ts` is the single source of truth. It is substituted into the template as `{{mcpProtocolVersion}}` wherever the revision appears. Bump it alongside the SDK dependencies in `templates/default/package.json`.

------------------------------------------------------------------------

## Reference

-   [The 2026-07-28 specification](https://modelcontextprotocol.io/specification/2026-07-28)
-   [Release notes](https://blog.modelcontextprotocol.io/posts/2026-07-28/)
-   [TypeScript SDK v2](https://ts.sdk.modelcontextprotocol.io/v2/)

MIT licensed.