Skip to main content
Glama
README.md
<div align="center">

<img src="docs/assets/banner.svg" alt="Strata — verified backend modules, composed and proven" width="620">

### Your agent writes the backend. Strata proves it runs.

An MCP server that composes verified backend modules into your codebase — reading your schema,
following your conventions, wiring them in the order Express actually requires — then writes one
command that boots the app and exercises every requirement against a live server.

<br>

[![npm](https://img.shields.io/npm/v/stratalib?style=flat-square&color=5EE7FF&label=npm&logo=npm&logoColor=white)](https://www.npmjs.com/package/stratalib)
[![node](https://img.shields.io/badge/node-%E2%89%A5%2018-5EE7FF?style=flat-square&logo=nodedotjs&logoColor=white)](https://nodejs.org)
[![MCP](https://img.shields.io/badge/protocol-MCP-F5C96A?style=flat-square)](https://modelcontextprotocol.io)
[![license](https://img.shields.io/badge/license-AGPL--3.0-7CF29A?style=flat-square)](LICENSE)
[![site](https://img.shields.io/badge/stratalib.com-E6EDF3?style=flat-square)](https://stratalib.com)
[![x](https://img.shields.io/badge/%40stratalib-0B0F14?style=flat-square&logo=x&logoColor=white)](https://x.com/stratalib)

[![version](https://img.shields.io/badge/v1.1-5EE7FF?style=flat-square)](CHANGELOG.md)
[![consistency](https://img.shields.io/badge/run--to--run%20spread-0.0%20pts-7CF29A?style=flat-square)](docs/BENCHMARK.md)
[![tokens](https://img.shields.io/badge/tokens-%E2%88%9266%25-7CF29A?style=flat-square)](docs/BENCHMARK.md)
[![turns](https://img.shields.io/badge/turns-%E2%88%9252%25-7CF29A?style=flat-square)](docs/BENCHMARK.md)
[![latency](https://img.shields.io/badge/wall--clock-3%C3%97%20faster-7CF29A?style=flat-square)](docs/BENCHMARK.md)
[![assertions](https://img.shields.io/badge/adversarial%20assertions-1%2C264-F5C96A?style=flat-square)](docs/BENCHMARK.md)
[![gates](https://img.shields.io/badge/admission%20gates-6-E6EDF3?style=flat-square)](#admission-gates)

</div>

<br>

```console
$ # your agent calls one tool, once
  strata_use  dir=./shop-api  task="product list API"
              capabilities=[ "cursor pagination with sorting",
                             "per-IP rate limiting",
                             "structured request logging" ]

  FILES CREATED
    server.js
    strata/lib.js       — the implementation these import from
    strata/verify.js    — boots the app and exercises the feature end to end

$ npm install && node strata/verify.js

  PASS  unit selftests — 3 passed, 0 failed
  PASS  server boots and answers /health
  PASS  correlation id honours an inbound x-request-id
  PASS  an authorization header is NOT written to the log
  PASS  a password in a request BODY is NOT written to the log
  PASS  a malformed body is a 4xx and leaks no stack trace to the caller
  PASS  /items walks pages by cursor without repeating a row
  PASS  a sort field that is not allowlisted is REJECTED, not honoured
  PASS  a burst past capacity yields 429 + Retry-After

  12/12 checks passed — the delivered feature works end to end.
```

<div align="center">
<sub>Real output. Those check names are the whole idea — anyone can generate pagination,<br>
the question is whether page two repeats page one.</sub>
</div>

---

**Key capabilities**

- **Schema-aware composition** — reads Prisma, Mongoose, Drizzle, TypeORM, Sequelize or plain JS and wires modules against your real entity, fields and ID column
- **Correct middleware ordering** — logging above body parsing, rate limits above routes, error handlers last, enforced by rank rather than left to the model
- **Generated end-to-end verifier** — `strata/verify.js` boots the app on a free port and drives every requirement against it
- **Six machine-checked admission gates** — no module reaches your project without passing all of them
- **Honest declines** — refuses roughly a third of tasks, where composing costs more than writing the code

<br>

```console
$ # asked for something the library does not cover
  strata_use  task="slugify helper"  capabilities=["convert a string to a url slug"]

  No verified Strata recall covers "slugify helper". Build it from scratch the
  normal way — a clean hand-written implementation is the right outcome here,
  not a forced match.
```

<div align="center">
<sub>Refusing is a feature. One module is not worth the cost of reading and verifying it,<br>
and a tool that always says yes is a tool you stop trusting.</sub>
</div>

- **Local by construction** — your source and schema never leave the machine; only the task text is sent

---

## The numbers

<div align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="docs/assets/savings-dark.svg">
    <img src="docs/assets/savings-light.svg" alt="Same task, same model: 66% fewer tokens, 52% fewer turns, 67% less wall-clock time." width="760">
  </picture>
</div>

<div align="center">

| | without Strata | **with Strata** | |
|---|---|---|---|
| **tokens** | 950,011 | **319,604** | **−66%** |
| **turns** | 31.3 | **15.0** | **−52%** |
| **wall-clock** | 190s | **63s** | **3× faster** |
| **cost** | $0.190 | **$0.077** | **−59%** |
| **checks passed** | 70.8% | **100%** | |

</div>

One backend task — a product API with pagination, per-IP rate limiting and request logging. Claude Haiku 4.5, three runs per arm, mean. **Every number is lower and the quality is higher.**

Tokens are the whole session: input, output and the cached context re-read on every turn. Across all 18 runs in this battery, **98–99% of a session's tokens are that re-read context** — output is under 2%. So output length is not the lever; turns are, and fewer turns is the same thing as fewer tokens is the same thing as less money.

---

## What the failed checks actually were

A score is easy to wave away. These are the failures themselves, re-graded from the archived trees. Every one is code that runs, answers 200-or-201, and looks finished.

### A malformed request returns your stack trace

One request with a truncated body. **Both apps answered 400 — only one of them is safe.**

<table>
<tr><th align="left" width="50%">without Strata</th><th align="left" width="50%">with Strata</th></tr>
<tr valign="top"><td>

```html
<!DOCTYPE html>
<html lang="en"><head><title>Error</title></head>
<body>
<pre>SyntaxError: Unexpected end of JSON input
    at JSON.parse (&lt;anonymous&gt;)
    at parse (C:\Users\...\node_modules\body-parser
              \lib\types\json.js:96:19)
    at C:\Users\...\body-parser\lib\read.js:128:18
    at AsyncResource.runInAsyncScope (node:async_...
```

HTML from a JSON API, the parser's internals, and **absolute paths from your server's filesystem** — handed to whoever sent the bad byte.

</td><td>

```json
{
  "error": "malformed JSON in request body",
  "details": [
    { "field": "body",
      "message": "could not be parsed as JSON" }
  ]
}
```

The same 400, in the same envelope as every other error, telling the caller what to fix and nothing else.

</td></tr>
</table>

**Failed in 6 of 6 unaided runs, across both tasks. Passed in 6 of 6 with Strata.** The grader records it as `LEAKS STACK TRACE`; nothing in the session's own output mentions it.

### A retried order with a different body was accepted anyway

```
POST /orders   Idempotency-Key: k-1   {"items":[ A ]}   →   201 Created
POST /orders   Idempotency-Key: k-1   {"items":[ B ]}   →   200 OK   ← order A returned
```

That is the subtle half of idempotency, and the half a naive implementation misses entirely. The client asked for a **different** order and was told its request succeeded. Nothing errors, nothing logs. Order B simply never exists, and the caller holds a 200 saying it does. The correct answer is 409 or 422.

**Failed in 3 of 3 unaided runs. Passed in 3 of 3 with Strata.**

### The API ignored the page size it was asked for

```
GET /products?limit=5   →   200 OK, 10 items
```

Pagination that returns whatever it likes. Nothing errors, nothing logs, and the bug reaches whoever consumes that endpoint. One unaided run in three.

### The database schema was edited, unasked

The task was *"if a client retries the same order request it should not create two orders."* It never mentions the data model. One unaided run in three rewrote **`prisma/schema.prisma`**; no Strata run touched it.

The wider problem is that you cannot predict which files come back changed. Across three runs of the same prompt, the unaided arm touched **six different files — and only three of them in every run**. Strata touched the same ten files in all three runs: an identical footprint, run to run.

---

## Run it three times. Get the same answer three times.

<div align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="docs/assets/consistency-dark.svg">
    <img src="docs/assets/consistency-light.svg" alt="Quality of every individual run. Without Strata the results scatter; with Strata every run of a task lands on the same score." width="760">
  </picture>
</div>

| task | without Strata | **with Strata** |
|---|---|---|
| product API | 63%, 75%, 75% | **100%, 100%, 100%** |
| idempotent orders | **14%**, 71%, 71% | **100%, 100%, 100%** |
| payments + queue | 0%, 0%, 50% | 0%, 100%, 100% |

**Zero variance on the tasks the library covers.** Three runs of the same prompt return the same score, three times out of three — against a spread of 26.9 points without it.

That **14%** is not a grading artefact; it repeats on re-grade. That session invented an order API whose create endpoint rejected every request shape it was sent, and because everything else depends on creating an order, five checks collapsed at once. **A cliff, not a slightly worse result** — and nothing in the session's own output says it happened.

---

## Where it does not help

**Payments is on the board with its failures intact.** Both arms shipped a build that does not run: one unaided run never wrote an entry point, and one Strata run pinned `bullmq@5.81.3` beside an incompatible `redis@4.7.1`, which cannot install. Both packages are the model's choice — Strata covers the webhook and nothing else on that task, and the cost ratio lands at 0.95×, a wash.

That is the rule the whole board obeys: **the advantage tracks how much of the task the library covers.** Where coverage is high the numbers above hold. Where it is one capability out of four, Strata is roughly free and roughly neutral.

**Strata also declines outright** when a task is below the point where composing beats writing — about a third of the time.

### Method

Checks were written from the task prompt alone and frozen before the first run. Every check has a negative control proving it can fail. Grading is a separate suite — never `strata/verify.js`, which Strata generates and which would be marking its own homework. Every output tree is archived.

`n=3`, Claude Haiku 4.5, one model per cell. Nothing here speaks to Sonnet or Opus. Cost and token figures move with the model and the prompt; the consistency figures do not.

Full method, per-run scores and every instrument defect found along the way: [`docs/BENCHMARK.md`](docs/BENCHMARK.md).

## Quick start

**Prerequisites:** Node.js ≥ 18 and any MCP client — Claude Code, Cursor, Windsurf, VS Code or Claude Desktop.

```jsonc
// .mcp.json  (or claude_desktop_config.json for Claude Desktop)
{
  "mcpServers": {
    "strata": { "command": "npx", "args": ["-y", "stratalib"] }
  }
}
```

Restart the client and ask for a backend feature that needs several parts:

```
Add cursor pagination, per-IP rate limiting and request logging to the products API.
```

Strata reads the project, composes the modules, writes the files, and prints what it created and what it modified. Then:

```bash
npm install && node strata/verify.js
```

> [!NOTE]
> No API key and no account. Modules are served from the hub; the task text is the only thing sent. Your source, schema and files stay on your machine.

---

## The tool

Strata registers exactly one tool. Every tool in an MCP schema is billed on every turn, so the surface is kept to one that does the whole job.

**`strata_use`**

| Argument | Purpose |
|---|---|
| `dir` | Absolute path to the project root — where the schema and conventions are read from |
| `task` | A short label for the work |
| `capabilities` | 3–6 phrases naming the parts of the job. Your model writes these; it has read the whole task |

Returns the files created and modified, the exports available from each module, and the command to verify the result.

---

## How it works

**1 · Reads the project** — locates the ORM and extracts the real entity: fields, types, enums and the actual ID column. Deterministic, in Node, before the model sees a byte. Where the entity cannot be identified with confidence, Strata leaves a slot rather than guessing.

**2 · Selects modules** — each capability phrase is scored against the library, and anything matching on shared vocabulary alone is discarded. Fewer than two surviving modules triggers a decline.

**3 · Composes** — modules contribute to the app rather than owning it, each contribution carrying a rank that fixes its position in the middleware chain. A malformed request throws during body parsing, so logging mounts above it; get that backwards and the one request most worth tracing is the one that loses its correlation id.

**4 · Writes the verifier** — `strata/verify.js` runs each module's own suite, boots the app on a free port, and exercises every requirement against it. Built against your entity, so the checks run on your fields and your routes.

---

## Admission gates

Every module passes six machine-checked gates before it can be served. A module that fails is discarded, not repaired — hand-patching generated modules returns coverage to craft and stops it scaling.

| Gate | Requirement |
|---|---|
| Exports | Loads, and every export it declares resolves at runtime |
| Selftest | Its own suite passes, with a stable assertion count across five runs |
| Adversarial | ≥ 8 assertions, hostile inputs, and assertions that something must **not** happen |
| Compose | Valid fragments with ranks, and declared factories that exist |
| Collisions | No exported name collides with another module |
| Composed boot | Composes with two others into an app that starts and verifies |

The adversarial gate is the one that matters. Every hand-written module in this library shipped with a real bug its own tests did not catch — a 404 that reset a circuit breaker's failure count, a dropped enum constraint, an attacker-controlled request id echoed into a response header. A confirmatory suite admits exactly those.

---

## Repository layout

| Path | Contents |
|---|---|
| `src/` | MCP server: project reading, selection, composition, verifier generation |
| `bin/` | CLI entry point |
| `templates/` | Express skeleton used during composition |
| `benchmark/` | Pre-registered check suites, negative controls, run records and archived output trees |
| `scripts/` | Admission gates, library indexing, selection tests |

Modules are served from the hub; the task text is the only thing sent. Your source, schema and files stay on your machine.
## Documentation

| Document | Subject |
|---|---|
| [`docs/BENCHMARK.md`](docs/BENCHMARK.md) | The full benchmark: method, per-run scores, and every instrument defect found |
| [`CHANGELOG.md`](CHANGELOG.md) | What shipped in each release |

---

## Development

```bash
npm install
node --max-old-space-size=8192 node_modules/typescript/bin/tsc -p tsconfig.mcp.json   # build
node scripts/admit-recall.js recalls/<domain>/<name>/v1                                # run the gates
node benchmark/quality/negative-control.js                                             # prove the checks can fail
node benchmark/run-quality-battery.js --tasks catalog --max 3                           # collect runs
```

`STRATA_MODE=local` composes against a local `recalls/` checkout instead of the hub — required when testing a module that has not been deployed.

---

## Acknowledgements

Built on the Model Context Protocol, Express, Prisma, Mongoose, Drizzle, TypeORM and Sequelize.

## License

AGPL-3.0-or-later. See [`LICENSE`](LICENSE).

---

<div align="center">
<sub>
<a href="https://stratalib.com">stratalib.com</a> · <a href="https://www.npmjs.com/package/stratalib">npm</a> · <a href="https://x.com/stratalib">@stratalib</a>
</sub>
</div>