[feature] Ship a first-class stdio MCP server so non-Agent-Skills clients can reach the vault

Author: CryptoJonesCreated Aug 2, 2026Updated Aug 2, 2026

Disclosure: I maintain omind, a separate Apache-2.0 project solving an adjacent problem (durable agent memory in a plain-Markdown Obsidian vault). I read claude-obsidian's source before filing and I'm linking omind's implementation as a worked reference for the design — not proposing you adopt its code. Per CONTRIBUTING's evidence rule, every claim below points at a specific file.

Problem

claude-obsidian is reachable only from Agent Skills hosts. bin/setup-multi-agent.sh links the portable skills into Codex, OpenCode, and Gemini; Cursor and Windsurf use workspace-local skill discovery. skills/wiki/references/mcp-setup.md covers configuring third-party MCP servers alongside the vault.

What's missing is claude-obsidian itself speaking MCP. That leaves out every MCP client that isn't an Agent Skills host — Claude Desktop, Zed, JetBrains, anything built on the Agent SDK, and any custom agent that already speaks MCP and nothing else. Those clients can't query the vault at all today, even read-only, despite the vault being plain files and the retrieval stack (scripts/retrieve.py, scripts/bm25-index.py) already being importable Python with a clean JSON contract.

The gap is distribution, not capability. retrieve.py already emits exactly the shape an MCP tool would return — chunk_id, page_path, absolute_path, bm25_score, rerank_score, snippet.

Proposed solution

Ship a stdio MCP server as an optional surface — python3 scripts/claude-obsidian.py mcp --vault PATH.

Start read-only, which sidesteps the hard part entirely:

Tool Backed by
retrieve scripts/retrieve.py (already JSON-out)
read_page transaction.read_vault_regular (bounded, symlink-safe)
lint claude_obsidian.lint_engine (already as_of-deterministic)
doctor existing readiness report

Writes, if ever added, should not get a direct tool. They should return a transaction bundle for the host to inspect and apply through the existing transaction inspect / transaction apply path, so the one-recoverable-transaction contract in skills/wiki/references/operation-transactions.md holds no matter which client is driving.

For reference, omind's equivalent is src/omind/server.py (omind node) — a stdio MCP server over the vault store, ~500 lines including paging and shutdown handling. One detail worth stealing regardless of whether you do this: it exits cleanly when the client closes stdin. Depending on the SDK's file-wrapper stdio path cost us a class of 40-minute hangs before we moved to fd readiness.

Alternatives considered

  • Status quo — skills only. Fine if Agent Skills hosts are the intended universe. But the vault is explicitly "a normal directory of Markdown," and that portability argument applies to clients as much as to files.
  • Point users at a generic filesystem/Obsidian MCP server. Works for raw reads, but loses BM25, the contextual chunks, and the vault-selection rules in claude_obsidian/paths.py — a generic server would happily treat the product checkout as a vault, which is exactly what your trust boundary is designed to prevent.
  • REST/HTTP API. More surface, needs auth, and no client asked for it.

Scope

  • A new script (scripts/<name>)
  • A new skill / agent
  • Change to existing skill
  • Change to plugin manifest / hooks / setup scripts
  • Documentation only

Additive. Nothing existing changes.

Compatibility

  • Behavior change for existing v1.x vaults? No — read-only, additive surface.
  • New opt-in (bin/setup-*.sh)? Yesbin/setup-mcp.sh, following the existing preview-then---apply pattern.
  • New dependency? Optional. The MCP Python SDK would be needed only when the server is actually launched, so the portable core keeps its stdlib-only posture. A hand-rolled JSON-RPC-over-stdio loop is also viable if a hard no-dependency rule applies — the protocol surface for four read-only tools is small.

Testing

Hermetic and offline. Drive the server in-process over a pipe pair against a tmp_path fixture vault, then assert on the JSON-RPC exchange: initialize handshake, tools/list matching the declared contract, tools/call for each tool, and clean exit on stdin EOF. No network, no model server — retrieval already degrades to BM25-only without Ollama, and retrieve.py already exits 10 when the index is missing, which the tests can pin.

Worth adding a case for vault-selection refusal: pointing the server at the product checkout must fail closed, same as every other command.

Additional context

Happy to be told this is deliberately out of scope — the skills-first distribution model is a coherent choice and I may be reading a decision as an omission.

Source: AgriciDaniel/claude-obsidian