{
  "markdown": "# freshvault\n\n[![CI](https://github.com/KIOKO-LAB/freshvault/actions/workflows/ci.yml/badge.svg)](https://github.com/KIOKO-LAB/freshvault/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/freshvault)](https://www.npmjs.com/package/freshvault)\n[![MCP Registry](https://img.shields.io/badge/MCP_registry-io.github.KIOKO--LAB%2Ffreshvault-blue)](https://registry.modelcontextprotocol.io/v0/servers?search=freshvault)\n![node](https://img.shields.io/node/v/freshvault)\n\n[한국어](./README.ko.md) · [日本語](./README.ja.md)\n\n**Your Obsidian vault is Claude's memory. Always fresh, always local.**\n\nEvery other vault-search MCP makes you re-run an index command, babysit a watch terminal, or click \"Update Index\" in a web UI. freshvault watches your vault from *inside* the MCP server: edit a note, and Claude sees it seconds later. Automatically. Forever.\n\n![freshvault demo: save a note, the watcher reindexes it automatically, semantic search finds it seconds later](https://raw.githubusercontent.com/KIOKO-LAB/freshvault/main/docs/demo.gif)\n\n- 🔄 **Never reindex** — the file watcher lives in the server process; boot catch-up absorbs offline edits\n- 🔒 **100% local** — embeddings via Ollama (`bge-m3`), your notes never leave your machine\n- 🌏 **Multilingual by default** — `bge-m3` handles Korean, Japanese, and 100+ languages that English-only defaults fail on\n- 🎯 **Benchmark-driven retrieval** — unconditional BM25 fusion wrecked Korean paraphrase queries (82.5% → 47.5% top-1), so it's off; pure dense missed identifier queries outright (57.1%), so it's not gone either. Lexical scoring is **gated on code-like query tokens**: paraphrase 82.5% → 82.5% (identical), identifiers 57.1% → 100% — [receipts](docs/ko-bench.md)\n- 🪶 **No vector DB, no Docker, no Python** — JSON metadata + a Float32 sidecar, plain Node, source you can read in one sitting\n- 🧠 **Chunking that respects sentences** — YAML frontmatter stripped, splits on paragraph/sentence boundaries (CJK-aware)\n\n## Install\n\nPrerequisites: [Node 20+](https://nodejs.org) and [Ollama](https://ollama.com).\n\n```bash\nnpx -y freshvault setup\n```\n\nThat's it. The wizard detects your Obsidian vault, pulls the embedding model, builds the index, and registers with Claude Code. There is no step 2, and there is never a step 2: no `index` command to re-run, no `watch` terminal, no background service.\n\n<details>\n<summary>Manual install (Claude Desktop / Cursor / Windsurf)</summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"freshvault\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"freshvault\", \"serve\"],\n      \"env\": { \"FRESHVAULT_VAULT\": \"/absolute/path/to/your/vault\" }\n    }\n  }\n}\n```\n\nClaude Code one-liner:\n\n```bash\nclaude mcp add freshvault -s user -- npx -y freshvault serve\n```\n\n</details>\n\n## Use\n\nJust ask Claude about your notes:\n\n> \"Search my notes for what I wrote about cache eviction strategies\"\n\nThree tools, all read-only:\n\n| tool | what it does |\n|---|---|\n| `search_notes` | semantic search + optional `folder` / `tags` / `modified_after` / `modified_before` scoping; exact-title lookups boosted; at most 2 chunks per note so one long note can't own the results (`max_per_file`) |\n| `get_note_context` | full note + its backlinks/outlinks from the vault link graph (path-traversal safe) |\n| `index_status` | freshness report: notes/chunks, excluded count, last sync, watcher state |\n\nScoped queries competitors gate behind settings or paywalls work per-query here:\n\n> \"Search my notes tagged #project modified after June for the budget discussion\"\n\n## How it works\n\n```\nObsidian vault ──fs.watch──▶ freshvault MCP server ──search_notes──▶ Claude\n   (.md files)               (chunks → bge-m3 embeddings              (generation)\n                              → one JSON index, incremental)\n```\n\n- **Incremental**: only changed/deleted notes are re-embedded (mtime+size diff), debounced 4s\n- **Safety net**: a 60s mtime sweep catches events the watcher misses (network drives, atomic-rename editors)\n- **Multi-client safe**: first server process becomes the writer (heartbeated lock); others are readers that hot-reload and promote themselves if the writer dies\n- **Transactional**: an embedding-server outage mid-index can never lose or corrupt notes\n- **Scale**: vectors live in a packed Float32 sidecar (fast startup, compact); brute-force cosine over thousands of chunks is milliseconds. Honest note: search is still linear — sub-100ms into tens of thousands of chunks, but this is not a vector DB replacement for huge corpora\n\n### Multiple vaults\n\nRegister one server per vault — index files are kept per-vault automatically:\n\n```bash\nclaude mcp add work-vault -s user -e FRESHVAULT_VAULT=/path/to/work -- npx -y freshvault serve\nclaude mcp add personal-vault -s user -e FRESHVAULT_VAULT=/path/to/personal -- npx -y freshvault serve\n```\n\n### Other embedding servers (LM Studio, LiteLLM, OpenAI-compatible)\n\n```bash\nFRESHVAULT_EMBED_API=openai FRESHVAULT_EMBED_URL=http://localhost:1234 npx -y freshvault serve\n```\n\nAnything speaking `/v1/embeddings` works; `FRESHVAULT_EMBED_KEY` for authenticated endpoints (never written to the config file).\n\n## Configuration\n\nEverything works with zero config after `setup`. Override when needed:\n\n| Flag | Env | Default |\n|---|---|---|\n| `--vault` | `FRESHVAULT_VAULT` | from `setup` |\n| `--model` | `FRESHVAULT_MODEL` | `bge-m3` |\n| `--ollama-url` | `FRESHVAULT_OLLAMA_URL` | `http://localhost:11434` |\n| `--data` | `FRESHVAULT_DATA` | platform data dir |\n| — | `FRESHVAULT_EMBED_API` | `ollama` (or `openai`) |\n| — | `FRESHVAULT_EMBED_URL` | `http://localhost:1234` (openai mode) |\n| — | `FRESHVAULT_EMBED_KEY` | none (openai mode, optional) |\n| — | `FRESHVAULT_IGNORE` | none — e.g. `Templates/,Daily/**` (or `ignore: []` in config) |\n| — | `FRESHVAULT_LEXICAL_GATE` | `on` — lexical scoring for code-like query tokens; `off` for pure dense (or `lexicalGate: false` in config) |\n\nCommands: `setup` · `serve` (default) · `index` (manual escape hatch) · `status`\n\n### Ignore patterns\n\nPatterns follow **gitignore** semantics, matched against vault-relative paths:\n\n| Pattern | Excludes |\n|---|---|\n| `Templates/` | any `Templates` directory at any depth — `Templates/`, `work/Templates/` |\n| `/Templates` | only the vault-root `Templates/` |\n| `Daily/**` | everything under the **root-level** `Daily/` only — the internal slash anchors it |\n| `**/Daily/**` | everything under any `Daily/`, at any depth |\n| `*.excalidraw.md` | that filename at any depth |\n| `work/wip.md` | exactly that path, relative to the vault root |\n\n- A slash **anywhere but the end** anchors the pattern to the vault root. A trailing slash only means \"directory\" — it does **not** anchor.\n- `*` and `?` never cross a `/`. `**` crosses directories only as a whole path segment (`**/x`, `x/**`, `x/**/y`); glued inside a segment it collapses to a single `*`, as in git.\n- Matches end on a segment boundary — `Templates` never matches `TemplatesOld`.\n- `!` negation and character classes are **not** supported; such patterns match nothing.\n- `FRESHVAULT_IGNORE` is comma-separated, so a pattern cannot contain a comma.\n\n> **Behaviour change since v0.3.0.** v0.3.0 treated a *trailing* slash as a root anchor, so `Templates/` excluded only the vault-root `Templates/` and silently indexed every nested one — no warning, just a quietly polluted index. Patterns shaped like `Templates/`, `/Templates`, `**/x` and `a/**/b` now exclude **more** than before (`/Templates` previously matched nothing at all). Conversely, `**` glued inside a segment (`Daily**md`) no longer crosses `/`. Run `freshvault status` after upgrading and compare the excluded count if it matters to you.\n\n## Benchmark\n\nTwo retrieval micro-benchmarks ship in-repo (`node scripts/bench.mjs`): **ko-bench** (30 Korean notes, 40 paraphrase queries) and **id-bench** (38 technical notes, 14 identifier queries). Both run under three scoring modes — dense, hybrid-always, and the shipped gated mode — and the script checks the no-regression contract itself. Results and the bge-m3-ko (85.0% top-1, 634MB) import guide in [docs/ko-bench.md](docs/ko-bench.md).\n\n## Roadmap\n\n- MCPB bundle for one-click Claude Desktop install\n- Reranking pass for large vaults\n- PDF text extraction at index time\n\n## License\n\nMIT © Kioko Lab\n",
  "bytes": 8278,
  "sha": "771d4a234144f1c494293dc4dc13b69b47440c7620c0c272bb8f100a8a778140",
  "repo_slug": "kioko-lab/freshvault",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_kioko_lab_freshvault_cd58b0d4/readme"
}