An MCP server that gives an agent structured access to a knowledge base of plain Markdown notes
1.1K
A knowledge base your agent can use and you can still read without it.
secondbrain is an MCPโ server that gives an LLM agent structured access to a vault of plain Markdown files โ full-text search, backlinks, tags, daily notes, tasks and git history.
secondbrain stores nothing of its own inside your notes. A vault is a directory
of .md files with optional YAML frontmatter โ the format Obsidian, Logseq,
Foam, grep and git already understand. Everything the server keeps for itself
lives in <vault>/.secondbrain/: a SQLite index, a trash directory, and a file
of conventions. All three are disposable.
Delete the container and you are left with a directory you can open in an editor, commit, rsync or read with your eyes. A knowledge base you cannot read without its software is a hostage, not an asset.
โโโโโโโโโโโ MCP (OAuth 2.1) โโโโโโโโโโโโโโโโ plain Markdown โโโโโโโโโโโโ
โ LLM โ โโโโโโโโโโโโโโโโโโโบ โ secondbrain โ โโโโโโโโโโโโโโโโโโบ โ /data โ
โ agent โ โโโโโโโโโโโโโโโโโโโ โ :2020 โ โโโโโโโโโโโโโโโโโโ โ vaults โ
โโโโโโโโโโโ notes, diffs โโโโโโโโโโโโโโโโ files on disk โโโโโโโโโโโโ
โ โฒ
34 MCP tools โ
search ยท read ยท write Obsidian,
curate ยท history git, rsync
docker run -d --name secondbrain -p 2020:2020 \
-v secondbrain-data:/data \
-e SECONDBRAIN_USERNAME=andreas \
-e SECONDBRAIN_PASSWORD='a-long-password' \
-e SECONDBRAIN_PUBLIC_URL=https://notes.example.com \
andreaskasper/secondbrain:latest
Three variables are all that is required. Everything else has a working default,
and a first start with no vaults creates default for you.
Then point an MCP client at https://notes.example.com/mcp. The client discovers
the OAuth endpoints, registers itself, opens the login page in a browser, and you
sign in. The tool list and the vault's own conventions arrive with the
initialize response.
secondbrain speaks plain HTTP and does not terminate TLS. Put a reverse proxy in front of it in production โ Traefik and Cloudflare Tunnel examples are in the repositoryโ .
The container runs as the distroless nonroot user, UID 65532, and will
refuse to start on a directory it cannot write.
mkdir -p ./data && sudo chown -R 65532:65532 ./data
This is the single most common way a first deployment fails.
services:
secondbrain:
image: andreaskasper/secondbrain:latest
ports: ["2020:2020"]
volumes:
- secondbrain-data:/data
environment:
SECONDBRAIN_USERNAME: andreas
SECONDBRAIN_PASSWORD: a-long-password
SECONDBRAIN_PUBLIC_URL: https://notes.example.com
restart: unless-stopped
volumes:
secondbrain-data:
Environment first. A config file is optional and only worth mounting for more than one user, or to restrict a user to some vaults.
| Variable | Default | Purpose |
|---|---|---|
SECONDBRAIN_USERNAME | โ | Login name. Required unless a config file defines users. |
SECONDBRAIN_PASSWORD | โ | Literal, bcrypt:<hash>, env:NAME or file:/path. A literal needs 8+ chars. |
SECONDBRAIN_PUBLIC_URL | โ | Required. External base URL, no trailing slash. |
SECONDBRAIN_LISTEN | :2020 | Listen address inside the container. |
SECONDBRAIN_DATA | /data | Vault root. One directory per vault below it. |
SECONDBRAIN_DEFAULT_VAULT | default | The vault a tool call means when it names none. |
SECONDBRAIN_GIT | true | Commit every write to <vault>/.git. |
SECONDBRAIN_TRASH_RETENTION | 720h | How long a trashed copy is kept. |
SECONDBRAIN_TOKEN_TTL | 12h | Access token lifetime. |
SECONDBRAIN_METRICS | false | Expose the Prometheus endpoint. Off unless you ask for it. |
SECONDBRAIN_LOG_LEVEL | info | debug, info, warn, error. |
SECONDBRAIN_CONFIG | /etc/secondbrain/config.yaml | Optional config file. Absence is normal, not an error. |
The full tableโ includes git remotes, metrics keys, a separate metrics listener, rate limits and response ceilings.
Generate a password hash with docker run --rm -it andreaskasper/secondbrain hashpw. Check a config before deploying with secondbrain validate โ it reports
every problem at once and never prints a password.
An agent editing prose fails differently from an agent editing code. Wrong code does not compile. A wrong edit to a note is a paragraph that quietly disappears, and you find out months later when you go looking for it.
Almost every design decision follows from that asymmetry:
old_string that appears twice is refused, not
guessed at.None of these make an agent careful. They make carelessness recoverable.
Thirty-four, grouped: discovery and search (note_search with FTS5 ranking,
vault_grep, note_list), reading (note_read, note_outline,
note_backlinks, note_related), writing (note_create, note_edit,
note_section_edit, note_frontmatter, note_move, note_delete), capture
(daily_note, inbox_capture), curation (vault_review, note_merge,
note_split), tasks, tag and vault-wide refactoring, attachments, and git
history (note_history, note_diff, note_restore).
The count is deliberate. A model picks a tool by reading its name and
description; tools with one clear purpose each get chosen correctly, and a single
tool with a mode parameter gets chosen incorrectly โ with the failure showing
up as a mangled note rather than an error.
A read_only user is never shown the eighteen mutating tools at all, so the
model does not learn they exist.
vault_create populates a new vault with a shape and an instructions.md
describing it, which is sent to the client on connect. That is how a vault
teaches an agent its own conventions without anything living in a system prompt
somewhere else.
| Layout | The organising idea |
|---|---|
wiki-raw (default) | Source material is never rewritten; distilled wiki notes constantly are. |
zettelkasten | Atomic notes, densely linked, no hierarchy beyond the buckets. |
para | Organised by actionability rather than by subject. |
empty | Directories only. Write your own conventions. |
None of it is enforced in code. The layout is an opinion, not a schema.
/data/
โโโ <vault>/
โโโ inbox/ raw/ wiki/ โฆ your notes, plain Markdown
โโโ attachments/ templates/
โโโ .git/ one repository per vault
โโโ .secondbrain/
โโโ index.db SQLite + FTS5. A cache. Delete it, it rebuilds.
โโโ trash/ timestamped copies of anything overwritten
โโโ instructions.md the vault's conventions, sent to the client
Path safety is structural, not a blacklist: after cleaning, a path must be
relative, stay inside the vault, and no component may begin with a dot. That one
rule also makes .git, .obsidian and .secondbrain unreachable through every
tool, with no special case to keep in sync.
S256 mandatory.The full threat model, including what secondbrain deliberately does not defend against: https://andreaskasper.github.io/secondbrain/security.htmlโ
FROM gcr.io/distroless/static-debian12:nonroot โ no shell, no package
manager, one static Go binary. Pure-Go SQLite, so no libc.nonroot, UID 65532. /data must be writable by that UID.linux/amd64 and linux/arm64 โ arm64 is a first-class target, a good deal of
this runs on small home servers.| Tag | Points at |
|---|---|
latest | the most recent release |
1.0.260731 | that exact release |
1.0, 1 | the newest release of that line |
sha-1a2b3c4 | one exact commit |
The identical image โ same digest, same build:
docker pull ghcr.io/andreaskasper/secondbrain:latest
Static binaries for Linux and macOS, if you would rather not run a container, are attached to each GitHub releaseโ .
Made by Andreas Kasperโ
Content type
Image
Digest
sha256:a519059e2โฆ
Size
8 MB
Last updated
10 days ago
docker pull andreaskasper/secondbrain