# 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": "" }`. 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)