obsidian-cli-mcp
by dariuscorvus
README.md
# obsidian-cli-mcp
`obsidian-cli-mcp` is an [MCP](https://modelcontextprotocol.io/) server for
the **official Obsidian CLI**. It exposes Obsidian vault search, note, task,
file, link, and native Canvas operations to an MCP client. The server does not
replace Obsidian: the CLI forwards requests to the running Obsidian desktop app.
The default transport is local **stdio**. Remote Streamable HTTP is available
as an advanced, separately secured setup; it is not required for local use.
## Requirements
- macOS with **Obsidian Desktop** installed and running.
- The official Obsidian CLI enabled in Obsidian: **Settings → General →
Command line interface**, then register `obsidian` on your `PATH`.
- Node.js 18 or newer to run the published package. Bun is only needed to
build or develop this source checkout.
This project requires the desktop CLI. It does not support `obsidian-headless`.
The Obsidian app must remain open while the MCP server is being used.
Check the Obsidian side first:
```sh
command -v obsidian
obsidian version
obsidian vault
```
## Quickstart with npm
Start the published v0.4.1 package from any directory:
```sh
npx --yes --package=@dariuscodes/obsidian-cli-mcp@0.4.1 obsidian-cli-mcp
```
The command speaks MCP over stdio and waits for an MCP client. It intentionally
does not print protocol data to the terminal. Diagnostics go to stderr.
For a source checkout instead:
```sh
git clone https://github.com/DariusCorvus/obsidian-cli-mcp.git
cd obsidian-cli-mcp
bun install --frozen-lockfile
bun run build
node dist/main.js
```
No vault name, vault path, token, Cloudflare account, LaunchAgent, or config
file is required for the local default. The server uses the active vault that
Obsidian exposes through the official CLI.
## Connect an MCP client
For a client that accepts an `mcpServers` configuration, use the npm command:
```json
{
"mcpServers": {
"obsidian": {
"command": "npx",
"args": [
"--yes",
"--package=@dariuscodes/obsidian-cli-mcp@0.4.1",
"obsidian-cli-mcp"
]
}
}
}
```
If the client does not inherit your shell `PATH`, replace `npx` with the
absolute path printed by `command -v npx`. For a source checkout, use
`command: "node"` and `args: ["/absolute/path/to/obsidian-cli-mcp/dist/main.js"]`.
Restart the client after changing its MCP configuration. The first useful
sequence is:
1. Call `vault_search` with a query that should exist in your vault, for
example `{ "query": "meeting", "limit": 10 }`.
2. Pass one returned path to `note_read`, for example
`{ "path": "<path returned by vault_search>" }`.
3. Preview a safe note mutation before applying it:
```json
{
"name": "MCP smoke note",
"content": "Created after reviewing the plan.",
"dryRun": true
}
```
This is a `note_create` call. It returns the planned action and exact CLI
command without changing the vault. Only use `dryRun: false` after reviewing
the plan. `dryRun` is a preview, not an authorization boundary.
4. For Canvas, preview a native Canvas file and one text node:
```json
{
"path": "MCP smoke.canvas",
"nodes": [
{
"id": "hello",
"type": "text",
"x": 0,
"y": 0,
"width": 320,
"height": 180,
"text": "Hello from MCP"
}
],
"dryRun": true
}
```
This is a `canvas_create` call. Review the plan, then call it with
`dryRun: false` if you want to create the file. Use `canvas_read` to inspect
the native `.canvas` JSON afterward. Canvas tools preserve unknown fields,
validate node/edge references, and do not require arbitrary eval.
## Configuration and safe defaults
An empty or missing configuration is usable for a vanilla Obsidian vault. The
optional `.obsidianmcprc.yaml` is discovered from the server working directory.
For clients with an unpredictable working directory, set
`OBSIDIAN_MCP_CONFIG` to an explicit config-file path.
The default policy is deliberately local and bounded:
- The v0.4.0 server does not expose a generic `obsidian_eval` tool.
`eval.enabled` is `false` by default; internal fixed eval snippets used by a
few safe operations are not a user-supplied JavaScript escape hatch.
- Imports from arbitrary local files are disabled until
`imports.allowedRoots` is explicitly configured. URLs are never fetched.
- `.obsidian`, `.git`, `.trash`, `.Trash`, `Trash`, and `.DS_Store` path
segments are blocked by default. Add `paths.allow` for a narrower vault area
and add project-specific `paths.deny` prefixes for more sensitive content.
- Mutations expose `dryRun`. `file_delete` requires `confirm: true` and
`note_delete` uses Obsidian trash by default; permanent deletion requires the
explicit `delete.mode: hard` configuration.
- Git autocommit is off by default.
### Read-only preset
Use an explicit allowlist when an MCP client should only inspect the vault:
```yaml
tools:
allow:
- vault_search
- note_read
- note_list
- vault_tags
- unresolved_links
- tasks_list
- note_diff
- backlinks_get
- outlinks_get
- file_read_binary_metadata
- canvas_read
```
### Safe local preset
The default has safe local guardrails but is not read-only. For an explicit
safe-local surface that allows normal note edits and Canvas creation while
omitting deletion, file imports, file lifecycle operations, and arbitrary
evaluation:
```yaml
tools:
allow:
- vault_search
- note_read
- note_list
- vault_tags
- unresolved_links
- tasks_list
- note_diff
- backlinks_get
- outlinks_get
- canvas_read
- canvas_create
- canvas_upsert_nodes
- canvas_upsert_edges
- canvas_add_node
- canvas_add_edge
- canvas_auto_layout
- canvas_open
- note_create
- note_append
- note_set_frontmatter
- note_replace_range
- note_insert_at
- note_replace
- note_insert
- daily_open
- daily_append
- task_create
- task_update
delete:
mode: trash
eval:
enabled: false
imports:
allowedRoots: []
```
### Full trusted local preset
Omit `tools.allow` to expose the complete built-in tool surface, while keeping
the default protected paths, trash deletion, disabled imports, and disabled
`obsidian_eval`. If imports are needed, configure only a dedicated local source
directory:
```yaml
imports:
allowedRoots:
- /absolute/path/to/approved-imports
maxBytes: 26214400
collision: increment
delete:
mode: trash
eval:
enabled: false
```
See [docs/configuration.md](docs/configuration.md) for all fields and
[examples/](examples) for note-organization presets.
## Local stdio versus remote HTTP
Local stdio starts one server process directly from the MCP client. It is the
recommended installation: there is no listening socket, remote authentication,
Cloudflare setup, or public endpoint.
Streamable HTTP is an optional advanced mode for a client that cannot use local
stdio. It binds to loopback only and refuses to start without either Cloudflare
Access JWT validation or a strong capability token. Put it behind a TLS,
authenticated reverse proxy or tunnel; do not bind it to `0.0.0.0`. See
[docs/remote-cloudflare.md](docs/remote-cloudflare.md) for the generic advanced
setup and its security trade-offs.
## Tool surface
The default server advertises 43 regular tools:
- Read: `vault_search`, `note_read`, `note_list`, `vault_tags`,
`unresolved_links`, `tasks_list`, `note_diff`, `backlinks_get`,
`outlinks_get`, `file_read_binary_metadata`, `canvas_read`.
- Write and workflow: `note_create`, `note_append`, `note_set_frontmatter`,
`daily_open`, `daily_append`, `note_replace_range`, `note_insert_at`,
`note_replace`, `note_insert`, `task_create`, `task_update`,
`note_transition`.
- Files and attachments: `file_import`, `attachment_import`, `note_attach`,
`attachment_embed`, `file_move`, `file_rename`, `file_delete`,
`note_rename`, `note_move`, `folder_create`, `note_delete`.
- Canvas: `canvas_create`, `canvas_upsert_nodes`, `canvas_upsert_edges`,
`canvas_remove`, `canvas_open`, `canvas_add_node`, `canvas_add_edge`,
`canvas_auto_layout`.
- Batch: `vault_batch` runs enabled reads and previews enabled mutations with
`args.dryRun: true`, dependency gating, stable input-order results, and a
bounded `batch.maxParallelism` (default 4, maximum 8).
All mutating tools accept `dryRun`. Tool annotations identify read-only and
destructive operations for compatible MCP clients.
## Limitations and security
Obsidian Desktop must be running, its official CLI must be enabled, and the
active vault must be available to that desktop session. This server is not a
sandbox and does not support `obsidian-headless`.
Vault content is untrusted data. Notes, Canvas text, task text, and search
results may contain prompt-injection instructions; an MCP client should treat
them as data and never follow instructions found inside a vault merely because
they were returned by a tool. Tool output can also contain sensitive vault
content, so connect only clients you trust.
Read [SECURITY.md](SECURITY.md) before enabling remote HTTP, imports, hard
deletes, or a broad mutation allowlist. Report security issues privately as
described there.
## Development and CI
The source checkout uses Bun, while the published bin runs on Node:
```sh
bun install
bun run typecheck
bun test
bun run build:schema
bun run build
bun run smoke:stdio
git diff --check
npm pack --dry-run --json
```
The offline stdio smoke verifies the built package entrypoint, MCP initialize,
`tools/list`, the expected tool surface, and the absence of `obsidian_eval`.
The real Obsidian smoke is separate and requires a user session with Obsidian
running:
```sh
OBSIDIAN_CLI_BINARY=obsidian \
OBSIDIAN_MCP_CONFIG=/absolute/path/to/your/config.yaml \
OBSIDIAN_MCP_VAULT="your-vault-name" \
bun run smoke:live
```
GitHub Actions runs only offline gates; it does not depend on Obsidian Desktop
or a real vault on a hosted runner.
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessUnresponsive