Graphmem design
Local, single-binary memory for coding agents.
Language Rust · Storage embedded SQLite · Transport MCP over stdio · Clients Claude Code, Codex, pi · Data ~/.graphmem (or $GRAPHMEM_HOME).
Purpose
Coding agents are strong within one session but lose context between them: architectural decisions, conventions, debugging discoveries, pitfalls, and how components relate. Graphmem keeps that context on disk and makes it retrievable from any MCP client and from a human CLI.
The bar is a tool that feels like git, rg, or sqlite3:
install one binary → register it as an MCP server → memory persists
No daemon, no external database, no server to run.
Non-goals
Not a general-purpose graph or vector database, not a hosted service, not a multi-user server, not an autonomous knowledge-extraction platform, and not a replacement for Git or project documentation. The priority is a tiny, reliable local memory layer.
Architecture
Claude Code / Codex / pi / human CLI
│ MCP stdio or CLI
▼
gmem (single Rust binary)
│ embedded
▼
SQLite + FTS5
memories · scopes · entities · relations
The process is launched by the client and exits with it. There is no daemon.
Storage
SQLite (rusqlite, bundled) is the store: one binary, transactions, schema migrations, indexes, recursive CTEs, FTS5, trivial backup, and no server. The expected dataset is small, so a graph database earns nothing — relations live in ordinary tables and traversal uses recursive CTEs. A key/value store such as RocksDB was rejected because it would mean rebuilding the filtering, search, and relationship indexes SQLite already provides.
Writes are transactional: a remember that fails to embed stores nothing.
Data model
Memory — one durable piece of remembered information: content, memory_type, importance, created_at, updated_at, last_accessed_at, access_count. memory_type is a free string (e.g. decision, convention, observation); there is no fixed ontology.
Scope — global or repo:/absolute/path. A memory may have several scopes. recall filters by scope before ranking, so one repository’s memories never leak into another’s results. When the caller omits scopes, the server uses the canonical Git common directory of its startup working directory (shared by linked worktrees) plus global, repository first. New writes and reads use the common-directory scope.
Entity — a named thing a memory refers to (kind + name + normalized form). Kinds are strings, not an enum.
Relation — a directed, labelled edge between two entities, with optional metadata. A memory links to the entities and relations it mentions.
Retrieval
recall seeds from the query’s semantic embedding and text matches, then runs Personalized PageRank over the memory↔entity graph to reach memories the query did not literally match. This is the non-trained HippoRAG 2
approach. Scope filtering happens before ranking.
SQLite FTS5 (BM25, then importance and recency tie-breakers) is the lexical fallback and a per-call comparison mode via use_embeddings: false.
Embeddings run locally through Candle, default sentence-transformers/msmarco-MiniLM-L6-cos-v5, downloaded on first use and cached under the data directory. GRAPHMEM_EMBEDDINGS=off disables them, leaving FTS5 ranking. A memory’s vectors are stored in the same transaction as the memory, so a model failure stores nothing and never leaves a half-indexed row.
MCP interface
Seven tools, kept small on purpose: remember, recall, stats, relate, graph, inspect, forget. The agent works with memory semantics, never with create_node/query_sql-style database operations. See mcp.md
for the contract.
CLI
gmem also has a human-facing CLI so the store stays inspectable without an LLM:
gmem remember "…" gmem list gmem show <id> gmem search "…"
gmem graph <kind> <name> gmem forget <id> gmem flush
gmem scopes gmem reembed gmem doctor gmem mcp
See cli.md .
Logging and stdout
MCP speaks newline-delimited JSON-RPC on stdout, so application logs must never touch it. Logs go to stderr and to ~/.graphmem/logs/graphmem.log (following GRAPHMEM_HOME).
Privacy
Local-first: no telemetry, no network requests beyond the one-time model download, no hosted API, no automatic upload. Secrets are not intentionally stored.
Testing
Behavior over implementation. Integration tests cover SQLite and the CLI/MCP boundary; unit tests are for pure domain logic such as ranking. The suite exercises persistence across process restarts, scope isolation, graph traversal (including cycles), and that logs never contaminate stdout.
Principles
- Keep the MCP surface small; add a tool only when a memory semantic needs one.
- SQLite is an implementation detail; clients never see the schema.
- Deterministic operations first; no autonomous extraction without evidence it helps.
- Optimize retrieval quality, not graph complexity — the graph serves recall, it is not the product.
- Everything a user stores stays inspectable, exportable, and deletable.
- Preserve the single-binary property when adding dependencies.