One MCP server for the whole Mirror Stack. Install once, add one server, and an agent has all four mirrors: claims, actions, provenance, and a stack-level verify-all.
💬 Discussions — questions · ideas · independent reproductions welcome.
🔎 See it on real records → the Sealed-Record Reading Room — a human-readable viewer over actual sealed ledgers: each experiment shows the kill-condition sealed before the run, the verdict (pass / kill / retracted / inconclusive), and every number auto-recomputed from the ledger. You can even tamper with a sealed value in the browser and watch the hash break. Shows what the discipline looks like in practice, including the failures and retractions — not just the wins.
Mirror is independent of Yeoul and LaneStack. Start with the workspace guide / 간편 사용·복구 안내.
pip install git+https://github.com/mirror-stack/mirror-stack-mcp@v0.3.0
mirror-stack setup ./my-records --mode record
mirror-stack record "First note" --workspace ./my-records
mirror-stack doctor --workspace ./my-records
mirror-stack connect --workspace ./my-recordsThe product CLI and managed MCP share operation receipts and locks. Setup asks for
confirmation; scripts can add --yes after review. Existing client settings are
not changed. Raw core CLIs and arbitrary shell commands are outside this boundary.
pip install git+https://github.com/mirror-stack/mirror-stack-mcpThat single install pulls measure-mirror + action-mirror + provenance-mirror as dependencies — you don't clone four repos. (Apache-2.0, zero-dep cores.)
For workspace-scoped permissions, serialized writes and restart-safe delivery receipts, see the managed stdio runtime contract. The minimal configuration below is trusted-local compatibility mode, not managed mode.
{
"mcpServers": {
"mirror-stack": { "command": "mirror-stack-mcp" }
}
}No cwd, no PYTHONPATH — it's a proper installed entry point. Works in Claude Code, Cursor,
Windsurf, any MCP client.
workspace_prepare, workspace_execute and workspace_tasks manage durable task
handles without caller-created operation IDs. They never approve permissions or
clear interrupted operations. See the workflow contract.
Release pins: measure-mirror v0.41.2, action-mirror v0.4.0, and
provenance-mirror v0.3.1. Exact tag commits are recorded in
pyproject.toml and checked by CI. Updating the checkout alone
does not refresh an already installed dependency or a running MCP process.
Reinstall the package and reconnect the client after an upgrade; do not rewrite ledgers.
mirror-stack-verify now recomputes both content hashes and chain links; pointer-only
fixtures do not certify integrity. Legacy 16-hex seals are accepted with a warning.
stack_verify_all labels checks HASH_RECOMPUTED, LOCAL_SNAPSHOT, or HEAD_WITNESS.
None certifies content truth, author identity, independent reproduction or external time.
The outsider CLI checks an optional OTS proof separately; ledger-to-proof binding is
not checked, so it does not claim this ledger's external-clock precedence.
The compute/publish gate verifies ledger integrity before reading the decision facts. Publish requires a reasoned retraction, or an explicit result bound to the original seal:
am_record(ledger_path="actions.jsonl", agent="researcher", action="result", target="claim-id",
payload={"status": "fail", "summary": "Measured value did not meet the preregistered bar",
"prereg_seal": "<seal returned by the first mm_preregister>"})Allowed statuses are pass, fail, and inconclusive. A started action does not
resolve a claim. Append a new result instead of rewriting historical records. GO permits
reporting a resolved result, including a failure; it does not certify scientific success.
| Tool | Mirror | Does |
|---|---|---|
mm_preregister |
🪞 claims | seal a claim + kill-condition before measuring (response carries an auto seal-quality lint) |
mm_verify |
🪞 | umbrella verify — every probe whose input key is present |
mm_audit |
🪞 | audit a result vs its sealed registration |
mm_power_check |
🪞 | false-negative guard — is n big enough? (design-time) |
mm_falsifiability_check |
🪞 | Popper gate — kill-condition registered & not tripped? |
mm_prereg_lint |
🪞 | seal quality lint (㉗) — leaked kill-condition, bar at/below declared chance, unstructured kill, low n, undeclared pre-seal checks; the compute gate BLOCKs on a lint FAIL |
mm_leakage_check |
🪞 | train∩test contamination |
mm_multiseed_check |
🪞 | unstable signal / lucky seed |
mm_retract |
🪞 | chain-linked retraction (cannot be silently deleted) |
mm_anchor |
🪞 | tamper-evident snapshot to store outside the ledger (local) |
mm_anchor_bitcoin |
🪞⏱️ | real external anchor — timestamp ledger heads into Bitcoin (OpenTimestamps) |
mm_anchor_upgrade |
🪞⏱️ | retrieve the Bitcoin block attestation once a calendar has committed (~1–3h) |
mm_anchor_verify |
🪞⏱️ | verify a Bitcoin anchor via a public block explorer — no local node needed |
mm_preflight |
🪞 | GO/BLOCK gate primitive — wire into a compute launcher / pre-commit hook |
am_record |
🪪 actions | seal an action; target=<claim_id> ties it to a claim |
am_witness |
🪪 | pin a peer's ledger head (catches whole-file replacement) |
am_verify |
🪪 | verify an action ledger's hash chain |
pm_verify |
🔎 provenance | inspect 5 provenance/integrity signals; marker-only results are PROVENANCE-UNVERIFIED, never signature verification |
stack_verify_all |
🪞🔎🪪 | supplied ledgers only: hash integrity (L1), optional local snapshots (L3) and pinned-head witness comparisons (L2); scope is reported |
(More granular measure-mirror probes are reachable via mm_verify — it dispatches by data key.)
mm_anchor writes a local snapshot — useful, but it's still your file. mm_anchor_bitcoin
upgrades the L3 layer to a real external clock: it timestamps your ledger heads into the
Bitcoin blockchain via OpenTimestamps, so anyone can later prove
your record existed before a given block — independent of you and of GitHub.
Honest scope (this is the whole point, so we say it plainly):
- ✅ proves your ledger heads existed before a Bitcoin block time → no backdating, no silent rewrite.
- ❌ does NOT prove the content is true (GIGO — an early-sealed lie is still a lie).
- ❌ does NOT summon an external judging witness (that's the social layer, L2). The clock is run now for a verifier who may arrive later.
Cryptography here doesn't block dishonesty — it makes a rewrite detectable.
pip install "mirror-stack-mcp[bitcoin] @ git+https://github.com/mirror-stack/mirror-stack-mcp"
# (pulls the `ots` CLI; needs network to reach the public OpenTimestamps calendars)Then, via the MCP tools (or the agent calling them):
- Stamp —
mm_anchor_bitcoin(ledger_paths=["/path/seara.jsonl", …], out_dir="…/anchors_ots")→ builds a manifest of the heads, submits it, returnsstate="pending"+ the.otspath. - Upgrade — in ~1–3h,
mm_anchor_upgrade(ots_path)→state="bitcoin_confirmed"+block_height. - Verify —
mm_anchor_verify(ots_path)cross-checks the block's merkle root on a public explorer (default blockstream.info) — no Bitcoin node required — and returns whether it matches.
Anyone can independently re-check: curl https://blockstream.info/api/block-height/<height>.
See docs/ANCHORING.md for the full walkthrough, CLI fallback, and the L1 / L2 / L3 model.
When an agent connects, the server injects a compact honest-measurement discipline via the
MCP instructions handshake — so the methodology arrives with the tools, not as a separate
doc someone has to find. It carries the one rule that matters most: separate the tool from
your judgment — say "mm flagged X" only when a probe returned a Finding you can quote; say
"applying the discipline, I suspect X" when it is your reasoning. Plus: seal-before-compute
(preregister + kill-condition), power before spending, verify-before-reporting, and "a missing
ledger is itself a signal."
(MCP clients that surface server instructions — e.g. Claude Code — show this on connect. It is guidance, not enforcement: the hard guarantee is still the chain + witness, not the prompt.)
The connect-time instructions fade over a long session, so the server also re-grounds the
discipline at the moment it matters — a one-line reminder appended to the relevant tool's
result. mm_preregister → "the result must be sealed too, not just prose"; mm_verify /
mm_audit / stack_verify_all → the before you state a number checklist (the language rule,
both directions, scope, seal negatives); am_record, mm_retract, mm_power_check,
mm_falsifiability_check → their own one-liner. It fires at the seal/verify/publish beat, not
every turn — a nudge, not noise.
Control it with the MIRROR_REMINDERS env var:
| value | behaviour |
|---|---|
once (default) |
append each reminder only the first time per session |
all |
append the reminder on every relevant call |
off |
never append |
once is the default so a long loop doesn't re-pay for the same reminder every turn
(context hygiene). Override per server:
{ "mcpServers": { "mirror-stack": {
"command": "mirror-stack-mcp",
"env": { "MIRROR_REMINDERS": "all" }
} } }Verification output piles up across a loop's iterations — context rot + cost. The server
compacts it without hiding signal: OK/INFO findings collapse to one summary line, while
every WARN/FAIL is kept verbatim (a dropped negative would defeat the whole point).
MIRROR_VERBOSITY |
behaviour |
|---|---|
compact (default) |
✓ N check(s) OK: … + every WARN/FAIL in full |
full |
every finding verbatim |
Per-call compaction is the server's job; summarising the running log across iterations is the loop/harness's — the server is stateless per call.
Reminders are soft. For a hard gate, mm_preflight returns a GO/BLOCK decision you wire
into the action site:
mm_preflight(ledger, claim_id, gate="compute")→ BLOCK unless a preregistration with a kill-condition is sealed. Wire it into your training/experiment launcher so it refuses to spend compute on an unsealed claim.mm_preflight(ledger, claim_id, gate="publish", am_ledger=…)→ BLOCK unless the result is also sealed (a reasoned retraction, or the explicit bound result above). Wire it into a pre-commit / pre-publish hook so unresolved claims can't ship.
The MCP only judges GO/BLOCK — your launcher/hook does the actual blocking. The server cannot intercept external compute or commits, and that is by design: the discipline is opt-in (a missing ledger is itself a signal), not something the channel forces on you.
The enforcer ships with the package. mm_preflight judges; the mirror-stack-gate CLI is
the part that actually exits non-zero, so a shell can do the blocking the MCP can't:
mirror-stack-gate compute --ledger L.jsonl --claim my_claim && python run.py
# exits 1 (run.py never starts) unless a kill-conditioned preregistration is sealed
mirror-stack-gate publish --ledger L.jsonl --claim my_claim --am-ledger A.jsonl
# exits 1 unless a reasoned retraction or an explicit bound result is sealed tooFor git, drop in hooks/pre-commit.sample: it runs the publish gate
so a result can't be committed until its claim is resolved. The CLI and the MCP tool share one
decide() (gate.py), so they can never drift apart.
The tamper-evidence comes from the hash chain + external witness, not from the MCP channel — so exposing the action ledger over MCP is fine: an edit still breaks the chain. This server makes honesty easy to prove; it does not (and cannot) prevent an agent that simply never records.
See what the stack guarantees — and the one it can't (the four pillars: integrity · non-erasure · falsifiability · verifiability), and the Mirror Stack conventions + case study.