Obsidian Semantic MCP
Allows AI agents to semantically search, read, write, and manage notes in an Obsidian vault using local embeddings.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Obsidian Semantic MCPsearch my vault for notes about machine learning"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Obsidian Semantic MCP
A persistent memory layer for Claude Desktop — semantic search across your entire Obsidian vault using local embeddings and PostgreSQL + pgvector.
The Problem
AI assistants forget everything between sessions. You repeat context, lose continuity, and start from zero every time. Your notes, projects, and preferences sit in Obsidian but never make it into your AI conversations automatically.
Related MCP server: Remember Me MCP Server
The Solution
Obsidian Semantic MCP turns your vault into a queryable brain for Claude. It:
Indexes every note as a vector embedding (via Ollama +
nomic-embed-text)Stores embeddings in PostgreSQL with pgvector for fast semantic search
Watches your vault for changes and re-indexes automatically
Provides full vault CRUD (read, write, search, list) — works even when Obsidian is closed
Exposes everything through MCP so Claude can retrieve and manage vault content on the fly
No cloud services. No API keys. Everything runs locally.
Quick Start
Start with the bootstrap installer for your platform. If you already cloned the repo, you can skip bootstrap and run uv run osm init from the project root.
First 60 seconds (new user)
If you're in a fresh environment (no osm launcher yet), run these from the repo root:
# 1) Preview setup actions safely (recommended: persistent data)
uv run osm init --dry-run --mode 1 --vault "/path/to/your/vault" --pg-password "obsidian" --persistent --data-dir "/path/to/data"
# 2) Run setup for real
uv run osm init --mode 1 --vault "/path/to/your/vault" --pg-password "obsidian" --persistent --data-dir "/path/to/data"
# 3) Open the monitoring dashboard
uv run osm dashboardOn macOS,
--mode 1is Native install. For Docker-first installs, use mode 3 on macOS or mode 2 on Linux/Windows. For ephemeral/CI setups, use--no-persistentinstead.
Before you start:
uvmust be installed —curl -LsSf https://astral.sh/uv/install.sh | sh(macOS/Linux) orpowershell -c "irm https://astral.sh/uv/install.ps1 | iex"(Windows)Docker Desktop — the wizard will offer to install it automatically if missing (
brewon macOS,wingeton Windows,get.docker.comon Linux). On Windows, enable the WSL2 backend.Know your vault path — in Obsidian: Settings → About → Vault location
1. Bootstrap and run the setup wizard
macOS / Linux:
curl -fsSL https://raw.githubusercontent.com/celstnblacc/obsidian-semantic-mcp/main/install.sh | bashIf
~/.local/share/obsidian-semantic-mcpalready exists, the installer updates that checkout before continuing. If that install directory has uncommitted local changes, the update step can abort with a Git merge error. In that case either commit/stash those changes there, or use your current checkout directly withuv run osm init.
Windows PowerShell:
powershell -c "irm https://raw.githubusercontent.com/celstnblacc/obsidian-semantic-mcp/main/install.ps1 | iex"Tip: The bootstrap installer launches
osm initfor you. After a full install, you can runosm init --dry-runfrom an existing checkout to preview every action without making any changes.If you already cloned the repo,
scripts/osm(macOS/Linux) andscripts/osm.ps1(Windows PowerShell) work the same as the bootstrap installers. If you prefer a manual checkout, clone the repo and runuv run osm initfrom the project root.One server, all projects:
obsidian-semanticis registered globally — runningosm initfrom any other project is safe and idempotent. If already configured, it skips registration and informs you.OpenCode/GitHub Copilot note: In this repository,
osmmeans the Obsidian Semantic MCP CLI, not OpenStreetMap. In a new chat session, run commands explicitly (for example,osm dashboard) to avoid acronym ambiguity.If
osmis not found: useuv run osm <command>from the repo root (for example,uv run osm init --dry-run).Session starter (copy/paste):
In this repo, "osm" means the obsidian-semantic-mcp CLI (not OpenStreetMap). Please execute shell commands directly when I type them. If `osm` is not found, use `uv run osm ...` from the repo root. Examples: osm init, osm dashboard.Exit the wizard at any prompt: type
q,quit,exit, orskip— or pressCtrl+C.
The bootstrap installers clone into the local data directory, create a PATH shim for osm, and launch the wizard.
The wizard detects your OS and asks which installation mode you want:
macOS:
1) Native Homebrew + local Postgres + local Ollama
2) Docker + host Ollama Postgres in Docker, Ollama already on this Mac
3) Full Docker Everything in containers (recommended)
4) Docker + remote Ollama Postgres in Docker, Ollama on another machineLinux:
1) Docker + host Ollama Postgres in Docker, Ollama on this machine
2) Full Docker Everything in containers (recommended)
3) Docker + remote Ollama Postgres in Docker, Ollama on another machineWindows (requires Docker Desktop with WSL2 backend):
1) Docker + host Ollama Postgres in Docker, Ollama already on this PC
2) Full Docker Everything in containers (recommended)
3) Docker + remote Ollama Postgres in Docker, Ollama on another machineWhich mode? Pick Full Docker (mode 3 on macOS, mode 2 on Linux/Windows) unless you already have Ollama running locally - in that case pick Docker + host Ollama to avoid re-downloading the Ollama image and model.
It then:
Sets up the local install directory and PATH shim
Installs prerequisites or verifies they already exist (Docker is installed and started automatically if missing)
Pulls
nomic-embed-textif neededWrites a
.envfile (gitignored) with your vault path and credentialsUpdates MCP client config automatically for Claude Desktop, Claude Code CLI, OpenCode, and pi (whichever are installed)
Uses the repo launcher script in generated MCP entries, so startup does not depend on a raw Docker command or container-name-specific config
2. Restart your MCP client(s)
Restart Claude Desktop / OpenCode to pick up the new server. For Claude Code CLI, the entry is registered live; verify with claude mcp list. For pi, run /reload inside an active session or restart pi.
pi users:
osm initalso patches~/.pi/agent/extensions/mcp-bridge.tsif present. obsidian-semantic requiresheartbeat: trueand a spawn-time heartbeat in the bridge due to its blocking asyncio stdin transport. Seedocs/pi_mcp_bridge_heartbeat.mdfor details.
Manual config (only if
osm initcould not detect your client)Add the same block to
~/.opencode.json,claude_desktop_config.json, or whatever JSON config your MCP client uses:{ "mcpServers": { "obsidian-semantic": { "command": "/absolute/path/to/obsidian-semantic-mcp/scripts/obsidian-semantic-mcp", "args": [], "env": {} } } }Replace
/absolute/path/to/obsidian-semantic-mcpwith your local clone path. The launcher prefers the running Docker stack and falls back to the repo-local.venvwhen Docker is unavailable.
3. First-run indexing
The server indexes your vault on first run, then watches for changes automatically.
First-run indexing takes roughly 1–2 seconds per note — expect 5–15 minutes for a 500-note vault. Monitor progress at http://localhost:8484. Claude will return no results until indexing completes.
Prefer running without Docker? See Native Install (macOS). Want to skip the wizard? See Manual start.
Manual start (without wizard)
OBSIDIAN_VAULT="/path/to/your/vault" POSTGRES_PASSWORD=obsidian docker compose up -dDocker Compose also reads a
.envfile in the repo root (gitignored).
First run pulls all images and the nomic-embed-text model automatically. This starts:
Service | Port | Description |
PostgreSQL + pgvector | 5433 | Vector storage (avoids conflict with host pg) |
Ollama | 11435 | Local embeddings (auto-pulls model) |
MCP server | stdio | Clients connect via |
Dashboard | 8484 |
Useful commands
# View server logs
docker compose logs -f mcp-server
# Rebuild after code changes
docker compose up -d --build mcp-server dashboard
# Stop everything
docker compose down
# Stop and wipe all data (re-index from scratch)
# ⚠️ WARNING: -v deletes all indexed embeddings. Re-indexing will restart from scratch.
docker compose down -vGPU support (optional)
For faster embeddings on Linux with NVIDIA GPU, add to the ollama service in docker-compose.yml:
deploy:
resources:
reservations:
devices:
- capabilities: [gpu]Using with Claude
Once the MCP server is connected, Claude can access your vault directly — no special syntax needed. Just talk to it naturally.
Example prompts
"Search my notes for anything about project X"
"What did I write about ketosis last month?"
"Find my notes on the Zettelkasten method"
"Read my Daily/2026-03-14.md note"
"Append this meeting summary to my inbox note"
"Write a new note at Projects/obsidian-mcp.md with this content"
"List all files in my Fleeting folder"
"Show me what's been modified recently"
"Re-index my vault"Claude will automatically choose the right MCP tool (search_vault, get_file, write_file, etc.) based on your request.
osm — Setup & management CLI
Use osm to set up, manage, and tear down the stack. The wizard installs all prerequisites, configures Docker, and updates Claude Desktop automatically.
osm in this repo means the Obsidian Semantic MCP CLI (not OpenStreetMap).
Command | Description |
| Interactive setup wizard |
| Non-interactive setup (agent/script friendly) |
| Preview all actions without making any changes |
| Check service health (Docker, Ollama reachability/inference, Claude Desktop) |
| Open monitoring dashboard in browser |
| Rebuild Docker images after a code change |
| Reconnect SSH tunnel (remote Ollama mode) |
| Stop services and wipe all volumes and config |
| Non-interactive teardown (agent/script friendly) |
| Full flag reference |
osm init flags: --mode, --vault, --pg-password, --persistent / --no-persistent, --data-dir, --ssh-host, --ssh-user, --ssh-port, --ssh-key, --vault-remote
osm status probes both Ollama reachability (/api/tags) and embeddings (/api/embeddings) so it can catch the case where the daemon is up but model execution is broken.
Windows launcher:
osm initinstallsosm.cmdinto%USERPROFILE%\.local\bin\. Windows resolves.cmdautomatically, so you invoke it asosmfrom any terminal. Ifosmis not found, add%USERPROFILE%\.local\binto yourPathenvironment variable.
Using with Claude Code, Codex, and OpenCode
When you type osm init or osm dashboard directly in your terminal, those commands execute normally.
In chat-based coding agents, a bare osm ... message can be interpreted as text (or as OpenStreetMap) instead of a shell command. To force execution, phrase it explicitly:
Run osm initRun osm dashboard
If the launcher is not installed yet (or not on PATH), use:
Run uv run osm init --dry-run --mode 1 --vault <vault-path> --pg-password <password>Run uv run osm dashboard
What to expect for new users:
First install:
osm initsets up services and registersobsidian-semanticin global MCP config for that OS user.Later sessions: re-running
osm initis safe and idempotent.osm dashboardopenshttp://localhost:8484; if the stack is not running yet, it warns and still opens the URL.
Session starter (copy/paste):
In this repo, "osm" means the obsidian-semantic-mcp CLI (not OpenStreetMap).
Please execute shell commands directly when I type them.
If `osm` is not found, use `uv run osm ...` from the repo root.
Examples: osm init, osm dashboard.Example Output
When Claude searches your vault, results come back ranked with similarity scores and content previews:
## Search: "what did I write about ketosis"
1. health/ketosis-diet.md (similarity: 0.87)
Ketosis is a metabolic state where the body burns fat for fuel...
2. Daily/2026-03-14.md (similarity: 0.72)
Started keto today. Meal prepped for the week...
3. research/low-carb-studies.md (similarity: 0.65)
Recent studies on low-carb diets show...For vault CRUD, get_file returns the full note content; recent_changes lists recently modified files with timestamps.
Architecture
Claude Desktop
↓ MCP protocol (stdio)
src/server.py (unified MCP server)
├── Semantic search (pgvector cosine similarity)
├── Vault CRUD (direct filesystem access)
└── Live file watcher (watchdog)
↓
PostgreSQL + pgvector (vector storage + IVFFlat index)
↓
Ollama / nomic-embed-text (local 768-dim embeddings)
↓
Your Obsidian vault (e.g. $HOME/Documents/MyVault)Project Structure
obsidian-semantic-mcp/
├── install.sh # Bootstrap installer for macOS/Linux
├── install.ps1 # Bootstrap installer for Windows PowerShell
├── scripts/
│ ├── osm # CLI wrapper (macOS/Linux) — `uv run osm init` or `scripts/osm init`
│ └── osm.ps1 # CLI wrapper (Windows) — `.\scripts\osm.ps1 init`
├── src/
│ ├── server.py # MCP server — semantic search + vault CRUD (11 tools)
│ └── dashboard.py # Monitoring dashboard (http://localhost:8484)
├── tests/
│ ├── test_unit.py # Unit tests (no real DB/Ollama needed)
│ ├── test_osm_init.py # Unit tests for the osm CLI wizard
│ ├── test_dashboard_smoke.py # Dashboard static analysis + live HTTP smoke tests
│ ├── test_setup.py # Prerequisites checker (deps, DB, Ollama) — run directly
│ └── test_e2e.py # End-to-end MCP protocol test — run directly
├── osm_init.py # Interactive setup wizard (used by scripts/osm)
├── Dockerfile # Python 3.13 + uv
├── docker-compose.yml # Full stack: postgres, ollama, server, dashboard
├── pyproject.toml # Project metadata + dependencies (osm script entry)
├── uv.lock # Pinned lockfile
└── LICENSE # Apache 2.0Prerequisites
An Obsidian vault on your filesystem
macOS native: Homebrew (auto-installs everything else)
Docker modes: Docker Desktop (macOS/Linux/Windows) — the wizard offers to install it if missing via
brew(macOS),winget(Windows), orget.docker.com(Linux), and starts the daemon automaticallyWindows: WSL2 backend enabled,
uvinstalled viapowershell -c "irm https://astral.sh/uv/install.ps1 | iex"
MCP Tools
Semantic Search
Tool | Description |
| Semantic search by meaning across the entire vault. Returns ranked excerpts with similarity scores. Supports |
| Exact text/keyword search across vault files. |
| Return all notes connected to a given note via wikilinks — both outgoing links and incoming backlinks. Useful for exploring the knowledge graph and finding related notes. |
Vault Management
Tool | Description |
| List files and directories in a vault directory. |
| Read the full content of a single file. |
| Read multiple files at once. |
| Append content to a file (creates if missing). |
| Overwrites the target file completely — existing content is replaced with no undo. Use |
| Get recently modified files. |
Index Management
Tool | Description |
| List all indexed notes with their last indexed timestamp. |
| Force a full re-index of all notes. Runs in the background. |
Multi-Vault Support
To index multiple vaults, set OBSIDIAN_VAULTS to a comma-separated list of absolute paths:
OBSIDIAN_VAULTS="/path/to/vault1,/path/to/vault2" docker compose up -dOr in docker-compose.yml (uncomment the multi-vault lines):
environment:
OBSIDIAN_VAULTS: /vault1,/vault2 # multi-vault
volumes:
- /path/to/vault1:/vault1
- /path/to/vault2:/vault2When using multiple vaults, the search_vault MCP tool gains a vault parameter to filter results by vault name. The dashboard also shows a vault selector. Each vault is watched and indexed independently.
How It Works
Indexing — On startup, the server walks your vault, reads each
.mdfile, generates a 768-dim embedding via Ollama, and upserts it into PostgreSQL with pgvector. Unchanged files (same SHA-256 hash) are skipped on subsequent runs. First-run indexing takes roughly 1–2 seconds per note with a local Ollama instance — expect 5–15 minutes for a 500-note vault. Watch progress at http://localhost:8484 or viadocker compose logs -f mcp-server.Watching — A file watcher (
watchdog) monitors the vault for creates, updates, deletes, and moves — re-embedding changed files automatically.Searching — When Claude calls
search_vault, the query is embedded and matched against stored vectors using cosine similarity (IVFFlat index). The top results are returned with similarity scores and content previews.CRUD — All file operations use direct filesystem access, so the server works whether Obsidian is open or not. Path traversal outside the vault is blocked.
Environment Variables
Variable | Description | Default |
| Absolute path to your Obsidian vault. Mounted read-write so MCP write/append tools can create and update notes. Mount with | required |
| Comma-separated list of vault paths for multi-vault mode. Overrides | — |
| Comma-separated vault-relative path segments to skip during indexing and watching. Defaults to |
|
| PostgreSQL password (Docker) | required for Docker |
| Full connection string (overrides POSTGRES_* vars) | built from POSTGRES_* vars |
| PostgreSQL host |
|
| PostgreSQL port |
|
| Database name |
|
| Database user |
|
| Ollama API endpoint |
|
| Ollama model for embeddings |
|
| Parallel threads for bulk indexing |
|
| Optional Ollama model for cross-encoder re-ranking (e.g. | — |
| Candidate pool size fetched before re-ranking |
|
Monitoring Dashboard
A built-in dashboard is available at http://localhost:8484 (started automatically with Docker). It shows:
Service health (PostgreSQL, Ollama, embedding model)
Indexed notes count, vault coverage, DB size
Recently indexed files
Re-index — incremental re-index (skips unchanged notes, fast)
Clear & Rebuild — wipes all embeddings and re-indexes from scratch
A "Start Ollama" button if Ollama is down
To run the dashboard without Docker:
OBSIDIAN_VAULT="/path/to/your/vault" uv run python3 src/dashboard.pyTesting
Unit tests (no real DB or Ollama needed)
uv run pytest -qRuns 230 fast unit tests covering embedding, search, vault path safety, connection pool, dashboard stats, the osm CLI wizard, and CI governance.
test_dashboard_smoke.py — Dashboard health checks (Docker stack)
Offline static analysis (always runs — no services needed) + live HTTP smoke tests (auto-skipped when the dashboard is unreachable).
# Offline + live (stack must be running)
uv run pytest tests/test_dashboard_smoke.py -v
# Target a remote instance
DASHBOARD_URL=http://host:8484 uv run pytest tests/test_dashboard_smoke.py -vChecks: JS string safety, DOM element completeness, /api/stats schema, service health, response time, and routing.
test_setup.py — Prerequisites check (native installs)
Verifies Python deps, vault path, PostgreSQL + pgvector, Ollama, and embedding smoke test. Run directly — not via pytest.
DATABASE_URL="postgresql://localhost/obsidian_brain" \
OBSIDIAN_VAULT="/path/to/your/vault" \
uv run python3 tests/test_setup.pytest_e2e.py — End-to-end MCP test (native installs)
Launches the server, initializes MCP protocol, waits for indexing, runs semantic search, and verifies results. Run directly — not via pytest.
Requires DATABASE_URL or POSTGRES_PASSWORD — the server will exit immediately without one.
DATABASE_URL="postgresql://localhost/obsidian_brain" \
OBSIDIAN_VAULT="/path/to/your/vault" \
uv run python3 tests/test_e2e.pyWindows + network vault (NFS / SMB)
If your vault lives on a NAS and is mounted on Windows as a drive letter (Z:\), osm init will fail to bind-mount it into the container — Docker Desktop on Windows uses WSL2, and WSL2 cannot follow a Windows-side network drive into a container. The UNC form (\\host\share\...) is rejected by the daemon outright; the drive-letter form silently mounts an empty directory.
Recommended fix: mount the share inside WSL2 and point osm init at the Linux path.
# inside your WSL2 distro (Ubuntu/Debian)
sudo apt install nfs-common
sudo mkdir -p /mnt/obsidian_vault
sudo mount -t nfs <nas-host>:/<export-path> /mnt/obsidian_vault
# add to /etc/fstab to persist across reboots
osm init --mode 2 --vault /mnt/obsidian_vaultDocker Desktop shares WSL2 paths cleanly with no volume-driver gymnastics. Starting in v0.5.11, osm init also fails fast when docker compose up is rejected and points you at this section instead of letting the postgres health check time out 90 seconds later.
Alternative: native NFS / CIFS named volumes (--vault-fs, v0.5.12+)
If WSL2 isn't an option, osm init can generate a docker-compose.override.yml that backs each vault with a Docker named volume using NFS or CIFS driver_opts. Vault entries use protocol-specific syntax instead of host paths:
# NFS (one or more vaults; entries use host:/export/path)
osm init --mode 3 \
--vault 10.0.0.1:/exports/coredev \
--vault-fs nfs
# CIFS / SMB
osm init --mode 3 \
--vault //nas.local/share/coredev \
--vault-fs cifs \
--vault-cifs-user alice --vault-cifs-pass 'secret'For multi-vault, pass each entry to a comma-joined OBSIDIAN_VAULTS env (or repeat --vault); each generates its own named volume (obsidian_vault_<basename>). osm remove drops these volumes on teardown.
Limitations: NFSv4 with no auth, SMB with username/password only. NFS Kerberos and CIFS credential files are not supported in v0.5.12. WSL2 is still the recommended path for most users.
Troubleshooting
Symptom | Cause | Fix |
|
| Run |
| Docker Desktop not running | The wizard offers to start it automatically; or start Docker Desktop manually from Applications (macOS) / system tray (Windows/Linux) |
| Vault path not readable by Docker | On macOS: Docker Desktop → Settings → Resources → File Sharing — add your vault path |
| System Python instead of venv | Use |
| Container built before venv PATH fix |
|
| IVFFlat index built on empty table | Run |
| First-boot indexing not complete | Wait for indexing to finish (check |
| Ollama not running | Run |
| Ollama returned empty embedding (blank/tiny file) | Expected — file is skipped and indexing continues |
| Ollama internal error (file too large or model issue) | Expected — file is skipped; try |
| Not installed for your PG version | Use Docker, or build from source (see native install) |
Server crashes on startup |
| Set the env var in your config or docker compose command |
Docker container can't see vault | Wrong path or missing volume | Ensure |
Native Install (macOS)
Manual install only. Use this if you want to control every step yourself instead of using the bootstrap installer.
1. Clone and install
git clone https://github.com/celstnblacc/obsidian-semantic-mcp.git && cd obsidian-semantic-mcp
uv sync2. Install system dependencies
brew install postgresql@17 pgvector ollama
brew services start postgresql@17
ollama serve &
ollama pull nomic-embed-textPostgreSQL 16: Homebrew's
pgvectorbottle requires pg17 or pg18. If you must use pg16, build pgvector from source:cd /tmp git clone --branch v0.8.2 --depth 1 https://github.com/pgvector/pgvector.git cd pgvector make PG_CONFIG=$(brew --prefix postgresql@16)/bin/pg_config make install PG_CONFIG=$(brew --prefix postgresql@16)/bin/pg_config
3. Set up the database
createdb obsidian_brain
psql obsidian_brain -c "CREATE EXTENSION vector;"4. Verify
DATABASE_URL="postgresql://localhost/obsidian_brain" \
OBSIDIAN_VAULT="/path/to/your/vault" \
uv run python3 tests/test_setup.py5. Configure Claude Desktop
Add to $HOME/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"obsidian-semantic": {
"command": "/absolute/path/to/obsidian-semantic-mcp/scripts/obsidian-semantic-mcp",
"args": [],
"env": {}
}
}
}The launcher reads the repo .env for local fallback, and uses the running Docker service automatically when it is available.
Important: Use
.venv/bin/python3— not system Python. Homebrew Python won't have the required packages.
6. Restart Claude Desktop
The server indexes your vault on first run, then watches for changes automatically.
Cost
Everything runs locally. No cloud APIs, no subscriptions. The only cost is disk space for the database (~a few MB for most vaults).
License
Apache 2.0
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/artificemachine/obsidian-semantic-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server