Skip to main content

DuckDB-backed MCP memory server for Obsidian vaults — structured search, read, and write access for AI coding agents.

Project description

DuckBrain

DuckBrain

DuckDB-backed MCP memory server for Obsidian vaults. Gives AI coding agents read/write access to your personal wiki — structured pages, full-text search, automatic indexing.

Installation

Install DuckBrain:

pip install duckbrain

... or with the dependency manager of your choice (uv, poetry, ...)

Then pick your agent:

  • OpenCode — MCP server + session plugin (recommended)
  • Claude Code — MCP server + CLAUDE.md + SessionStart hook
  • Cursor — MCP server + rules + hooks
  • Hermes — MCP server + AGENTS.md
  • Other Agents (e.g. Codex, Gemini) will follow after testing

OpenCode

Best experience (as I use OpenCode myself) — session plugin gives the AI automatic vault awareness.

Add to opencode.json:

{
  "mcp": {
    "duckbrain": {
      "command": "duckbrain",
      "env": { "VAULT_PATH": "/path/to/your/vault" }
    }
  }
}

Download the session plugin (you need bun for the install):

mkdir -p ~/.config/opencode/plugins/
cd ~/.config/opencode/plugins

for f in vault-context.ts vault-context-helpers.ts package.json; do
  curl -O "https://raw.githubusercontent.com/timhiebenthal/duckbrain/main/opencode/plugins/$f"
done

bun install

Restart OpenCode. The plugin gives your AI automatic vault awareness and a few useful defaults:

  • Vault-aware context — your AI sees your vault's topics, recent notes, and today's daily entry automatically. No need to ask "what's in my vault" or paste paths.
  • Daily note journaling — the AI captures session progress, decisions, and discoveries into your daily note as you work, so you don't lose context between sessions.
  • Real local-time timestamps — entries are timestamped with your actual local time, not whatever the AI guesses.
  • End-of-session save — when you finish a session, the AI is prompted to journal anything new before the conversation closes. (Window close is still a loss — use /journal for guaranteed save.)

Claude Code

Prerequisite: uv or uvx on your PATH — the plugin runs the MCP server via uvx duckbrain (no manual pip install needed; uvx fetches and caches it automatically)

Install via plugin marketplace

claude plugin marketplace add /path/to/duckbrain/claude/
claude plugin install duckbrain@duckbrain-local
# Claude Code prompts for your vault path, or pass it non-interactively:
# claude plugin install duckbrain@duckbrain-local --config vault_path=/path/to/vault

Claude Code prompts for your vault path at enable time — no manual VAULT_PATH env var needed.

What you get

Enable plugin → prompted for vault path once
     ↓
Session start → SessionStart injects LEARNINGS guard + tags + daily notes
     ↓
During session → guard prompts Claude to journal after non-trivial work
     ↓
Every ~15 min → UserPromptSubmit re-surfaces the concise journal nudge (throttled)
     ↓
Context full → PreCompact injects snapshot + journal nudge
     ↓
Session end → type /journal → Claude writes summary to daily note
     ↓
SessionEnd hook → appends "Session end — HH:MM" timestamp

The plugin bundles four hooks (SessionStart, UserPromptSubmit, PreCompact, SessionEnd), the duckbrain MCP server config, and a /journal slash command. No manual settings.json editing.

Manual wiring (advanced)

If you prefer wiring hooks yourself without the plugin, the standalone scripts in scripts/claude-vault-*.sh still work — see the comments in each file for the required settings.json entries.


Cursor

Disclaimer: Not tested yet

Add to .cursor/mcp.json:

{
  "mcpServers": {
    "duckbrain": {
      "command": "duckbrain",
      "env": { "VAULT_PATH": "/path/to/your/vault" }
    }
  }
}

Optionally add .cursor/rules/duckbrain.mdc with alwaysApply: true:

---
description: DuckBrain vault knowledge base integration
alwaysApply: true
---
# DuckBrain vault
Call vault_info() at session start to discover vault topics.
Use vault_search() when the query matches vault content.

SessionStart hook (prototype — context injection)

Prototype — Cursor's additional_context from sessionStart hooks has a confirmed bug (dropped due to timing). The env output works, but context injection does not yet. Track: Cursor forum

mkdir -p ~/.cursor/hooks/
curl -o ~/.cursor/hooks/vault-context.sh \
  https://raw.githubusercontent.com/timhiebenthal/duckbrain/main/scripts/cursor-vault-context.sh
chmod +x ~/.cursor/hooks/vault-context.sh

Add to ~/.cursor/hooks.json:

{
  "version": 1,
  "hooks": {
    "sessionStart": [
      { "command": "/full/path/to/.cursor/hooks/vault-context.sh" }
    ]
  }
}

SessionEnd hook (auto-journal)

curl -o ~/.cursor/hooks/vault-journal.sh \
  https://raw.githubusercontent.com/timhiebenthal/duckbrain/main/scripts/cursor-vault-journal.sh
chmod +x ~/.cursor/hooks/vault-journal.sh

Add to ~/.cursor/hooks.json:

{
  "version": 1,
  "hooks": {
    "sessionEnd": [
      { "command": "/full/path/to/.cursor/hooks/vault-journal.sh" }
    ]
  }
}

Appends ## Session end — HH:MM to today's daily note when a session ends.

Restart Cursor.


Hermes Agent

Has been tested and validated.

Add to mcp.json:

{
  "mcpServers": {
    "duckbrain": {
      "command": "uv",
      "args": ["run", "duckbrain"],
      "env": { "VAULT_PATH": "/path/to/your/vault" }
    }
  }
}

Add to AGENTS.md:

# DuckBrain vault
Call vault_info() at session start to discover vault topics.
Use vault_search() when the query matches vault content.
After non-trivial work, save learnings with vault_write().

Restart Hermes.


Tools

Tool What it does
vault_search Full-text search over vault pages
vault_read Read a page by title or filepath
vault_write Create a page or append to today's daily note
vault_context Load daily notes + keyword search in one call
vault_info Vault stats: page counts, tags, last modified

Vault Schema

your-vault/
├── wiki/
│   ├── entities/       # people, orgs, tools
│   ├── concepts/       # ideas, frameworks
│   ├── sources/        # summaries of ingested content
│   ├── synthesis/      # cross-cutting analysis
│   ├── index.md        # page catalog (auto-updated)
│   ├── log.md          # write history (auto-updated)
│   └── tags.md         # topic index (auto-updated)
├── daily/              # daily notes (YYYY-MM-DD.md)
└── .env                # VAULT_PATH (optional)

Pages use YAML frontmatter:

---
title: Claude Mem
item-type: entity
tags: [ai, memory, mcp]
created: 2026-05-28
updated: 2026-05-28
---

Nerdy Details

Implementation internals — not needed for installation.

Architecture

┌──────────────────────────────────────────────────────────────┐
│                      AI Agent                                │
│  ┌──────────────────────────┐  ┌──────────────────────────┐  │
│  │ MCP Client (stdio)       │  │ Hooks / Plugins          │  │
│  │  vault_search,           │  │ (SessionStart, system    │  │
│  │  vault_read, vault_write │  │  transform — inject      │  │
│  │  vault_context, vault_info│  │  vault context into      │  │
│  │                          │  │  system prompt)          │  │
│  └──────────┬───────────────┘  └──────────┬───────────────┘  │
└─────────────│──────────────────────────────│──────────────────┘
              │ MCP stdio                    │ reads directly
              ▼                              ▼ from vault
┌──────────────────────────────┐  ┌──────────────────────────────┐
│  DuckBrain MCP Server        │  │ Side channel:                │
│  vault_info   ──► DuckDB FTS │  │  wiki/tags.md                │
│  vault_search ──► DuckDB FTS │  │  daily/YYYY-MM-DD.md         │
│  vault_read   ──► Filesystem │  │  wiki/log.md                 │
│  vault_write  ──► Filesystem │  │                              │
└──────────────┬───────────────┘  └──────────────────────────────┘
               │ reads/writes
               ▼
