Skip to main content
Glama

brain

A personal memory engine and MCP server: hybrid RAG (SQLite FTS5 + vector search) over a git-backed markdown store.

CI Python 3.12+ License: MIT

Replace OWNER in the CI badge with your GitHub account once you push this to a repo.

Why

An LLM's context is amnesiac: everything it learns about you, your projects, and your decisions evaporates when the session ends. brain fixes that by persisting durable facts as plain markdown in git — the kind of store you can read, edit, grep, and diff by hand — and making it recallable to any MCP client through hybrid search. The markdown is the source of truth; the search index is a rebuildable cache you can delete at any time.

Related MCP server: mechabrain

Architecture

          SOURCE OF TRUTH (markdown + git)          DERIVED INDEX (rebuildable)
        ┌───────────────────────────────────┐      ┌─────────────────────────┐
        │  memories/     curated facts       │      │  cache/brain.db         │
        │  summaries/    session digests     │─────▶│  ── FTS5 (BM25)         │
        │  ~/.claude/…   ingested transcripts│ ingest  ── sqlite-vec (256-d   │
        │                 (episodes)         │      │     nomic-embed vectors)│
        └───────────────────────────────────┘      └───────────┬─────────────┘
                    ▲                                           │
                    │ writes auto-commit                        │ hybrid recall
                    │ (optional push to a private remote)       ▼
        ┌───────────┴───────────┐                   ┌─────────────────────────┐
        │   remember(fact,type) │◀──── MCP ────────▶│  recall(query,k,scope)  │
        │                       │     (stdio)       │  get_episode(id)        │
        └───────────────────────┘                   └─────────────────────────┘
                                                     any MCP client (Claude Code, …)
  • Three kinds of memory. memories/*.md are curated, durable facts (preferences, runbooks, per-project state notes). summaries/YYYY-MM/*.md are per-session digests. Episodes are ingested Claude Code transcripts (read from ~/.claude/projects/**.jsonl). The first two are git-tracked text you own; episodes derive from live transcripts — though on an emitting host, ingest also writes each episode as a git-tracked file under episodes/YYYY/MM/, so a transcript-less machine can index them by git pull alone (see Running across two machines).

  • The index is a cache. cache/brain.db holds an FTS5 table and a sqlite-vec table of 256-dimension nomic-embed-text-v1.5 vectors (Matryoshka-truncated from 768). It is always rebuildable and never committed — delete it freely and brain-ingest --full recreates it.

  • Hybrid recall. Each query runs both a lexical (FTS5/BM25) and a semantic (vector) leg; the two rankings fuse via reciprocal-rank fusion, then a prior re-weights by kind (a curated memory outranks a raw episode at equal evidence) and recency (exponential decay with a per-kind half-life, floored so age never fully erases relevance). Every hit carries a score in (0, 1]. The whole ranking surface is env-tunable — see Configuration.

  • Durability & sync. Memory writes auto-commit, so a fact is safe the moment it is written. Point origin at any private git remote you control and the markdown store syncs across machines; the index never leaves the box. Sync is off unless you configure a remote, and hard-disabled with BRAIN_SYNC=0.

Quickstart

Requires uv and Python 3.12+. This repo ships a tiny synthetic store under examples/ so you can try recall without any data of your own.

uv sync                                              # install deps into .venv

# Build the index over the shipped examples only.
# BRAIN_CLAUDE_PROJECTS points at an empty dir so no real transcripts are read;
# drop it to also ingest your own ~/.claude/projects transcripts.
BRAIN_DIR=$PWD/examples BRAIN_CLAUDE_PROJECTS=$(mktemp -d) \
  uv run brain-ingest --full

# Hybrid recall from the shell.
BRAIN_DIR=$PWD/examples uv run brain-recall "postgres backup"

Expected top hit:

scope=all
mem_…  memory  2026-01-12  …  score=0.67…  How the demo acme-webapp Postgres database is backed up each night. …

Register the MCP server with Claude Code (or any MCP client that speaks stdio):

claude mcp add brain -- uv run --directory "$PWD" brain-server

Need a read-only server — e.g. behind a gate that requires every exposed tool to be auto-allowable? Register brain-server-readonly instead; it exposes only recall + get_episode, dropping the remember write that such a gate would reject.

Write a fact mid-session from the shell (brain-remember reads one JSON object from stdin — only fact is required):

echo '{"fact": "Staging DB resets nightly at 03:00 UTC.", "type": "reference"}' \
  | uv run brain-remember

First run downloads the embedding model (nomic-embed-text-v1.5, a few hundred MB) to $FASTEMBED_CACHE_PATH (default cache/fastembed/, gitignored). Subsequent runs are instant. Everything runs locally — no API key, no external inference calls.

Commands

Every entry point is a console script; run it with uv run <name>.

Command

What it does

brain-ingest

Incrementally index memories, summaries, and episodes — from live transcripts or committed episode files (--full rebuilds).

brain-server

MCP stdio server exposing recall, get_episode, remember.

brain-server-readonly

Read-only MCP stdio server: recall + get_episode only (no remember).

brain-recall

Hybrid search / full-text fetch from the shell (no MCP needed).

brain-remember

Write a durable memory from the shell (dedup-guarded, auto-commits).

brain-sync

status / pull / push against the optional private remote.

brain-usage-report

Recall/open telemetry report (feeds the consolidation pass).

brain-hook

UserPromptSubmit hook: ambient auto-recall (Claude-Code-specific).

brain-session-start

SessionStart hook: injects a project's state note (Claude-Code-specific).

brain-autoreflect

Wrapper that triggers a headless consolidation pass when debt accrues.

brain-freshness-audit

Refreshes memories/*-state.md notes that have fallen behind their project.

Memory file format

A memory is YAML frontmatter plus a markdown body (the same shape Claude Code uses for auto-memory), so it stays readable and hand-editable:

---
name: postgres-nightly-backup
description: How the demo acme-webapp Postgres database is backed up each night.
metadata:
  type: reference
date: 2026-01-12
---
The acme-webapp production Postgres runs a nightly logical backup at 02:00 UTC…
  • name — kebab-case slug (defaults to the filename).

  • description — one line; indexed alongside the body.

  • metadata.type — one of user (preferences), feedback, project (per-project living state notes), reference (facts/runbooks).

  • date — optional ISO date; drives recency decay.

See examples/memories/ for one of each kind, and examples/summaries/ for the session-digest format.

Configuration

All configuration is environment variables with sensible defaults — see src/brain/config.py for the full surface. Highlights:

Variable

Default

Purpose

BRAIN_DIR

the repo root

Root of the markdown store + cache/.

BRAIN_CLAUDE_PROJECTS

~/.claude/projects + ~/.claude-work/projects

Colon-separated list of transcript roots (each included only if it exists).

BRAIN_INGEST_SOURCE

auto

Episode source: auto / episodes / transcripts (see split-host).

BRAIN_EPISODE_EXCLUDE

inherits BRAIN_DIGEST_EXCLUDE

Comma-separated cwd/project substrings whose episodes get no git-tracked episodes/ file (still indexed in the local cache DB).

FASTEMBED_CACHE_PATH

cache/fastembed/

Where the embedding model is cached.

BRAIN_EMBED_BATCH

32

Embedder inference batch size; lower it on small-RAM boxes (32 can OOM a 2 GB box).

BRAIN_EMBED_THREADS

4

onnxruntime intra-op thread cap for the embedder.

BRAIN_EMBED_BACKFILL_CHUNK

128

DB paging chunk size for the embedding backfill.

BRAIN_SYNC

on (if remote set)

0 hard-disables all git sync.

BRAIN_NO_AUTOREFLECT

unset

1 disables the in-process debt-spawn reflect trigger (set on the emitting server + reflect/refresh children).

BRAIN_AUDIT_MAX_REFRESH

3

Max freshness-audit refresh agents per run.

BRAIN_AUDIT_REFRESH_TIMEOUT

1800

Per-refresh-agent timeout (seconds) in the freshness audit.

BRAIN_RRF_K

60

Reciprocal-rank-fusion damping constant.

BRAIN_KIND_WEIGHT_{MEMORY,SUMMARY,EPISODE}

1.0 / 0.85 / 0.70

Per-kind rank multipliers.

BRAIN_HALF_LIFE_{MEMORY,SUMMARY,EPISODE}

180 / 90 / 45 days

Per-kind recency half-lives.

BRAIN_RECENCY_FLOOR

0.35

Floor the recency factor decays toward.

Optional integrations (Claude Code)

These are conveniences for a Claude Code workflow and are entirely optional — the core (ingest + recall/remember + MCP server) has no dependency on them.

  • Ambient auto-recall — a UserPromptSubmit hook (brain-hook) that runs a fast, read-only FTS pass on every prompt and silently injects the strongest matching memories as context.

  • SessionStart injectionbrain-session-start injects a project's rolling state note when a session opens, stamped with a freshness banner.

  • Skillsskills/reflect (consolidation pass), skills/catchup (deep resume), and skills/handoff.

  • Reflect triggering — on a workstation, consolidation is triggered solely by the in-process reflection-debt spawn from the MCP server (no cron or scheduled job); on an always-on box, a systemd timer runs brain-autoreflect --if-debt. See Running across two machines.

Design docs for these live under specs/.

How multi-machine sync works (optional)

The markdown store can sync across machines through any private git remote you control — there is nothing brain-specific about it:

git remote add origin <your-private-repo>   # e.g. git@github.com:you/brain.git
git config user.name  "Your Name"           # any identity you like
git config user.email you@example.com
git push -u origin main

cache/brain.db is rebuildable and never pushed. Sync auto-detects the remote: with no origin, brain behaves exactly as a local-only store, zero config. BRAIN_SYNC=0 disables it entirely regardless of remote. memories/ and proposals/ carry a merge=union attribute so concurrent edits from two machines concatenate losslessly rather than conflict; the next consolidation pass dedups them. See specs/git-sync.md for the full design.

Memory commits record provenance as git trailers (Session, Project, Host) so you can audit which machine and session produced each fact.

Running across two machines (split-host)

The store is built to run split across an emitting workstation and an always-on box (a small VM), both talking to the same markdown store through the private remote from How multi-machine sync works. They divide the labour:

  • The workstation emits. It has the live Claude Code transcripts, so its brain-ingest parses them, writes new memories, and — the key part — emits one markdown file per episode under episodes/YYYY/MM/<id>.md. Unlike cache/ (gitignored), episodes/ is git-tracked and committed (as brain-ingest: emit N episodes) and pushed to the remote. The workstation runs its MCP server with BRAIN_NO_AUTOREFLECT=1, so it never spends a reflect itself — it delegates that to the box.

  • The always-on box indexes. It has no transcripts of its own, so it git pulls and indexes straight from the committed episode files, then runs the maintenance loops on a timer. It sets BRAIN_INGEST_SOURCE=episodes so ingest reads episodes/**/*.md only and never scans, emits, or commits transcripts — important because the box accrues its own claude -p reflect transcripts that auto would otherwise wrongly index and re-emit.

