Skip to content

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 22 May 10:31
8b6e1c1

obsidian-mcp-rs v0.2.0

Vault-wide operations now run in parallel, frontmatter parsing moved to a real YAML library, and the crate ships with a benchmark suite. This release also carries the security fixes made during the internal refactor. Same public MCP API and config-file output as v0.1.6.

⚠️ Behaviour changes

  • .gitignore / hidden files are now respected. Vault walks (search-vault, rename-tag) moved from walkdir to the ignore crate, so gitignored and hidden notes are skipped — including by rename-tag.
  • Strict frontmatter parsing. tags: is now parsed with serde_yml instead of a hand-rolled scanner. A note whose frontmatter body is invalid YAML yields no tags (no best-effort line scraping), and non-string tag values (e.g. tags: [2024]) are ignored. Well-formed vaults are unaffected.

🚀 Performance

Vault walks now process files in parallel with rayon. Measured with the new criterion suite on a 2000-note synthetic vault (Apple Silicon, 10 logical cores) vs. the same code pinned to one thread (RAYON_NUM_THREADS=1):

Operation Serial (1 thread) Parallel Speedup
Content search (2000 notes) 52.8 ms 26.3 ms ~2.0×
Tag search (2000 notes) 45.6 ms 24.4 ms ~1.9×
Tag rename (500 notes) 84.3 ms 60.0 ms ~1.4×
  • Case-insensitive content search now lowercases each file once instead of once per line.
  • Single-note operations (read-note, create-note, edit-note, …) touch one file and are unaffected.

🔒 Security

  • Path traversal in add-tags / remove-tags. The v0.1.6 sandbox routed every other path-bearing tool through safe_join, but the two tag tools still used a bare root.join(file) per entry — a crafted files: ["../../../etc/hosts"] (or any absolute path) could read/overwrite files anywhere the server process could reach. Both tools now resolve every entry through safe_join. Regression tests added.

🐛 Fixed

  • Frontmatter terminator false-positives — the closing --- was located with find("\n---"), which also matched \n----, \n---foo, etc., splitting frontmatter at the wrong byte and corrupting the body on later writes. A new find_closing_fm requires --- to stand alone on a line.
  • Inline-tag rewrites corrupted overlapping tags — renaming foo also clobbered #foobar/#foo-extra. A new replace_inline_tag enforces a right-boundary check.
  • Vault basename collisions silently shadowed earlier paths — colliding basenames are now disambiguated as <name>-2, <name>-3, … with a warning.

✨ Added

  • move-note prunes empty source folders. When a move leaves the source directory empty it is removed (best-effort — a failed cleanup never fails the move; the vault root is never deleted).
  • Benchmark suite (benches/vault_bench.rs) covering content/tag search and tag rename; CI compiles it via cargo bench --no-run so it can't bitrot.
  • CLAUDE.md onboarding notes; Russian README (README.ru.md) synced.

🔧 Internal

  • Split the 1700-line src/vault.rs into a src/vault/ module (mod orchestrator + path, frontmatter, tags, search, walk).
  • Reworked install/writer.rs around a ConfigBackend trait — adding a JSON-shaped client is now one match arm.
  • Split the crate into a library (src/lib.rs) + thin binary (src/main.rs) so benches and tests can link the domain logic; cargo test --lib now works.
  • Removed the unused regex dependency and dead code; the build is warning-clean.

📦 Install

npx obsidian-mcp-rs install

Full changelog: v0.1.6...v0.2.0