vault-mcp
Remote MCP server for Obsidian vaults, enabling secure access to markdown notes from Claude clients without the Obsidian desktop app. Provides tools for searching, reading, listing recent notes, resolving daily notes, and creating/appending notes via HTTPS.
README
vault-mcp
[!WARNING] Work in progress. The security model and core workflows are implemented and tested, but the project has not reached a stable release. Expect breaking configuration changes, review the threat model before deployment, and do not treat the current
mainbranch as production-ready yet.
A remote MCP server for your Obsidian vault. Add it once as a connector in Claude's settings and your notes become available in any conversation — on Claude web, iOS, Android or Desktop — with no Obsidian desktop app required anywhere. The server reads the markdown files directly from disk on a small VPS that stays in sync with your vault.
Your vault stays a folder of markdown files. No database, no proprietary index, no custom format. If this project disappears tomorrow, your notes are exactly where they were.
How it works
Three zones with strictly separated responsibilities:
Claude web / iOS / Android / Desktop
│
│ MCP over HTTPS (OAuth 2.1 bearer token)
▼
┌─────────────────────────────────────┐
│ EDGE — Cloudflare Worker │
│ · OAuth 2.1 + PKCE, consent page │
│ · token storage (Workers KV) │
│ · fixed redirect_uri allowlist │
│ · forwards with x-origin-secret │
│ (never stores vault content) │
└─────────────────────────────────────┘
│
│ Cloudflare Tunnel — outbound-only
│ from the VPS; no inbound ports
▼
┌─────────────────────────────────────┐
│ ORIGIN — your VPS │
│ · mcp-server, bound to loopback │
│ answers 404 without the secret │
│ · vault-core: path validation, │
│ atomic writes, search │
│ · vault-guards: read/write │
│ sanitization, warn heuristics │
│ · ~/vault ◀─ sync client ─▶ your │
│ sync service (bidirectional) │
│ · git autocommit every 30 min │
└─────────────────────────────────────┘
The client never reaches the VPS directly. The Worker knows nothing about the vault. The origin is not reachable from the internet — it only receives traffic through the outbound tunnel, and answers 404 to anything that does not carry the shared origin secret.
The sixteen tools
| Tool | Type | What it actually does |
|---|---|---|
search_notes |
read | Case-insensitive literal substring search across markdown notes (no regex, no semantic ranking). Returns path, line number and snippet. Supports pagination (offset), folder scoping (path_prefix), tag filtering (tag) and visiting newest notes first (sort_by: mtime). Low-trust folders (imported clippings) are excluded unless explicitly included. |
read_note |
read | Full content of one note by vault-relative path, with a header reporting size and the note's version hash (for expected_hash on later edits). Output is sanitized (see below); very large notes are truncated and flagged. |
read_notes |
read | Up to 10 notes in one call; per-note errors are reported inline. |
list_recent |
read | Most recently modified notes, newest first (paths and timestamps only). |
get_vault_tree |
read | Every folder with its note count — the vault's table of contents. Structure only, never content. |
get_daily_note |
read | Resolves the daily note for a date (default today) using the vault's own settings — core Daily Notes (.obsidian/daily-notes.json) or the Periodic Notes plugin. If the note doesn't exist it returns the path it would have — it never creates it. |
create_daily_note |
write | Creates the daily note at the configured location, seeded from the configured daily-notes template ({{title}}, {{date}}, {{time}}, {{date:FORMAT}}). Idempotent: an existing note is left untouched. |
create_note |
write | Creates a new note. Fails if the note already exists — it never overwrites. Parent folders are created as needed. Writes are atomic. |
append_to_note |
write | Appends to the end of an existing note — the note must already exist (create it first), and existing content is never edited or overwritten. |
append_to_section |
write | Inserts at the end of a specific heading's section (before the next same-or-higher-level heading; code fences don't count as headings) — capture into ## 📥 Inbox without landing after a trailing dataview block. |
edit_note |
write | Exact search-and-replace edits, atomic and all-or-nothing: each old_string must match exactly once; supports expected_hash, checked again immediately before replacement, so stale edits normally fail with CONFLICT. Plain filesystems provide no portable CAS against unrelated external writers, so a narrow final race remains. |
move_note |
write | Moves/renames a note. Never overwrites the destination. Wiki-links are not rewritten. |
delete_note |
write | Moves the note to the vault's own .trash/ (same as Obsidian's "move to vault trash") — nothing is permanently erased. |
list_tasks |
read | Checkbox tasks across the vault, parsed with Obsidian Tasks plugin conventions (📅 ⏳ 🛫 ✅, priorities, 🔁). Filters by status, due-date window and folder; sorted by due date; each task reports path, line and note version hash. |
complete_task |
write | Flips [ ] to [x] and appends ✅ YYYY-MM-DD in the exact Tasks-plugin format. Recurring tasks are completed but the next occurrence is not generated. |
postpone_task |
write | Changes (or sets) a task's 📅 due date in the exact Tasks-plugin format. |
Remote images in written content are de-embedded into plain links before touching disk, and note content returned to the model is stripped of channels for invisible instructions (HTML comments, CSS-hidden elements, invisible characters, the Unicode tag block). See the threat model for why.
Non-goals — on purpose
- Single user, single vault. One instance serves one person. Multi-tenancy is a declared non-goal: it reintroduces an entire class of isolation problems that simply doesn't exist today. Each user runs their own instance.
- No Obsidian runtime. No Templater, no Dataview, no plugins, no
eval. Notes created through the server are plain markdown; plugin syntax in your templates will not be expanded. - No
delete_note, nomove_note. Deliberately absent: they turn noise into data loss, and a bad move breaks wikilinks across every synced device. The minimal tool inventory is a security control, not an oversight. - No HTTP-request tools, no shell, no JavaScript execution. Closing these channels is what keeps the worst case of a successful prompt injection at "junk in a note" instead of exfiltration.
- No semantic index / embeddings in this version. Full-text search covers most cases and adds no state to maintain.
Getting started
Follow docs/setup.md end to end: VPS bootstrap, sync client, tunnel, Worker deploy, and adding the connector in Claude's settings. There is also a local-only mode for trying the server on your own machine without any of the edge pieces.
Before hosting this, read docs/threat-model.md — you are exposing personal notes to the internet, even behind authentication, and you should understand exactly what protects them and what the residual risks are. Day-2 procedures (token revocation, secret rotation, restore from git) live in docs/operations.md.
Repository layout
apps/
mcp-server/ MCP server on the VPS; executes the tools over HTTP
auth-worker/ Cloudflare Worker at the edge; OAuth + authenticated proxy
packages/
vault-core/ reading, atomic writes, path validation, search
vault-guards/ input/output sanitization, warn-only heuristics
tool-contract/ tool schemas and descriptions, shared
infra/
scripts/ bootstrap · configure · start · doctor · autocommit
systemd/ user units for server, sync, tunnel, autocommit
tunnel/ cloudflared configuration example
docs/
setup.md step-by-step installation
threat-model.md assets, adversaries, defenses, residual risk
operations.md runbooks: revoke, rotate, restore
The architecture rationale (in Portuguese) is in ARCHITECTURE.md. The exact tool surface — names, schemas and the descriptions the model sees — lives in packages/tool-contract/src/index.ts.
Contributing
See CONTRIBUTING.md — including the dependency policy (a server with access to personal notes is a supply-chain target) and the standing answer to multi-tenancy requests.
License
MIT © Juliano Sirtori
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。