Skip to main content
Glama

LLM Second Brain

License: MIT Python 3.12 Docker

A self-hosted long-term memory server for LLMs running in harnesses (primarily Open WebUI). Models get MCP access to a shared knowledge store — notes, three knowledge areas (skills, terms, facts about the user) and the assistant log: they can search it (hybrid vector + full-text), read, write, update and delete records, and follow the version history of what changed.

What it is

  • One Docker container, self-hosted, non-root.

  • MCP Streamable HTTP (/mcp, natively supported by Open WebUI) with a Bearer token.

  • 31 MCP tools: 9 memory_* for notes, comments and namespaces; 5 skills_*, 5 user_* and 3 terms_* for the knowledge areas; 6 logs_* for the assistant log; history_list / history_get for version history; warnings_list for the "notes not in their place" job.

  • Storage: one SQLite database + sqlite-vec (vector search) + FTS5 (full-text), merged via Reciprocal Rank Fusion; every knowledge area has its own tables and indexes, isolated from notes and from each other.

  • Three knowledge areas next to notes (since v3.0): skills — stored procedures, read in full only when needed, with a version archive; terms — terminology keyed by (term + context), every sense returned and never overwritten; user — atomic facts about the user with dedup hints. Each area has MCP tools and operator REST mirrors.

  • Assistant log next to notes (since v3.2): the assistant's own record of "what I did, when and why" — the changed object and the result, what was checked; its own tables and indexes, its own logs_* tools, a compose flag, isolated from note search.

  • Version history (since v3.2): every content change to a note, an area record or an assistant log is versioned with line-level deltas for long texts and a full baseline for the first version; the operator reads it over REST (GET /history/<entity>/<id>) and can roll back — a rollback writes a NEW version and never rewrites history; the agent gets history_list and history_get.

  • Slots that wait instead of failing (since v3.2): every slot call has a typed outcome (unavailable / auth / timeout / empty / truncated / off_contract); a broken slot opens a break, does one cheap probe per pause and resumes by itself, while its work stays visible as waiting instead of burning calls. decision is a third slot type: the model answers structured questions instead of generating text, and the product falls back to the generative path on unavailable / unsupported / low_confidence.

  • Output budget "as much as fits" (since v3.2): a slot spends the model's context minus the prompt (a reserve plus a protective ceiling) instead of a fixed small cap; done_reason / finish_reason, thinking tokens and the applied budget land in the journal, and an empty answer is distinguished from a truncated one.

  • Measured injected text (since v3.2): one token counter measures and guards everything the server injects into the agent — the instructions core, the tool descriptions and the call weight (app/services/injection_budget.py), so the surface cannot grow silently.

  • Vectorization & summarization are external LLM calls. Each of the three slots (embedding / summary / judge) is configured independently with its own provider (ollama or an OpenAI-compatible API), base URL, model and optional API key.

  • Hierarchical namespaces: the store is split into large sections; the map is exposed to models via MCP instructions and memory_namespaces.

  • Related notes (since v3.1): reading a note also returns its links — neighbours by meaning or by shared entities, from other namespaces; search, list and read outputs carry chars, the size of the full note text.

  • Listings that page (since v3.1): memory_list and skills_list report total / has_more / next_offset / next_cursor and a +N more hint when a page is capped; the ceiling is tighter on the MCP surface than on REST.

  • Background worker: pending vectors, summaries, dedup, classification, title generation and the new knowledge areas are processed asynchronously; scheduled maintenance jobs (related-notes recompute, default-namespace tidy-up) run on their own intervals; failures never break CRUD (pending states + back-off retry).

  • Observability (since v3.1, extended in v3.2): /health (no token) reports the running application version, queues (how many jobs wait in each background queue, how long the oldest has waited, and how many need a human), slots (the last outcome and the pause of every slot, plus call diagnostics), decision, areas, warnings, update and backups.

  • Backups: periodic online SQLite snapshots with rotation.

