{"id":"9bebb91c-1a9b-4d35-9a88-f4f88e82dd01","slug":"state-canon-mcp","kind":"mcp","name":"state-canon-mcp","summary":"A state-grounded RAG that puts verified current state in the agent's path, so it reasons from ground","owner":"rm-w3kufe","repoUrl":"https://github.com/rm-w3kufe/state-canon-mcp","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":1,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-07-21T03:16:51.000Z","addedAt":"2026-10-21T21:35:00.329Z","logoUrl":"https://avatars.githubusercontent.com/u/175173985?u=879e1ce2efb93be92d28a0636064f63d94c181e1&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["search"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T09:13:22.426Z","sample":false,"url":"https://conduits.dev/tools/state-canon-mcp","language":"Python","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# state-canon — verified ground truth for agents\n\n[![CI](https://github.com/rm-w3kufe/state-canon-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/rm-w3kufe/state-canon-mcp/actions/workflows/ci.yml)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-green.svg)](LICENSE)\n[![MCP Compatible](https://img.shields.io/badge/MCP-compatible-brightgreen.svg)](https://modelcontextprotocol.io/)\n[![No Dependencies](https://img.shields.io/badge/dependencies-none-brightgreen.svg)](#install)\n\n> **An agent grounded in verified canon does more careful work than one running on recall — and burns fewer tokens.**\n> `state-canon` keeps a **canonical, reconciled record of the system** — the authoritative version of the\n> world state, written on every change and reconciled against reality — and puts it *in the agent's path*,\n> so the agent reasons from ground truth instead of stale memory or costly re-derivation. State you can\n> query, verify, and govern.\n\n**Status:** early / exploratory — the thesis and the method are battle-tested in daily use; the public\ntooling is being extracted and **the token-savings claim is being measured, not proclaimed**. Full status\nbelow. Stdlib-only, no dependencies. The method itself is a separate read: **[METHODOLOGY.md](./METHODOLOGY.md)**.\n\n---\n\n## The bet\n\nThe industry is racing toward multi-agent **swarms** on the premise that *more agents = better work*. We\ntake the opposite bet: the bottleneck was never *how many agents* — it's **whether each decision is grounded\nin verified canon instead of the agent's own recall.** Fix that, and a **single agent** goes a long way, at\nlower token cost.\n\n- **One agent**, grounded in canon — it designs, executes, and *verifies its own claims against ground\n  truth*, not against its memory of what it did.\n- **A human** — sets policy and holds the ceiling: irreversible / outward-facing changes need approval.\n\nThe audit property that matters — *what verifies a claim is not what produced it* — holds with one\nagent **because the canon is external to the agent's reasoning.** The agent doesn't check its work by\nre-reading its own recall; it reconciles against a canonical store that reality writes to. The\nfull pattern — the single-agent loop, boundaries, the real case study — is in **[METHODOLOGY.md](./METHODOLOGY.md)**.\n\n```mermaid\nflowchart LR\n    D[\"agent:<br/>design + spec\"] --> F[\"framed task<br/>with stop-points\"]\n    F --> E[\"agent:<br/>build / deploy / test\"]\n    E --> R[\"claim: done\"]\n    R --> V{\"verify LIVE<br/>against canon,<br/>not the claim\"}\n    V -- defect found --> C[\"finding<br/>written to canon\"]\n    C --> F\n    V -- verified --> G{\"blast radius<br/>gate\"}\n    G -- pilot passes --> N[\"next stage<br/>wider rollout\"]\n    G -- human approval --> H[\"human decides\"]\n```\n\n### Why it works\n\n1. **Grounded, not re-derived.** The current, reconciled state of the system is injected into the agent's\n   path. It doesn't re-explore the world each session; it reads verified ground truth. *This is what this\n   tool provides.*\n2. **Verify against canon, not recall.** The agent never trusts a *report* — its own or anyone's — over the\n   live state (\"is the service actually running?\", not \"did I say it is?\"). Canon is the authority; recall\n   is a lead to check.\n3. **Compact, machine-primary artifacts.** State and handoffs are terse and structured, not prose — the\n   token-cost lever.\n\n## What it is — a canon layer, exposed over MCP\n\nA **canonical store** written on every change, a **reconciler** that keeps it ≡ reality (drift / orphan\ndetection), and an *onboard* step that injects the current state into the model's context. The agent\nqueries it or has the digest front-loaded — either way it reads reconciled ground truth, not its own recall.\nThe discipline in one line: **recall is not canon — verify against state.**\n\nExposed over the Model Context Protocol so *any* agent can plug in:\n\n| tool | does |\n|---|---|\n| `state_onboard()` | the whole current picture in one call (services + status, open drift, rules, last decision) |\n| `state_query(domain, filter)` | a narrow slice, on demand |\n| `state_verify(claim)` | ★ *don't trust the report — check it against ground truth* → `{holds, actual, evidence}` |\n| `state_reconcile(domain)` | model≡reality → the drift / orphan report |\n| `state_journal_mark(session_id?, drifts?)` | *opt-in (`--journal PATH`)* — save a session snapshot for later diff/trend |\n| `state_journal_diff(from_id?, to_id?)` | *opt-in* — what changed between two snapshots |\n| `state_journal_history(limit?)` | *opt-in* — recent snapshots |\n| `state_focus_mark(ref, status?, note?)` | *opt-in (`--focus PATH`)* — upsert a focus entry by ref (create or update status/note) |\n| `state_focus_close(ref, note?)` | *opt-in* — mark a focus entry as done; state_query('focus') reads back |\n\nPlus resources (`state://digest`, `state://schema`, `state://rules`, `state://handoff`) for clients that\ninject context up front. The split is deliberate — **resources** feed the front-load (inject the digest at\nsession start); **tools** feed the lazy path (query only what you need).\n\n**It's domain-agnostic.** You register your own `StateProvider` (SQLite, JSON, Git, an API — anything) and\na `Reconciler` (how to observe reality for each domain). Full abstraction, reference instances, the Git/VCS\ninstance, remote mode, and composing with other context sources: **[INTERFACE.md](./INTERFACE.md)**.\n\n**It composes.** state-canon is one instrument, not the whole rack: attach it over MCP side-by-side with your\ncontent RAG, your memory server, your other tools. The discipline that keeps the ensemble honest is\n**authority ordering** — for *current* truth, reconciled state outranks memory and documents.\n\n## Install\n\n**Requirements:** Python **3.10+**. Nothing else — the server is stdlib-only, there is no `pip install`.\n\n```bash\ngit clone https://github.com/rm-w3kufe/state-canon-mcp.git state-canon\npython3 state-canon/tests/test_state_canon.py   # optional: 28 checks, ~1s\n```\n\nThe server is one command (stdio; your MCP client spawns it):\n\n```bash\npython3 /abs/path/state-canon/mcp_server.py --state      /abs/path/your-state.json\npython3 /abs/path/state-canon/mcp_server.py --sqlite     /abs/path/your-state.db     # any SQLite DB\npython3 /abs/path/state-canon/mcp_server.py --git        /abs/path/your-repo         # a git working tree\npython3 /abs/path/state-canon/mcp_server.py --microstack /abs/path/state-canon/corpus/microstack  # demo\npython3 /abs/path/state-canon/mcp_server.py --instance   /abs/path/your-instance.py:arg  # bring-your-own\n```\n\n> **Architecture note:** the server runs **locally, beside your agent** (stdio). Your state lives where\n> your agent lives. It is not a network service and does not phone anywhere. (Remote *state* is supported at\n> the provider layer — see [INTERFACE.md](./INTERFACE.md).)\n\n### Per-client configuration\n\nAll stdio MCP clients need the same three facts: `command: python3`, `args: [launcher, --flag, path]`.\nUse **absolute paths** everywhere.\n\n**Claude Code (CLI):**\n```bash\nclaude mcp add state-canon -- python3 /abs/path/state-canon/mcp_server.py --state /abs/path/state.json\n```\nor per-project in `.mcp.json` / **Claude Desktop** in `claude_desktop_config.json`:\n```json\n{ \"mcpServers\": { \"state-canon\": {\n    \"command\": \"python3\",\n    \"args\": [\"/abs/path/state-canon/mcp_server.py\", \"--state\", \"/abs/path/state.json\"] } } }\n```\n\n**OpenCode** (`opencode.json`, 0.x shape):\n```json\n{ \"mcp\": { \"state-canon\": {\n    \"type\": \"local\",\n    \"command\": [\"python3\", \"/abs/path/state-canon/mcp_server.py\", \"--state\", \"/abs/path/state.json\"] } } }\n```\n\n**Cursor** (`.cursor/mcp.json`), **Cline** (`cline_mcp_settings.json`), **Windsurf**\n(`~/.codeium/windsurf/mcp_config.json`) — all use the same `mcpServers` shape as Claude Desktop above.\n\n### Verify the install (no client needed)\n\n```bash\nprintf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}' \\\n  | python3 /abs/path/state-canon/mcp_server.py --microstack /abs/path/state-canon/corpus/microstack\n```\n\nYou should see one JSON line with `\"serverInfo\": {\"name\": \"state-canon\"}`.\n\n### Updating\n\nstate-canon-mcp is a plain `git clone`, not a package — updating is a `git pull`:\n\n```bash\ncd state-canon && git pull\n```\n\nCheck what changed: **[CHANGELOG.md](./CHANGELOG.md)**, or `git log --oneline v0.2.0..HEAD`\nagainst the version your `initialize` response currently reports.\n\nYour own customizations are safe across an update: a `--state` JSON file, a\n`--sqlite`/`--git` target, or a full `--instance` module all live **outside** this repo\nand are loaded at runtime — a `git pull` never touches them. The only thing that could\nbreak a custom instance is a breaking change to the `StateProvider` / `Reconciler`\ninterface itself, and that would always be called out under `### Changed` in the\nchangelog, never buried in `### Added`.\n\n## Quickstart — the 60-second demo\n\nThe repo ships a tiny fictional deployment (`corpus/microstack/`) with **three seeded drifts**: a service\ndeclared active but actually stopped, a rule violation, and an orphan process.\n\n**1. Ask for the whole picture:**\n```bash\ncd state-canon\nprintf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"state_onboard\",\"arguments\":{}}}' \\\n  | python3 mcp_server.py --microstack corpus/microstack\n```\n→ one compact digest: 9 services, the 3 drifts (recomputed live from the raw files, not read from a\nsnapshot), the rules, the last recorded decision.\n\n**2. Catch a lie** — claim that `cache` is running, against ground truth:\n```bash\nprintf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"state_verify\",\"arguments\":{\"domain\":\"services\",\"filter\":{\"name\":\"cache\"},\"expect\":{\"actual\":\"running\"}}}}' \\\n  | python3 mcp_server.py --microstack corpus/microstack\n```\n→ `\"holds\": false` **with evidence**: declared `active`, observed `stopped`. That is the whole philosophy in\none call: *don't trust the report — verify against state.*\n\n**3. Combined demo — full agent canon with session journaling, per-ref focus tracking, and a VSM task provider:**\n```bash\npython3 /abs/path/state-canon/mcp_server.py \\\n  --instance /abs/path/instances/tasks_provider.py:/abs/path/your-TASKS.vsm \\\n  --journal  /abs/path/sessions/journal.db \\\n  --focus    /abs/path/current_focus.json\n```\nThis enables `state_onboard`/`state_query`/`state_reconcile` (task and focus\nreconcilers), `state_journal_*` (snapshot/diff/history), and\n`state_focus_mark`/`state_focus_close` (per-agent focus tracking) — the full\ncanon loop with no external dependencies.\n\n**4. With your agent attached**, ask it:\n- *\"What's the current state of the system? Any drift?\"* → it calls `state_onboard` once.\n- *\"Is the cache service consistent with what's declared?\"* → it calls `state_query` / `state_verify`.\n\n**The usage pattern that measured best** (see [Measured](#measured-not-proclaimed)): **inject the digest\nonce at session start** (cheapest for broad questions), then **use targeted `state_query` for follow-ups**\n(cheapest for narrow ones). Cold re-exploration is always the most expensive.\n\n## Onboarding your system — three steps\n\n### Step 1 — point the server at your state\n\n**JSON (quickest start).** Any JSON document whose top-level keys hold lists of records:\n```json\n{ \"system\": \"my-stack\",\n  \"services\": [ {\"name\": \"api\", \"state\": \"running\", \"version\": \"1.2.0\"} ],\n  \"rules\":    [ \"api and worker versions must match\" ],\n  \"last_decision\": {\"id\": \"D-1\", \"text\": \"approved cache upgrade\"} }\n```\n```bash\npython3 mcp_server.py --state /abs/path/my-state.json\n```\n\n**SQLite.** Every table/view becomes a queryable domain automatically — or pass an explicit mapping (see\n`instances/sqlite_ops.py` for a real production example). Opened read-only by construction (`mode=ro`):\n```python\nfrom state_canon.sqlite_provider import SqliteStateProvider\nprovider = SqliteStateProvider(\"ops.db\",\n    domains={\"services\": \"services\", \"blockers\": \"v_open_blockers\"},\n    meta={\"system\": \"my-stack\"})\n```\n\n**Git.** `--git /path/to/repo` — the working tree becomes a live drift report (see [INTERFACE.md](./INTERFACE.md)).\n\n**An API / anything.** Implement two methods (this **is** remote mode — the server stays local, the\nprovider crosses the wire; an SSH-channel provider is the same shape):\n```python\nfrom state_canon.provider import StateProvider\nimport json, urllib.request\n\nclass ApiStateProvider(StateProvider):\n    def __init__(self, base_url): self.base = base_url\n    def list_domains(self): return [\"services\", \"incidents\", \"meta\"]\n    def query(self, domain, filter=None):\n        if domain == \"meta\": return [{\"system\": \"my-stack\"}]\n        with urllib.request.urlopen(f\"{self.base}/{domain}\") as r: records = json.load(r)\n        if filter: records = [x for x in records if all(x.get(k) == v for k, v in filter.items())]\n        return records\n```\n\n### Step 2 — shape the digest (what matters at onboard)\n\nRaw dumps don't belong in a model's context (our first real-canon digest weighed 44k chars; the policy cut\nit to 12k). Declare what matters per domain — detail is never lost, it stays one `state_query` away:\n```python\nDIGEST_POLICY = {\n    \"services\": {\"fields\": [\"name\", \"state\", \"version\"], \"max\": 60},\n    \"deploys\":  {\"fields\": [\"component\", \"result\"], \"max\": 5, \"last\": True},\n    \"audit_log\": {\"skip\": True},          # queryable, but not onboard material\n}\n```\n\n### Step 3 (optional but recommended) — add a reconciler\n\nThis is where canon earns its keep: **declared vs observed → typed drift**, computed live.\n```python\nfrom state_canon.reconcile import Reconciler\n\nclass MyReconciler(Reconciler):\n    domain = \"services\"\n    def declared(self):   # the model: your manifest / DB / config\n        return load_manifest()\n    def observe(self):    # the reality: ps, systemctl, an API probe...\n        return probe_running_processes()\n```\nThe generic diff yields `declared_but_missing`, `orphan`, and your registered `rule_violation`s — exactly\nthe three drift kinds seeded in the demo. Wire it in an instance module (`instances/microstack.py` is the\n40-line reference).\n\n## Use cases\n\n| Scenario | Call pattern |\n|---|---|\n| Agent session start — \"where am I?\" | `state://digest` resource injected, or one `state_onboard` |\n| \"Is service X ok?\" mid-session | `state_query(services, {name: X})` — ~80 tokens |\n| Reviewing another agent's (or person's) report | `state_verify(domain, filter, expect)` → holds/mismatches + evidence |\n| Post-deploy sanity | `state_reconcile()` → live drift list (orphans, missing, rule violations) |\n| CI gate | run the reconcile in a script; fail the pipeline if drift ≠ expected |\n\n## Measured, not proclaimed\n\nWe refuse to ship a token-savings number we haven't earned. [EXPERIMENT.md](./corpus/microstack/EXPERIMENT.md)\ndefines a reproducible experiment: the same tasks run **cold** (agent re-explores) vs **onboard** (state\ninjected) vs **MCP** (state queried), on a small controlled corpus, same model, ≥5 trials, **scored for\ncorrectness** (savings that produce a wrong answer are disqualified). The honest finding is a *trade-off* —\nonboard wins broad tasks, lazy MCP wins narrow ones, cold always loses — reported that way in\n[RESULTS.md](./corpus/microstack/RESULTS.md), caveats and all.\n\n## Status (honest)\n\n| piece | state |\n|---|---|\n| Thesis + method | ✅ in daily use |\n| MCP server (stdlib-only) | ✅ 28/28 checks + end-to-end stdio smoke |\n| Controlled corpus + experiment | ✅ trade-off characterized (onboard wins broad, lazy query wins narrow, cold always loses; correctness intact) |\n| Second living instance (SQLite over a real production canon) | ✅ 14/14 structural checks; digest policy born from a real 44k→12k lesson |\n| Git/VCS instance | ✅ `GitStateProvider` + reconciler (`--git`); the three drift kinds fall out of `git status`; read-only; 17/17 checks |\n| SSH SQLite instance | ✅ `SshSqliteStateProvider` — remote SQLite over SSH subprocess; read-only by construction; `shlex.quote()` for shell metacharacters; 20/20 checks |\n| Remote mode (remote state, local server) | ✅ by design at the provider layer; remote MCP *transport* = open integration point, not shipped |\n| Session journal (snapshot / diff / trend) | ✅ opt-in via `--journal`; 28/28 checks incl. degrade-to-zero when the instance-specific `JOURNAL_STATS_FN`/`JOURNAL_RAG_FN` hooks aren't supplied |\n| VSM task-file provider + Focus (read/write) | ✅ tasks_provider: 68/68 checks (VSM parser + TaskSessionReconciler catches stale tasks + FocusTaskReconciler catches stale focus entries: `stale_focus_task_done`, `stale_focus_session_resolved`; provider + both reconcilers reload on mtime change, `bind_focus_tracker` keeps focus reads and writes on the same live store). FocusTracker: 42/42 checks (`--focus` flag, `state_focus_mark`/`close`, atomic write, dogfooded on own focus.json) |\n| End-to-end smoke (subprocess stdio) | ✅ 27/27 checks — spawns real MCP server with `--instance + --focus + --journal`, sends JSON-RPC over stdio, asserts reconcile patterns, focus mark/close, journal persist, onboard digest |\n| FreshnessReconciler | ✅ `state_canon/freshness.py` — generic core reconciler: flags missing or stale files by mtime vs max_age. Two drift kinds (`missing`, `stale`). Domain-configurable. Stdlib-only, 6 pytest tests. |\n| Provider-contract safeguard (state_verify vs declared/observed) | ✅ `state_verify` cross-checks the provider against any registered reconciler's `observe()` and surfaces a warning on disagreement; 22/22 checks (`tests/test_housekeeping_fixes.py`, also covers duplicate-key drift detection, focus.py concurrent-write locking, JsonStateProvider mtime reload + unknown-filter parity with SqliteStateProvider, digest.py fallback labeling) |\n| Skills (9) + agent spec (1) | ✅ the disciplines and the single-agent spec, installable |\n| Publication-grade token counters (real MCP attach + billed usage) | ⬜ pending |\n| Realistic corpus + published numbers | ⬜ after validation |\n\n*Legend: ✅ built and tested · ⬜ declared/roadmap. `METHODOLOGY.md`, `skills/` and `agents/` are practice\ndocumentation (the pattern we run daily), not orchestration code — nothing here pretends to be what it isn't.*\n\n## Going deeper\n\n- **[METHODOLOGY.md](./METHODOLOGY.md)** — the canon-grounded agent: roles, the four boundaries where quality\n  is made, the real four-round case study, and the honest limits.\n- **[INTERFACE.md](./INTERFACE.md)** — extending & integrating: the provider abstraction, reference\n  instances, the Git/VCS instance, remote mode, and composing with other context sources.\n- **[skills/](./skills/)** — the nine disciplines as portable, installable skills (each with its scar).\n- **[agents/](./agents/)** — the agent as an adoptable spec (`agent.md`).\n- **[corpus/microstack/](./corpus/microstack/)** — the measurement corpus, experiment design, and results.\n- **[CHANGELOG.md](./CHANGELOG.md)** — what changed, by version.\n\n## Repository layout\n\n```\nstate-canon/\n├── README.md            ← this file (what it is · install · usage · status)\n├── METHODOLOGY.md       ← the canon-grounded agent (the method behind the tool)\n├── INTERFACE.md         ← extending & integrating (abstraction · git · remote · composition)\n├── CHANGELOG.md         ← what changed, by version\n├── LICENSE\n├── skills/              ← the disciplines as portable skills (9 · installable in Claude Code)\n├── agents/              ← the agent as an adoptable spec\n├── mcp_server.py        ← one-file launcher for any MCP client\n├── state_canon/         ← the library (stdlib)\n│   ├── provider.py      ← StateProvider ABC + JsonStateProvider\n│   ├── server.py        ← MCP stdio server (JSON-RPC dispatch, 9 tools, 4 resources)\n│   ├── digest.py        ← compact onboard digest\n│   ├── reconcile.py     ← Reconciler ABC + Drift dataclass\n│   ├── journal.py       ← StateJournal (snapshot/diff/trend over SQLite)\n│   ├── focus.py         ← FocusTracker (atomic read-write, flock)\n│   ├── freshness.py     ← FreshnessReconciler (stale/missing files by mtime)\n│   ├── sqlite_provider.py ← SqliteStateProvider (read-only)\n│   ├── ssh_sqlite_provider.py ← SshSqliteStateProvider (remote SQLite over SSH)\n│   └── git_provider.py  ← GitStateProvider + GitReconciler\n├── instances/           ← reference instances\n│   ├── microstack.py    ← demo (manifest vs processes)\n│   ├── sqlite_ops.py    ← production ops DB\n│   ├── ssh_sqlite_ops.py ← remote SSH variant\n│   └── tasks_provider.py ← VSM task/session parser + reconcilers\n├── tests/               ← test suites (254 check() calls + 6 pytest tests)\n└── corpus/microstack/   ← controlled measurement corpus\n    ├── raw/             ← what a COLD agent faces (must synthesize)\n    ├── synthesized/     ← the reconciled state (the canon's product)\n    ├── EXPERIMENT.md · RESULTS.md · GROUND_TRUTH.md · TASKS.md\n```\n\n## Lineage & philosophy\n\nBuilt on Stafford Beer's **Viable System Model** and the **Cybersyn** project (Chile, 1971) — a system is\nviable when it can be *described*, *governed*, and *audited*. \"Don't trust, verify\" isn't a slogan here;\nit's the reconciler and the verify-at-every-boundary discipline made mechanical. Community-first, and\ndeliberately **from the Global South** — the heir to Cybersyn's bet that good cybernetics serves people.\n\n## License\n\nCode: **Apache-2.0** ([LICENSE](./LICENSE)) · Docs: **CC-BY-4.0**.\n\nThis module is deliberately more permissive than the AGPL core it was extracted from — it is meant to be\nadopted, embedded, and improved by the community. Improvements can flow back; the module stays clean of\nAGPL code by construction.\n"}