The engineering here is unusually explicit about failure. Every write stages bytes in a temporary inode, fsyncs, and only then publishes: creation uses a kernel-atomic hard link that refuses to clobber, and the soft delete uses a single non-replacing rename. Where the filesystem cannot do that, the operation refuses with a named error rather than degrading to something that can overwrite. The system checks are the same story — an old kernel or an old pgvector makes it exit at startup and say which one, instead of quietly returning worse search results. The requirements list is long, and it is long on purpose.
A self-hosted server that indexes an Obsidian vault into PostgreSQL with pgvector and serves it over MCP. Agents read what you read, follow the same wikilinks, and pick up your folder layout, frontmatter schema and tag conventions on the first call.
- 25 tools across five families. Search: `keyword_search` over PostgreSQL tsvector, `semantic_search` via pgvector, `list_notes`, `get_recent`, `get_tags`, and `get_vault_guide` which serves your vault's own CLAUDE.md live
- Read and write: `read_note` bounded by MAX_READ_RESPONSE_CHARS with `section` for one heading and `offset` to continue; `create_note` refuses overwrite; `edit_note` has four exclusive modes — full replace, append, find/replace and section — with `dry_run=True` returning a unified diff
- `move_note` optionally rewrites incoming `[[Old]]`, `[[Old|alias]]` and `![[Old]]` references; `delete_note` soft-deletes into `.trash` by default; `set_frontmatter` mutates YAML leaving the body byte-identical
- Non-markdown files get peers rather than overloads: `read_file` returns text, an inline image block, or base64; `write_file`, `list_files` and `delete_file` round it out — all sharing the path-traversal guard that excludes dot-directories
- File transfer closes the gap MCP cannot: `request_upload` mints a single-use link bound to one destination path, `check_upload` reports its state, `request_download` mints a save link, and `import_from_url` fetches a public https asset under a deny policy that blocks private, loopback, link-local and metadata addresses at every redirect
- Wikilink graph: `get_backlinks`, `get_links` with resolved and dangling entries, `get_neighborhood` as an undirected BFS, `find_related` by averaged chunk embeddings, and `find_orphans`
- API keys carry `read` or `readwrite` scopes and write tools refuse on a read-only key; OAuth 2.0 with PKCE covers ChatGPT, Claude Desktop and claude.ai; every call is logged with name, params, duration and calling credential
Docker and Docker Compose on Linux, kernel 5.6 or newer — every directory below the vault root is opened with a single openat2 call, so the kernel proves a write stayed inside the vault, and there is no fallback. Kernel 5.8 is needed for the file-transfer tools; below that they refuse and everything else works. PostgreSQL 16 with pgvector 0.8.0 or newer, and either an Ollama instance running bge-m3 or an OpenAI API key. The filesystem must be case-sensitive and support hard links and RENAME_NOREPLACE. macOS and Windows hosts are out of scope.
One command plus a key — npx -y mcp-remote https://obsidian-mcp.<your-domain>/mcp, then supply credentials
