obsidian-mcp
by nakielski
README.md
# obsidian-mcp
> MCP server exposing an Obsidian vault to LLM clients (Claude, Cursor, Continue, ...).
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io/)
[](LICENSE)
[](#development)
## Why?
A Second Brain should be queryable by AI agents — **without shipping your data to a third-party API**.
This server gives any MCP-compatible client read and write access to a local Obsidian vault
via the Model Context Protocol.
**Key differentiators** (vs. existing Obsidian MCP servers):
- **Headless & filesystem-native** — no Obsidian desktop app required, container-ready
- **Structured output schemas** on every tool — LLMs know exactly what to expect
- **Vault graph analytics** — wikilink graph, orphan detection, outlink tracing
- **Vault health check** — automated quality scoring (broken links, untagged, TODOs, duplicates)
- **Policy enforcement** — write log, raw/ immutability, agent locking, and wiki-index
upkeep enforced at the filesystem choke point, not left to client discipline
- **Ranked full-text search** — Whoosh-backed index with Lucene-style query syntax
(fields, booleans, phrases, wildcards, fuzzy, ranges, did-you-mean)
- **AI-optimized tool descriptions** — 5-element docstring pattern with actionable error messages
## Features
- Read and Search notes (full-text, case-insensitive)
- Create, Update, Delete notes with proper YAML frontmatter
- Append to notes (upsert), patch sections under headings
- Frontmatter and tag management (get/set/delete, dedup, case-insensitive)
- Daily notes (YYYY-MM-DD format with date validation)
- Search & replace (literal + regex with capture groups)
- Backlink graph traversal (wikilinks)
- Vault structure as a JSON resource
- Path traversal protection on all entry points (vault sandbox)
- Tag extraction (frontmatter + inline tags)
- Structured output schemas for every MCP tool (22/22)
- Vault write policies: write log (.vault-write-log.jsonl), raw/ immutability,
agent lock (.vault-lock), wiki/index.json upkeep, link checking
- archive_note: archive-instead-of-delete with backlink reporting
- Incremental Whoosh full-text index (BM25, stemmed, field queries, fuzzy,
did-you-mean) with configurable index directory
- Vault graph analytics (nodes, edges, stats, broken links)
- Vault health scoring (0–100 with actionable issue report)
## Documentation
| Document | Description |
|---|---|
| [Build Guide](docs/BUILD_GUIDE.md) | Architecture walkthrough, code tour, extension guide, Docker/K8s deployment |
| [AI Optimization Guide](docs/MCP_AI_OPTIMIZATION_GUIDE.md) | Best practices for AI-optimized MCP tool descriptions |
| [Changelog](CHANGELOG.md) | Notable changes with added, fixed, and changed items |
## Quick Start
```bash
pip install -e .
# Point at your real vault
export OBSIDIAN_VAULT_ROOT=/path/to/your/obsidian/vault
# Run (stdio transport)
obsidian-mcp
```
Or use the bundled mock vault (no config needed):
```bash
obsidian-mcp # uses examples/mock-vault/
```
## Client Integration
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp",
"env": {
"OBSIDIAN_VAULT_ROOT": "/path/to/your/vault"
}
}
}
}
```
### Cursor
Cursor supports MCP from version 0.42+. Configure in `~/.cursor/mcp.json` (same format as Claude Desktop).
### Any other MCP client
The server runs on stdio. Register it with your client's standard MCP configuration
(command: `obsidian-mcp` or `python3 -m obsidian_mcp.server`, env: `OBSIDIAN_VAULT_ROOT`).
## Tools (22)
| Tool | Description |
|---|---|
| `read_note(path)` | Read a note (title, tags, metadata, content) |
| `search_notes(query)` | Full-text search across title and body |
| `list_notes(folder?)` | List notes, optionally scoped to a folder |
| `list_note_paths(folder?)` | List note paths only (lightweight) |
| `create_note(path, content, tags?)` | Create a note with frontmatter |
| `update_note(path, content)` | Update body (frontmatter preserved) |
| `delete_note(path)` | Permanently delete a note |
| `append_to_note(path, content)` | Append content (create if missing) |
| `manage_frontmatter(path, action, key, value?)` | Get/set/delete a frontmatter key |
| `manage_tags(path, action, tags?)` | Add/remove/list tags |
| `patch_note(path, heading, action, content)` | Insert/prepend/replace under a heading |
| `daily_note(action, content?, date?)` | Read/append daily notes (YYYY-MM-DD.md) |
| `search_and_replace(path, find, replace, use_regex?, case_sensitive?)` | Find/replace in note body |
| `get_backlinks(title)` | Find notes linking via wikilinks |
| `vault_graph()` | Full vault link graph (nodes, edges, stats) |
| `find_orphans()` | Notes with no incoming or outgoing wikilinks |
| `get_outlinks(path)` | Outgoing wikilinks from a note |
| `vault_health()` | Health score + issue report (0–100) |
| `archive_note(path, archive_dir?)` | Move note to archive folder; report backlinks |
| `search_query(query, limit?)` | Ranked Lucene-style full-text query (fields, fuzzy, ranges) |
| `rebuild_index()` | Rebuild the full-text index from all notes |
| `index_status()` | Index health: docs indexed vs. vault notes |
## Prompts (3)
| Prompt | Arguments | Description |
|---|---|---|
| `session-start` | `project?` | Review daily notes and project context before starting work |
| `session-end` | `project?` | Document accomplishments, decisions, and open questions |
| `project-checkin` | `project` (required) | Review and update a specific project's documentation |
## Resources (1)
| URI | Description |
|---|---|
| `vault://structure` | Folder/file tree of the vault as JSON |
## Architecture
```
MCP Client ←—MCP stdio—→ obsidian-mcp server → vault.py → Obsidian Vault
(Claude/Cursor/...) (22 tools + 3 prompts) (filesystem) (*.md + YAML)
+ 1 resource │
policies.py ←——————┘ (write log, raw lock,
wiki index, link check)
search_index.py → Whoosh index (BM25)
```
**Design decisions:**
- **Filesystem-native** instead of REST API: headless, containerizable, no app dependency
- **Path traversal protection** at all entry points (`_resolve()`, `_resolve_folder()`)
- **Singleton pattern** for Vault instance (testable via `set_vault()`)
- **Pydantic BaseModel** return types for `outputSchema` on all 22 tools
## Development
```bash
pip install -e ".[dev]"
pytest tests/ # 167 tests, all passing
```
CI runs on Python 3.10, 3.11, and 3.12 with `pytest` + `ruff`.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues