The memory
Claude Code forgets everything when a session ends. So I gave it a database. This is what's inside it, and how it's wired together.
October 2025 → October 2026 · roughly 12 months of history ·
How it works
Five steps, none of them exotic. The interesting part isn't any single piece — it's that they run continuously and unattended.
- 1
Claude Code writes JSONL
Every session is appended to a transcript on disk. This is stock behaviour — nothing custom yet.
- 2
A parser ingests it
jsonl_processor.py walks the transcripts, decodes project paths, extracts messages, tool calls, tokens and cost, and upserts them. Re-runnable and idempotent.
- 3
Embeddings get generated
Messages and memories are embedded as vector(1536) and stored in Postgres via pgvector, so recall is semantic rather than keyword-only.
- 4
An MCP server exposes it
37 tools over stdio — search, recall, stats, observations. Claude queries its own history as a first-class tool.
- 5
The agent reads its own past
Ask about work from months ago and it searches the database instead of guessing. That is the whole point.
The schema
23 tables, 100 indexes, 481,161 rows. The two big ones are big because of embeddings, not text.
| Table | Rows | Size | Purpose |
|---|---|---|---|
memories | 18,056 | 484 MB | Long-term facts with vector embeddings. Semantic recall across sessions. |
conversation_messages | 384,084 | 412 MB | Every message, embedded for hybrid semantic + trigram search. |
resources | 8,494 | 159 MB | Saved URLs, docs, and reference material. |
observations | 48,529 | 154 MB | Behavioural notes captured during sessions. |
observation_digests | 943 | 35 MB | Periodically compacted observations, to keep recall cheap. |
session_stats | 6,161 | 5656 kB | Per-session metrics: tokens, tool calls, cost, duration. |
conversation_sessions | 5,990 | 3776 kB | Session index — project path, timing, message counts. |
session_summaries | 1,063 | 3320 kB | Generated summaries used to warm-start later sessions. |
generated_images | 519 | 1488 kB | Image generation log. |
tool_usage | 3,506 | 1040 kB | Daily tool-call counts. Drives the tool distribution chart. |
usage_hourly | 2,078 | 392 kB | Hourly rollup. Drives the 24-hour clock. |
notifications | 396 | 384 kB | — |
model_usage | 643 | 336 kB | Per-model, per-day tokens and cost. |
project_profiles | 274 | 256 kB | Detected framework and dependencies per project. |
pi_memories | 55 | 248 kB | Pi scout system memory store. |
usage_daily | 260 | 112 kB | Daily rollup. Drives the heatmap. |
time_entries | 61 | 104 kB | Manual time tracking. |
clients | 18 | 88 kB | — |
capture_amendments | 4 | 80 kB | — |
session_attributions | 10 | 48 kB | — |
bucket_list | 14 | 32 kB | Personal backlog. |
reminder_timed_sent | 3 | 24 kB | — |
pi_observations | 0 | 16 kB | Pi scout system observations. |
How it filled up
Messages recorded per month, with the running total behind it. The early months are thin because they were backfilled from migrated transcripts — the archive only becomes continuous once the database was running live.
The database
- Engine
- PostgreSQL 16 (pgvector/pgvector:pg16, Docker)
- Embeddings
vector(1536)- Extension
-
pg_trgm1.6 - Extension
-
vector0.8.2 - Indexes
- 100
- Host
- Docker, localhost only
The MCP server
- Language
- Python, stdio transport
- Tools exposed
- 37
- Lines of code
- 5,664
Modules
api.py conversations.py db_config.py embeddings.py jsonl_processor.py memory.py observations.py stats.py stats_db.py What this isn't
It isn't a product. It's a local Docker container on one machine, bound to localhost. There's no hosted version and no install script.
It isn't magic recall. Semantic search over 384,084 messages returns plausible neighbours, not guaranteed answers. It's a research tool for the agent, and it still has to read what it finds.
It isn't free. Embedding 481,161 rows costs real money and real disk — 1271 MB of it, most of that vectors rather than prose.
It isn't the hard part. Storing history is easy. Deciding what's worth remembering, and getting the agent to actually consult it before answering, is where the work is.