Second Brain
Sends Home Assistant push notifications when the nightly curator fails, the vault is unavailable, or contributed files are deferred, using configured Home Assistant URL, notify service, and token.
Provides read/search/contribution tools for an Obsidian-format Markdown vault, letting Claude sessions query notes, backlinks, recent changes, and submit new material to an inbox that a curator later merges; the same vault can be opened directly in Obsidian.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
second-brain-mcp
An MCP server that lets many Claude sessions share one "LLM wiki" (an Obsidian-format Markdown vault, in the style of Karpathy's LLM Wiki) without trusting any of them to edit it.
Sessions on your LAN or VPN can search and read the wiki and contribute new material to an inbox. Only a separate curator, a locked-down headless Claude Code run each night, ever edits the wiki itself.
Claude sessions --Bearer token--> second-brain-mcp container (:8765)
(laptop, desktop, server) | reads wiki/ scripts/ raw/ (mounted read-only)
| writes raw/inbox/ only (contribute)
v
vault folder (local disk or a NAS share)
^
nightly curator timer --------------+ headless Claude Code: raw/inbox/ -> wiki/
Obsidian on your PC ----------------+ opens the same folderDesign rules
Single writer. The server can't edit
wiki/. Docker mounts it read-only, and the only writable path israw/inbox/. Curating is a separate step that follows the vault'sCLAUDE.mdrulebook.Contributions are data, not instructions. Each one is a new file
raw/inbox/YYYY-MM-DDTHHMMSS-<host>-<slug>.mdwith frontmattertype: contribution,title,kind(fact | decision | session-summary | snippet),origin_host(the token label, which is authenticated),origin_client(the MCP client info, self-reported),origin_ip,submitted, plus optionalrelatedandsource_url. It's written to a temp file and renamed into place. Bodies over 100 KB, unsafe titles and non-http(s) URLs are refused. The curator defers anything that tries to instruct it or contains secrets.No model in the server. Search is BM25.
auditand/graphrun the vault's ownscripts/audit_graph.pyandscripts/build_graph.py; the audit runs on a scratch copy, so the vault is never written.Secrets stay out of the vault. Tokens live in
secrets/tokens(directory mode 700, file mode 600), which is never committed. One token per client, so each can be revoked alone.LAN and VPN only. The server binds one address you choose (loopback by default). Don't bind
0.0.0.0and don't port-forward it: reach it from outside over your VPN.
Related MCP server: mindin
Endpoints and tools
Path | Auth | Purpose |
| Bearer token | MCP over streamable HTTP |
| none, private networks only ( | Interactive graph, rebuilt when |
| none |
|
Read-only tools: get_index, search(query, limit, type), read_note(title) (title, then
alias, case-insensitive; suggests close matches), backlinks(title),
list_notes(type, tag, status), recent_changes(n), audit(), inbox_status().
Write tool: contribute(title, body, kind, related, source_url).
Resources: wiki://index, wiki://note/{title}.
The vault
The server expects a folder laid out like this. examples/vault is a starter
vault you can copy, including CLAUDE.md, the rulebook the curator follows:
raw/ your drop-zone for source material
inbox/ where `contribute` writes
_processed/ ingested files are archived here
wiki/ the curated notes: index.md, log.md, MAP.md, concepts/, entities/
scripts/ audit_graph.py and build_graph.py
CLAUDE.md the rulebookThe vault is plain files. Don't make it a git repo that the curator can push from, and keep
this repo's secrets/, logs/ and backups/ outside it.
Setup
Put the vault somewhere the Docker host can read. A local folder works. For a NAS share over NFS, a line like this in
/etc/fstabkeeps an outage from hanging the host:nas.example:/volume1/SecondBrain /mnt/secondbrain nfs4 soft,timeo=30,retrans=3,noatime,bg,tcp,actimeo=5,nosharecache,_netdev,nofail 0 0Keep
actimeolow, or edits made in Obsidian take a long time to show up in search.For a network mount, add placeholders under the mount point (see If the vault is unavailable). With the share unmounted, create root-owned, read-only
wiki/ scripts/ raw/inbox/ raw/_processed/and aNAS-NOT-MOUNTEDmarker file in the mount point.Build the image:
docker build -t second-brain-mcp:0.1.0 .Tokens:
install -d -m 700 secrets && install -m 600 /dev/null secrets/tokens, then add clients (below). The container runs as uid 1000 and must be able to read this file.Configure:
cp .env.example .envand setBIND_IPto the host's LAN address,VAULT_HOST_PATHto the vault, andOWNER_NAMEto your name.Deploy:
docker compose up -d. In Portainer, pastedocker-compose.ymlinto a new stack instead and giveSECRETS_DIR_HOSTan absolute path.pull_policy: neverstops either tool trying to pull the locally built image.Curator (optional): see The curator.
Operations
curl -s http://<host>:8765/healthz # health + note and inbox counts
docker logs --tail 50 second-brain-mcp # server log (rotated 3 x 10 MB)
systemctl list-timers second-brain-curator.timer # next curator run
journalctl -u second-brain-curator -n 50 # curator summaries and failures
tail logs/curator.log # one line per run
curator/run-curator.sh # run the curator now (skips if nothing is pending)
curator/run-curator.sh --test-notify # check the Home Assistant pushUpgrading: bump version in pyproject.toml, build the image with the new tag, set
SB_VERSION to match and redeploy.
Development: uv sync --extra dev, then uv run pytest && uv run ruff check . && uv run mypy.
Adding a client
printf 'laptop:%s\n' "$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')" >> secrets/tokens
grep '^laptop:' secrets/tokens # copy the tokenThe server re-reads the file when it changes, so no restart is needed. On the client machine:
claude mcp add --scope user --transport http second-brain http://<host>:8765/mcp \
--header "Authorization: Bearer <token>"
claude mcp get second-brain # expect: ConnectedThen tell that machine's sessions when to use it, in ~/.claude/CLAUDE.md:
## Second Brain (MCP server `second-brain`)
- Before asking me for background I may already have recorded, call `get_index` or `search`
first, then `read_note`.
- When a session produces something durable (a decision and its reason, a fact about my
systems, a short summary of completed work), call `contribute`. Don't contribute chit-chat,
guesses or anything secret.
- The wiki is read-only to you. Contributions land in `raw/inbox/` and a curator merges them
later, so don't expect one to show up in `search` straight away.The label becomes origin_host on that machine's contributions and appears in their
filenames, so use a short machine name. Hosted clients whose connectors run in the vendor's
cloud can't reach a LAN-only server.
Revoking or rotating a client
Revoke: delete the client's line from
secrets/tokens. It takes effect on the next request.Rotate: replace the token after the
label:with a new one, then on that machine runclaude mcp remove second-brain -s userand add it again.Rotate any token that has been pasted into a chat, ticket or screenshot.
The curator
curator/run-curator.sh turns inbox files into wiki edits. It needs Claude Code installed
and signed in for the user that runs it. The script:
Takes a lock and checks the vault is really mounted, not the placeholder.
Backs up the whole vault to
backups/vault-<ts>.tar.gz(last 30 kept).Exits if
raw/inbox/andraw/are empty, without calling a model.Otherwise runs
claude -pwithcurator/prompt.md, restricted like this:--strict-mcp-configwith no servers, so the run has no connectors at all--permission-mode dontAskand--setting-sources projectcurator-settings.json: Edit/Write only inwiki/**; deniesmv,rm,curl,wget,ssh,git, web access, scheduling and sub-agentsa 45-minute timeout
Runs
archive_ingested.py, which moves toraw/_processed/every file that got a new## [..] ingest | raw/...entry inwiki/log.md. The model never moves files; deferred files stay put.Re-runs the audit and logs the result. With
NOTIFY=1it sends a Home Assistant push on failure, a missing vault or deferred files (HA_URL,HA_NOTIFY, and a mode-600HA_ENVfile holdingHA_TOKEN=...).
To schedule it nightly, fill in the template unit and enable the timer:
sed -e "s|YOUR_USER|$USER|g" -e "s|/opt/second-brain-mcp|$PWD|g" curator/second-brain-curator.service |
sudo tee /etc/systemd/system/second-brain-curator.service >/dev/null
sudo install -m 644 curator/second-brain-curator.timer /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now second-brain-curator.timerEdit the unit's /mnt/secondbrain paths if your vault lives elsewhere, and set VAULT to match.
To undo a bad night, extract the backup from before it (tar -xzf backups/vault-<ts>.tar.gz -C /tmp)
and copy back what you need.
If the vault is unavailable
This matters when the vault is a network mount.
Share down while running: with a
softmount, reads fail with I/O errors rather than hanging./healthzreturns 503 and Docker marks the container unhealthy. Tools return "Vault unavailable" andcontributereturns "Inbox unavailable". It recovers on its own when the share returns.Share down at boot:
bg,nofaillets the host boot, and the container starts against the read-only placeholders under the mount point. It reports unhealthy, andcontributefails rather than leaving notes on the host's own disk. After 10 unhealthy minutes (UNHEALTHY_EXIT_AFTER) the server exits on purpose. Docker restarts it, which re-binds the real mount once it's back. That's at most one restart per 10 minutes.Curator: the unit's
ConditionPathExistsplus the script's own mount check skip the night.
Troubleshooting
Symptom | Check |
|
|
401 with the right token | Token line malformed ( |
| Vault not mounted: |
Search misses a note you just edited | NFS attribute cache plus the index recheck (2 s); wait a few seconds |
| Your client IP isn't in |
Curator never runs |
|
A file sits in the inbox for days | It was deferred: read its |
Redeploy fails pulling the image | Keep |
Licence
MIT. See LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Notes and memory assistant: one Markdown vault that Claude, ChatGPT and any MCP client can share.
Persistent, governed institutional memory for Claude Code — specs, decisions, learnings.
A self-hostable feed of reminders, learnings and work that Claude and ChatGPT can read and write
Shared notes, logs, issues and secrets for Claude, ChatGPT and local models.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables multiple isolated Claude sessions to read markdown knowledge bases versioned in git and deposit proposals, while only a manager role integrates changes.61Apache 2.0
- AlicenseAqualityBmaintenanceMemory for Claude and coding agents, kept as markdown notes you own: a schema that holds, search that reads your languages, a lifecycle for tasks and decisions, and a tie to your git history. Runs on a folder, an S3 bucket or your own vault server.19576 PyPI2MIT
- AlicenseNot gradedqualityBmaintenanceEnables Claude to read, search, edit, and publish notes in an Obsidian vault directly from markdown files over HTTP, without needing the Obsidian app or plugin.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to build and maintain a shared, git-backed Markdown wiki: they search, file, update and link notes with YAML frontmatter, keep verbatim sources, and optionally read code repos, so knowledge one assistant records is available to all the others. Runs without LLM calls, embeddings or a database, with the vault's own files defining page types, instructions and tools.MIT