Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Cross-tool agent memory control plane. Start, stop, and check the M0 operational-thread server; find the store; drain deferred writes; wire the MCP tools and session hooks. Local SQLite, no dependencies, no network. Triggers on: 'm0', 'm0 status', 'start m0', 'cross-tool memory', 'operational thread', 'agent memory server', 'where is my memory stored'.
.claude/skills/coco-research-m0/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 181% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 154% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 140% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 85% | 0% |
M0 keeps one local operational thread per project: what was done, what was verified, what is next. Any tool can write to it and read it back, so a session that starts cold can continue work another tool began.
Two operations, one SQLite table, standard library only. The wire contract is in systems/m0/SPEC.md; the write path is /m0-remember, the read path is /m0-recall, and the session handoff flow is /m0-handoff.
bashM0S="$HOME/.claude/skills/m0/scripts" # installed location M0="${M0_BASE_URL:-http://127.0.0.1:8787}" # server base URL # Is it up, and where does it store? curl -s "$M0/api/health" | python3 -m json.tool # Start it (foreground) python3 "$M0S/m0_server.py" serve # Start it in the background, logging to the store directory nohup python3 "$M0S/m0_server.py" serve > "$HOME/.local/share/coco-m0/server.log" 2>&1 & # Write and read without any server at all python3 "$M0S/m0_server.py" write --project my-project --text "Did the thing." python3 "$M0S/m0_server.py" read --project my-project --limit 5 # Land any writes that were deferred while the store was busy python3 "$M0S/m0_server.py" drain
If the scripts are not at $HOME/.claude/skills/m0/scripts, the bundle was installed elsewhere: run bash install.sh --systems m0 from the Coco checkout, or use the in-repo path systems/m0/skills/m0/scripts.
bashM0="${M0_BASE_URL:-http://127.0.0.1:8787}" if curl -fsS "$M0/api/health" >/dev/null 2>&1; then curl -s "$M0/api/health" | python3 -m json.tool else echo "No M0 server at $M0." echo "Direct store access still works:" python3 "$HOME/.claude/skills/m0/scripts/m0_server.py" health fi
Report to the user, in this order:
<url>, or not running (and that this is fine; the CLIand the MCP tools both fall back to the store directly).
db path and the rows count. This is the whole dataset.pending_sidecars is above zero, some writes are spooled butnot yet landed. Run drain.
degraded is set, another process holds the write lock.Reads still work; writes will defer rather than fail.
Ask which the user wants:
python3 "$M0S/m0_server.py" servenohup line above.is only needed when several tools should share one process, or when something can only speak HTTP.
Useful flags and variables:
| Setting | Effect | |---------|--------| | --port / M0_PORT | Listen port (default 8787) | | --host / M0_HOST | Bind address (default 127.0.0.1; anything else warns, there is no auth) | | --db / M0_DB | Store path (default $XDG_DATA_HOME/coco-m0/thread.db) | | --busy-timeout-ms / M0_BUSY_TIMEOUT_MS | Per-call SQLite busy timeout (default 10000) | | M0_PROJECT | Default project when a caller omits one | | M0_SOURCE_TOOL | Stamped on writes, so you can tell which tool wrote what |
Never set M0_BUSY_TIMEOUT_MS to a large value. A long wait inside a shutdown hook is indistinguishable from a hang; the deferred-write path exists so waiting is never necessary.
bashclaude mcp add coco-m0 -- python3 "$HOME/.claude/skills/m0/scripts/m0_mcp.py" claude mcp list # confirm it is registered
That exposes m0_remember and m0_recall as tools the agent can call directly, which is the lowest-friction way to make memory habitual. For editors that read a project .mcp.json, the equivalent entry is:
json{ "mcpServers": { "coco-m0": { "command": "python3", "args": ["<absolute path>/skills/m0/scripts/m0_mcp.py"], "env": { "M0_PROJECT": "<this project>" } } } }
The MCP server prefers the HTTP server and falls back to the store directly when nothing is listening, so either mode works. Each result reports which path served it under via.
Automatic capture is opt-in. These are recipes for the user to install; do not edit the user's settings files without asking first. Show the snippet, explain what it does, and let them decide.
Session end, so a closing session always leaves a trace:
bashpython3 "$HOME/.claude/skills/m0/scripts/m0_server.py" write \ --project "$(basename "$PWD")" --kind session_end \ --source-tool claude-code \ --branch "$(git rev-parse --abbrev-ref HEAD 2>/dev/null)" \ --head-sha "$(git rev-parse --short HEAD 2>/dev/null)" \ --text "Session ended."
This is exactly the case the deferred-write path was built for: if the store is locked at that moment, the entry is spooled to a sidecar file and lands on the next start rather than being lost.
Session start, to inject the thread into a fresh session:
bashpython3 "$HOME/.claude/skills/m0/scripts/m0_server.py" read \ --project "$(basename "$PWD")" --limit 10
bashpython3 "$HOME/.claude/skills/m0/scripts/m0_server.py" drain # {"drained": 1, "skipped": 0, "quarantined": 0}
drained — entries moved from the spool into the store.skipped — the store was still busy; they stay spooled for next time.quarantined — files that were not valid entries, renamed *.json.bad so theystop blocking the queue. Inspect them; they are plain JSON.
A server drains automatically on start, so this is only needed when running serverless or after an unusual amount of write contention.
bashbash systems/m0/tests/m0-smoke.sh
27 assertions in a throwaway directory on an ephemeral port: the round trip, idempotency, a deferred write under a genuinely locked store, the drain on restart, and the MCP tools over stdio. It touches no existing store. Run it before trusting a change to this bundle, and quote the output rather than summarising it.
Nothing. The store is a SQLite file on local disk, the server binds to loopback, there are no outbound calls in the request path and no telemetry. To inspect, back up or forget:
bashDB="$(python3 "$HOME/.claude/skills/m0/scripts/m0_server.py" health | python3 -c 'import json,sys;print(json.load(sys.stdin)["db"])')" sqlite3 "$DB" "SELECT ts, kind, substr(text,1,60) FROM operational_thread ORDER BY ts DESC LIMIT 10;" cp "$DB" ~/m0-backup.db # back up rm "$DB" # forget everything
/m0-recall at the start of a session, or when picking up work startedelsewhere, before asking the user to repeat context.
/m0-remember after a step lands, a decision is made, or something isverified.
/m0-handoff before context is compacted or a session ends./m0 (this skill) for the plumbing: server, store, spool, wiring.M0 answers "where were we and what is next". It does not do semantic search, entity extraction or summarisation — see the comparison with the cognee bundle in systems/m0/README.md before choosing.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 11,022 | 21,571 | +96% | 1 | 1 | 0% | 453 | 2,652 | +485% | 0 | 0 | — |
case-02 | fail→fail | 4,768 | 13,774 | +189% | 1 | 1 | 0% | 622 | 2,484 | +299% | 0 | 0 | — |
case-03 | fail→fail | 18,445 | 18,062 | -2% | 1 | 1 | 0% | 1,989 | 2,597 | +31% | 0 | 0 | — |
case-04 | fail→pass | 11,663 | 7,800 | -33% | 1 | 1 | 0% | 985 | 2,768 | +181% | 0 | 0 | — |
case-05 | fail→pass | 14,539 | 8,369 | -42% | 1 | 1 | 0% | 2,083 | 2,854 | +37% | 0 | 0 | — |
case-06 | fail→pass | 11,814 | 10,505 | -11% | 1 | 1 | 0% | 1,193 | 3,032 | +154% | 0 | 0 | — |
case-07 | fail→pass | 7,104 | 10,237 | +44% | 1 | 1 | 0% | 1,341 | 3,224 | +140% | 0 | 0 | — |
case-08 | fail→pass | 16,791 | 11,513 | -31% | 1 | 1 | 0% | 1,815 | 3,362 | +85% | 0 | 0 | — |
case-09 | fail→fail | 9,227 | 9,128 | -1% | 1 | 1 | 0% | 1,632 | 3,067 | +88% | 0 | 0 | — |
case-10 | fail→pass | 13,170 | 10,113 | -23% | 1 | 1 | 0% | 1,679 | 3,238 | +93% | 0 | 0 | — |
case-11 | fail→pass | 18,164 | 7,981 | -56% | 1 | 1 | 0% | 2,236 | 2,786 | +25% | 0 | 0 | — |
case-12 | fail→pass | 26,429 | 3,493 | -87% | 1 | 1 | 0% | 1,370 | 2,895 | +111% | 0 | 0 | — |
case-13 | pass→pass | 12,129 | 6,581 | -46% | 1 | 1 | 0% | 2,193 | 3,352 | +53% | 0 | 0 | — |
case-14 | pass→pass | 8,737 | 4,239 | -51% | 1 | 1 | 0% | 1,648 | 3,026 | +84% | 0 | 0 | — |
case-15 | fail→pass | 50,902 | 8,022 | -84% | 1 | 1 | 0% | 3,161 | 2,652 | -16% | 0 | 0 | — |
case-16 | fail→pass | 6,803 | 7,298 | +7% | 1 | 1 | 0% | 1,110 | 2,584 | +133% | 0 | 0 | — |
case-17 | fail→pass | 17,505 | 3,344 | -81% | 1 | 1 | 0% | 2,132 | 2,819 | +32% | 0 | 0 | — |
case-18 | fail→pass | 16,434 | 11,753 | -28% | 1 | 1 | 0% | 1,959 | 3,356 | +71% | 0 | 0 | — |
case-19 | fail→pass | 20,178 | 13,322 | -34% | 1 | 1 | 0% | 3,767 | 4,730 | +26% | 0 | 0 | — |
case-20 | fail→fail | 25,703 | 19,509 | -24% | 1 | 1 | 0% | 3,648 | 4,785 | +31% | 0 | 0 | — |
case-21 | fail→fail | 35,413 | 22,996 | -35% | 1 | 1 | 0% | 4,457 | 5,262 | +18% | 0 | 0 | — |
case-22 | fail→pass | 15,942 | 9,144 | -43% | 1 | 1 | 0% | 1,574 | 2,798 | +78% | 0 | 0 | — |
case-23 | pass→pass | 19,791 | 9,441 | -52% | 1 | 1 | 0% | 1,997 | 2,858 | +43% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 19 counted toward the lift figure. The other 4 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +61 percentage points is the difference between those two pass rates over the 19 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.