Skip to main content
Glama
jclement

obsidian-mcp

by jclement

obsidian-mcp

A self-hosted MCP server + management UI for your Obsidian vault. One container gives you:

  • An MCP server (Streamable HTTP) with 10 carefully designed tools for reading, searching, editing, and organizing notes — built for Claude Desktop, claude.ai, and Claude Code.

  • A passkey-protected management UI (passkeys only, no passwords) for tokens, OAuth connections, sync, and snapshot history.

  • OAuth 2.1 + Dynamic Client Registration so Claude Desktop/claude.ai connect with just a URL, plus named bearer tokens for Claude Code and other clients.

  • Optional Obsidian Sync via the official obsidian-headless client (ob sync --continuous), supervised by the server and configured entirely from the web UI — including end-to-end-encrypted vaults. Requires an Obsidian Sync subscription; skip it and bring your own sync (Syncthing, git, nothing).

  • Automatic git snapshots of the vault (debounced after every MCP mutation, before destructive operations, hourly, and at shutdown) stored in a bare repo outside the vault. Browse and restore from the UI.

Obsidian MCP management UI

All state lives in a single bind-mounted ./data directory:

data/
├── Vault/               your Obsidian vault
├── db/app.db            server state (passkeys, tokens, OAuth grants)
├── obsidian-headless/   ob CLI auth + sync state
└── snapshots/vault.git  bare git repo with vault history

Quick start

The server must be reachable over HTTPS (passkeys and Claude connectors require it). The recommended setup is a tunnel — no ports exposed at home:

mkdir obsidian-mcp && cd obsidian-mcp
curl -fsSLo docker-compose.yml https://raw.githubusercontent.com/jclement/obsidian-mcp/main/deploy/docker-compose.gatecrash.yml
# create .env with tunnel credentials (see comments in the compose file)
docker compose up -d
docker compose logs app        # ← copy the FIRST-RUN SETUP token

Just want to try it locally (no tunnel, no published image)? From a checkout:

docker compose -f deploy/docker-compose.local.yml up --build
docker compose -f deploy/docker-compose.local.yml logs app   # ← setup token
# open http://localhost:3000/setup

Open https://your-host/setup, paste the token, and register a passkey. That passkey is now the only way into the management UI; add more from the Passkeys page.

Connect Claude Desktop / claude.ai

Settings → Connectors → Add custom connector → https://your-host/mcp. Claude discovers the OAuth endpoints, registers itself, and sends you to your server to approve with your passkey. Done.

Connect Claude Code

Create a token in the UI (Tokens → name it → copy once), then:

claude mcp add --transport http obsidian https://your-host/mcp \
  --header "Authorization: Bearer obmcp_..."

Connect Obsidian Sync (optional)

UI → Sync → sign in with your Obsidian account (+2FA), pick a remote vault, optionally provide the end-to-end encryption password. The server runs ob sync --continuous, restarts it with backoff if it dies, and shows a live log. Credentials are passed straight to the ob CLI and never stored by this server.

Related MCP server: obsidian-mcp

The MCP tools

Tool

What it does

vault_info

Orientation: counts, daily-note config, templates, recent notes

browse_vault

Folder listing or compact tree

search_vault

Unified content + filename + tag search with highlighted snippets, filters, pagination

read_note

Frontmatter + body + content hash; outline/section modes for huge notes; batch reads; images render inline

create_note

New notes, structured frontmatter, template instantiation ({{date}}, {{time}}, {{title}})

edit_note

Surgical edits: exact-match replace, append (under a heading), section replace, frontmatter set/remove — atomic, hash-guarded

manage_note

Move/copy/delete with automatic backlink rewriting; deletes go to .trash/

get_links

Backlinks (with context), outgoing links, embeds, unresolved links

list_tags

All tags (frontmatter + inline, nested) with counts

daily_note

Read/append today's (or any date's) daily note honoring the vault's own settings and template

Design notes:

  • Safe concurrent editing. The vault can change underneath the server (Obsidian Sync). Every read returns a content hash; edits verify it and fail with a retryable error if the note changed. Whole-content replacement requires the hash. Writes are atomic (temp file + rename) and serialized per note.

  • Obsidian-native behavior. Wikilink resolution, daily-note paths, templates, and link rewriting all honor the vault's .obsidian/ settings. The server never writes into .obsidian/.

  • Recoverable by design. Deletes are soft (.trash/), every mutation is git-snapshotted, and destructive ops take a synchronous pre-snapshot.

Configuration

Env var

Default

Purpose

PUBLIC_URL

— (optional)

Pin the external URL. Leave unset to auto-derive it from your reverse proxy's X-Forwarded-Proto/X-Forwarded-Host per request (the common case behind a single tunnel). Set it only when you want to hard-pin the origin or run behind multiple/untrusted proxies.

PORT

3000

Listen port

DATA_DIR

/data

Root for all persistent state

VAULT_DIR

$DATA_DIR/Vault

Vault location

SYNC_AUTOSTART

true

Resume ob sync on boot if previously enabled

OB_KILL_GRACE_MS

5000

SIGTERM→SIGKILL grace for ob (it ignores SIGTERM)

SNAPSHOT_DEBOUNCE_MS

30000

Snapshot debounce after mutations

SNAPSHOT_INTERVAL_MS

3600000

Periodic dirty-check snapshot

AUTH_RESET

Set to 1 to wipe passkeys/sessions and re-enter setup (e.g. after a hostname change)

LOG_LEVEL

info

debug / info / warn / error

Security model

  • Management UI: passkeys only, user verification required, discoverable credentials (one-tap). Sessions are DB-backed, HttpOnly, SameSite=Lax cookies.

  • First-run trust: /setup requires a token printed to server logs (proof of box ownership); the page 404s once a passkey exists.

  • Hostname handling: when PUBLIC_URL is unset the origin/rpID are derived from the proxy's forwarded headers, and the WebAuthn rpID is pinned at first-passkey setup — a later request arriving with a different host is rejected (use AUTH_RESET=1 to re-key if the hostname legitimately changes). WebAuthn's crypto binds each credential to the browser-signed origin regardless, so a forged forwarded header cannot impersonate your host.

  • MCP: OAuth 2.1 (PKCE S256 mandatory, RFC 8414/9728 metadata, RFC 7591 DCR, refresh rotation with reuse detection) or static bearer tokens. All tokens stored as SHA-256 hashes, shown once, revocable from the UI; revocation also kills live streams.

  • Host-header guard (DNS rebinding), Origin checks on /mcp, CSRF protection on the UI, rate-limited auth endpoints.

  • data/db/app.db contains only hashes and public keys — safe to back up.

Development

Requires mise (pins bun + node) and git.

mise install
bun install
mise run dev          # server with hot reload + tailwind watch → http://localhost:3000
mise run test         # bun test (133 tests)
mise run typecheck
mise run docker:build
mise run ob:install   # optional: real obsidian-headless for local sync testing

Local dev uses the same ./data layout as production and passkeys work on http://localhost.

Recovery cheatsheet

# full vault history
git --git-dir data/snapshots/vault.git --work-tree data/Vault log --oneline

# restore everything to a snapshot
git --git-dir data/snapshots/vault.git --work-tree data/Vault checkout <sha> -- .

# lost your passkey?
AUTH_RESET=1 docker compose up -d   # re-enters setup mode; unset afterwards

License

MIT © Jeff Clement

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    An MCP server for managing Obsidian-style note vaults, providing tools for full-text search, note creation, and backlink tracking. It enables users to navigate, structure, and update their personal knowledge base through natural language.
    9
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Single-container MCP server for an Obsidian vault, providing 14 tools to manage notes, directories, tags, and trash with authentication, CORS, and SSE streaming.
    2,778 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Self-hosted MCP server that provides embedding-powered semantic search with graph context over Obsidian vaults, supporting multiple vaults, local embeddings, and a web dashboard.
    MIT