obsidian-cli-mcp
Gives Claude and other MCP clients full control of a running Obsidian vault through the official Obsidian CLI, with fast filesystem reads for bulk operations and link-safe writes via the app.
README
obsidian-cli-mcp
An MCP server that gives Claude and other MCP clients full control of a running Obsidian vault through the official Obsidian CLI (Obsidian 1.12+), with fast direct filesystem reads where correctness allows it.
Companion project to things-for-mac-mcp.
What makes this different?
Most Obsidian MCP servers either talk to a community REST plugin or read the vault folder directly. The first requires installing and trusting a plugin. The second silently breaks wikilinks the moment it moves or renames a file, because only Obsidian knows about every link, alias, and embed pointing at it.
This server routes every operation by capability:
| Typical filesystem-only MCPs | obsidian-cli-mcp | |
|---|---|---|
| Full-text search across thousands of notes | Fast | Fast (filesystem) |
| Move or rename a note | Breaks every inbound link | Link-safe (Obsidian CLI) |
| Backlinks, aliases, unresolved links | Guesswork | Obsidian's own resolver |
| Bases queries, template variables | Impossible | Runtime evaluation via the app |
| Writes land in Obsidian's index and file recovery | No | Yes |
| iCloud evicted files | Read as empty notes | Detected, read through Obsidian |
| Requires a community plugin | Sometimes | No |
The architecture mirrors its sibling project exactly:
| things-for-mac-mcp | obsidian-cli-mcp | |
|---|---|---|
| Fast reads | SQLite direct | Filesystem direct |
| Authoritative writes | AppleScript | Obsidian CLI |
| Convenience creates | URL scheme | Obsidian CLI |
The rule behind the split: bulk reads go to the filesystem because they need throughput, and anything that moves, renames, deletes, or depends on link resolution or app state goes through the CLI because it needs Obsidian's knowledge. The filesystem adapter structurally cannot mutate the vault, it exports no writing function at all.
Requirements
- macOS, Windows, or Linux desktop with Obsidian 1.12 or later
- The Obsidian CLI enabled: Obsidian, Settings, General, Command line interface
- Obsidian must be running. The CLI is a client to the app, not a standalone binary. This is desktop only, mobile is not supported.
- Node.js 18 or later
Installation
git clone https://github.com/jabaho9523/obsidian-cli-mcp.git
cd obsidian-cli-mcp
npm install
npm run build
Connect to an MCP client
Claude (Desktop / Code)
Add to claude_desktop_config.json (Claude Desktop) or run claude mcp add (Claude Code):
{
"mcpServers": {
"obsidian": {
"command": "/absolute/path/to/node",
"args": ["/absolute/path/to/obsidian-cli-mcp/dist/index.js"],
"env": {
"OBSIDIAN_VAULT": "YourVaultName"
}
}
}
}
Use the absolute path to node, not the bare word. GUI-launched apps do not inherit your shell's PATH, so "command": "node" fails silently in many clients. Find yours with which node.
Set OBSIDIAN_VAULT if you have more than one vault. The CLI otherwise targets whichever vault was focused last, which is a terrible property for automated writes. With a single vault the server pins it automatically at startup.
Configuration
| Variable | Default | Purpose |
|---|---|---|
OBSIDIAN_BIN |
/usr/local/bin/obsidian |
Path to the Obsidian CLI binary |
OBSIDIAN_VAULT |
auto-pinned if exactly one vault exists | Vault name every command targets |
OBSIDIAN_VAULT_PATH |
auto-detected via the CLI | Vault folder for the filesystem adapter |
OBSIDIAN_MCP_TIMEOUT |
20000 |
Per-command timeout in ms |
OBSIDIAN_MCP_ALLOW_DANGEROUS |
unset | Set to 1 to unlock tier 3 commands |
OBSIDIAN_MCP_READONLY |
unset | Set to 1 to reject every mutating tool |
Guardrails
Three tiers, enforced before the binary is ever spawned:
- Tier 1, free: reads, searches, and additive writes (
create_note,append_note,append_daily,set_property,update_task,capture). - Tier 2, requires
confirm: truein the tool call:delete_note,move_note,rename_note,remove_property,run_obsidian_command, and through the passthrough:history:restore,publish:*,plugin:enable/disable/reload,theme:*,snippet:*,sync,sync:restore,reload,template:insert,workspace:save/delete. Any call carrying anoverwriteorpermanentflag is escalated to tier 2 as well. - Tier 3, blocked unless the server runs with
OBSIDIAN_MCP_ALLOW_DANGEROUS=1:eval,restart,plugin:install,plugin:uninstall,plugins:restrict,devtools,dev:cdp,dev:debug,dev:mobile, anddelete_notewithpermanent: true.
An honest note on what these are. Tier 2 is a speed bump against accidental calls, not security: the calling model can set confirm: true itself. Tier 3 is a real boundary, because only whoever configures the server environment can unlock it. If you point an autonomous agent at a vault you care about, run with OBSIDIAN_MCP_READONLY=1, which rejects every mutating command before dispatch regardless of tier.
Link-safe moves and renames
The single most important rule in this project: files are never moved, renamed, or deleted through the filesystem. Obsidian updates every wikilink in the vault when it performs the operation. A plain mv does not.
Before, with Projects/Roadmap.md linked from three notes:
Weekly Review.md: Progress on [[Roadmap]] is on track.
Team Notes.md: See [[Roadmap#Q3]] for the plan.
Index.md: - [[Roadmap|2026 roadmap]]
After move_note with to: "Archive/2026 Roadmap.md":
Weekly Review.md: Progress on [[2026 Roadmap]] is on track.
Team Notes.md: See [[2026 Roadmap#Q3]] for the plan.
Index.md: - [[2026 Roadmap|2026 roadmap]]
All three links updated, including the heading anchor and the alias, because Obsidian did the move. A filesystem move would have left three broken links and no error.
Why hybrid? The performance rationale
Every CLI invocation is one full IPC round trip through the running Obsidian app. That is correct but slow: reading 2,000 notes via obsidian read is 2,000 round trips, minutes of wall time. Reading them from disk is one directory walk, well under a second on any SSD.
So bulk reads (search, listings, tag and property scans, exports, digests) hit the filesystem, and the CLI is reserved for what only Obsidian can answer (links, aliases, Bases, templates, app state) and for every write. To compare on your own vault, time search_notes against the passthrough obsidian_cli with ["search", "query=..."].
Troubleshooting
"Obsidian is not running." The most common failure. The CLI needs the app open and fully loaded. Start Obsidian and retry.
"Could not find the Obsidian CLI binary." Enable the CLI in Obsidian under Settings, General, Command line interface, or point OBSIDIAN_BIN at the binary.
Timeouts on the first command. A cold Obsidian start can exceed the 20s default. Raise OBSIDIAN_MCP_TIMEOUT.
Notes read as missing or the server falls back to the CLI a lot. If your vault lives in iCloud with Optimize Mac Storage on, evicted files exist only as .name.icloud stubs. The server detects these and reads them through Obsidian, which re-downloads them, instead of reporting empty notes. Bulk scans skip evicted files and say so in their output.
Writes land in the wrong vault. You have multiple vaults and no OBSIDIAN_VAULT set. The server warns about this on stderr at startup. Pin one.
Tools do not appear in the client. Check the client's MCP logs, and check the absolute node path issue above.
Staying up to date
git pull && npm install && npm run build
The server checks for updates at startup, at most once per 24 hours, caching the result in ~/.config/obsidian-cli-mcp/update-check.json. It fails silently offline and prints a single stderr line when a newer version exists.
Tools (39 total)
Read tools (18)
| Tool | Adapter | Description |
|---|---|---|
read_note |
Filesystem, CLI fallback | Read a note by wikilink-style name or exact path |
search_notes |
Filesystem | Full-text search with folder, case, context, and limit options |
list_notes |
Filesystem | List files, filtered by folder and extension |
list_folders |
Filesystem | List folders |
get_note_info |
CLI | Path, size, created and modified dates |
get_outline |
Filesystem | Heading tree with line numbers |
get_backlinks |
CLI | Inbound links, resolved by Obsidian |
get_outgoing_links |
CLI | Outbound links |
get_tags |
Filesystem | All tags with counts, frontmatter and inline |
get_properties |
Filesystem | Vault-wide frontmatter keys with counts |
read_property |
Filesystem | One frontmatter key on one note |
get_vault_info |
CLI | Vault name, path, stats |
get_recents |
CLI | Recently opened files |
list_bases |
CLI | All .base files |
query_base |
CLI | Run a Bases view query, evaluated by the app |
list_templates |
CLI | Templates in the configured folder |
read_template |
CLI | Template content, optionally with variables resolved |
get_word_count |
Filesystem | Words and characters, excluding frontmatter |
Write tools (16)
All writes go through the CLI. Every one requires an explicit file or path target, none can fall through to the currently active file.
| Tool | Guard tier | Description |
|---|---|---|
create_note |
1, 2 with overwrite |
Create a note, optionally from a template |
append_note |
1 | Append content |
prepend_note |
1 | Prepend content after frontmatter |
read_daily |
1 | Read today's daily note |
append_daily |
1 | Append to today's daily note |
prepend_daily |
1 | Prepend to today's daily note |
get_daily_path |
1 | Path of today's daily note |
set_property |
1 | Set a frontmatter property |
remove_property |
2 | Remove a frontmatter property |
move_note |
2 | Link-safe move |
rename_note |
2 | Link-safe rename |
delete_note |
2, 3 with permanent |
Delete to trash, or permanently |
list_tasks |
1 | List markdown tasks with refs |
update_task |
1 | Toggle or set task status by ref or line |
open_note |
1 | Open in the Obsidian UI, navigation only |
run_obsidian_command |
2 to execute | List or run command palette commands, including plugin commands |
run_obsidian_command is the widest door in the server: it reaches every command palette action, including those registered by community plugins. It is exposed deliberately and gated at tier 2.
Workflow tools (4)
| Tool | Description |
|---|---|
capture |
Timestamped append to today's daily note, the highest frequency operation in practice |
daily_digest |
Aggregate a date range of daily notes into one document |
export_notes |
Export a folder as JSON, Markdown, or CSV, inline or to a file outside the vault |
vault_health |
Orphans, dead ends, unresolved links, and empty notes in one report. Deliberately scoped to the link graph |
Escape hatch (1)
| Tool | Description |
|---|---|
obsidian_cli |
Run any CLI command. Takes args as a pre-split array of strings, never a shell string, so the server spawns without a shell and content cannot break out of quoting. All guard tiers apply |
MCP Resources
Client support for resources varies, Claude Desktop does not currently surface them.
| Resource | Content |
|---|---|
obsidian://vault |
Vault info |
obsidian://daily |
Today's daily note |
obsidian://tags |
All tags with counts |
obsidian://recents |
Recently opened files |
obsidian://orphans |
Notes with no inbound links |
obsidian://note/{path} |
Any note by vault-relative path |
MCP Prompts
| Prompt | Purpose |
|---|---|
daily_note_review |
Summarize a daily note, surface open tasks, suggest follow-ups |
vault_cleanup |
Walk the vault health report and propose link-safe fixes |
note_from_source |
Turn pasted material into a note using an existing template |
weekly_digest |
Summarize a week of daily notes into a digest note |
Architecture
src/
├── index.ts MCP server entry, stdio transport
├── config.ts Environment configuration
├── adapters/
│ ├── cli.ts execFile wrapper, vault injection, error contract
│ └── filesystem.ts Read-only vault access, iCloud stub detection
├── tools/
│ ├── common.ts Shared note loading with CLI fallback
│ ├── read.ts 18 read tools
│ ├── write.ts 16 write tools
│ ├── workflow.ts 4 composite tools
│ └── passthrough.ts obsidian_cli escape hatch
├── resources/
│ └── vault.ts MCP resources
├── prompts/
│ └── workflows.ts MCP prompts
└── utils/
├── guardrails.ts Tier policy, readonly allowlist
├── markdown.ts Frontmatter, headings, tags, word counts
├── output.ts Truncation at 60,000 characters
└── update-check.ts Daily update check
Tests run against a stub binary that scans its full argv and can be told to fail, hang, or emit oversized output, so the whole suite passes with no Obsidian installed:
npm test
Support
Issues and feature requests: GitHub issues.
More from the author
- things-for-mac-mcp, the sibling MCP server for Things 3
License
MIT
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
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 模型以安全和受控的方式获取实时的网络信息。