v0.2.0
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 fromwalkdirto theignorecrate, so gitignored and hidden notes are skipped — including byrename-tag.- Strict frontmatter parsing.
tags:is now parsed withserde_ymlinstead 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 throughsafe_join, but the two tag tools still used a bareroot.join(file)per entry — a craftedfiles: ["../../../etc/hosts"](or any absolute path) could read/overwrite files anywhere the server process could reach. Both tools now resolve every entry throughsafe_join. Regression tests added.
🐛 Fixed
- Frontmatter terminator false-positives — the closing
---was located withfind("\n---"), which also matched\n----,\n---foo, etc., splitting frontmatter at the wrong byte and corrupting the body on later writes. A newfind_closing_fmrequires---to stand alone on a line. - Inline-tag rewrites corrupted overlapping tags — renaming
fooalso clobbered#foobar/#foo-extra. A newreplace_inline_tagenforces 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-noteprunes 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 viacargo bench --no-runso it can't bitrot. CLAUDE.mdonboarding notes; Russian README (README.ru.md) synced.
🔧 Internal
- Split the 1700-line
src/vault.rsinto asrc/vault/module (modorchestrator +path,frontmatter,tags,search,walk). - Reworked
install/writer.rsaround aConfigBackendtrait — 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 --libnow works. - Removed the unused
regexdependency and dead code; the build is warning-clean.
📦 Install
npx obsidian-mcp-rs installFull changelog: v0.1.6...v0.2.0