┌──────────────────────────────────────────────────────────────────────┐
│                     Your Obsidian Vault                              │
│  wiki/entities/  wiki/concepts/  wiki/sources/  wiki/synthesis/      │
│  daily/          wiki/index.md   wiki/log.md    wiki/tags.md         │
└──────────────────────────────────────────────────────────────────────┘
  • Reads vault files directly — no index to sync, no watchers, no duplicate storage
  • Searches via DuckDB FTS (BM25 ranking), rebuilt fresh from disk on every query
  • Writes new pages with YAML frontmatter, auto-updating index, log, and tags

Inspirations

This project stands on the shoulders of several ideas and tools:

  • Andrej Karpathy's LLM wiki pattern — the idea that a personal markdown wiki, co-maintained by humans and AI agents, compounds into a persistent knowledge base. The vault schema (entities, concepts, sources, synthesis, daily log) is directly inspired by this.
  • DuckDB — the embedded analytical database that makes full-text search over flat files viable without a server, index sync, or persistent storage. The decision to use in-memory FTS instead of a vector database was a deliberate trade-off for simplicity.
  • Obsidian — the local-first, markdown-native note-taking tool that treats your files as the truth. DuckBrain exists because Obsidian vaults deserve tooling that respects the filesystem.
  • MemSearch and Open Brain (OB1) — early experiments in cross-tool agent memory that demonstrated the need for structured vault write-back while choosing different architectures. Their strengths and gaps directly informed DuckBrain's design.
  • Agent Memory Systems (6-level taxonomy) — Simon Scrapes' comprehensive comparison of Claude Code memory approaches provided the framework for understanding where DuckBrain fits in the ecosystem (Level 6: cross-tool MCP with dedicated server).
  • trellis-datamodel — the same author's data modeling tool whose CI/CD patterns (trusted PyPI publishing, version-diff release detection, Keep a Changelog) were borrowed for this project's repository readiness.
  • mondayDB 3 — Solving HTAP for a Trillion-Table System — monday.com's engineering blog on their DuckDB-powered CQRS read serving layer at production scale. Proved that DuckDB in-process with per-tenant file isolation is a viable architecture — the same pattern DuckBrain applies at personal-wiki scale.

The core decision — build, don't integrate — came from a structured comparison of 7 existing tools. All failed on one requirement: vault schema-aware write-back. Rather than fork or extend, DuckBrain started from first principles: what's the simplest thing that gives agents structured read/write access to an Obsidian vault? The answer was DuckDB + MCP + ~500 lines of Python.


Building from Source

git clone https://github.com/timhiebenthal/duckbrain.git
cd duckbrain
uv sync
uv run duckbrain  # will hang waiting on stdio — that's correct

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

duckbrain-0.5.0.tar.gz (17.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

duckbrain-0.5.0-py3-none-any.whl (22.5 kB view details)

Uploaded Python 3

File details

Details for the file duckbrain-0.5.0.tar.gz.

File metadata

  • Download URL: duckbrain-0.5.0.tar.gz
  • Upload date:
  • Size: 17.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for duckbrain-0.5.0.tar.gz
Algorithm Hash digest
SHA256 e3ccc95539439992b9e5ed9aecb7fe03d079a1463b930040ea3783dfaa77da69
MD5 cfa3d59bfe1a1a2e622b6facbd99ca4f
BLAKE2b-256 aece70afabc5cd2624235c99f0faabcd0759173ed2876ba3c1898f0bff6df6af

See more details on using hashes here.

Provenance

The following attestation bundles were made for duckbrain-0.5.0.tar.gz:

Publisher: publish.yml on timhiebenthal/duckbrain

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file duckbrain-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: duckbrain-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 22.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for duckbrain-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 958bbf954eca96b4acebe908fa960d7ac7ca558e0e42fee13ef9ff38d0306e08
MD5 e380421d9b7133d9edbf98da9e6e5bce
BLAKE2b-256 598382fc9d85086c098f07cef523e3acaebb632247e66396ad2367ee6bc1127d

See more details on using hashes here.

Provenance

The following attestation bundles were made for duckbrain-0.5.0-py3-none-any.whl:

Publisher: publish.yml on timhiebenthal/duckbrain

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page