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.
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/
├── 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- uv — that's it, uv handles Python and dependencies
mkdir -p ~/.config/obsidian-mcp
curl -o ~/.config/obsidian-mcp/config.toml https://github.com/fufic123/obsidian-mcp/main/config.toml.exampleEdit ~/.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 = 20Add more namespaces as needed — see Configuration for the full format.
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
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_contextConfig is loaded in this order (first found wins):
$OBSIDIAN_MCP_CONFIGenv var~/.config/obsidian-mcp/config.toml./config.toml(for development)
Memory notes come in three types (highlight, core, conversation). A single
set of CRUD tools dispatches by the type field on the note payload.
| Tool | Description | When to call |
|---|---|---|
create_note(note) |
Create a highlight / core / conversation. Note self-identifies via type. |
New insight, user preference, or end-of-session summary |
read_note(note_type, name, project?) |
Load one note's full content | Need full details of a specific note |
list_notes(note_type, project?) |
List notes of a type, optionally by project | Browsing what exists |
update_note(note_type, name, updates, project?) |
Patch fields — only provided keys change. Renames / project moves rewrite the file. | Fixing or refining a saved note |
delete_note(note_type, name, project?) |
Permanently remove a note | Cleaning up stale memory |
get_core_context() |
Load all core notes inline | Start of every conversation |
get_relevant_context(query, project?) |
Search highlights and summaries by frontmatter | When you need context on a topic |
rebuild_index() |
Regenerate MEMORY.md and sub-indexes | After bulk edits outside the MCP |
create_note payload shape by type:
- highlight —
{"type": "highlight", "name", "description", "content", "tags"?, "project"?} - core —
{"type": "core", "name", "description", "content"} - conversation —
{"type": "conversation", "title", "key_points", "full_content", "project"?, "tags"?}
| 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 |
| 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 |
| 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 |
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 →
create_note({type: "core", ...}) or update_note("core", name, updates)
- When an important insight, decision, or knowledge emerges →
create_note({type: "highlight", ..., project?})
At conversation end:
- If the conversation was meaningful →
create_note({type: "conversation", ..., project?})
| 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
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.