MCP server by fufic123
obsidian-mcp
MCP server that turns an Obsidian vault into persistent memory for AI models. Works with any MCP client (Claude Code, Claude Desktop, Cursor, Windsurf). Vault syncs via iCloud/Git/Obsidian Sync — same memory across all devices and clients.
How it works
AI Client (Claude, Cursor, ...)
↕ MCP protocol (stdio) : ?http
obsidian-mcp server
↕ file read/write
Obsidian vault (synced across devices)
Memory is organized in three tiers to minimize token usage:
| Tier | What | When loaded |
|------|------|-------------|
| 1 | memory/core/ — who you are, how to work with you | Always (at conversation start) |
| 2 | memory/highlights/ + conversations/summaries/ | On demand via get_relevant_context(query) |
| 3 | conversations/archive/ — full conversation text | Never automatically |
The server picks which vault to use based on your working directory ($PWD) mapped through config.toml namespaces. Different projects inside one vault are separated by project: tags in frontmatter.
Vault structure
vault/
├── memory/
│ ├── MEMORY.md ← auto-generated index (max 200 lines / 25KB)
│ ├── core/ ← persistent context about you
│ │ └── *.md
│ ├── highlights/ ← insights, decisions, knowledge
│ │ └── *.md
│ └── conversations/
│ ├── summaries/ ← Q&A callout format
│ └── archive/ ← full conversation text
├── tasks/ ← Obsidian Tasks format
└── daily/ ← daily notes
Every note has YAML frontmatter:
---
name: SSH multi-account setup
description: Configure multiple Git accounts via ~/.ssh/config
type: highlight
project: devops
tags: [ssh, git]
created: 2026-04-01
---
MEMORY.md index (auto-generated by rebuild_index()):
# Memory Index
## Core
- [Role and preferences](core/role.md) — senior engineer, Python, uv
## Highlights
- [SSH multi-account](highlights/ssh-setup.md) — hostinger + personal config
- [MCP architecture](highlights/mcp-design.md) — decisions on Obsidian MCP server
Installation
Requirements
- uv — that's it, uv handles Python and dependencies
1. Create config
mkdir -p ~/.config/obsidian-mcp
curl -o ~/.config/obsidian-mcp/config.toml https://github.com/fufic123/obsidian-mcp/main/config.toml.example
Edit ~/.config/obsidian-mcp/config.toml — set your vault paths:
[namespaces.default]
vault = "~/Documents/my-vault"
[memory]
max_index_lines = 200
max_index_bytes = 25600
frontmatter_scan_lines = 30
max_search_results = 20
Add more namespaces as needed — see Configuration for the full format.
2. Connect to your MCP client
All MCP clients use the same server config — the only difference is where the config file lives.
Create a .mcp.json file with this content:
{
"mcpServers": {
"obsidian": {
"command": "uvx",
"args": ["obsidian-mcp"],
"env": {
"OBSIDIAN_MCP_CONFIG": "/Users/username/.config/obsidian-mcp/config.toml"
}
}
}
}
OBSIDIAN_MCP_CONFIGis optional if your config lives at~/.config/obsidian-mcp/config.toml.
Where to place .mcp.json:
| Client | Path | Scope |
|--------|------|-------|
| Claude Code (global) | ~/.mcp.json | All projects |
| Claude Code (project) | .mcp.json in project root | Single project |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json | All conversations |
| Cursor | Settings → MCP Servers → Add (paste the server object) | Workspace or global |
| Windsurf | MCP settings (same format) | Workspace or global |
Claude Code notes:
.mcp.jsonin a project directory applies only when Claude Code is launched from that directory~/.mcp.jsonapplies globally to all projects — put it here if you want memory everywhere- After adding/changing
.mcp.json, restart Claude Code — MCP servers are loaded at startup - On first launch Claude Code will ask to approve the new server — confirm it
Configuration
Each namespace has a human-readable name, a match prefix for $PWD resolution, and a vault path. default is the fallback — no match needed.
[namespaces.personal]
match = "~/Documents/personal" # matches any $PWD starting with this
vault = "~/Documents/personal/obsidian"
[namespaces.work]
match = "~/work"
vault = "~/vaults/work"
[namespaces.default] # fallback — no match needed
vault = "~/Documents/personal/obsidian"
[memory]
max_index_lines = 200 # MEMORY.md max lines (truncated beyond this)
max_index_bytes = 25600 # MEMORY.md max bytes — 25KB (dual truncation)
frontmatter_scan_lines = 30 # lines read per file for frontmatter parsing
max_search_results = 20 # max results from get_relevant_context
Config is loaded in this order (first found wins):
$OBSIDIAN_MCP_CONFIGenv var~/.config/obsidian-mcp/config.toml./config.toml(for development)
MCP tools
Memory
| Tool | Description | When to call |
|------|-------------|-------------|
| get_core_context() | Load all core notes | Start of every conversation |
| get_relevant_context(query, project?) | Search highlights and summaries by frontmatter | When you need context on a topic |
| save_highlight(name, description, content, tags?, project?) | Save an insight or decision | Important realization or decision made |
| save_core(name, description, content) | Save persistent user context | User expresses a preference or identity |
| save_conversation(title, key_points, full_content, project?) | Save conversation summary + archive | End of a meaningful conversation |
| rebuild_index() | Regenerate MEMORY.md from all files | After bulk edits or periodically |
Knowledge
| Tool | Description | When to call |
|------|-------------|-------------|
| search_vault(query) | Full-text search across all vault files | Looking for specific content |
| get_note(path) | Read a note by path | Need full content of a specific note |
Tasks
| Tool | Description | When to call |
|------|-------------|-------------|
| create_task(title, due?, project?) | Create an Obsidian Tasks entry | User mentions a todo |
| list_tasks(project?) | List open tasks | Reviewing what needs to be done |
Productivity
| Tool | Description | When to call |
|------|-------------|-------------|
| create_daily_note(content?) | Create/append to today's daily note | Daily logging |
| generate_moc(folder) | Generate Map of Content for a folder | Organizing a folder |
Recommended system prompt
Add to your CLAUDE.md or client system prompt:
You have access to an Obsidian vault via MCP tools for persistent memory.
At conversation start:
1. Call get_core_context() to load who the user is and how to work with them.
2. Call get_relevant_context(topic) for the current conversation topic.
During conversation:
- When the user expresses a preference or tells you about themselves → save_core()
- When an important insight, decision, or knowledge emerges → save_highlight()
At conversation end:
- If the conversation was meaningful → save_conversation()
Recommended Obsidian plugins
| Plugin | Why |
|--------|-----|
| Dataview | Query notes by frontmatter — filter highlights by project, type, date |
| Tasks | Render and manage tasks created by create_task() |
| Templater | Templates for notes you create manually — keeps frontmatter consistent |
| Calendar | Navigate daily notes created by create_daily_note() |
| Git | Auto-commit vault — version history + sync across machines without Obsidian Sync |
| Minimal Theme | Renders callout format used by save_conversation() cleanly |
Dataview example — show all highlights for a project:
TABLE description, file.mtime AS updated
FROM "memory/highlights"
WHERE project = "my-project"
SORT file.mtime DESC
Development
git clone https://github.com/fufic123/obsidian-mcp
cd obsidian-mcp
uv sync
# Run tests
uv run pytest
# Type checking
uv run mypy app/
# Lint
uv run ruff check app/
Project structure:
app/
├── domain/ ← models, interfaces (ABC), exceptions
├── adapters/ ← file system implementations
├── services/ ← business logic
├── tools/ ← thin MCP wrappers
└── main.py ← DI wiring, FastMCP init
Dependencies flow downward only: tools → services → adapters → domain.