umo-memory
by UmoLab
README.md
# umo-memory
> Persistent memory for AI coding agents. Local-first. Zero cloud. Plug-and-play with Claude Code via MCP.
[]()
[]()
[]()
[]()
[]()
**Stop losing context between sessions.** `umo-memory` gives your AI agent a long-term brain that runs entirely on your laptop. Stored facts (preferences, decisions, conventions) survive across sessions and are recalled by semantic similarity — so the next time you ask Claude to "use my dark theme," it remembers without you re-explaining.
Built for [Claude Code](https://claude.ai/code) but works with any [MCP](https://modelcontextprotocol.io/)-compatible client.
---
## Why
Every Claude Code session starts cold. You re-explain your preferences, your project's conventions, your architecture decisions. After 50 sessions, you've typed the same context thousands of times.
`umo-memory` solves this with persistent memory:
- **Stored automatically** by your agent via MCP tools (`store`, `recall`, etc.)
- **Recalled by semantic similarity** (vector search, no exact-key matching)
- **Survives sessions, restarts, machines** (single SQLite file)
- **100% local** — no API calls, no telemetry, no cloud lock-in
- **Battle-tested** — 179 tests, 92% coverage (with branch tracking), real concurrency tests
---
## 30-second quickstart
### 1. Install
```bash
pip install umo-memory
```
(First run downloads a ~80MB embedding model — one-time, then cached.)
### 2. Initialize the database
```bash
umo init
```
Creates `~/.local/share/umo-memory/memory.db`.
### 3. Wire into Claude Code
Add to `~/.claude/mcp_servers.json` (or `.mcp.json` in your project):
```json
{
"mcpServers": {
"memory": {
"command": "umo",
"args": ["start"],
"env": {"UMO_PATH": "~/.local/share/umo-memory/memory.db"}
}
}
}
```
### 4. Use it
Open Claude Code. It now has access to:
- `store` — save a fact (preference, decision, convention)
- `recall` — semantic search across stored facts
- `update` / `forget` — mutate stored facts
- `recent` / `browse` — inspect what's stored
Tell Claude: *"Remember that I prefer dark mode in all my projects."* Next session, it'll know.
---
## First day with umo
A 5-minute walkthrough to prove the round-trip works:
```bash
$ umo init
Initialized umo-memory at ~/.local/share/umo-memory/memory.db
$ umo list
No memories yet. Run `umo init` then store something.
# Stash something via Python (or via Claude Code)
$ python -c "from umo import Memory; \
m = Memory(); \
m.store('I prefer dark mode in editors', 'fact', 0.8, subject_key='user.theme')"
$ umo list
01KVF2CKW772 fact imp=0.80 [user.theme] I prefer dark mode in editors
$ umo search "what theme does the user like"
1. 01KVF2CKW772 fact imp=0.80 [user.theme] I prefer dark mode in editors
$ umo show 01KVF2CKW772
Memory 01KVF2CKW772QQG4ZRPSDEH99E
kind: fact
subject_key: user.theme
importance: 0.80
source: manual
...
content:
I prefer dark mode in editors
```
If you see this — Claude Code will too. The next session, just open Claude and ask *"what theme does the user like?"* — it'll know.
---
## What's stored
Three kinds of memories:
| Kind | Use for | Example |
|------|---------|---------|
| `fact` | User preferences, project facts, conventions | "User prefers dark mode" |
| `event` | Things that happened, dated context | "Migrated from Postgres to SQLite on 2026-01-15" |
| `lesson` | Procedural rules the agent should follow | "When using sqlite-vec, always include `AND k = ?`" |
Each memory can have a `subject_key` (e.g. `user.theme`, `project.db_choice`) for deterministic lookup, plus metadata and importance (0.0–1.0).
---
## Architecture
```
┌──────────────────────────────────────────────────┐
│ Claude Code (or any MCP client) │
│ │ │
│ │ MCP protocol (JSON-RPC over stdio) │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ umo_memory mcp_server │ │
│ │ (9 tools: store, recall, update, …) │ │
│ └──────────────┬───────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ umo_memory core (Python) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ │
│ │ │ Store │ │ Recall │ │ Embedder│ │ │
│ │ │ (SQLite) │ │(vec+fts) │ │ (sbert)│ │ │
│ │ └──────────┘ └──────────┘ └─────────┘ │ │
│ └──────────────┬───────────────────────────┘ │
│ │ │
│ ▼ │
│ SQLite + FTS5 + vec0 (~/.local/share/…) │
└──────────────────────────────────────────────────┘
```
Everything in one SQLite file. SQLite gives ACID transactions, WAL for concurrent reads, and single-file backup. No Docker, no Postgres, no Redis.
---
## Comparison
| | umo-memory | mem0 | LangChain Memory | custom RAG |
|---|---|---|---|---|
| **Local-first** | ✅ single file | ❌ cloud default | ❌ external DB | depends |
| **MCP integration** | ✅ built-in | ❌ DIY | ❌ DIY | ❌ DIY |
| **No external services** | ✅ | ❌ | ❌ | depends |
| **Vector search built-in** | ✅ sqlite-vec | ✅ | depends | depends |
| **FTS5 keyword fallback** | ✅ | ❌ | ❌ | ❌ |
| **Subject-key dedup** | ✅ | ✅ | ❌ | ❌ |
| **Setup time** | 30 sec | 5 min | 30 min | hours |
| **Dependency footprint** | 7 packages | heavy | very heavy | DIY |
**When to use `umo-memory`:** you want persistent memory that just works, runs on your laptop, doesn't phone home, and integrates with Claude Code in under a minute.
**When NOT to use it:** you need multi-tenant cloud storage, embeddings for non-text modalities, or 100k+ memories per user (vector search in SQLite caps out around there).
---
## Backing up and restoring
Everything is in one SQLite file. To back up:
```bash
cp ~/.local/share/umo-memory/memory.db backup.db
# Or export to portable JSONL:
umo export ~/.local/share/umo-memory/memory.db > backup.jsonl
```
To restore:
```bash
# From a SQLite file:
cp backup.db ~/.local/share/umo-memory/memory.db
# From a JSONL file:
umo init ~/.local/share/umo-memory/memory.db --force
umo import_data ~/.local/share/umo-memory/memory.db backup.jsonl
```
## Upgrading the embedding model
If you switch `UMO_EMBEDDING_MODEL` or upgrade `sentence-transformers`, old memories
have embeddings in the old dimension. Run:
```bash
umo reembed
```
This re-encodes every memory using the current model. Without it, sqlite-vec
queries on mixed-dim embeddings will crash.
---
## Usage
### CLI
```bash
umo init # create empty DB
umo verify # health check (8 checks)
umo stats # show counts, model, schema version
umo list # show recent memories (formatted)
umo show <id> # show one memory by id
umo search "query" # semantic search via CLI
umo start # run MCP server on stdio
umo export <path> > backup.jsonl # export all memories to JSONL
umo import_data <path> <file.jsonl> # import from JSONL backup
umo import <path> <pm-db-path> # import from PMB-format DB
umo reembed # re-encode stale embeddings
```
### Python API
```python
from umo import Memory
mem = Memory(path="~/.local/share/umo-memory/memory.db")
# Store a fact (with subject_key for deterministic dedup)
mem.store(
content="User prefers dark mode",
kind="fact",
importance=0.8,
subject_key="user.theme",
)
# Semantic recall (results are MemoryRecord, sorted by relevance)
results = mem.recall("what theme does the user prefer?")
for r in results:
print(f"[{r.kind}] {r.content}")
# Update
mem.update(id=results[0].id, content="User prefers dark mode in editors, light in terminals")
# Forget
mem.forget(id=results[0].id)
# Browse by prefix
for item in mem.browse(subject_key_prefix="user."):
print(item.content)
```
### MCP tools
When running as an MCP server, these tools are exposed:
| Tool | Purpose |
|------|---------|
| `store` | Store a fact/event/lesson |
| `recall` | Semantic search across all memories |
| `update` | Update content/importance/metadata |
| `forget` | Soft-delete (archive) |
| `recent` | Browse recent memories |
| `browse` | List by subject_key prefix |
| `stats` | Counts, schema version, model info |
| `store_many` | Atomic batch insert |
| `recall_many` | Batch semantic search |
For migration from PMB-format DBs, use the Python API:
`from umo_memory.importers.pmb import import_pmb`.
---
## Storage format
Single SQLite file with three tables:
- `memories` — content + metadata (subject_key, kind, importance, source, timestamps)
- `memory_fts` — FTS5 virtual table for keyword fallback
- `memory_vec` — sqlite-vec virtual table for semantic search
Schema migrations: `schema_version` table tracks applied migrations; `umo init` upgrades existing DBs in place.
---
## Testing
```bash
git clone https://github.com/yourname/umo-memory
cd umo-memory
pip install -e ".[dev]"
pytest # 179 tests, ~10 min on CPU
pytest --cov=umo_memory # with coverage
ruff check src tests # lint
```
---
## Documentation
- [Installation guide](docs/installation.md)
- [Claude Code integration](docs/claude-code-integration.md)
- [Python & MCP API reference](docs/api.md)
- [CHANGELOG](CHANGELOG.md)
- [CONTRIBUTING](CONTRIBUTING.md)
---
## License
Apache 2.0. See [LICENSE](LICENSE).
---
## Credits
- [sqlite-vec](https://github.com/asg017/sqlite-vec) — vector search in SQLite
- [sentence-transformers](https://www.sbert.net/) — local embeddings
- [fastmcp](https://github.com/jlowin/fastmcp) — MCP server framework
- [python-ulid](https://github.com/aztechverder/python-ulid) — sortable IDs
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing