Skip to main content
Glama
README.md
# umo-memory

> Persistent memory for AI coding agents. Local-first. Zero cloud. Plug-and-play with Claude Code via MCP.

[![Tests](https://img.shields.io/badge/tests-179%20passing-brightgreen)]()
[![Coverage](https://img.shields.io/badge/coverage-92%25-brightgreen)]()
[![Python](https://img.shields.io/badge/python-3.11%2B-blue)]()
[![License](https://img.shields.io/badge/license-Apache%202.0-blue)]()
[![MCP](https://img.shields.io/badge/MCP-compatible-purple)]()

**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