Episode source selection (BRAIN_INGEST_SOURCE)

Value

Behaviour

auto (default)

If transcripts are present, transcript mode (parse + emit episode files); otherwise, if episode files are present, episode-file mode.

episodes

Index episodes/**/*.md only — never scan, emit, or commit transcripts.

transcripts

Force transcript mode.

A transcript-less box therefore stays current on episodes purely by git pull. See specs/episodes-in-git.md.

reflect triggering

There is no nightly cron or scheduled OS job. reflect is triggered one way per host:

  • On the workstation, solely by the in-process reflection-debt spawn from the MCP server: when accrued debt crosses the threshold, the server spawns a headless consolidation pass. BRAIN_NO_AUTOREFLECT=1 disables it (as the split-host workstation does).

  • On the always-on box, by a systemd timer running brain-autoreflect --if-debt — a debt-gated reflect that only spends a claude -p /reflect when reflection debt has actually accrued.

systemd timers (deploy/vm/)

The deploy/vm/ dir ships user-level systemd units (plus a README) for the always-on box:

  • brain-reflect.timer (hourly, jittered) runs brain-ingest, then brain-autoreflect --if-debt.

  • brain-freshness-audit.timer (daily) runs brain-ingest, then brain-freshness-audit: it sweeps the memories/*-state.md project notes, reuses the SessionStart STALE rule (repo drift or enough new episodes since the note was written) to find notes that have fallen behind their project, and dispatches one Opus refresh agent per stale note to rewrite it in-format and auto-land it. Runs are bounded by BRAIN_AUDIT_MAX_REFRESH (default 3) agents; contradictions, archival candidates, and filename mapping-failures are written out as proposals/needs-user-<date>-<note>.md for a human instead.

Scope & honesty

  • Single-user personal project. Extracted from a working personal system and published with fresh git history — not a hardened multi-tenant service.

  • Developed on macOS. The core is pure Python and portable. The only platform-specific pieces are optional deployment glue: the workstation runs on macOS, and the always-on box uses Linux user-level systemd units (deploy/vm/).

  • Local embeddings. Vectors are computed locally via fastembed — no API key and no external inference calls. (CI runs FTS-only, since the model download and native sqlite-vec wheel may be unavailable on the runner; recall degrades gracefully to lexical-only when the vector table is absent.)

  • Sync is bring-your-own and off by default. Nothing leaves your machine unless you add a private remote.

License

MIT.

Available Tools

3 tools
get_episodeA

Fetch full stored text + metadata for one recall hit.

id is the string from a recall hit; its ep_/mem_/sum_ prefix selects the kind (episode → the USER/ASSISTANT exchange; memory → body + path; summary → a per-session digest written by reflect). ep_/mem_ ids are stable across re-ingests and rebuilds; sum_ ids are ephemeral (pair recall → get_episode within one session). Text capped at 8k chars with a truncation marker.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully discloses behavioral traits: id prefix determines kind, stability of ep_/mem_ ids versus ephemeral sum_ ids, and a text cap of 8k chars with a truncation marker. This covers the burden completely.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise (~4 sentences), front-loaded with the purpose, and every sentence adds value. No redundancy or fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with one parameter and an existing output schema, the description covers all necessary context: purpose, id semantics, constraints (truncation). It is complete enough for an agent to correctly invoke the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description compensates fully by explaining the id parameter's format, prefix meanings (ep_, mem_, sum_), and their behavioral implications (stability, ephemeral nature). This adds essential meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states 'Fetch full stored text + metadata for one recall hit', clearly defining the verb and resource. It distinguishes from siblings recall (search) and remember (store) by focusing on retrieval of a specific hit's details.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use the tool (after a recall hit) and details the id prefix semantics, guiding correct parameter usage. However, it does not explicitly state when not to use it or mention alternatives to siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

recallA

Search past Claude Code sessions (episodes), memory notes (memories), and per-session digests (summaries, written by the nightly reflect pass).

Hybrid full-text + semantic search. scope: "all" | "episodes" | "memories" | "summaries" ("all" spans all three). Filters: project matches path segments of the session dir (e.g. "mobile" => ~/src/mobile); since/until are "YYYY-MM-DD" or "Nd" (N days ago), inclusive. Returns compact hits: kind, id, date, project, snippet, session_id (episodes and summaries) or name (memories), and a score in (0, 1] (recency- and kind-weighted, normalized RRF); hits are score-descending. Episode/memory ids are stable content-derived strings (ep_…/mem_…) — safe to persist; summary ids (sum_…) are ephemeral. Use get_episode(id) for full text.

ParametersJSON Schema
NameRequiredDescriptionDefault
kNo
queryYes
scopeNoall
sinceNo
untilNo
projectNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It details hybrid search, scope, filter formats, return fields, scoring (recency/kind-weighted, normalized RRF), and ID stability (ephemeral summary IDs), leaving no ambiguity.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Concise (~6 sentences) with front-loaded purpose, well-structured layout, no fluff. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema, the description still provides complete context: scoring details, ID stability, and links to sibling tools. Covers all aspects for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage, but description compensates by explaining scope values, since/until format, and project matching. The 'k' parameter is only given a default but not explained (e.g., number of results), a minor gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'Search past Claude Code sessions (episodes), memory notes (memories), and per-session digests (summaries...)' with specific verb and resources. It distinguishes from siblings like 'get_episode' and 'remember'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains when to use this tool (searching across multiple kinds of data), scope options, filters, and even notes about ID stability and fallback to 'get_episode' for full text, giving clear guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rememberA

Persist a new memory: write memories/.md, commit, index — immediately recallable.

fact: the durable fact (short markdown, first sentence becomes the description). type: user|feedback|project|reference. name: optional kebab-slug filename. session/project: provenance trailers ($CLAUDE_CODE_SESSION_ID from Bash env / workspace dir); omitted when empty. If a near-duplicate memory exists it is returned instead of writing; force=true writes anyway. On success returns a stable mem_… id — pair recall → get_episode(id) to read it back.

ParametersJSON Schema
NameRequiredDescriptionDefault
factYes
nameNo
typeNoproject
forceNo
projectNo
sessionNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavior: file persistence, deduplication logic, force flag, and return of stable mem_* ID.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence adds value, but the description is slightly dense and could be structured into sections for easier parsing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 6-param tool with no annotations, the description provides complete context: behavior, return value, and integration with siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 0% schema description coverage, the description adds complete meaning for all 6 parameters, including defaults, purpose, and provenance.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states 'Persist a new memory' and explains the write-commit-index workflow. It implicitly distinguishes from siblings recall and get_episode, clarifying the tool's unique role.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly says when to use the tool (to persist a new memory) and mentions near-duplicate handling, but lacks explicit when-not or alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 3 tool updatesv0.1.0
    • First observedget_episode
    • First observedrecall
    • First observedremember

TDQS

A4.6/5.0
Disambiguation5/5

Each tool has a clearly distinct purpose: get_episode retrieves full content for a recall hit, recall searches across memories and episodes, and remember stores new facts. There is no overlap or ambiguity.

Naming Consistency4/5

The naming pattern is mostly verb_noun (get_episode, remember) with recall as a single verb. This minor inconsistency does not hinder understanding, but a more uniform pattern would improve predictability.

Tool Count4/5

Three tools is a small but reasonable set for a memory recall and persistence system. Each tool provides a core function (search, fetch detail, store) without unnecessary complexity.

Completeness3/5

The set covers search, retrieval, and creation of memories, but lacks update or delete operations. This may force agents to work around missing lifecycle management, but the core workflows are supported.

Maintenance

ActivitySlowing
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    A self-hosted MCP server that gives AI agents shared, long-term memory over a git-backed folder of markdown, enabling persistent knowledge search, read, and write without a database.
    16
    21
    11
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server that provides agentic memory management for markdown vaults, enabling hybrid search, governed writing, and maintenance of episodic, semantic, procedural, and working memories for LLM agents.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for persistent, cross-session, local-first memory for AI agents, storing memories as Markdown files with SQLite indexing for hybrid search.
    24
    Apache 2.0

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sysangel/brain-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server