No description
  • Python 98.7%
  • Dockerfile 1.3%
Find a file
Sebastiaan Koetsier 07575cde76 Initial public release: obsidian-brain MCP server
Self-hosted semantic-search MCP server for Obsidian. Read-only vector
search over notes synced to CouchDB via self-hosted LiveSync, powered by
ChromaDB + Ollama embeddings.

Stack: CouchDB 3.4.3, ChromaDB 0.6.3, Ollama, obsidian-sync (CouchDB->_changes
-> Chroma indexer), obsidian-mcp (FastMCP HTTP server, 4 tools, Bearer auth).

Docker Compose, local builds, no private registry, no baked-in secrets
(all config via .env). Includes sync unit tests (14) and stdio->HTTP client
bridges for stdio-only MCP clients.
2026-07-08 10:11:32 +02:00
client Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
config/couchdb Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
docs Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
mcp-server Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
sync Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
.env.example Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
.gitignore Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
docker-compose.yml Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
LICENSE Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00
README.md Initial public release: obsidian-brain MCP server 2026-07-08 10:11:32 +02:00

obsidian-brain

A self-hosted semantic-search MCP server for Obsidian. Turns your Obsidian notes (synced to CouchDB via the self-hosted LiveSync plugin) into a vector database that any MCP-compatible AI client (Claude Code, Cursor, etc.) can query — read-only, Bearer-token authenticated, no filesystem access.

This is the "brain" half of a two-server setup:

Server This repo The npm obsidian-mcp package
Backing store ChromaDB (vectors) Local vault files
Transport HTTP + Bearer auth stdio, local-only
Tools search_notes, get_note, list_notes, search_by_filename read/create/edit/move/tag notes
Use when You want semantic search over a remote/synced vault You want an agent to edit notes on the same machine

See docs/architecture.md for the full data flow.

Stack

Five Docker Compose services, all local builds / official images:

  • CouchDB 3.4.3 — note storage (Obsidian LiveSync backend)
  • ChromaDB 0.6.3 — vector database
  • Ollama — embeddings model (nomic-embed-text by default)
  • obsidian-sync — watches CouchDB, embeds notes, upserts into Chroma
  • obsidian-mcp — the MCP server (this is what your AI client talks to)

No private container registry, no baked-in secrets.

Prerequisites

  • Docker + Docker Compose v2
  • Obsidian desktop + the self-hosted LiveSync community plugin
  • An MCP-capable client (Claude Code, Cursor, etc.) — only if you want to query it

Quick start

git clone <this-repo> obsidian-brain
cd obsidian-brain

# 1. Create your config (secrets live here, never committed)
cp .env.example .env

# 2. Fill in real values
#    - COUCHDB_PASSWORD: a long random string
#    - BRAIN_API_KEYS:   a comma-separated list of Bearer tokens
#      generate each with:  openssl rand -hex 32
$EDITOR .env

# 3. Build + start (first run pulls the embedding model — can take minutes)
docker compose up -d --build
docker compose logs -f ollama-init   # wait for "Embedding model ready"

Verify the brain is up:

# Should return 401 (no token) — confirms auth is working
curl -i http://localhost:8080/mcp

# Authenticated tool call
TOKEN=<one of your BRAIN_API_KEYS>
curl -s http://localhost:8080/mcp/tools/call \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"search_notes","arguments":{"query":"test","n_results":3}}'

Connect Obsidian to CouchDB

The brain only knows about notes that LiveSync replicates into CouchDB.

  1. Install the self-hosted LiveSync plugin in Obsidian.
  2. Set its remote database URI to your CouchDB: http://<COUCHDB_USER>:<COUCHDB_PASSWORD>@<host>:5984/<COUCHDB_DB> (e.g. http://admin:...@localhost:5984/obsidian)
  3. Run a full sync / rebuild from the plugin so your notes are pushed.
  4. obsidian-sync will pick up the CouchDB changes and index them. Watch: docker compose logs -f obsidian-sync — you should see Indexed <path> lines.

If you change sync.py or switch embedding models later, force a full reindex — see sync/REINDEX.md.

Point an AI client at the brain

Option A — HTTP directly (Claude Code, Cursor with HTTP MCP)

{
  "mcpServers": {
    "obsidian-brain": {
      "url": "http://localhost:8080/mcp",
      "headers": { "Authorization": "Bearer <BRAIN_API_KEYS token>" }
    }
  }
}

Option B — stdio bridge (clients that only speak stdio)

Use client/mcp-bridge.py or client/local-mcp-server.py. Requires pip install fastmcp==2.3.4 requests.

{
  "mcpServers": {
    "obsidian-brain": {
      "command": "python3",
      "args": ["/abs/path/to/client/mcp-bridge.py"],
      "env": {
        "BRAIN_URL": "http://localhost:8080",
        "BRAIN_API_KEY": "<BRAIN_API_KEYS token>"
      }
    }
  }
}

Tools exposed

Tool Args Returns
search_notes query: str, n_results: int = 5 Semantic hits: path, title, folder, excerpt, score, similarity
get_note path: str {path, title, content}
list_notes folder: str = "" All notes (optionally filtered by folder prefix): path, title, folder
search_by_filename query: str Notes whose path/title contains the query (case-insensitive): path, title, folder

Configuration reference (.env)

Variable Default Purpose
COUCHDB_USER admin CouchDB admin user (created on first boot)
COUCHDB_PASSWORD (required) CouchDB admin password
COUCHDB_DB obsidian Database name LiveSync replicates into
EMBED_MODEL nomic-embed-text Ollama embedding model. Alternatives: qwen3-embedding:0.6b, mxbai-embed-large, bge-m3
BRAIN_API_KEYS (required) Comma-separated Bearer tokens accepted by the brain
FULL_REINDEX 0 Set 1 once to rebuild the Chroma collection from scratch

Running the tests

The sync indexer has a unit-test suite (no live services needed — everything is mocked):

cd sync
pip install -r requirements.txt pytest
python -m pytest

Ports

Host port Service Bind
5984 CouchDB all interfaces
8001 ChromaDB all interfaces
8080 obsidian-mcp (the brain) all interfaces
11434 Ollama localhost only

CouchDB and the brain are bound to all interfaces so they're reachable from other machines / a reverse proxy. Put them behind a firewall / TLS proxy in production. Ollama is loopback-only by default.

Production notes

  • Reverse-proxy obsidian-mcp (:8080) with TLS for remote access; keep Bearer auth on.
  • Back up the couchdb-data and chromadb-data volumes — they hold your indexed vault.
  • If you change EMBED_MODEL, run a FULL_REINDEX=1 cycle: existing vectors were made with the old model and won't match queries embedded with the new one.
  • ollama-init only runs on docker compose up. If you change EMBED_MODEL later, re-run it: docker compose run --rm ollama-init.

License

MIT — see LICENSE.