Fast MCP server for local Obsidian vault usage by AI Agents
Overview
A high-performance MCP server that gives AI agents full read/write access to Obsidian vaults. Written in Rust. Ships as a single binary. Existing solutions for AI + Obsidian require the Obsidian app to be running with a community plugin (obsidian-local-rest-api) and an MCP wrapper (mcp-obsidian) on top. That's fragile, slow, and limited to what the REST API exposes. obsidian-mcp talks directly to the filesystem. It understands the Obsidian format natively — wikilinks, frontmatter, tags, block references, periodic notes, the full graph. It builds fast in-memory indices on startup and keeps them synced via a filesystem watcher. The result is a single dependency-free binary that works whether Obsidian is open or not. Add to your MCP client config (Cursor, Claude Desktop, etc.) and you're done — 19 tools are available immediately. Metadata and BM25 indices update immediately through the filesystem watcher.
README
obsidian-mcp
A high-performance MCP server that gives AI agents full read/write access to Obsidian vaults. Written in Rust. Ships as a single binary.
No Obsidian plugins. No REST API. No Obsidian running. Just your vault on disk.
Why?
Existing solutions for AI + Obsidian require the Obsidian app to be running with a community plugin (obsidian-local-rest-api) and an MCP wrapper (mcp-obsidian) on top. That’s fragile, slow, and limited to what the REST API exposes.
obsidian-mcp talks directly to the filesystem. It understands the Obsidian format natively — wikilinks, frontmatter, tags, block references, periodic notes, the full graph. It builds fast in-memory indices on startup and keeps them synced via a filesystem watcher. The result is a single dependency-free binary that works whether Obsidian is open or not.
Quick Start
# Install
cargo install obsidian-mcp
# Run (stdio — for MCP clients that launch the process)
obsidian-mcp /path/to/your/vault
# Run (HTTP — shared server for multiple agents)
obsidian-mcp --http /path/to/your/vault
Add to your MCP client config (Cursor, Claude Desktop, etc.) and you’re done — 19 tools are available immediately.
What It Can Do
| Category | Capabilities |
|---|---|
| Navigate | List files, tree view, vault stats |
| Read & write | Create, read, overwrite, append, prepend, move, delete notes |
| Patch | Edit individual heading sections, block references, or frontmatter fields without touching the rest of the note |
| Search | BM25 full-text (Tantivy) with stemming, fuzzy matching, and per-field filtering. Semantic search via managed local or API embeddings. Regex. Tag and frontmatter queries. |
| Graph | Backlinks, outgoing links, broken link detection, orphan discovery |
| Frontmatter | Get/set/remove fields, query notes by metadata |
| Periodic notes | Daily, weekly, monthly, quarterly, yearly — with Obsidian-compatible date formats and template expansion |
Metadata and BM25 indices update immediately through the filesystem watcher. Embeddings reconcile asynchronously through a managed background runtime, so model loading, inference, and cache writes never delay MCP startup or core tools.
Installation
From crates.io
cargo install obsidian-mcp
For semantic search local compatibility mode (OBSIDIAN_SEMANTIC_MODE=local), build with embeddings:
cargo install obsidian-mcp --features embeddings
The embeddings feature adds ~60 MB to the binary (ONNX Runtime). In daemon mode, model/runtime cache is shared under semantic home; local mode keeps in-process embedding support in the MCP binary.
On Windows, local embeddings builds require a recent MSVC Build Tools toolset for ONNX Runtime. MSVC 14.44+ is known to work; older VS 2019-era 14.2x toolsets may fail to link with __std_find_trivial_8. Use --features embeddings-api instead if you want semantic search through an external /v1/embeddings server without bundling ONNX Runtime.
For API embedding backend (OpenAI, Ollama, vLLM, LM Studio, or any /v1/embeddings-compatible endpoint):
cargo install obsidian-mcp --features embeddings-api
This adds no model weight to the binary — embeddings are computed by an external server. Both features can be compiled simultaneously; OBSIDIAN_EMBEDDING_PROVIDER selects which runs.
API backend examples
# OpenAI
OBSIDIAN_EMBEDDINGS=true
OBSIDIAN_EMBEDDING_PROVIDER=api
OBSIDIAN_EMBEDDING_API_KEY=sk-...
OBSIDIAN_EMBEDDING_API_MODEL=text-embedding-3-small
# Ollama (local, no auth)
OBSIDIAN_EMBEDDINGS=true
OBSIDIAN_EMBEDDING_PROVIDER=api
OBSIDIAN_EMBEDDING_API_BASE=http://localhost:11434/v1
OBSIDIAN_EMBEDDING_API_MODEL=nomic-embed-text
OBSIDIAN_EMBEDDING_API_KEY=unused
# vLLM / LM Studio
OBSIDIAN_EMBEDDINGS=true
OBSIDIAN_EMBEDDING_PROVIDER=api
OBSIDIAN_EMBEDDING_API_BASE=http://localhost:8000/v1
OBSIDIAN_EMBEDDING_API_MODEL=BAAI/bge-small-en-v1.5
OBSIDIAN_EMBEDDING_API_KEY=unused
# OpenRouter (25+ embedding models from multiple providers)
OBSIDIAN_EMBEDDINGS=true
OBSIDIAN_EMBEDDING_PROVIDER=api
OBSIDIAN_EMBEDDING_API_BASE=https://openrouter.ai/api/v1
OBSIDIAN_EMBEDDING_API_MODEL=openai/text-embedding-3-small
OBSIDIAN_EMBEDDING_API_KEY=sk-or-v1-...
Pre-built binaries
Grab the latest from GitHub Releases:
| Platform | Archive |
|---|---|
| Linux x86_64 | obsidian-mcp--x86_64-unknown-linux-gnu.tar.gz |
| Linux ARM64 | obsidian-mcp--aarch64-unknown-linux-gnu.tar.gz |
| macOS Intel | obsidian-mcp--x86_64-apple-darwin.tar.gz |
| macOS Apple Silicon | obsidian-mcp--aarch64-apple-darwin.tar.gz |
| Windows x86_64 | obsidian-mcp--x86_64-pc-windows-msvc.zip |
Semantic daemon release assets are also published per target:
obsidian-semanticd--.tar.gz(Unix)obsidian-semanticd--.zip(Windows)
Install from source (edge)
If you have the repo cloned, you can install the latest main build locally:
./bin/install-edge
This pulls the latest main and runs cargo install --path . --features embeddings.
Semantic Runtime Compatibility
Current semantic daemon API version: 2 (DAEMON_API_VERSION in src/daemon/protocol.rs).
| Component | Compatibility contract | Enforcement |
|---|---|---|
obsidian-mcp |
Daemon API version must match exactly | MCP daemon health handshake validates min_api_version/max_api_version against current API version and fails fast on mismatch |
obsidian-semantic-search-plugin |
Daemon API version must match exactly | Plugin bootstrap performs daemon health handshake and surfaces explicit incompatibility notices |
Daemon API v2 applies each search request’s optional allowed_paths scope before ranking and distinguishes an unattached vault from an index that is warming. Clients must support v2; a v1 daemon cannot safely serve scoped searches.
Release binaries for Linux, Windows, and Apple Silicon macOS include local and API embedding backends. Intel macOS binaries include the API backend only because the current ONNX dependency has no prebuilt Intel macOS library. Cargo users on Intel macOS should install with --features embeddings-api instead of --features embeddings. Every release archive is smoke-tested on its native target.
Release asset compatibility expectations:
- Daemon auto-install clients expect release assets named
obsidian-semanticd--.{tar.gz|zip}pluschecksums.sha256. - Plugin releases should publish
main.js,manifest.json, and optionalstyles.cssas GitHub release assets.
Upgrade guidance:
- Use
obsidian-mcp upgradefor Cargo-installedobsidian-mcpandobsidian-semanticdbinaries; it preserves the compiled feature set and reconciles the locally owned daemon. - Upgrade
obsidian-semantic-search-pluginthrough Obsidian’s plugin manager. - If versions are skewed, startup/handshake fails with explicit API incompatibility errors rather than silent fallback.
Transport Modes
obsidian-mcp supports two transports. Both are always compiled in — choose the one that fits your workflow.
stdio (default)
The standard MCP transport. The AI client spawns the process and communicates over stdin/stdout.
Use when: your MCP client manages the server process (Cursor, Claude Desktop, most MCP clients).
obsidian-mcp /path/to/vault
Each client connection spawns its own process. Simple, zero-config, works everywhere.
Streamable HTTP
A persistent HTTP server that multiple clients can connect to simultaneously.
Use when: you run multiple headless agents (cursor agent -p, parallel Claude sessions, etc.) against the same vault and want to share a single server process instead of spawning one per agent.
# Foreground (for development / process managers like launchd, systemd)
obsidian-mcp --http /path/to/vault
# Background (ad-hoc daemonize — spawns child, parent exits)
obsidian-mcp serve /path/to/vault
The serve command daemonizes the server and redirects logs to a platform-specific file:
- macOS:
~/Library/Logs/obsidian-mcp.log - Linux:
$XDG_STATE_HOME/obsidian-mcp/obsidian-mcp.log - Windows:
%LOCALAPPDATA%/obsidian-mcp/obsidian-mcp.log
Default: http://127.0.0.1:37842. MCP tools are served at /mcp, health check at /health.
The same MCP endpoint supports server/discover and stateless tool requests with protocol 2026-07-28, plus the legacy initialize and session flow used by 2025-11-25 clients. Modern requests carry the protocol version and client metadata on each call. Reverse proxies must preserve MCP headers and forward an allowed loopback Host to the local server.
Benefits over stdio for multi-agent setups:
- Shared index — one in-memory BM25/embedding index instead of N copies
- Lower resource usage — single filesystem watcher, single process
- Process independence — server stays up when agents come and go
- Standard observability — HTTP health checks, logging, standard networking
Configure the address:
obsidian-mcp --http --port 9000 --host 0.0.0.0 /path/to/vault
# or via env vars
OBSIDIAN_TRANSPORT=http OBSIDIAN_HTTP_PORT=9000 obsidian-mcp /path/to/vault
Server Management
obsidian-mcp serve /path/to/vault # Start HTTP server in background
obsidian-mcp stop # Stop running server (default port)
obsidian-mcp stop --port 9000 # Stop server on specific port
obsidian-mcp restart /path/to/vault # Stop + start (picks up new binary after upgrade)
serve and restart wait for the server to pass a /health check before reporting success (up to 15s). If the server fails during startup, the exit code and log path are reported.
Client Setup
Cursor (stdio)
~/.cursor/mcp.json:
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp",
"args": ["/path/to/your/vault"]
}
}
}
Cursor (HTTP — shared server)
Start the server once:
obsidian-mcp serve /path/to/your/vault
Then point Cursor at it:
{
"mcpServers": {
"obsidian": {
"url": "http://127.0.0.1:37842/mcp"
}
}
}
All Cursor agents (IDE, CLI, headless) share the same server.
Claude Desktop
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp",
"env": {
"OBSIDIAN_VAULT_PATH": "/path/to/your/vault",
"OBSIDIAN_EMBEDDINGS": "true"
}
}
}
}
Config file location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Any MCP client
obsidian-mcp supports both stdio and Streamable HTTP MCP transports. Any MCP-compatible client can connect via either method. Pass the vault path as the first argument or via OBSIDIAN_VAULT_PATH.
Running as a Service
For always-on HTTP mode, use your OS process manager with --http (not serve). This lets the process manager handle restarts, logging, and lifecycle — serve daemonizes itself, which conflicts with process managers that expect to own the child process.
macOS (launchd)
Create ~/Library/LaunchAgents/com.obsidian-mcp.plist:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Label
com.obsidian-mcp
ProgramArguments
/Users/YOU/.cargo/bin/obsidian-mcp
--http
EnvironmentVariables
OBSIDIAN_VAULT_PATH
/path/to/your/vault
RunAtLoad
KeepAlive
StandardOutPath
/Users/YOU/Library/Logs/obsidian-mcp/launchd.out.log
StandardErrorPath
/Users/YOU/Library/Logs/obsidian-mcp/launchd.err.log
Important: launchd does not source your shell profile (
~/.zshrc,~/.bashrc). All environment variables must be defined in the plist’sEnvironmentVariablessection.
# Load
launchctl load ~/Library/LaunchAgents/com.obsidian-mcp.plist
# Unload (stop)
launchctl unload ~/Library/LaunchAgents/com.obsidian-mcp.plist
# A direct, running user LaunchAgent is restarted automatically by:
obsidian-mcp upgrade
Linux (systemd)
Create ~/.config/systemd/user/obsidian-mcp.service:
[Unit]
Description=obsidian-mcp MCP server
After=network.target
[Service]
ExecStart=%h/.cargo/bin/obsidian-mcp --http
Environment=OBSIDIAN_VAULT_PATH=/path/to/your/vault
# Add embedding env vars if needed:
# Environment=OBSIDIAN_EMBEDDINGS=true
# Environment=OBSIDIAN_EMBEDDING_PROVIDER=api
# Environment=OBSIDIAN_EMBEDDING_API_KEY=sk-...
# Environment=OBSIDIAN_EMBEDDING_API_BASE=https://api.openai.com/v1
# Environment=OBSIDIAN_EMBEDDING_API_MODEL=text-embedding-3-small
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now obsidian-mcp
# Check status
systemctl --user status obsidian-mcp
# A direct, running user service is restarted automatically by:
obsidian-mcp upgrade
# View logs
journalctl --user -u obsidian-mcp -f
Upgrading
# Inspect the verified install and exact command without changing anything
obsidian-mcp upgrade --dry-run
# Install the latest crates.io version and activate it
obsidian-mcp upgrade
upgrade supports official crates.io installations recorded in Cargo’s install tracker. It preserves the current target, build profile, and exact compiled feature set; verifies both shipped binaries after Cargo finishes; restarts directly managed running launchd/systemd user services; and reconciles a locally owned semantic daemon without changing its home, model, or caches. Explicit semantic-daemon overrides and PATH-owned daemons are left alone.
Vault settings and service definitions are never rewritten. Stdio clients cannot be restarted from inside their active protocol session, so reconnect them after an update. An ad-hoc background process started with obsidian-mcp serve must also be restarted with the same arguments or environment; the upgrader cannot safely reconstruct that launch context. If installation succeeds but activation fails, the command exits non-zero with diagnostics and does not silently roll back the Cargo installation.
Homebrew, Nix, source/path, Git, alternate-registry, and pre-built binary installs are rejected before mutation. Upgrade those through the tool that owns the installation.
See Upgrading for the support matrix, guarantees, outcomes, and recovery guidance.
Search
Three tiers, each suited to different needs.
BM25 Full-Text — search_text
On by default. Powered by Tantivy, rebuilt in-memory on startup (< 100 ms for 5k notes).
- Stemming — “deploy” matches “deployment”, “deployed”, “deploying”
- Field boosts — title 5x, tags 4x, headings 3x, frontmatter 2x, body 1x
- Fuzzy matching —
fuzzy: truetolerates single-character typos (edit distance 1) - Field filtering —
fields: ["title", "tags"]to restrict scope - Returns ranked results with context snippets and match offsets
Semantic — search_semantic
Uses the shared local semantic daemon by default (OBSIDIAN_SEMANTIC_MODE=auto). Results keep the same MCP schema while backend execution can run through daemon or local compatibility mode.
Semantic runtime modes:
auto(default): prefer daemon, fallback to local in-process embeddings only when the daemon is genuinely unavailable and local embeddings are enabled. A daemon that is attached but warming does not start a competing local index.daemon: daemon-only path; semantic calls fail clearly if daemon is unavailable.local: force the in-process embedding runtime.
Daemon startup policy:
- MCP only initializes/starts the daemon when
OBSIDIAN_WATCH=true. - When
OBSIDIAN_WATCH=false, MCP skips daemon initialization and semantic search must use local mode (if enabled) or returns a clear error.
Embedding lifecycle:
| Phase | Queryable | Meaning |
|---|---|---|
warming |
No | The model is loading or the first verified index is being built. A semantic query returns an explicit retryable not-ready error. |
warming |
Yes | A same-space last-known-good cache is serving queries while changed notes reconcile in the background. |
ready |
Yes | Every currently indexed note has completed reconciliation. An empty vault is also ready. |
degraded |
Yes or no | Some work failed. Verified last-known-good/partial vectors remain usable when any exist. Note-read, inference, and persistence failures retry; a model-load/configuration failure requires correction and restart. |
Core note, graph, BM25, write, and MCP initialization behavior stays available in every phase. Repeated note changes are coalesced by path, and the newest write or removal wins over in-flight inference.
Embedding caches are versioned and atomically replaced. A cache is reusable only when its backend, canonical model, vector dimension, embedding-input version, and—for API backends—the normalized endpoint fingerprint all match. Legacy payloads, incomplete first-pass checkpoints, corrupt data, and mismatched identities are never published; they trigger a one-time background rebuild. Cache locations do not change: local mode uses {mcp_data}/embeddings/embeddings.bin, while the daemon uses /vaults//embeddings.bin. Legacy-location relocation is non-destructive, runs behind the background coordinator, and publishes through the same atomic no-partial-file boundary.
For a remote API, the identity can prove the configured endpoint and model name, but it cannot detect a provider silently replacing model weights behind the same name. Change the configured model identifier (or remove the derived cache) when a provider makes such a revision.
- Finds notes by meaning, not keywords — “making money from software” surfaces notes about monetization
- Hybrid mode —
lexical_prefetch: truecombines BM25 candidate retrieval with semantic re-ranking - Tunable blending —
alphacontrols the weight between lexical and semantic scores (default 0.25) - Daemon cache-location migration is one-way and non-destructive: the active local cache is preferred, then legacy
.obsidian/obsidian-mcp/embeddings.bin; relocation runs in the background, never overwrites an existing daemon cache, and an old payload format is rebuilt rather than trusted
Regex — search_regex
Always available. Full regex syntax for pattern matching across all notes.
When to Use What
| Need | Tool |
|---|---|
| Known keyword or phrase | search_text |
| Keyword with possible typo | search_text + fuzzy: true |
| Conceptual — “notes about X” | search_semantic |
| Best precision + recall | search_semantic + lexical_prefetch: true |
| Structural pattern (URLs, IDs) | search_regex |
| By tag or metadata | search_metadata |
Tool Reference
Tool Filtering
Control which tools are exposed via the OBSIDIAN_TOOLS environment variable or the X-Obsidian-Tools HTTP header. The header can only further restrict the server-wide tool set; disabled tools are hidden from listings and cannot be called.
For legacy HTTP sessions, send the header with initialize; that filter lasts for the session. With protocol 2026-07-28, send it on every request that needs the restriction. Stateless requests do not retain a previous request’s filter. A missing header uses the server-wide tool set; invalid profile names or invalid header encoding return HTTP 400.
| Value | Effect |
|---|---|
full (or unset) |
All 19 tools |
core |
15 tools — drops search_semantic, wikilinks, periodic, open_in_obsidian |
read |
10 tools — read-only (excludes all mutation tools, including frontmatter) |
minimal |
6 tools — vault_list, vault_info, note_read, note_create, note_write, search_text |
tool1,tool2,... |
Allow-list — only the named tools |
!tool1,!tool2,... |
Deny-list — all tools except the named ones |
The read profile exposes frontmatter as part of the raw Markdown returned by note_read. For structured frontmatter get, set, and remove actions, use full, core, or an explicit allow-list containing frontmatter (for example, OBSIDIAN_TOOLS=note_read,frontmatter). Allowing frontmatter enables all three actions.
Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
OBSIDIAN_VAULT_PATH |
Yes* | — | Absolute path to your Obsidian vault |
OBSIDIAN_TRANSPORT |
No | stdio |
Transport mode: stdio or http |
OBSIDIAN_HTTP_PORT |
No | 37842 |
HTTP listen port |
OBSIDIAN_HTTP_HOST |
No | 127.0.0.1 |
HTTP bind address |
OBSIDIAN_WATCH |
No | true |
Filesystem watcher for live index updates |
OBSIDIAN_LOG_LEVEL |
No | info |
trace, debug, info, warn, error |
OBSIDIAN_TANTIVY |
No | true |
BM25 full-text index |
OBSIDIAN_TOOLS |
No | full |
Tool filtering: profile name, comma-separated allow-list, or !-prefixed deny-list |
OBSIDIAN_MCP_DATA |
No | {vault}/.obsidian-mcp |
External obsidian-mcp data directory. When set, cache/config data is stored under {value}/vaults/{vault_slug}/ and merged with vault-local config |
OBSIDIAN_EXCLUDE_PATHS |
No | (empty) | Comma-separated glob patterns excluded from indexing, search, graph, and stats. Merged with .obsidian-mcp/ignore; restart after changing ignore files |
OBSIDIAN_EMBEDDINGS |
No | false |
Semantic embedding search (requires embeddings or embeddings-api feature) |
OBSIDIAN_EMBEDDINGS_MODEL |
No | BAAI/bge-small-en-v1.5 |
HuggingFace model for embeddings |
OBSIDIAN_EMBEDDING_PROVIDER |
No | (infer) | Embedding backend: local (fastembed) or api (OpenAI-compatible) |
OBSIDIAN_EMBEDDING_API_KEY |
When api | Fallback: OPENAI_API_KEY |
API authentication key |
OBSIDIAN_EMBEDDING_API_BASE |
No | https://api.openai.com/v1 |
Embedding API base URL (fallback: OPENAI_BASE_URL) |
OBSIDIAN_EMBEDDING_API_MODEL |
When api | Fallback: OPENAI_MODEL |
Model name for embedding API |
OBSIDIAN_EMBEDDING_DIM |
No | (probed) | Override embedding dimension (skips API probe) |
OBSIDIAN_EMBEDDING_CA_CERT |
No | — | Path to PEM CA certificate for API TLS |
OBSIDIAN_EMBEDDING_TLS_VERIFY |
No | true |
Set to false to skip TLS verification |
OBSIDIAN_SEMANTIC_MODE |
No | auto |
Semantic backend mode: auto, daemon, local |
OBSIDIAN_SEMANTIC_HOME |
No | OS data-dir default | Shared semantic runtime home for daemon bin/model/cache state |
OBSIDIAN_SEMANTIC_DAEMON_PATH |
No | unset | Override daemon binary path used by bootstrap |
OBSIDIAN_SEMANTIC_DAEMON_DOWNLOAD_URL |
No | https://github.com/lstpsche/obsidian-mcp/releases/download/v/obsidian-semanticd--. |
Override daemon download URL when binary is missing |
OBSIDIAN_SEMANTIC_MODEL |
No | BAAI/bge-small-en-v1.5 |
Default model used by semantic daemon runtime |
OBSIDIAN_SEMANTIC_ENDPOINT |
No | /ipc/... |
Daemon-only endpoint override (internal/runtime use) |
OBSIDIAN_SEMANTIC_CONNECT_TIMEOUT_MS |
No | 2000 |
Per-call daemon timeout in milliseconds |
OBSIDIAN_SEMANTIC_CONNECT_RETRIES |
No | 2 |
Daemon retry attempts after the first failed call |
OBSIDIAN_SEMANTIC_RETRY_BACKOFF_MS |
No | 250 |
Base retry backoff in milliseconds |
OBSIDIAN_SEMANTIC_PREFETCH |
No | 50 |
Default lexical prefetch candidate count for hybrid daemon queries |
OBSIDIAN_SEMANTIC_ALPHA |
No | 0.25 |
Default hybrid blend weight (alpha * BM25 + (1-alpha) * semantic) |
OBSIDIAN_HYBRID_ALPHA |
No | alias | Backward-compatible alias for OBSIDIAN_SEMANTIC_ALPHA |
FASTEMBED_CACHE_DIR |
No | /model/fastembed-cache |
Daemon-internal shared fastembed cache root |
* Also accepted as the first CLI argument: obsidian-mcp /path/to/vault
Architecture
AI Client(s)
│
├─ stdio (1:1 process) ← default, single-client
│ OR
├─ HTTP POST /mcp (N:1) ← shared server, multi-client
│
▼
obsidian-mcp
├─ tools/ MCP handlers — translate protocol into vault ops
├─ vault/
│ ├─ index In-memory metadata (tags, links, headings)
│ ├─ tantivy BM25 full-text search
│ ├─ embeddings Model + strict cache contracts (optional)
│ ├─ embedding_runtime Background reconciliation + readiness
│ └─ watcher Filesystem events → index sync
├─ models.rs Shared types
└─ error.rs VaultError → MCP ErrorData
│ fs
▼
Vault directory (.md files + .obsidian/)
The vault layer is a pure Rust library with no knowledge of MCP. The tools layer is a thin adapter. This separation means the vault code is independently testable and reusable.
In HTTP mode, each legacy session or modern stateless request gets its own handler instance, but all handlers share a single Vault (thread-safe through shared state). One filesystem watcher, one BM25 index, and one managed embedding coordinator/store serve all connected agents.
Development
cargo build # default build
cargo build --features embeddings # with local semantic search (fastembed)
cargo build --features embeddings-api # with API semantic search (OpenAI-compatible)
# stdio mode (default)
OBSIDIAN_VAULT_PATH=~/vault cargo run
# HTTP mode
OBSIDIAN_VAULT_PATH=~/vault cargo run -- --http
cargo fmt --check && cargo clippy && cargo test
npx @modelcontextprotocol/inspector cargo run # interactive testing (stdio)
License
Install
npx @modelcontextprotocol/inspector cargo run # interactive testing (stdio)Configuration
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp",
"args": ["/path/to/your/vault"]
}
}
}