Long-term memory for Claude Code, built on SurrealDB.
Don't summarize your memory — search it. The complete session history for Claude Code: full-text + vector + graph, 100% local, no API.
Claude Code forgets everything between sessions. brethof-mind gives it a persistent, searchable memory: an MCP server with twelve tools and four lifecycle hooks, backed by a local SurrealDB. Decisions, status, and bugs are saved as curated memory; every conversation is mirrored into a complete, searchable archive. All of it stays on your machine.
Claude Code's context gets compacted and truncated — and summaries are lossy: they're one model's guess at what mattered. brethof-mind doesn't summarize. It keeps the complete transcript of every session and makes it searchable, so recall never depends on what survived a summary or fit in a context window.
No summarization model. No separate memory agent. No API keys. Just your raw history — searchable three ways: full-text, vector, and graph — 100% local.
brethof-mind keeps two stores, and the pairing is the point:
- Curated memory — what the agent writes down on purpose: decisions, architecture, status, your corrections. Dense and high-signal. Your notes.
- Full chat memory — every transcript line, captured automatically. Complete and raw. The recording.
Notes are fast but lossy; the recording is the insurance. When the notes missed something, the full history still has it — searchable. You get the speed of a curated view without ever depending on one.
you open a new session ─► SessionStart hook loads pinned rules, recent
memory, and a recap of where you left off
you ask a question ─────► the agent searches memory before answering
the agent works ───────► saves decisions as they lock (save_memory)
every turn ends ────────► the whole turn is archived (chat_stop hook)
next week, different repo ─► "what did we decide about X?" → search_chat
- Cross-session recall. The agent picks up where it left off — no re-explaining context every morning.
- Two memories, working together. A dense curated layer (your notes) backed by a complete chat archive (the recording) — so recall never depends on what a summary kept.
- Three ways to search. Full-text (stemmed, BM25-ranked) over both the curated notes and the full chat archive, semantic (vector), and graph (SurrealQL traversal + relations).
- Multi-project. One DB, one table per project. A
projects.jsonmaps each repo to its own memory. - Local-first, no API. SurrealDB in a local container; embeddings computed locally (fastembed, CPU). No API keys, no per-call cost — runs offline after a one-time ~23 MB model download. Nothing leaves your machine.
- Robust archiving. The Stop hook chunks oversized turns, retries per-statement, and tracks a byte offset so it never loses or duplicates a line.
The memory tools run in any MCP-capable client; the hooks (auto-capture + auto-load) come with Claude Code — and Claude Desktop runs Claude Code as its engine, so you get the full experience in both.
- ✅ Claude Code — full: memory tools + automatic capture & recall (hooks)
- ✅ Claude Desktop — full: it runs on Claude Code, so the same hooks + tools apply
- 🔜 OpenClaw — coming soon
- ✅ Hermes — full: memory provider plugin (see
integrations/hermes/)
Any other MCP client can use the memory tools too — the automatic capture is the Claude Code (and Desktop) piece.
Four parts — a SurrealDB container, an MCP server, four hooks, and a
projects.json that ties them together. Full diagram and data flow in
docs/ARCHITECTURE.md.
Requirements: Docker + Docker Compose, Python 3.9+, and Claude Code.
git clone https://github.com/BrethofAI/brethof-mind.git
cd brethof-mind
# 1. Configure
cp .env.example .env # optional: change DB credentials
cp projects.example.json projects.json # edit: map your repos → tables
mkdir -p ~/.config/brethof-mind
cp projects.json ~/.config/brethof-mind/ # the location every part checks
# 2. Start SurrealDB (+ Surrealist UI on :8201)
docker compose up -d
# 3. Install the MCP server + create the schema/indexes
python -m venv mcp-server/.venv
mcp-server/.venv/bin/pip install -r mcp-server/requirements.txt
mcp-server/.venv/bin/python mcp-server/scripts/init_db.py
# 4. Register the MCP server with Claude Code (name it "memory")
claude mcp add memory -- \
"$(pwd)/mcp-server/.venv/bin/python" "$(pwd)/mcp-server/server.py"
# 5. Install the hooks
cp hooks/*.py ~/.claude/hooks/
# then merge settings.example.json's "hooks" block into ~/.claude/settings.json
# (point the Stop hook at mcp-server/.venv/bin/python — it needs fastembed)
# 6. Install the slash commands (/curate and /recall)
cp commands/*.md ~/.claude/commands/Restart Claude Code. A new session now opens with your memory loaded.
Optionally, import the history you already have:
mcp-server/.venv/bin/python mcp-server/scripts/ingest_transcripts.pyThe MCP server exposes twelve tools (registered above as memory):
| Tool | What it does |
|---|---|
load_project(project) |
Dump a project's recent curated memory. |
save_memory(project, id, type, title, content) |
UPSERT a curated prose record/note (auto-embedded). |
save_record(project, id, fields, embed_text?) |
UPSERT a structured record — fields is a JSON object, each key becomes a real queryable field. embed_text enables semantic search. |
search_memory(query, project?) |
Full-text search over curated memory (stemmed, BM25-ranked). |
semantic_search(query, project?, top_k?) |
Vector search over curated memory. |
search_chat(query, project?, top_k?) |
Vector (meaning) search over the full chat archive. |
search_chat_text(query, project?, top_k?) |
Keyword (BM25) search over the full chat archive — exact strings (paths, errors, hashes, flags) that vector search misses, and lines that were never embedded. |
get_memory(record_id, project?) |
Fetch ONE record's full, untruncated content by id (search results are previews). |
list_memory(project, type?, limit?) |
Browse a project's record ids/titles (no content), newest first. |
recent_records(project, days?, where?, limit?) |
Recent structured rows from a table, newest first — a scoped/filtered reader for high-volume ledgers. |
query_raw(sql) |
Arbitrary SurrealQL — graph traversal, aggregates. |
save_commit(project, hash, message, files, branch?) |
Record a git commit. |
| Hook | Event | Role |
|---|---|---|
load_memory.py |
SessionStart | Inject the memory index, a per-area state dashboard, pinned rules, recent memory, and a last-session recap. |
memory_nudge.py |
UserPromptSubmit | Remind the agent to search memory first. |
chat_stop.py |
Stop | Mirror new transcript lines into the archive. |
save_commit.py |
git post-commit | Record commits (copy into a repo's .git/hooks/). |
Two Claude Code slash commands — these are agent tasks (judgment), not scripts:
| Command | When | What it does |
|---|---|---|
/curate |
Before /compact, or at the end of a work session |
Distills the session into memory — updates each touched area's state, saves new decisions/rules, logs shipped content, and deletes what became false (history stays in the chat archive). This is what makes the next session pick up where you left off. |
/recall <topic> |
Any time you need to remember | Multi-modal retrieval — vector + graph + keyword, not just one keyword search — then a single grounded answer that cites the record ids it used. |
Everything is driven by projects.json (copied to ~/.config/brethof-mind/):
{
"surrealdb": { "url": "http://localhost:8200", "ns": "ai", "db": "memory" },
"embedding_model": "sentence-transformers/all-MiniLM-L6-v2",
"embedding_dim": 384,
"default_project": "global",
"projects": [
{ "key": "global", "match": [] },
{ "key": "webapp", "match": ["my-webapp", "frontend"] },
{ "key": "api", "match": ["my-api", "backend"] }
]
}Each match is a list of substrings tested against the working directory's
absolute path. First match wins; unmatched dirs fall into default_project.
The key is the table name. After adding a project, re-run init_db.py to
create its indexes. Credentials never go in this file — they come from the
environment (see .env.example).
Overriding the detected project. Path-substring matching misfiles work
whose cwd doesn't reflect the project — SSH/remote sessions, monorepos, or
driving one project from another's directory. Two overrides take precedence
over match (highest first):
BRETHOF_MIND_PROJECT=<key>in the environment — pins the whole session. Set it before launching Claude Code when, e.g., you'll be operating a remote box from an unrelated local directory.- A
.brethof-mind-projectfile (first line = a project key) anywhere from the cwd upward — pins a directory tree whose path matches nomatchsubstring. Handy to commit into a repo whose folder name isn't inmatch.
Built and tested against SurrealDB v3.0.5 (pinned in docker-compose.yml).
SurrealQL evolves across major versions — 3.0, for instance, renamed the
full-text index clause from SEARCH ANALYZER to FULLTEXT ANALYZER — so a
newer server may need small tweaks.
init_db.py reads the running server's version and warns if its major version
differs from the tested one, so you get a clear heads-up rather than a cryptic
parse error. When you validate brethof-mind against a new SurrealDB release,
bump it in two places: the image: tag in docker-compose.yml and
SUPPORTED_SURREALDB in mcp-server/_config.py.
The Stop hook mirrors every transcript line — including tool output and
file contents — into the local archive. It stays on your machine and is never
committed (data/ is gitignored), but you should choose deliberately which
projects you point it at. Read docs/PRIVACY.md before
installing, and see it for how to exclude projects, disable archiving, and
purge data.
SurrealDB is a graph database, so memory isn't limited to flat rows. Add your
own domain tables and relations and surface them at session start — see
examples/content_graph/ for a worked template.
MIT — see LICENSE. Built on SurrealDB, FastMCP, and fastembed; not affiliated with or endorsed by their respective projects.