Stratum MCP
UnexploredStructured execution for coding agents: contracts, postconditions, gates, guardrails.
Install
Terminal
$npx -y @smartmemory/stratummcp_config.json
{
"mcpServers": {
"ai-smartmemory-stratum-mcp": {
"args": [
"-y",
"@smartmemory/stratum"
],
"command": "npx"
}
}
}Documentation
Stratum
State machine dispatch server for AI agent workflows.
Your agent proposes the step. Stratum decides whether it actually finished.
Stratum gives AI coding agents (Claude Code, Codex, etc.) a formal execution model. Instead of improvising a plan and retrying blindly, the agent writes a typed spec, the server tracks state, enforces postconditions, and returns structured failure context on retry. Every step produces an auditable trace record.
Where it sits. Stratum is the execution kernel, one layer below the thing most people run day to day. Compose drives the product lifecycle (design, blueprint, plan, review gates) and calls Stratum to execute each step. Reach for Stratum directly when you want the state machine and the postconditions without a lifecycle on top of them.
The founding intent behind this machinery is recorded in docs/VISION.md: a spec language that keeps LLMs on rails invisibly, so the same conversation yields stronger results than freeform execution.
One shipped component:
ts/— the TypeScript engine (@smartmemory/stratum): IR validation (version: 1specs), flow execution with ensure postconditions, MCP server for Claude Code,query/gate/guardCLI, background flows and background agent runs. Published to npm as@smartmemory/stratum(bins:stratum,stratum-mcp) and listed in the MCP registry asai.smartmemory/stratum-mcp.
Engine status (2026-07-18, STRAT-PY-RETIRE): the TS engine is the ONLY engine. The Python library (
stratum-py) and Python MCP server (stratum-mcp) are retired. Their source is archived on thepython-legacybranch, and PyPI packages are frozen at their final releases. The engine executesversion: 1specs exclusively. Legacy v0.x specs are rejected byvalidateand classified (report-only) bystratum migrate --check. The authoritative v1 shape is the Zod IR schema ints/src/ir/plusstratum validateoutput.
Governed workflows as auditable flows — on any agent, not just one vendor. Unlike a single-vendor in-context orchestrator, Stratum runs as an MCP server and a library under Claude Code, Codex, or any MCP host; enforces typed contracts and ensure postconditions on every flow execution; stops at real human gates; dispatches Claude and Codex agents in one flow (so an independent reviewer can be a different model from the implementer); and persists flow state across sessions. Where you want raw in-context fan-out, reach for an in-host workflow runtime; where you want the run governed, portable, and auditable, that's a Stratum workflow.
Table of Contents
- Installation
- Quick Start
- Core Concepts
- YAML Spec Reference
- MCP Tools API
- Step Types
- Ensures (Postconditions)
- Contracts and Output Validation
- Gates (Human-in-the-Loop)
- Flow Composition
- Routing
- Iterations
- Checkpoints
- Recovery and Retry Logic
- Workflows
- Task Compiler
- Skills
- CLI Reference
- Configuration
- Python Library (Track 1)
- Examples
- Development
- License
Installation
Install from npm (Node >= 22):
npm install -g @smartmemory/stratum # provides `stratum` (CLI) and `stratum-mcp` (MCP server)
Or run from a checkout for development:
git clone https://github.com/smartmemory/stratum
cd stratum/ts && npm install # or pnpm install
Requires node >= 22 (erasable-syntax type stripping; node >= 24 needs no flags — the CLI
bootstrap gates --experimental-transform-types automatically).
MCP Server (for Claude Code)
Register the server in your project's .mcp.json. From the npm package:
{
"mcpServers": {
"stratum": {
"command": "npx",
"args": ["-y", "-p", "@smartmemory/stratum", "stratum-mcp"]
}
}
}
From a checkout:
{
"mcpServers": {
"stratum": {
"command": "node",
"args": ["/absolute/path/to/stratum/ts/src/mcp/bin.mjs"]
}
}
}
Restart Claude Code to activate. Optionally append the Stratum execution model block to your CLAUDE.md.
CLI
stratum help # validate | migrate | query | gate | guard | watch (npm install)
node ts/src/cli/bin.mjs help # same, from a checkout
From a checkout, a thin wrapper script (e.g. ~/bin/stratum-ts) pointing at ts/src/cli/bin.mjs avoids a PATH collision with the installed stratum bin.
Quick Start
When Claude Code has Stratum installed, it uses it automatically for non-trivial tasks:
- Claude writes a
.stratum.yamlspec internally (never shown to you) - Calls
stratum_planto validate the spec and get the first step - Executes each step using its own tools (reading files, writing code, running tests)
- Calls
stratum_step_doneafter each step -- the server checks postconditions - If a postcondition fails, Claude gets back the specific violation and retries
- Calls
stratum_auditat the end for a full execution trace
You see plain English narration throughout. The spec, state management, and postcondition enforcement happen behind the scenes.
Core Concepts
Specification vs Flow
A specification is the authored, version-controlled .stratum.yaml document. It declares contracts and one or more flows. The flows.entry field selects the flow that starts a run.
A flow is an executable directed acyclic graph of steps. Running the entry flow creates a persisted run with a runId. The v0.x top-level workflow: registration block and stratum_list_workflows were retired with the Python server.
Flows
A flow declares typed input fields, a typed output, optional limits, and steps. References and after lists form data and ordering edges. Gate routing and on_fail add explicit routing edges.
Steps
A step has an id, optional after dependencies, an optional when condition, and exactly one construct: do, set, gate, fanout, or run.
Tasks
A do step is an agent-dispatched task. The task text is declared inline, and ${...} references inject flow input or prior step output values. The v0.x functions: registry and function: steps have no place in a v1 document.
Contracts
Contracts define named output shapes. A do or set step declares its output contract with out. A flow declares both the step-output reference that supplies its result and the contract used to validate that result.
Ensures
Ensures are structured postconditions on do, set, and fanout stage results. V1 supports expression, file existence, file content, and judged predicates.
Retries
A do or fanout step can set the positive integer attempts limit. The default is two attempts. Contract failures, ensure failures, task failures, and exhausted iterations use the same failure path. A deterministic set failure terminates the flow without retrying.
Gates
Gate steps pause execution for an external approve, revise, or kill decision. Approve and kill routes may name a later step or use null. A revise route may name a strict ancestor and requires a flow-level max_rounds limit.
YAML Spec Reference
The Zod IR schema in ts/src/ir/ and the errors produced by stratum validate are authoritative. The root is strict and has exactly three fields: version, contracts, and flows. Unknown fields are rejected.
Minimal Example
version: 1
contracts:
SentimentResult:
label: string
confidence: number
flows:
entry: classify
classify:
input:
text: string
output:
from: "${classify_text.output}"
contract: SentimentResult
steps:
- id: classify_text
do: "Classify the sentiment of ${input.text}"
agent: claude
out: SentimentResult
ensure:
- expr: "result.label != ''"
- expr: "result.confidence > 0.7"
attempts: 2
Full Example with a Gate
version: 1
contracts:
WorkOutput:
result: string
quality_score: number
flows:
entry: reviewed_work
reviewed_work:
input:
text: string
output:
from: "${work.output}"
contract: WorkOutput
max_rounds: 3
steps:
- id: work
do: "Produce the deliverable requested in ${input.text}"
agent: codex
out: WorkOutput
ensure:
- expr: "result.quality_score >= 0.8"
attempts: 3
- id: review
after: [work]
gate:
on_approve: null
on_revise: work
on_kill: null
max_rounds: 2
Full Field Reference
version (required)
The only accepted value is the number 1. Quoted strings such as "1" and all v0.x values are rejected.
contracts (required)
Each contract maps field names to type strings. Objects are strict at runtime, so undeclared output fields are rejected.
| Type form | Meaning |
|---|---|
string, integer, number, boolean | Scalar value |
object, array | Untyped JSON object or array |
string[], Result[] | Typed array |
| `draft | final` |
| `(draft | final)[]` |
Result | Another named contract |
string?, Result[]? | Optional field |
Named contract references use an initial capital letter. Recursive contract references and unknown contract names are rejected.
flows (required)
flows.entry must name a flow in the same mapping. Every flow has these fields:
| Field | Required | Shape |
|---|---|---|
input | yes | Field-to-type mapping using the contract type language |
output | yes | {from: "${step_id.output}", contract: ContractName} |
steps | yes | Array of strict step objects |
budget | no | Positive limits for one or more of usd, tokens, dispatches, or ms |
max_rounds | no | Positive integer required when a gate can revise to an ancestor |
carry | no | Named loop-carried flow values; entry flow only |
The output.from value must be one full reference to a step output with a known contract. A fanout output is an array, so a flow output must select an item such as ${fan.output[0]}.
Steps
Step IDs start with a lowercase letter and may contain lowercase letters, digits, underscores, and hyphens. IDs are unique within a flow. Every step accepts id, optional after, and optional when, then exactly one construct with only the fields listed below.
| Construct | Purpose | Additional fields |
|---|---|---|
do | Dispatch an inline task | agent, out, ensure, attempts, iterate, budget, on_fail |
set | Build an output object from expressions | required out, optional ensure |
gate | Pause for a decision | no fields outside the nested gate object |
fanout | Run stages over an array | attempts, budget, on_fail |
run | Invoke a subflow | required with, optional budget, on_fail |
agent is either claude or codex. attempts is a positive integer. An iterate object requires positive integer max and string expression until.
The nested gate object requires nullable on_approve, on_revise, and on_kill fields. It may also set a positive integer max_rounds.
The nested fanout object requires over, one or more steps, positive integer concurrency, isolation of worktree or none, require of all, any, or a positive integer, and merge: sequential. Optional fields are pre_merge and dispatch, whose value is engine or consumer. Each fanout stage requires do and may use agent, out, ensure, attempts, and when.
References
V1 uses ${...} references in task templates, subflow inputs, fanout sources, and flow outputs.
| Pattern | Resolves to |
|---|---|
${input.field} | Flow input field |
${step_id.output} | Full output of a prior step |
${step_id.output.field} | Field in a prior step output |
${fan.output[0].field} | Field in one fanout item output |
${item} | Current item inside a fanout stage task |
${prev} | Previous stage output for the same fanout item |
${name} | A declared carry variable |
${name.field} | Field inside a carry variable |
A full-value reference preserves its JSON type in with and fanout.over. A reference embedded in other text is interpolated into a string. Step-output references create data dependencies. Use after for ordering dependencies that are not implied by references.
Expressions in ensure, when, set, and iterate.until use the v1 expression bindings instead of ${...} interpolation. input is the flow input. In an ensure, result is the output under test. In when and set, result is a mapping of completed step IDs to outputs. Fanout stage expressions also receive item and prev.
Carry
The entry flow may declare a carry: block: a mapping from carry variable name to a shape with
one required initial reference and an optional on_revise mapping.
carry:
wave:
initial: "${plan.output.tasks}"
on_revise:
assess_gate: "${assess.output.tasks}"
initial and every on_revise value must be one full ${...} reference, exactly like a
step-output reference — no interpolation, no expression syntax. The initial source must be an
unconditional, non-gate step that is not the target of any routing edge (on_fail, on_approve,
or on_kill). Materialisation happens once per source epoch: the value is written the moment that
source step succeeds, and is written again if a revise resets the source and it succeeds anew.
Each on_revise key names a gate step id. When that gate resolves with revise, the engine
resolves the declared reference against the run's pre-reset scope and writes the result as the
variable's new value, alongside provenance (which gate, its consumed token, the source epoch, and
the round). A gate that declares nothing for a variable leaves it unchanged, so a merge-retry
revise re-fans over the same list.
A carry reference (${name} or ${name.field}) is legal wherever a step-output reference is
legal on a rendered field — do, with, evaluate.in, fanout.over, and a fanout stage's
do — but is rejected in the six expression fields: when, set, a fanout stage's when,
iterate.until, and ensure (both flow-level and stage-level). Carry values do not participate
in the v1 expression bindings.
A carry path may not begin with the exact segment output — ${wave.output} collides with the
step-output grammar and is rejected, while ${wave.outputs} (or any other field name) is fine.
Carry creates no dependency edge. A step that reads ${wave} needs an explicit after to
order it after the variable's initial source, and a gate that declares an on_revise for a
variable must have a revise target whose reset closure covers every step that reads that
variable. The gate itself must be ordered after those steps through after or step-output
dependencies — a routing edge into the gate does not count as ordering.
Carry is entry-flow only — a subflow's spec may not declare carry, and a carry variable is not
visible inside a subflow. Carry values are snapshotted with checkpoints (stratum_commit /
stratum_revert restore them alongside step outputs) and are exposed by stratum_audit.
Migrating v0.x Specs
stratum migrate --check <old.yaml> is a report-only classifier. It does not emit translated YAML.
| V0.x construct | V1 construct |
|---|---|
functions with infer or compute, plus function steps | Inline do task. Keep the step's agent when present, and do not translate compute to set |
| Gate function plus routing fields | Nested gate object |
Inline intent step | do task |
| Deterministic or judged predicates with one antecedent | Ensures on that antecedent |
decompose without cross-item dependencies | Task contract with tasks: T[], followed by fanout |
depends_on | Data references plus after for remaining ordering edges |
skip_if | when with the condition inverted |
flow step plus inputs | run plus with |
max_iterations plus exit_criterion | iterate: {max, until} |
parallel_dispatch | Re-authored fanout |
Pipeline stage when | Fanout stage when |
Flow max_rounds | Flow max_rounds |
Reducible next routing | DAG edges and gate routing |
on_fail | on_fail targeting a topologically later step |
| Legacy ensure expressions | Re-authored v1 expressions |
output_schema or output_contract | Named v1 contract language |
| Route-dependent flow output | One static output: {from, contract} producer |
Some legacy constructs have no v1 equivalent. These include verified or applied-gate judge predicates, judge budgets, score and accumulator fields, cross-item decompose dependencies, branch or manual parallel merge modes, certificate and timeout fields, nested pipeline regions, pipeline exit_when, gate policies, gate timeouts, and the top-level workflow block. Flatten or re-author supported regions. Unsupported regions must be redesigned before they can run on v1.
MCP Tools API
All tools are exposed via the MCP protocol. Claude Code calls them as tool invocations.
stratum_validate
Validate a .stratum.yaml spec without creating a flow.
Inputs: spec (str, inline YAML)
Returns: {valid: bool, errors: list}
stratum_plan
Validate a spec, create execution state, and return the first step to execute.
Inputs:
spec(str) -- inline YAMLflow(str) -- flow nameinputs(dict) -- flow-level inputs
Returns: Step dispatch object with status: "execute_step" or status: "await_gate", including:
flow_id-- unique identifier for this executionstep_id,step_number,total_stepsfunction,intent,inputs(resolved)output_contract,output_fields,ensureretries_remainingagent,step_mode
stratum_step_done
Report a completed step result. The server validates the result against output schemas and ensure expressions.
Inputs:
flow_id(str)step_id(str)result(dict) -- step output matching the output contract
Returns one of:
- Next step to execute (
status: "execute_step") - Ensure failure with retry info (
status: "ensure_failed",violations,retries_remaining) - Schema validation failure (
status: "schema_failed",violations) - Flow completion (
status: "complete",output,trace,total_duration_ms) - Retries exhausted (
status: "error",error_type: "retries_exhausted") - Routed to recovery step (
routed_from,violations)
stratum_audit
Return the full execution trace for a flow.
Inputs: flow_id (str)
Returns:
flow_id,flow_name,status(complete,in_progress,killed)steps_completed,total_stepstrace-- array of step records (step_id, function_name, attempts, duration_ms, type, round)round,rounds-- round history for gate revise cyclesiterations,archived_iterations-- iteration historychild_audits-- audit snapshots from sub-flow executionstotal_duration_ms
stratum_gate_resolve
Resolve a gate step with a human/agent/system decision.
Inputs:
flow_id(str)step_id(str) -- must be the current gate stepoutcome(str) --"approve","revise", or"kill"- `r
Sourced from the repository README.
More in Automation
- GlifGenerate images, video, and audio with Glif's media-generation agent203
- adeuAutomated DOCX Redlining Engine166
- Unraid RMCPRust MCP server and CLI for Unraid GraphQL operations across NAS, Docker, VM, and storage workflows.135
- runxThe governed runtime for agent skills. Search the catalog and inspect a skill before running it.95
- kesslerio-attio-mcp-serverConnect AI to your Attio CRM. Manage contacts, companies, deals, and sales pipelines. Create tasks…69
- kesslerio-attio-mcp-server-betaStreamline your Attio workflows using natural language to search, create, update, and organize com…69