Local semantic memory that runs entirely on your Mac using MLX. It stores memories as Markdown files in an Obsidian-compatible vault and indexes them with sqlite-vec for hybrid search. The MCP interface exposes save, search, recall, and ask operations. The standout feature is time-machine: you can snapshot the corpus at any past date to debug agent regressions or reproduce historical behavior. It includes a recall daemon that keeps the embedder warm for sub-200ms queries, auto-capture that extracts insights after exchanges, and ambient recall that injects top-3 memories before every prompt. Ships with a CLI for direct interaction and comes pre-wired with hooks for Claude Code session lifecycle events. Everything runs in-process with no external services or API keys.
Local-first semantic memory for AI agents — with time-travel, contradiction radar, and automatic synthesis.
memo is an MCP server for semantic memory. It gives any AI agent (Claude Code, Devin, Cursor, Cline) a persistent, searchable knowledge base that:
No Ollama, no Qdrant, no cloud APIs, no keys.
Normal memo-mcp startup makes no outbound network request and does not rewrite
Claude or Codex configuration. Release checks, automatic updates, statusline
self-healing, and hook self-healing are all explicit opt-ins. Commands such as
update and cross-machine sync, plus requested model or benchmark downloads, may
use the network. See the privacy and network policy for the
exact flags and boundaries.
memo gives any MCP-aware agent (Claude Code, Codex, Devin, OpenCode, Cursor, Cline, Continue, …) a long-term memory that runs entirely on your own machine — macOS on Apple Silicon via Apple MLX, or Linux / Ubuntu on a CPU sentence-transformers backend (pipx install "mlx-memo[cpu]", see docs/ubuntu.md). Each memory is a plain Markdown file; embeddings live in a single sqlite file; the embedder, reranker, and LLM run in-process — no Ollama, no Qdrant, no cloud API, no keys. Your prompts and memories never leave the machine.
| Capability | memo | mem0 | letta | cognee | engram | basic-memory | cipher |
|---|---|---|---|---|---|---|---|
| 100% local (no cloud API) | ✅ | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ | ⚠️ |
| Time-machine (rewind corpus to any date) | ✅ | ❌ | ⚠️ | ❌ | ❌ | ⚠️ | ⚠️ |
| Contradiction radar (detect + resolve conflicts) | ✅ | ⚠️ | ⚠️ | ❌ | ⚠️ | ❌ | ❌ |
| Synthesis pipeline (auto-infer cross-cluster insights) | ✅ | ❌ | ✅ | ⚠️ | ❌ | ❌ | ⚠️ |
| Cross-Mac git sync (shared corpus, no server) | ✅ | ❌ | ⚠️ | ❌ | ✅ | ✅ | ⚠️ |
| Cloud sync (opt-in replication) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| TUI (terminal UI) | ✅ | ❌ | ⚠️ | ❌ | ✅ | ❌ | ✅ |
| Obsidian as source-of-truth | ✅ | ❌ | ⚠️ | ❌ | ❌ | ✅ | ❌ |
| Knowledge graph + entity extraction | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ⚠️ |
| Eval regression gate (pre-commit wireable) | ✅ | ⚠️ | ⚠️ | ⚠️ | ❌ | ❌ | ❌ |
| Multi-modal (images, audio OCR) | ✅ | ⚠️ | ⚠️ | ✅ | ❌ | ❌ | ❌ |
| MCP surface profiles (token economy) | ✅ | ❌ | ❌ | ⚠️ | ⚠️ | ✅ | ❌ |
| Passive capture (auto-extract from transcripts) | ✅ | ⚠️ | ✅ | ✅ | ✅ | ❌ | ⚠️ |
| Session timeline (context before/after) | ✅ | ❌ | ❌ | ❌ | ✅ | ⚠️ | ⚠️ |
✅ first-class · ⚠️ partial, config-gated, or add-on · ❌ absent. Verified mid-2026 against each project's docs/repo: mem0, letta (formerly MemGPT), cognee, engram, basic-memory, cipher. Closest comparators: basic-memory (local-first + Obsidian + MCP — memo's exact thesis) and cipher (memory layer for coding agents).
memo is built to spend fewer tokens, not more.
agent profile exposes 14 tools / ~1.4k schema tokens, versus 131 tools / ~15k tokens for the full surface — that overhead is paid every session, in every client. memo trims it to almost nothing.On a ~200-memory corpus, memo roi estimates ~80k tokens of model work avoided per session. The number is corpus-specific; it grows as memo learns more.
memo tokens tracks the savings in real-time:
| Technique | How to enable | Typical saving |
|---|---|---|
| Compact recall format | export MEMO_RECALL_FORMAT=compact | ~65% per injection |
| Trivial prompt gate | On by default | ~25% fewer injections |
| Context file compression | memo compress-context CLAUDE.md | 30–40% smaller context |
memo verbatim search can find the precise wording of past local transcript turns through a private, lexical-only FTS5 index. It never enters ambient recall.sentence-transformers backend (search + recall + save, no MLX). One command: pipx install "mlx-memo[cpu]". See docs/ubuntu.md for what works and the trade-offs.~/Documents/memo/.Python ≥ 3.13 is required if you install without uv. The
curl | bashinstaller handles this automatically — it detectsuvand uses its managed Python if no system Python ≥ 3.13 is on PATH.
curl -fsSL https://raw.githubusercontent.com/jagoff/memo/master/install.sh | bash
The installer auto-detects uv (preferred) or falls back to pipx. It downloads MLX models, and wires memo into every agent client it finds (Claude Code, Codex, Devin, Devin Desktop, OpenCode).
Prefer a manual install? Any of these expose the same two binaries — memo (CLI) and memo-mcp (MCP server):
uv tool install mlx-memo # recommended
pipx install mlx-memo
brew tap jagoff/memo && brew install mlx-memo
Keep memo isolated as its own tool (uv tool / pipx / Homebrew). Don't vendor it inside another project's
.venv.memo doctor --strict-runtimeverifies the install.
On Linux, or just want to try it without installing anything? Run the Docker
image (CPU backend, cross-platform — search/recall/save; the reranker + ask/
synthesize/dream verbs are Apple-Silicon-only):
docker run --rm ghcr.io/jagoff/memo:latest memo doctor
Details in docs/docker.md.
First install downloads ~8 GB of MLX models (5–15 min); later installs hit the HuggingFace cache. Full installer knobs and "move to a new Mac" steps: docs/reference.md › Install.
Migrating from another Mac? Install first, then restore your corpus:
curl -fsSL https://raw.githubusercontent.com/jagoff/memo/master/install.sh | bash
memo sync bootstrap git@github.com:yourname/memo-sync.git # restore from git
memo installs itself if you hand the repo (or just the install line) to an AI agent:
curl -fsSL https://raw.githubusercontent.com/jagoff/memo/master/install.sh | bash
memo doctor --strict-runtime # verify runtime is healthy
After install, tools surface as mcp__memo__memo_* (memo_save, memo_search, memo_context, memo_ask, memo_get, memo_graph, memo_unified_briefing, plus capture/session/version helpers on the default profile). Per-client setup (Claude Desktop, Cursor, Cline, Continue, manual JSON) is in docs/reference.md › MCP setup.
memo doctor # self-check: models, vault, sqlite-vec
memo config # terminal-only configuration center
memo save 'MLX prefill ~30% faster than Ollama on M3 Max' --title 'MLX bench' -t mlx -t bench
memo search 'how fast was the MLX benchmark' # search by meaning, not just keywords
memo list --limit 5 # most recent
memo ask 'what changed in the embedder this month?' # RAG — cites memories by id
# Optional: build and query a private 90-day transcript index (no embeddings)
memo verbatim index
memo verbatim search 'exact deployment decision' --limit 10
memo config is a native TUI for editing every persistent setting with search,
source badges, validation, review, and transactional rollback. Markdown under
~/.config/memo/ remains directly editable; MEMO_* variables are temporary
overrides. In pipes/CI or with MEMO_NONINTERACTIVE=1, bare memo config prints
help and the existing config show|validate|set|unset commands remain headless.
/remember calls.Stop hook extracts durable insights from each exchange through a quality gate. The corpus grows on its own.SessionStart surfaces open loops, a memory of the day, and one-line crash recovery.memo resume reopens any recent session from any agent (Claude Code, Codex, Devin, Gemini, OpenCode) in one arrow-key picker, resumed natively. Details ↓memo context "<question>" (and the memo_context MCP tool) show exactly what memory would be injected before an agent answers — the recall block on demand, with no LLM call. Add --explain to memo search to see why each hit ranked where it did.memo resume opens one arrow-key picker over every recent coding session on the machine — Claude Code, Codex, Devin, Gemini, and OpenCode — merged with memo's own recall-grounded snapshots. Crashed, closed the terminal, or switched agents mid-task? Reopen the project, run one command, and continue exactly where any of them left off.
memo resume # picker across all agents (↑/↓ browse, type to search, Enter resumes)
memo resume --all-cwd # widen beyond the current project
memo resume 019f51e9 # jump straight to one session by id / prefix
memo episodes search "vec0 timeout bug" # find a past session by meaning, not recency
Each row resumes natively in its own agent — claude --resume, codex resume, devin -r, gemini --session-file, opencode --session — so control returns to that agent's real session, not a copy. Type-to-search re-ranks by meaning over the full session history (episodic memory); Tab toggles the cwd/all filter and the updated/created sort. Any agent that writes a transcript is discovered automatically — nothing to configure.
Rewind the corpus to any past date and query it as it was then:
memo as-of ask "what was the deployment strategy?" --date 2026-02-01
memo as-of search "redis config" --date 2026-01-15
memo diff --from 2026-01-01 --to 2026-03-01 # what changed
No other agent-memory system offers this. Full historical reconstruction via reverse-replay of history.db.
memo contradict scan # detect conflicting facts corpus-wide
memo contradict triage # resolve interactively: fuse / newer-wins / dismiss
The LLM classifies each candidate pair. Results persist in contradictions.db; resolved conflicts inform future saves.
memo synthesize # generate cross-cluster insights (LLM)
MEMO_SYNTHESIS_ENABLED=1 runs synthesis automatically during memo maintain, creating type=synthesis memories from inferred cross-cluster patterns. Memories are cited with their source cluster.
memo dream runs nightly (03:00 AM by default) as a fully autonomous pipeline that synthesizes insights, resolves contradictions, decays old memories, and optimizes the corpus — with zero user intervention:
memo dream run # run once (foreground)
memo dream status # show receipt + changes made
memo dream if-due # no-op unless > 24h since last run (for cron)
The 7 phases: (1) Orientation (inventory corpus), (2) Signal gather (mine transcript labels), (3) Heal (resolve conflicts + consolidate duplicates), (4) Prune (archive 365d+ untouched, decay idle >30d), (5) Enrich (synthesize cross-cluster insights, extract entities), (6) Optimize (prune low-quality, evict if full, compress context), (7) Prewarm (cache top-100 query embeddings for <200ms recall next session).
Every phase logs to a receipt (~/.memo-state/dream/last.json): timestamp, duration, phase counts (resolved, consolidated, archived, synthesized, etc.), and any errors. Failures never silently vanish — they live in the receipt. A failed phase doesn't stop the pipeline; subsequent phases run anyway.
Optional self-improvement (all default OFF):
MEMO_DREAM_TUNE_ENABLED=1) — mines ground-truth labels from real usage, auto-tunes MEMO_RECALL_MIN_SIM, auto-reverts if eval baseline regresses. memo dream tune.MEMO_DREAM_CONSOLIDATE_EPISODES_ENABLED=1) — abstracts recurring cross-session work into synthesis memories. memo dream consolidate.MEMO_DREAM_ANTICIPATE_ENABLED=1) — surfaces unmet gaps + hot queries, pre-warms their embeddings. memo dream anticipate.memo is the only MCP memory system that self-optimizes: auto-tuning recall quality, auto-detecting gaps, consolidating patterns — all from real usage, all nightly, all without asking.
memo sync bootstrap git@github.com:yourname/memo-sync.git # wire a shared corpus
memo sync once # push/pull now
Pull-rebase-before-push. flock-based single owner per machine. Async debounced hooks keep the corpus current without blocking.
MEMO_MEMORIES_IN_VAULT=1 memo init # store memories inside your vault
memo migrate --into-vault # non-destructive migration
Human edits in Obsidian win on the next memo reindex. The sqlite index is always rebuildable from the .md files.
memo graph neighbors "MLX" # what's related
memo graph path "embedder" "reranker" # how two concepts connect
memo graph why "mlx" "daemon" # weighted path + evidence memories
memo graph hubs # broad entities that may act as hubs
memo graph relations rebuild # deterministic semantic-relation backfill
memo search "mlx daemon" --explain # ranking reasons, including graph signal when enabled
memo entities # list extracted entities
memo links --id abc123 # backlinks + outlinks
Entity extraction uses a dependency-free regex backend. For code-heavy corpora, memo can merge a codegraph symbol graph as the graph's primary layer (opt-in, MEMO_GRAPH_USE_CODEGRAPH) — callers, callees, and imports become first-class edges, so recall and memo graph path reason over real code structure, not just text similarity. The merged graph also powers the memo_graph MCP tool and the entity-centric "Knowledge map" briefing.
Graph ranking is opt-in and hub-suppressed: MEMO_GRAPH_SIGNAL_ENABLED=1 MEMO_GRAPH_REASON_ENABLED=1 memo search "recall hook budget" --explain adds bounded graph boosts plus a human/JSON graph_reason field on graph-touched hits. Semantic graph relations are also opt-in (MEMO_GRAPH_SEMANTIC_RELATIONS=1) and live in the rebuildable graph DB; Markdown remains the source of truth. memo eval recall --graph-ab compares the same recall configs with graph signal off/on before you make graph ranking part of a gate.
memo temporal facts add postgres is "primary datastore" --valid-at 2026-01-01
memo temporal facts list --as-of 2026-03-01 # facts live at that date
memo extracts subject–predicate–object fact edges from your memories and fuses a temporal fact-edge leg into hybrid search via RRF (MEMO_FACT_RETRIEVAL_ENABLED, on by default; weight MEMO_FACT_RETRIEVAL_WEIGHT=0.6). Query-relevant facts attach to search/ask results (MEMO_FACT_SURFACE_ENABLED) and surface in the SessionStart briefing's Temporal facts section. Every edge carries validity windows (--valid-at / --invalid-at / --expired-at) so list --as-of <date> returns only what was true then. Disable with MEMO_FACT_RETRIEVAL_ENABLED=0.
memo health # grounded rate, ROI, usefulness verdict
memo eval recall --labels eval/regression_labels.json --k 5
memo eval recall --gate # exit non-zero if precision drops
memo eval recall --update-baseline # snapshot current best
Wire --gate into a pre-commit hook to catch retrieval regressions before they ship. Recall eval also reports graph diagnostics (graph_recall_gain, graph_noise_rate, graph_explanation_coverage, hub_noise_rate, latency_ms_graph) when graph attribution is present, but the hard gate remains precision/noise. memo feedback records per-source 👍/👎 votes that teach the retriever which memories to surface (or hide) for similar queries.
memo ocr-image screenshot.png # one-shot macOS Vision OCR
MEMO_VLM_CAPTION_ENABLED=1 memo ingest ~/Vault --include-orphan-images
memo ingest ~/Vault --include-audio # mlx-whisper, optional
memo search "whiteboard diagram" # finds OCR/caption/transcript text
The old placeholder memo multimodal store was removed; images and audio now
become searchable by flowing OCR, VLM captions, and whisper transcripts through
the normal text index.
export MEMO_SECRET_STORAGE_ENABLED=1 # explicit opt-in
memo secret save --name openai-api-key --kind api_token # value read from stdin
memo secret get --name openai-api-key # decrypt one secret
memo secret list # names only, never values
Secret storage is off by default and requires MEMO_SECRET_STORAGE_ENABLED=1. Values are sealed with AES-256-GCM plus authenticated name/kind metadata under a random 256-bit master key at $MEMO_STATE_DIR/secret-master.key; memo enforces a private state directory (0700) and key/database permissions (0600). Credentials live only in the isolated secret_store table: no markdown marker is created, and they are never indexed, embedded, recalled, added to generated context, backed up as memory markdown, or committed by memo's git sync. memo secret list exposes metadata only. Treat memo secret get and memo secret export as explicit plaintext disclosure operations and redirect their output carefully.
memo runs four background daemons:
| Daemon | Command | Purpose |
|---|---|---|
| recall-daemon | memo recall-daemon start | Warm MLX embedder over socket (<200 ms recall) |
| idle-daemon | auto-started by memo-mcp | Auto-capture for MCP-only clients (Devin, OpenCode) |
| ingest-daemon | memo ingest-daemon start | Bulk vault ingestion |
| maint-daemon | memo maint-daemon start | Background cleanup + synthesis |
Core: save search ask get edit rename delete list
Recall & Hooks: recall recall-hook context briefing continuity prewarm capture-tick capture-stop interject ask-gaps guard
Session & History: history as-of diff record-history session resume reflect mine-history episodes chronicle
Maintenance: reindex maintain dream consolidate synthesize dedupe cross-dedup retier contradict invalidate temporal compress-context
Analysis & Quality: health stats doctor lint analytics eval roi tokens token-savings usefulness gaps outcome profile confidence graduation hype
Knowledge Graph: graph entities entity extract-entities links version related
Advanced Search: embed rerank contextual retrieve context-pack chat chat-ask repo
Import / Export / Sync: import export backup restore sync ingest
Visualization: tui dashboard map logs hook-log
Setup & Config: init config install-mcp install-watcher uninstall-watcher install-slash install-statusline install-recall-hook install-shell-wrapper install-shims startup-banner migrate migrate-vault update upgrade self-update watch release onboard
Daemons: recall-daemon ingest-daemon maint-daemon embed-daemon idle-daemon
Other: backend-native collaborative feedback query mandate sleep-cycle ocr-image provenance secret verbatim mcp-command codex-badge debug-recall http-api mine-git token-gate fix undo
| Profile | Tools | Schema tokens | Use when |
|---|---|---|---|
agent (default) | 14 | ~1.4k | Standard agent work — max token economy |
core / slim | 34 | ~3.0k | Constrained clients (Codex, OpenCode), admin-lite |
full / default | 131 | ~15k | Power users, debugging |
Set via MEMO_MCP_PROFILE=full or in each client's MCP env config.
Non-MCP clients: memo http-api serves the same operations as a localhost REST API (plain JSON). Every /api/* route requires Authorization: Bearer <token>; only /health is public. The first run creates a private token at $MEMO_STATE_DIR/http-api-token (normally ~/.local/share/memo/http-api-token), or you can provide a 32+ character MEMO_HTTP_API_TOKEN. Both REST and MCP HTTP reject non-loopback binds unless explicitly acknowledged, never allow unauthenticated non-loopback exposure, add defensive response headers, and limit each source to 300 requests per minute per process.
memo http-api
curl -H "Authorization: Bearer $(tr -d '\n' < ~/.local/share/memo/http-api-token)" \
http://127.0.0.1:8080/api/stats
For an authenticated network bind, use memo http-api --host 0.0.0.0 --allow-non-loopback; for MCP set MEMO_MCP_TRANSPORT=http, MEMO_MCP_HOST=0.0.0.0, and MEMO_MCP_ALLOW_NON_LOOPBACK=1. Use TLS or a trusted reverse proxy whenever traffic leaves the machine. --allow-no-auth / MEMO_MCP_ALLOW_NO_AUTH=1 exist only for explicit loopback development.
Hybrid search: vec leg (MLX embedding on Apple Silicon, CPU sentence-transformers on Linux) + BM25 leg (FTS5/Tantivy, diacritic-folding for Spanish) fused via Reciprocal Rank Fusion → optional MLX cross-encoder rerank (Apple Silicon).
Markdown is the source of truth. The .md files are canonical; sqlite is a rebuildable index. A hand-edit in Obsidian wins on the next memo reindex. delete() removes the index first, then the file — no silent data loss.
Embedding models:
| Model | Dims | Disk | Use |
|---|---|---|---|
Qwen3-Embedding-0.6B-4bit | 1024 | ~0.6 GB | Default (fast, good) |
Qwen3-Embedding-4B-4bit | 2560 | ~3 GB | Higher recall quality |
Qwen3-Embedding-8B-4bit | 4096 | ~5 GB | Maximum quality |
Switch with MEMO_EMBEDDER_MODEL + MEMO_EMBEDDER_DIMS (requires memo reindex --rebuild).
| Topic | Where |
|---|---|
| Full install detail, installer knobs, new-Mac migration | docs/reference.md › Install |
Per-client MCP setup + the /memo slash command | docs/reference.md › MCP setup |
| MCP profile tools and advanced domains | docs/reference.md › MCP tools |
| Ambient memory, recall daemon, capture & recall tuning | docs/reference.md › Ambient memory |
| Time-machine, session briefing, semantic map | docs/reference.md › Surfaces |
Full CLI reference + live dashboard (memo tui) | docs/reference.md › CLI |
Stable/common MEMO_* flags, model profiles, upgrading the embedder | docs/reference.md › Configuration |
| Architecture, sync tiers, design notes | docs/reference.md › Design & comparison |
Contributors: git clone https://github.com/jagoff/memo && cd memo && uv pip install -e '.[dev]'. See CONTRIBUTING.md.
memo is local-first: everything you save is stored on your own machine as
markdown files plus a rebuildable SQLite index. Embeddings and any LLM steps run
in-process (MLX / CPU) — no cloud API, no keys, no telemetry. The only way
memory leaves your device is if you configure a git memo-sync remote you
own. Full detail: PRIVACY.md.
MIT — see LICENSE. Forked philosophically from mem-vault (storage layout + frontmatter schema); the MLX backend pieces are ported from obsidian-rag. memo is one of three sovereign systems in a wider stack (Memflow, Synapse) — the integration is opt-in everywhere; single-Mac users see zero behaviour change.
hovecapital/read-only-local-postgres-mcp-server
cocaxcode/database-mcp
io.github.infoinlet-marketplace/mcp-mysql
io.github.cybeleri/database-admin
io.github.yash-0620/postgres-mcp-secured