muninn-mcp
by oaustegard
README.md
# muninn-mcp
Remote MCP server for Muninn's memory. **Read-only. Stage 1.**
Green half of the migration in
[`muninn-utilities/docs/mcp-migration.md`](https://github.com/oaustegard/muninn-utilities/blob/main/docs/mcp-migration.md) ยง5.
Blue โ the Python skill talking to Turso directly โ stays installed and functional
throughout. Data is single-homed; blue-green applies to the **access path**, not
the stack, because a memory database cannot be forked and merged: supersede
chains, `refs` provenance and `is_superseded` flags encode order.
## Status
| Stage | | |
|---|---|---|
| 0 | write provenance (`source` column) | โ
shipped, muninn-utilities #98 |
| 1 | green read-only + parity harness | ๐จ this repo โ gate at 67/74, 0 regressions |
| 2 | reads promoted to production | |
| 3 | writes, shadowed | |
| 4 | cutover | |
| 5 | collapse the double implementation | |
Five tools, all read-only. Write tools do not exist here โ not disabled,
**absent** โ and must not appear until the parity harness is green on the
production corpus.
| Tool | |
|---|---|
| `recall` | search memories by text and tags |
| `memory_get` | one memory by id, its reference chain, or a decision's alternatives |
| `muninn_config` | the profile / ops / journal config store |
| `muninn_docs` | the deferred reference, for clients that don't read resources |
| `boot` | the composed boot payload |
Plus resources: `muninn://reference/{recall,types,vocabulary}`,
`muninn://utilities` and `muninn://utilities/{name}` (17 pages), and
`muninn://boot`. ยง8's progressive disclosure โ ~30KB of reference is addressable
without any of it entering a tool schema.
## Layout
Same shape as [`sage`](https://github.com/oaustegard/sage)'s `mcp/`, deliberately โ
one architecture across sage-mcp, muninn-mcp and later muninnd.
- `src/turso.ts` โ retrieval layer. Ported from `remembering/scripts/turso.py`.
- `src/expansion.ts` โ blue's multi-stage expansion, the layer above the SQL.
- `src/queries.ts` โ the by-id and config reads.
- `src/boot.ts` โ `_format_boot_output`, byte-equal against blue.
- `src/tools.ts` โ tool bodies and output formatting.
- `src/docs.ts` + `src/docs-generated.ts` โ the ยง8 registry. Generated by
`scripts/build-docs.mjs` from muninn-utilities; run `npm run build:docs`.
- `src/resources.ts` โ both doors onto that registry, plus `muninn://boot`.
- `src/server.ts` โ `buildServer()`: registration only, Zod schemas.
- `src/index.ts` โ bearer-token Worker entry, and the shared dispatch module.
- `src/mcp-oauth.ts` โ the OAuth entry claude.ai connectors need. `wrangler` main.
- `harness/` โ the blue-vs-green parity gate. See [`harness/README.md`](harness/README.md).
Docs: [`deploying.md`](docs/deploying.md) to get it running,
[`operating.md`](docs/operating.md) to run the gate and read its verdicts.
## The gate
ยง5 is explicit that the harness, not the Worker, is Stage 1's deliverable. It
lives in `harness/`: 64 frozen `recall` shapes run through blue-Python and
green-TypeScript against the same database, normalized to **id order** and
diffed.
```bash
export TURSO_URL=... TURSO_TOKEN=... # both sides read these two names
npm run harness # capture blue, capture green, diff
```
Id order, not row contents: `access_count` and `last_accessed` are written *by
the act of reading*, and `composite_score` embeds `julianday('now')`, so neither
is comparable across two captures. Order is stable under both and is what ยง5
actually asks for.
No credentials to hand? `npm run harness:fts` verifies the FTS vectors, and both
capture scripts take `--dry-run`.
## Parity, not unit tests
`src/fts-golden.json` was **generated by running the live Python**
(`_escape_fts5_server`) over 33 inputs. Those are blue's actual outputs, frozen โ
not hand-written expectations. Regenerate them from Python and any divergence
fails CI โ `npm run harness:fts` is the regenerator, and it needs no credentials.
That file matters more than it looks. FTS escaping is the one place where a
difference between blue and green changes *which memories come back* instead of
failing loudly, which is why the migration doc calls it the highest-value test of
the whole port.
The ranking itself is **server-side SQL in both worlds** โ BM25, recency decay,
priority and confidence are computed by Turso, not by application code. `turso.ts`
transcribes SQL strings; it does not reimplement retrieval. That is what keeps the
port tractable.
## Protocol
`@modelcontextprotocol/server` v2, serving the **2026-07-28** revision and the
2025-era handshake from one route and one factory. `tools/list` carries a public
cache hint (SEP-2549); nothing else may, since recall results are private memory.
## Develop
```bash
npm install
npm test # 620 assertions across 7 suites
npx tsc --noEmit # typecheck
npm run dev # local worker
npm run build:docs # regenerate the muninn:// resource content
npm run harness # the blue-vs-green gate (needs live credentials)
npm run harness:selftest # prove the gate still refuses a real reordering
```
Point `TURSO_URL` at a **branch** database for Stage 1:
```bash
turso db create muninn-green --from-db <production>
```
Full deployment walkthrough โ KV namespace, secrets, the claude.ai connector, and
what the design concentrates in one place โ is in [`docs/deploying.md`](docs/deploying.md).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues