memo - Local-first memory for AI agents — offline, no keys

by
Give any MCP agent (Claude Code, Cursor, Cline) a long-term memory that runs 100% on your machine. Time-machine, contradiction radar, auto-synthesis. MLX on Mac, CPU/Docker on Linux. Markdown is the source of truth. No cloud, no keys. MIT.

Add a comment

Replies

Best
Maker
📌
Maker here 👋 I built memo because I was tired of re-explaining the same decisions to my AI agents every morning. Claude Code would forget "we use Postgres, not Mongo" the moment the session closed — so I gave it a memory that survives sessions. Everything runs 100% on your machine: MLX embeddings on Apple Silicon (or a CPU backend on Linux/Docker), hybrid semantic + keyword search, and plain Markdown files as the source of truth — your agent's memory is just notes you can read, edit and graph in Obsidian. The parts I'm most proud of: a time-machine that rewinds the whole corpus to any past date ("what did we know about this bug last month?"), a contradiction radar that flags stale decisions so agents don't reintroduce them, and a nightly synthesis pass that infers new insights from clusters of related memories. It's my first MCP server, MIT licensed. Demo GIF + docs in the repo: Try it in one command (no install): docker run --rm ghcr.io/jagoff/memo:latest memo doctor Happy to answer anything!

How does the contradiction radar actually detect conflicts between new notes and existing memories? Is it doing real semantic comparison locally or is it more of a keyword/tag diff? Curious how well it catches subtle contradictions vs obvious ones.

Maker

 

It's real semantic comparison, and it all runs on your machine. Two steps.

First, a similarity pass: each memory's embedding gets compared against the rest, and only pairs scoring above a cosine threshold of 0.55 go forward. It never looks at keywords or tags.

Then a small local LLM reads both notes and picks a label: contradiction, evolution (the newer note replaces the older one), consistent, or unrelated. Only contradictions and evolutions with at least 70% confidence get stored.


Obvious conflicts are the easy case. "I use Ollama" vs "I migrated to MLX" is the same topic, so the embeddings land close together, and the conflict is explicit enough for a small model to catch. Subtle ones are harder. If two notes disagree but phrase things very differently, their similarity never clears the threshold and the pair never gets compared at all. Each note is also only checked against its 5 nearest neighbors, and the model only sees the first 1000 characters of each. Stated conflicts it handles fine. Implied ones, where you'd have to reason your way to the contradiction, it mostly misses.


The evolution category is a smart move, though. "I switched to Y" gets treated as an update rather than a false alarm, and that's the most common case that would otherwise look like a contradiction.

Thank you for your interest!

ran it locally with mlx on my m2 and the contradiction radar caught a stale preference i forgot i had, which was both creepy and useful. local-only memory feels like it should have been table stakes ages ago.

Maker

 Great! I'm glad you're finding it useful.