Related MCP server: Snackbar MCP Server

Why

  1. Distributed knowledge with fast access. Knowledge lives in a separate store, not in the system prompt or chat history; the model fetches only what is relevant, on demand.

  2. Token economy. Instead of a monolithic context — a measured, guarded overhead for the tool spec (the instructions core 525 tokens, the 31 tool descriptions 2170 tokens), a compact skills announce (budget 2000 characters, refreshed on every connect) and targeted retrieval of short summaries, not full texts.

Quick start

git clone <repo> llm-second-brain && cd llm-second-brain
mkdir -p data prompts
# Edit docker-compose.yml: set MCP_AUTH_TOKEN (openssl rand -hex 32) and the
# three LLM slot addresses/models. Full reference: docs/CONFIG.md.
docker compose up -d --build
curl -s http://localhost:8080/health | python -m json.tool

/health answers without a token. See Installation for the first-run walkthrough (compose, token, Open WebUI, /health).

Documentation

  • Installation — setup, first run, Open WebUI, /health.

  • Configuration — environment variables, the prompt files, and the OLLAMA_KEEP_ALIVE note.

  • Changelog — release history (Keep a Changelog).

Operational notes

  • keep_alive is not sent by the client (since v2.1). Model residency is managed by the server: set OLLAMA_KEEP_ALIVE on the Ollama side if you want models to stay loaded. With the server default (5 min) models are unloaded more often, and a cold start (~22.6 GB for the summarizer) returns to latency.

  • Changing EMBEDDING_PROVIDER / EMBEDDING_MODEL / EMBEDDING_DIM triggers an automatic full reindex on startup: all notes go to pending and the worker re-encodes them, and the knowledge-area indexes are rebuilt the same way. Search/dedup thresholds are calibrated for qwen3-embedding:8b — recalibrate after changing the model.

  • Knowledge areas (since v3.0): skills, terms and user facts live in the same database but in their own tables and indexes; they are isolated from notes in both directions. Their form limits and similarity thresholds are environment-tunable and validated at startup.

  • Privacy: an openai provider sends note texts to an external API (the dedup judge and classifier see full texts). Choose providers per slot deliberately.

Restore from a snapshot

Snapshots are ordinary SQLite files in BACKUP_DIR (default /data/backups, inside the ./data volume); the name carries the UTC timestamp. To restore one:

  1. Take the newest notes-*.db. GET /backups lists the snapshots with verified (ok / failed / unknown) — prefer an ok one; a file marked failed also has a <file>.bad sidecar next to it. POST /backups/verify checks one on demand.

  2. Stop the service: docker compose down (nothing must write to the file you are about to replace).

  3. Put the snapshot in place of the live database (DB_PATH, default data/notes.db), e.g. cp data/backups/notes-<stamp>.db data/notes.db, and delete the stale notes.db-wal / notes.db-shm beside it if any. To keep the old file untouched, drop the copy next to it and point DB_PATH at the copy.

  4. Start the service (docker compose up -d) and check /health: status is ok, notes_count matches the snapshot, and embedding_ok turns true after the first successful call to the embedding slot.

  5. If the snapshot was taken with a different embedding model (the verification note embedding_mismatch), a full reindex starts on its own — wait for pending_vector to reach 0 before searching.

Shipping a verified copy off the box is optional and driven by BACKUP_SHIP_CMD (empty by default); scripts/backup_ship.sh is a secret-free template. The command and its output never reach the logs.

License

Distributed under the MIT license.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server that provides semantic memory with search, related-content traversal, and write-back capabilities, all powered by local embeddings of your notes, documents, and chat histories.
    3
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    A personal note store exposed as an MCP server. Enables any MCP-speaking assistant to create, search, list, and categorize notes, with per-client bearer tokens for author attribution.
    533 npm
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Provides cross-session persistent memory for coding agents via MCP tools to store, retrieve, and manage notes with hybrid keyword/semantic search and automatic deduplication.
    8
    306 npm
    MIT