@oomkapwn/enquire-mcp 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,108 @@
2
2
 
3
3
  All notable changes to this project will be documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
4
 
5
+ ## [2.10.0] — 2026-05-08
6
+
7
+ **Sprint 10 — OCR for image-only / scanned PDFs.** Closes the v2.7-v2.8-v2.9 PDF retrieval story. v2.7.0 added text-extraction tools; v2.8.0 blended PDF chunks into hybrid search; v2.9.0 added cross-encoder reranking. v2.10.0 makes the **scanned / camera-captured** PDFs in your vault searchable too — Tesseract.js OCR over each page bitmap.
8
+
9
+ ### Added — `obsidian_ocr_pdf`
10
+
11
+ Runs Tesseract OCR over each page of an image-only / scanned PDF and returns the same shape as `obsidian_read_pdf` plus a per-page `confidence` score (0-100) and a doc-level `mean_confidence`. Use this when `obsidian_read_pdf` returns `has_text: false` (typical for scans, photographed paper, image-only PDFs).
12
+
13
+ - **Multilingual** via `lang` (default `'eng'`; multi-lang via `'+'`, e.g. `'eng+rus'` for English+Russian mixed scans). Trained-data files for each language download on first use into Tesseract's local cache (~10 MB per language).
14
+ - **Optional `pages` range** for partial OCR of long docs — OCR is the slowest step in the pipeline (~1-2s per page on M1 CPU), so a 100-page paper takes minutes.
15
+ - **Optional `scale`** (DPI multiplier, default 2 ~ 150 DPI, capped at 4 server-side to prevent adversarial-PDF OOM).
16
+ - **Per-page failure isolation** — one bad page doesn't sink the document.
17
+ - **Tesseract worker terminated after each call** so HTTP transport doesn't accumulate per-request state.
18
+
19
+ ### Added — two new optional dependencies
20
+
21
+ `tesseract.js@^7.0.0` (~1.4 MB unpacked, pure WebAssembly OCR engine) and `@napi-rs/canvas@^1.0.0` (~125 KB unpacked, native PDF→bitmap rendering with platform-specific binaries downloading conditionally) — both `optionalDependencies` so the markdown-only path stays zero-cost.
22
+
23
+ Lazy-imported via the same pattern as `pdfjs-dist` (v2.7.0), `better-sqlite3` (v1.x), and `@huggingface/transformers` (v2.0.0). Missing-deps surface a clean install-hint error rather than a cryptic module-not-found stack.
24
+
25
+ ### Server-side hardening
26
+
27
+ - `isEvalSupported: false`, `useSystemFonts: false`, `verbosity: 0` on pdfjs's `loadingTask` (matches v2.7.0 PDF read path).
28
+ - Render scale clamped to `[0.5, 4]` so adversarial PDFs claiming 100-DPI multipliers don't OOM the server.
29
+ - Tesseract worker terminated in a `finally` block so WebAssembly state never leaks even if a render or recognize call throws mid-page.
30
+ - Same path-safety + privacy filter (`--exclude-glob` / `--read-paths` / `vault.stat`) as `obsidian_read_note` and `obsidian_read_pdf`. Audit-tested at every read boundary.
31
+
32
+ ### Tests
33
+
34
+ 507 unit tests pass (was 502 in v2.9.0, +5 new):
35
+ - **ocrPdf path + privacy contract (+5):** rejects missing path arg, rejects non-existent file, refuses paths excluded by `--exclude-glob`, refuses paths outside `--read-paths` allowlist, accepts both `.pdf` and bare-stem paths consistently.
36
+
37
+ End-to-end OCR validation (loading a real Tesseract worker against a synthetic image-only PDF) is deferred to manual smoke — Tesseract.js + @napi-rs/canvas startup is heavy (~2s) and a real synthetic image-PDF fixture would inflate the test repo.
38
+
39
+ ### Surface delta vs v2.9.0
40
+
41
+ - **+1 read tool** (`obsidian_ocr_pdf`)
42
+ - **+2 optional deps** (`tesseract.js`, `@napi-rs/canvas`) — both lazy-loaded, markdown-only path zero-cost
43
+ - **Total surface:** 39 tools (28 always-on read + 1 opt-in `--persistent-index` + 3 opt-in diagnostic + 7 opt-in write) + 17 prompts
44
+
45
+ ### Migration
46
+
47
+ **No-op for default users.** OCR runs only when an agent explicitly calls `obsidian_ocr_pdf`. Existing `obsidian_read_pdf` behavior unchanged — it still returns `has_text: false` for scanned PDFs and now points at `obsidian_ocr_pdf` in its tool description.
48
+
49
+ Users on `--omit=optional` who try to call `obsidian_ocr_pdf` get a clean error message naming exactly what to install (`npm install tesseract.js @napi-rs/canvas`).
50
+
51
+ ### Strategic position
52
+
53
+ The PDF retrieval story is now complete:
54
+ - v2.7.0 — extraction tools (`obsidian_list_pdfs` / `obsidian_read_pdf`)
55
+ - v2.8.0 — blended into hybrid search (`obsidian_search` returns PDF chunks with `kind: "pdf"` + page citations)
56
+ - v2.9.0 — cross-encoder reranking on the blended candidate set
57
+ - **v2.10.0 — OCR for the image-only / scanned PDFs** the v2.8.0 pipeline previously skipped
58
+
59
+ **No other Obsidian-MCP currently does OCR for scanned PDFs.** Combined with the v2.0-v2.9 retrieval moats, enquire is now the only Obsidian-MCP that gives an agent searchable access to **every** PDF in your vault — text-PDFs, scanned-PDFs, multilingual content — with hybrid retrieval + cross-encoder reranking on top.
60
+
61
+ ## [2.9.0] — 2026-05-08
62
+
63
+ **Sprint 9 — BGE cross-encoder reranking on top of RRF.** Cross-encoder reranking is the SOTA technique in IR for boosting retrieval quality over bi-encoder candidates: after RRF fusion, the top-N hits are re-scored by a model that sees query+document interaction directly (instead of comparing pre-computed embeddings). Typical wins: +5-10 NDCG@10 on real-world retrieval. **No other Obsidian-MCP currently does cross-encoder reranking** — this extends our retrieval quality leadership claim.
64
+
65
+ ### Added — `--enable-reranker` CLI flag
66
+
67
+ Off by default — opt-in because the cross-encoder model is downloaded from HuggingFace on first call (~25-110 MB depending on alias) and adds ~30-50ms per query at top-50 candidates on M1 CPU. When enabled:
68
+
69
+ - `enquire-mcp serve --vault <path> --persistent-index --enable-reranker` → boots; reranker model lazy-loads on first search call.
70
+ - After RRF fusion + graph-boost, top-N candidates (default 50; tunable via `--reranker-top-n <n>`) are re-scored by a cross-encoder, then re-sorted before the response is truncated to `limit`.
71
+ - Each reranked hit carries a `reranker_score` field in `[0, 1]` (sigmoid of the model's relevance logit) so agents see the cross-encoder's relevance estimate alongside RRF observability.
72
+
73
+ ### Added — `RERANKER_MODELS` catalog
74
+
75
+ Two models ship out of the box, both via the existing `@huggingface/transformers` `optionalDependency`:
76
+
77
+ - **`rerank-multilingual`** (default) — `Xenova/mxbai-rerank-xsmall-v1`, ~25 MB, multilingual. Best balance of speed × quality × language coverage.
78
+ - **`rerank-bge`** — `Xenova/bge-reranker-base`, ~110 MB, English-only. Higher peak quality on English content; recommended only when you don't need multilingual support.
79
+
80
+ Choose via `--reranker-model <alias>`. Same lazy-load pattern as embedding models — first call downloads weights into `~/.cache/huggingface/transformers.js/`; subsequent queries hit the warm cache.
81
+
82
+ ### Wiring
83
+
84
+ - `searchHybrid(vault, args, ctx)` accepts an optional `ctx.reranker?: { alias?, topN? }`. When set, the reranker runs after RRF + graph-boost; failures surface via `signal_errors.reranker` (matching the existing per-signal failure-reporting pattern from v2.0.0-beta.2). The fused order is preserved if reranking fails, so a model load problem doesn't break search.
85
+ - A `ctx.rerankerOverride` injection point lets unit tests validate the rerank-and-resort plumbing without pulling in the real ML model.
86
+ - Reranker passages are derived from each candidate's best snippet (BM25 > embeddings > TF-IDF preference), with FTS5 highlight markers stripped and length capped at 600 chars to fit safely under the 512-token model budget.
87
+
88
+ ### Tests
89
+
90
+ 502 unit tests pass (was 493 in v2.8.0, +9 new):
91
+ - **RERANKER_MODELS catalog (+5):** rerank-multilingual is the multilingual default, rerank-bge is English-only, defaults to rerank-multilingual on undefined alias, throws on unknown alias with helpful list, every entry has sensible approxSizeMB.
92
+ - **searchHybrid + reranker plumbing (+4):** reranker invoked when override is set, top-N re-orders by reranker score (high.md beats mid.md beats low.md by synthetic scores), errors surface via `signal_errors.reranker` with original RRF order preserved, `topN` caps how many candidates carry `reranker_score`.
93
+
94
+ ### Surface delta vs v2.8.0
95
+
96
+ - **No new tools.** Reranking is a property of `obsidian_search`, not a new tool surface.
97
+ - **+3 CLI flags** (`--enable-reranker`, `--reranker-model <alias>`, `--reranker-top-n <n>`) on `serve` (and via the same options shape, on `serve-http`).
98
+
99
+ ### Migration
100
+
101
+ **No-op for default users.** Reranking is opt-in via `--enable-reranker`. Existing users keep working unchanged. Once you opt in, the first search call downloads the reranker model (~25 MB for default `rerank-multilingual`); subsequent queries reuse the cached weights.
102
+
103
+ ### Strategic position
104
+
105
+ Combined with v2.0-v2.8's hybrid RRF + wikilink graph-boost + breadcrumb chunking + multilingual embeddings + remote MCP transport + PDF retrieval, **enquire-mcp is now the only Obsidian-MCP that runs cross-encoder reranking on top of hybrid retrieval over markdown + PDFs**. Smart Connections (paid) doesn't rerank. Khoj doesn't either. The retrieval-quality moat widens.
106
+
5
107
  ## [2.8.0] — 2026-05-08
6
108
 
7
109
  **Sprint 8 — PDF retrieval integration.** v2.7.0 added PDF text-extraction tools (`obsidian_list_pdfs` / `obsidian_read_pdf`); v2.8.0 makes PDFs **first-class citizens of `obsidian_search`**. Index PDFs into the same FTS5 + embedding stores as markdown, blend them in hybrid retrieval (BM25 + TF-IDF + embeddings → RRF fusion), and surface a `kind: "md" | "pdf"` flag on every hit so agents can distinguish content sources at a glance.
package/README.md CHANGED
@@ -1,283 +1,265 @@
1
1
  <div align="center">
2
2
 
3
- <a href="https://github.com/oomkapwn/enquire-mcp"><img src="./assets/social-preview.png" alt="enquire — MCP server for Obsidian vaults. Hybrid retrieval (BM25 + TF-IDF + ML embeddings via RRF). Wikilinks, frontmatter, backlinks, Dataview, multilingual semantic search. For Claude Code, Cursor, Codex." width="100%"></a>
3
+ <a href="https://github.com/oomkapwn/enquire-mcp"><img src="./assets/social-preview.png" alt="enquire — MCP server for Obsidian vaults. Hybrid retrieval (BM25 + TF-IDF + ML embeddings via RRF + cross-encoder reranking). Wikilinks, frontmatter, backlinks, Dataview, multilingual semantic search, PDFs, remote MCP. For Claude Code, Cursor, Codex." width="100%"></a>
4
4
 
5
- # enquire — MCP server for Obsidian
5
+ # enquire — give your AI a search engine for your Obsidian vault
6
6
 
7
- **Hybrid retrieval (BM25 + TF-IDF + ML embeddings, RRF-fused) for your Obsidian vault.** Drop-in for Claude Code, Cursor, OpenClaw 🦞, Codex, Devin, and any MCP-compatible agent. Free. Multilingual (50+ languages). Offline-capable. No Obsidian plugin required.
7
+ **Hybrid retrieval. Cross-encoder reranking. PDFs. Multilingual. Remote MCP. Free.**
8
+
9
+ The most advanced Obsidian-MCP you can run today — drop into Claude Code, Claude.ai web, Cursor, ChatGPT, or any MCP client and your agent gets a single `obsidian_search` tool that fuses BM25 + TF-IDF + ML embeddings, reranks with a BGE cross-encoder, and surfaces blended markdown + PDF hits with page citations.
8
10
 
9
11
  [![CI](https://github.com/oomkapwn/enquire-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/oomkapwn/enquire-mcp/actions/workflows/ci.yml)
10
- [![npm](https://img.shields.io/npm/v/@oomkapwn/enquire-mcp/latest.svg?label=npm)](https://www.npmjs.com/package/@oomkapwn/enquire-mcp)
11
- [![beta](https://img.shields.io/npm/v/@oomkapwn/enquire-mcp/beta.svg?label=beta)](https://www.npmjs.com/package/@oomkapwn/enquire-mcp/v/beta)
12
- [![Tests](https://img.shields.io/badge/tests-408_passing-brightgreen.svg)](#engineering)
13
- [![License](https://img.shields.io/badge/license-MIT-yellow.svg)](./LICENSE)
14
- [![MCP](https://img.shields.io/badge/MCP-1.29-8A2BE2.svg)](https://modelcontextprotocol.io/)
12
+ [![npm](https://img.shields.io/npm/v/@oomkapwn/enquire-mcp/latest.svg?label=npm%20%40latest&color=cb3837)](https://www.npmjs.com/package/@oomkapwn/enquire-mcp)
13
+ [![tests](https://img.shields.io/badge/tests-502%20passing-brightgreen.svg)](#trust)
15
14
  [![SLSA-3](https://img.shields.io/badge/SLSA-3-blue.svg)](https://slsa.dev/spec/v1.0/levels#build-l3)
15
+ [![MCP](https://img.shields.io/badge/MCP-1.29-8A2BE2.svg)](https://modelcontextprotocol.io/)
16
+ [![License](https://img.shields.io/badge/license-MIT-yellow.svg)](./LICENSE)
17
+ [![Node](https://img.shields.io/badge/node-%E2%89%A520-3c873a.svg)](https://nodejs.org)
16
18
 
17
19
  </div>
18
20
 
21
+ ---
22
+
23
+ ## ⚡ 30-second quick start
24
+
19
25
  ```bash
20
26
  npm install -g @oomkapwn/enquire-mcp
21
27
  enquire-mcp serve --vault ~/Documents/Obsidian\ Vault
22
28
  ```
23
29
 
24
- That's it. Your AI now has structured access to wikilinks, backlinks, frontmatter, Dataview queries, and **`obsidian_search`** — a single hybrid-retrieval tool that auto-fuses BM25 + TF-IDF + ML embeddings.
30
+ That's it. Your AI now has structured access to wikilinks, backlinks, frontmatter, Dataview, and **`obsidian_search`** — the umbrella retrieval tool.
31
+
32
+ **For Claude Code / Cursor / Codex / any MCP client:**
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "obsidian": {
38
+ "command": "npx",
39
+ "args": ["-y", "@oomkapwn/enquire-mcp", "serve", "--vault", "/path/to/vault"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ **Want hybrid retrieval at full power?** One-time setup, ~10 min for a 100-note vault:
46
+
47
+ ```bash
48
+ enquire-mcp install-model multilingual # ~120MB, 50+ languages
49
+ enquire-mcp build-embeddings --vault <path> # ~30ms/chunk on M1
50
+ # then: serve --persistent-index for BM25 + --enable-reranker for cross-encoder
51
+ ```
25
52
 
26
53
  ---
27
54
 
28
- ## Why enquire (vs alternatives)
55
+ ## 🎯 The only Obsidian-MCP with…
29
56
 
30
- | | Other Obsidian-MCPs | Smart Connections (paid plugin) | **enquire** |
31
- |---|:---:|:---:|:---:|
32
- | Read-only by default | varies | n/a | |
33
- | Resolves wikilinks (alias / section / block) | partial | n/a | ✅ full |
34
- | Backlinks ranked + snippeted | rare | n/a | |
35
- | Dataview-style queries | needs plugin | n/a | first-class |
36
- | Canvas (`.canvas`) read | rare | n/a | typed nodes + edges |
37
- | BM25 full-text search | rare | | FTS5 SQLite |
38
- | TF-IDF semantic search | ❌ | ❌ | ✅ |
39
- | **ML embeddings (multilingual)** | | paid | **free** |
40
- | **Hybrid (BM25+TF-IDF+embeddings, RRF)** | ❌ | ❌ | ✅ **only here** |
41
- | **PDFs blended into hybrid search** | ❌ | ❌ | ✅ **only here** (v2.8.0) |
42
- | Per-signal observability on each hit | ❌ | ❌ | ✅ |
43
- | Privacy filter (`--exclude-glob` / `--read-paths`) | ❌ | n/a | ✅ verified at search + write paths |
44
- | Standalone (no Obsidian plugin) | varies | ❌ requires Obsidian | ✅ direct vault read |
45
- | MCP-native (any agent) | varies | ❌ Obsidian-only | ✅ stdio JSON-RPC |
46
- | **Remote MCP (HTTP transport, bearer auth)** | ❌ | ❌ | ✅ **only here** (v2.6.0) |
47
- | SLSA-3 provenance | ❌ | n/a | ✅ |
48
- | Test suite | rare | n/a | ✅ 493 unit tests |
57
+ - **Hybrid retrieval** (BM25 + TF-IDF + ML embeddings, RRF-fused)
58
+ - ✅ **Cross-encoder reranking** on top of RRF (+5-10 NDCG@10) — `v2.9.0`
59
+ - **PDFs blended into hybrid search** with `[page: N]` citation markers — `v2.8.0`
60
+ - **OCR for scanned / image-only PDFs** (Tesseract.js, multilingual) `v2.10.0`
61
+ - **Wikilink graph-boost** as a retrieval signal (1-step personalised PageRank seeded by RRF top-K)
62
+ - **Remote MCP** over HTTP with bearer auth + rate-limit + CORS — `v2.6.0`
63
+ - **Multilingual** semantic search (50+ languages, runs on CPU, free)
64
+ - **Note-tethered AI chat threads** persisted as markdown Smart Connections' #1 paid feature, free here
65
+
66
+ **Read-only by default.** All 7 write tools gated behind `--enable-write`. Privacy filter (`--exclude-glob` / `--read-paths`) verified at every search + write path. SLSA-3 release provenance.
49
67
 
50
68
  ---
51
69
 
52
- ## Architecture
70
+ ## 🏗️ How retrieval works
53
71
 
54
72
  ```mermaid
55
73
  graph LR
56
74
  Q[Query]
57
75
  Q --> S[obsidian_search]
58
- S --> BM25[BM25 / FTS5<br/>opt-in: --persistent-index]
76
+ S --> BM25[BM25 / FTS5<br/>--persistent-index]
59
77
  S --> TFIDF[TF-IDF<br/>always on]
60
- S --> EMB[ML embeddings<br/>opt-in: build-embeddings]
78
+ S --> EMB[ML embeddings<br/>build-embeddings]
61
79
  BM25 --> RRF{RRF fusion<br/>k=60}
62
80
  TFIDF --> RRF
63
81
  EMB --> RRF
64
- RRF --> R[Ranked hits<br/>per_signal observability]
82
+ RRF --> GB[Graph boost<br/>α × in-degree]
83
+ GB --> RR[Cross-encoder<br/>reranker<br/>--enable-reranker]
84
+ RR --> R[Ranked hits<br/>per_signal observability]
65
85
  ```
66
86
 
67
- `obsidian_search` auto-detects available signals and fuses them via Reciprocal Rank Fusion (Cormack et al, 2009). Returns per-signal contributions on every hit so agents can see WHY each result ranked.
87
+ `obsidian_search` auto-detects available signals and fuses them via Reciprocal Rank Fusion (Cormack et al, 2009). Wikilink graph-boost reranks top-K by 1-step personalised PageRank. Optional cross-encoder reranking (BGE) re-scores top-N for +5-10 NDCG@10. Every hit returns `per_signal` observability so you see WHY each result ranked.
68
88
 
69
- ```
70
- Tier 1: serve --vault <path> → TF-IDF (zero setup, instant)
71
- Tier 2: serve --vault <path> --persistent-index → + BM25 (sub-100ms top-10)
72
- Tier 3: + install-model + build-embeddings → + ML embeddings (multilingual)
73
- Tier 4: serve-http --bearer-token <token> → remote MCP (v2.6.0)
74
- same retrieval stack, exposed over HTTP for Claude.ai web,
75
- ChatGPT, Cursor HTTP, mobile. Tailscale Funnel / Cloudflare
76
- Tunnel for HTTPS. See docs/http-transport.md.
77
- ```
89
+ | Tier | Setup | What you get |
90
+ |---|---|---|
91
+ | **1** | `serve --vault <path>` | TF-IDF (zero setup, instant) |
92
+ | **2** | + `--persistent-index` | + BM25 (sub-100ms top-10) |
93
+ | **3** | + `install-model` + `build-embeddings` | + multilingual ML embeddings |
94
+ | **4** | + `--enable-reranker` | + BGE cross-encoder reranking |
95
+ | **5** | + `--include-pdfs` | + PDFs blended into all of the above |
96
+ | **6** | `serve-http --bearer-token …` | + remote MCP for Claude.ai web, ChatGPT, Cursor HTTP, mobile |
78
97
 
79
98
  ---
80
99
 
81
- ## Quick start
82
-
83
- > **Two channels:** `npm install @oomkapwn/enquire-mcp` → **v2.0** (stable, hybrid retrieval). `@beta` → preview track. `@1` → legacy v1.x line.
100
+ ## 🆚 vs alternatives
84
101
 
85
- **Claude Desktop / Claude Code / Cursor / Codex / any MCP client**:
102
+ | | Other Obsidian-MCPs | Smart Connections (paid) | **enquire** |
103
+ |---|:---:|:---:|:---:|
104
+ | Wikilinks (alias / section / block) | partial | n/a | ✅ full |
105
+ | Backlinks ranked + snippeted | rare | n/a | ✅ |
106
+ | Dataview-style queries | needs plugin | n/a | ✅ first-class |
107
+ | Canvas (`.canvas`) read | rare | n/a | ✅ typed nodes + edges |
108
+ | BM25 full-text | rare | ❌ | ✅ FTS5 SQLite |
109
+ | TF-IDF semantic | ❌ | ❌ | ✅ |
110
+ | ML embeddings (multilingual) | ❌ | 💰 paid | ✅ **free** |
111
+ | **Hybrid (BM25+TF-IDF+embeddings, RRF)** | ❌ | ❌ | ✅ **only here** |
112
+ | **Wikilink graph-boost retrieval signal** | ❌ | ❌ | ✅ **only here** |
113
+ | **PDFs blended into hybrid search** | ❌ | ❌ | ✅ **only here** |
114
+ | **OCR for scanned / image-only PDFs** | ❌ | ❌ | ✅ **only here** |
115
+ | **Cross-encoder reranking** | ❌ | ❌ | ✅ **only here** |
116
+ | **Remote MCP (HTTP + bearer auth)** | ❌ | ❌ | ✅ **only here** |
117
+ | Per-signal observability per hit | ❌ | ❌ | ✅ |
118
+ | Privacy filter (exclude/allow globs) | ❌ | n/a | ✅ verified at search + write paths |
119
+ | Standalone (no Obsidian plugin) | varies | ❌ requires Obsidian | ✅ direct vault read |
120
+ | MCP-native (any agent) | varies | ❌ Obsidian-only | ✅ stdio + HTTP |
121
+ | SLSA-3 release provenance | ❌ | n/a | ✅ |
122
+ | Test suite | rare | n/a | ✅ 507 unit tests |
86
123
 
87
- ```json
88
- {
89
- "mcpServers": {
90
- "obsidian": {
91
- "command": "npx",
92
- "args": ["-y", "@oomkapwn/enquire-mcp", "serve", "--vault", "/path/to/vault"]
93
- }
94
- }
95
- }
96
- ```
124
+ > **Strategic claim:** enquire is the open-source backend for [Karpathy-style LLM Wikis](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) on top of your existing Obsidian vault. The `vault_synth` / `vault_wiki_compile` / `vault_lint_extended` prompts implement the ingest → query → lint → compile workflow natively over `.md` + `[[wikilinks]]`. Knowledge that compounds, traceable to sources.
97
125
 
98
- | Client | Config file |
99
- |---|---|
100
- | Claude Desktop | macOS `~/Library/Application Support/Claude/claude_desktop_config.json` · Windows `%APPDATA%\Claude\claude_desktop_config.json` |
101
- | Claude Code (CLI) | `~/.claude.json` (global) or `.mcp.json` (per-project) |
102
- | Cursor | `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project) |
103
- | Codex / OpenClaw / Devin | per-tool MCP config |
126
+ ---
104
127
 
105
- **Enable hybrid retrieval** (one-time setup, ~10 min for a 100-note vault):
128
+ ## 🛠️ All 39 tools at a glance
106
129
 
107
- ```bash
108
- enquire-mcp install-model multilingual # ~120MB, 50+ languages
109
- enquire-mcp build-embeddings --vault <path> # ~30ms/chunk on M1
110
- # Add --persistent-index to your serve invocation for BM25.
111
- ```
130
+ The umbrella `obsidian_search` plus 38 specialized tools for wikilinks, backlinks, Dataview, frontmatter, canvas, PDFs, OCR, vault stats, graph navigation, and writes.
112
131
 
113
- **Remote MCP** (v2.6.0 — Claude.ai web, ChatGPT, Cursor HTTP, mobile):
132
+ <details>
133
+ <summary><b>28 always-on read tools</b> — click to expand</summary>
114
134
 
115
- ```bash
116
- enquire-mcp gen-token > ~/.enquire/token # 256-bit bearer
117
- enquire-mcp serve-http \
118
- --vault ~/Obsidian \
119
- --bearer-token "$(cat ~/.enquire/token)" \
120
- --persistent-index
121
- # Front with Tailscale Funnel / Cloudflare Tunnel for HTTPS — see
122
- # docs/http-transport.md.
123
- ```
135
+ `obsidian_search` · `obsidian_context_pack` · `obsidian_chat_thread_read` · `obsidian_frontmatter_get` · `obsidian_frontmatter_search` · `obsidian_read_note` · `obsidian_list_notes` · `obsidian_resolve_wikilink` · `obsidian_get_backlinks` · `obsidian_get_outbound_links` · `obsidian_get_unresolved_wikilinks` · `obsidian_get_recent_edits` · `obsidian_list_tags` · `obsidian_dataview_query` · `obsidian_find_path` · `obsidian_find_similar` · `obsidian_get_note_neighbors` · `obsidian_stats` · `obsidian_lint_wiki` · `obsidian_open_questions` · `obsidian_paper_audit` · `obsidian_validate_note_proposal` · `obsidian_list_canvases` · `obsidian_read_canvas` · `obsidian_list_pdfs` · `obsidian_read_pdf` · `obsidian_ocr_pdf` · `obsidian_open_in_ui`
124
136
 
125
- No other Obsidian-MCP currently ships a remote-HTTP transport. Same vault, same tools, same hybrid retrieval — just over HTTPS instead of stdio.
137
+ </details>
126
138
 
127
- ---
139
+ <details>
140
+ <summary><b>4 opt-in read tools</b> (diagnostic single-rankers) — click to expand</summary>
128
141
 
129
- ## Tools (38 total)
142
+ `obsidian_full_text_search` (`--persistent-index`) · `obsidian_search_text` · `obsidian_semantic_search` · `obsidian_embeddings_search` (all 3 require `--diagnostic-search-tools`)
130
143
 
131
- ### 27 always-on read tools
144
+ </details>
132
145
 
133
- | Tool | What it does |
134
- |---|---|
135
- | `obsidian_search` | **Hybrid retrieval** — fuses BM25 + TF-IDF + ML embeddings via RRF. The default search tool. Auto-detects available signals. v2.2.0: `granularity: "block"` arg returns chunks instead of notes. **v2.8.0:** with `--include-pdfs`, PDF chunks blend in alongside markdown — every hit carries `kind: "md" \| "pdf"` and PDF snippets include `[page: N]` markers for citation. |
136
- | `obsidian_context_pack` | **v2.2.0.** Token-budgeted context bundling: takes a question, runs hybrid search, gathers note bodies + backlinks + optionally recent dailies, returns one ready-to-paste markdown bundle. Saves ~5 tool calls. |
137
- | `obsidian_chat_thread_read` | **v2.2.0.** Parse a note's `## Chat: <title>` block into structured messages (role/timestamp/content/line-range). Pair with `_append` (write) for note-tethered AI conversations. |
138
- | `obsidian_frontmatter_get` | **v2.3.0.** Read parsed YAML frontmatter for a note. With `key`, returns just that field. |
139
- | `obsidian_frontmatter_search` | **v2.3.0.** Find notes by frontmatter predicate (`equals` / `exists` / `contains`). Useful as a precursor to bulk `_set`. |
140
- | `obsidian_read_note` | Full content + frontmatter + wikilinks + embeds + tags. Also accepts periodic-note aliases (`title: "today"` / `"weekly"` / `"monthly"`). |
141
- | `obsidian_list_notes` | Vault-wide or folder-scoped. Includes title + tags + mtime + counts. |
142
- | `obsidian_resolve_wikilink` | Resolves `[[Note]]`, `[[Note\|Alias]]`, `[[Note#Section]]`, `[[Note#^block]]`, with did-you-mean on near-miss. |
143
- | `obsidian_get_backlinks` | Every note linking to X, ranked + snippeted. |
144
- | `obsidian_get_outbound_links` | Outbound `[[wikilinks]]` from one note, with resolution status. |
145
- | `obsidian_get_unresolved_wikilinks` | Vault-wide broken-link audit. |
146
- | `obsidian_get_recent_edits` | Notes modified in the last N minutes. |
147
- | `obsidian_list_tags` | Tag census across vault (frontmatter + inline). |
148
- | `obsidian_dataview_query` | First-class Dataview-style queries (`LIST` / `TABLE`, `WHERE`, `AND` / `OR` / `LIKE` / `contains`). |
149
- | `obsidian_find_path` | Multi-hop graph BFS between two notes (with alternatives). |
150
- | `obsidian_find_similar` | Note-to-note similarity (tag + folder + content signals). |
151
- | `obsidian_get_note_neighbors` | Outbound + inbound + tag-sibling for one note. |
152
- | `obsidian_stats` | Vault dashboard: note count, tag count, broken links. |
153
- | `obsidian_lint_wiki` | Karpathy LLM-Wiki `/lint` workflow (orphans / broken / stubs / stale). |
154
- | `obsidian_open_questions` | Find open `?` questions across the vault. |
155
- | `obsidian_paper_audit` | Track arXiv references and read-status. |
156
- | `obsidian_validate_note_proposal` | Lint a draft note before writing (closes the #1 LLM-write pain). |
157
- | `obsidian_list_canvases` | List `.canvas` files with node + edge counts. |
158
- | `obsidian_read_canvas` | Parse `.canvas` into typed nodes (text/file/link/group) + edges. |
159
- | `obsidian_list_pdfs` | **v2.7.0.** List `.pdf` files with size + mtime. PDFs are the #1 non-markdown content kind in real vaults; no other Obsidian-MCP indexes them. |
160
- | `obsidian_read_pdf` | **v2.7.0.** Extract page-by-page text + doc metadata (title/author/etc) from a PDF. Optional 1-indexed page-range slice. Image-only / scanned PDFs surface `has_text: false`. Powered by Mozilla PDF.js (Apache-2.0, pinned to `optionalDependencies` so the markdown-only path stays zero-cost). |
161
- | `obsidian_open_in_ui` | Emit `obsidian://open?vault=...` URI. |
162
-
163
- ### 4 opt-in read tools
164
-
165
- | Tool | Flag | Notes |
166
- |---|---|---|
167
- | `obsidian_full_text_search` | `--persistent-index` + `--diagnostic-search-tools` | BM25 ranking, sub-100ms. |
168
- | `obsidian_search_text` | `--diagnostic-search-tools` | Token search (all/any/phrase modes). |
169
- | `obsidian_semantic_search` | `--diagnostic-search-tools` | TF-IDF cosine standalone. |
170
- | `obsidian_embeddings_search` | `--diagnostic-search-tools` | ML embeddings standalone. |
146
+ <details>
147
+ <summary><b>7 opt-in write tools</b> (require <code>--enable-write</code>) — click to expand</summary>
171
148
 
172
- ### 7 opt-in write tools (`--enable-write`)
149
+ `obsidian_create_note` · `obsidian_append_to_note` · `obsidian_rename_note` (rewrites every wikilink across vault, code-fence-aware) · `obsidian_replace_in_notes` (bulk find/replace) · `obsidian_archive_note` · `obsidian_chat_thread_append` · `obsidian_frontmatter_set` (atomic YAML manipulation, `dry_run` supported)
173
150
 
174
- | Tool | Notes |
175
- |---|---|
176
- | `obsidian_create_note` | Refuses overwrite without `overwrite=true`. Empty path rejected. |
177
- | `obsidian_append_to_note` | Symlink-safe; respects `--max-file-bytes`. |
178
- | `obsidian_rename_note` | Rewrites every wikilink across vault. Code-fence-aware. `dry_run` available. |
179
- | `obsidian_replace_in_notes` | Bulk find/replace. Per-file errors collected; `partial: true` on mid-loop fail. |
180
- | `obsidian_archive_note` | Wraps rename to `Archive/`. Preserves backlinks. |
181
- | `obsidian_chat_thread_append` | **v2.2.0.** Append a user/assistant/system message to a note's `## Chat:` block. Creates note + heading if absent. Threads stored as markdown — searchable, version-controllable, survive sessions. |
182
- | `obsidian_frontmatter_set` | **v2.3.0.** Surgical YAML manipulation — set/unset keys on a note atomically. Pass `null` as value to delete. Round-trips through gray-matter so YAML formatting stays consistent. `dry_run` supported. |
151
+ </details>
183
152
 
184
- Plus **2 + 1 opt-in MCP resources** (`obsidian://note/...`, `obsidian://vault-info`, `obsidian://chunk/...`) and **17 MCP prompts** (`summarize_recent_edits`, `weekly_review`, `monthly_review`, `find_orphans`, `extract_todos`, `process_inbox`, `review_tag`, `consolidate_tags`, `find_duplicates`, `lint_wiki`, `search_with_query_expansion`, `vault_synth`, `vault_wiki_compile`, `vault_lint_extended`, `vault_capture`, `vault_persona_search`, `vault_automation_setup`).
153
+ **Plus:** 2 + 1 opt-in MCP resources, and **17 MCP prompts** (`summarize_recent_edits`, `weekly_review`, `monthly_review`, `find_orphans`, `extract_todos`, `process_inbox`, `review_tag`, `consolidate_tags`, `find_duplicates`, `lint_wiki`, `search_with_query_expansion`, `vault_synth`, `vault_wiki_compile`, `vault_lint_extended`, `vault_capture`, `vault_persona_search`, `vault_automation_setup`).
185
154
 
186
- > **v2.4.0 strategic position:** enquire-mcp is the open-source backend for **Karpathy-style LLM Wikis** on top of your existing Obsidian vault. The `vault_synth` + `vault_wiki_compile` + `vault_lint_extended` prompts implement the [Karpathy LLM-Wiki workflow](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (ingest → query → lint → compile) natively over Obsidian's `.md` + `[[wikilinks]]` substrate. Knowledge that compounds, traceable to sources.
155
+ 📖 Full reference: **[docs/api.md](./docs/api.md)** · Remote-MCP deployment guide: **[docs/http-transport.md](./docs/http-transport.md)**
187
156
 
188
157
  ---
189
158
 
190
- ## Configuration
159
+ ## ⚙️ Configuration
160
+
161
+ The flags you'll actually use:
191
162
 
192
- | Flag | Default | Notes |
163
+ | Flag | Default | What it does |
193
164
  |---|---|---|
194
- | `--vault <path>` | required | Path to Obsidian vault root. |
195
- | `--enable-write` | off | Register the 5 write tools. |
196
- | `--exclude-glob <pat...>` | none | Privacy denylist. Repeatable. Example: `'02_Personal/**'`. |
197
- | `--read-paths <pat...>` | none | Privacy allowlist (only matching paths visible). Repeatable. |
198
- | `--persistent-index` | off | SQLite FTS5 BM25. Enables `obsidian_full_text_search` (with `--diagnostic-search-tools`). |
199
- | `--persistent-cache` | off | Persist parsed-note cache across restarts. |
200
- | `--watch` | off | Live invalidation on `.md` add/change/unlink. |
201
- | `--diagnostic-search-tools` | off | Register 4 single-ranker search tools (defaults: hybrid `obsidian_search` only). |
202
- | `--enabled-tools <name...>` | all | Strict allowlist (gate to a subset). |
203
- | `--disabled-tools <name...>` | none | Denylist. |
204
- | `--max-file-bytes <n>` | 5 MB | Per-file read/write cap. |
205
- | `--cache-size <n>` | 1024 | LRU cap for parsed-note cache. |
206
-
207
- Subcommands: `serve` · `serve-http` (v2.6.0 — remote MCP) · `gen-token` (v2.6.0) · `clear-cache` · `clear-index` · `index` (cold-build FTS5) · `install-model` · `build-embeddings` · `clear-embeddings`.
208
-
209
- Full reference: [docs/api.md](./docs/api.md). Remote-MCP deployment guide: [docs/http-transport.md](./docs/http-transport.md).
165
+ | `--vault <path>` | required | Path to Obsidian vault root |
166
+ | `--persistent-index` | off | SQLite FTS5 BM25, sub-100ms top-10 |
167
+ | `--include-pdfs` | off | Index PDFs into FTS5 + embeddings |
168
+ | `--enable-reranker` | off | BGE cross-encoder reranking on RRF top-N |
169
+ | `--enable-write` | off | Register the 7 write tools |
170
+ | `--exclude-glob <pat...>` | none | Privacy denylist (e.g. `'02_Personal/**'`) |
171
+ | `--read-paths <pat...>` | none | Privacy allowlist (only matching paths visible) |
172
+ | `--watch` | off | Live invalidation on `.md` add/change/unlink |
173
+ | `--persistent-cache` | off | Survive cold starts |
174
+
175
+ Subcommands: `serve` · `serve-http` · `gen-token` · `clear-cache` · `clear-index` · `clear-embeddings` · `index` · `install-model` · `build-embeddings`.
176
+
177
+ **Remote MCP** for Claude.ai web / ChatGPT / Cursor HTTP / mobile:
178
+
179
+ ```bash
180
+ enquire-mcp gen-token > ~/.enquire/token # one-time
181
+ enquire-mcp serve-http \
182
+ --vault ~/Obsidian \
183
+ --bearer-token "$(cat ~/.enquire/token)" \
184
+ --persistent-index --include-pdfs --enable-reranker
185
+ # Front with Tailscale Funnel / Cloudflare Tunnel for HTTPS.
186
+ ```
210
187
 
211
188
  ---
212
189
 
213
- ## Security
190
+ ## 🛡️ Trust
214
191
 
215
- - **Read-only by default.** Write tools require `--enable-write`.
216
- - **Path traversal blocked.** Realpath check on every read+write target. Symlinks inside the vault that resolve outside are rejected.
217
- - **Privacy boundary verified across all paths**, including persistent indexes (FTS5 / embed-db search-time filter) and the `obsidian://chunk/...` resource. Privacy fail-closed: empty `--read-paths` / `--exclude-glob` patterns refuse to start.
192
+ - **Read-only by default.** Every write tool requires `--enable-write`.
193
+ - **Path traversal blocked.** Realpath check on every read+write target. Symlinks resolving outside the vault are rejected.
194
+ - **Privacy boundary verified across all paths** including persistent FTS5 + embed indexes and the `obsidian://chunk/...` resource. Privacy fail-closed: empty `--read-paths` / `--exclude-glob` patterns refuse to start.
195
+ - **HTTP transport hardened.** Bearer auth (constant-time SHA-256 + `timingSafeEqual`), per-token sliding rate-limit, strict CORS allowlist with credential-leak guard.
218
196
  - **`gray-matter` (`js-yaml` safeLoad)** — no code execution via frontmatter.
219
- - **DQL parser** — no shell, no `eval`, no template expansion.
220
197
  - **Cache + index files** — chmod 0600, parent dir 0700.
221
198
  - **SLSA-3 provenance** on every npm release.
222
- - **Branch protection ruleset** with `bypass_mode: pull_request` — every change goes through PR with audit trail. Release pipeline verifies SHA-on-main + 8 required CI checks before publishing.
223
-
224
- Full posture: [SECURITY.md](./SECURITY.md). Report vulnerabilities to `oomkapwn@gmail.com`.
225
-
226
- ---
227
-
228
- ## Engineering
199
+ - **Branch protection** with `bypass_mode: pull_request` — every change goes through PR review. Release pipeline verifies tagged SHA is on `main` AND all 8 required CI checks reported `success` on it.
229
200
 
230
201
  | Surface | Posture |
231
202
  |---|---|
232
- | Language | TypeScript strict + `noUncheckedIndexedAccess` |
233
- | Lint | Biome 2 (zero-warning policy) |
234
- | Tests | 493 unit tests across 23 files |
235
- | CI | ubuntu × {Node 20, 22, 24} required + macOS advisory job |
203
+ | Tests | 507 unit tests across 25 files, 8 required CI gates per PR |
236
204
  | Coverage | Lines ≥86%, statements ≥82%, functions ≥75%, branches ≥73% (gated) |
237
205
  | Audit | `npm audit --audit-level=moderate` for prod; high for dev |
238
- | Runtime deps | 5 mandatory (`@modelcontextprotocol/sdk`, `chokidar`, `commander`, `gray-matter`, `zod`) + 2 optional (`better-sqlite3` for FTS5 / embed-db; `@huggingface/transformers` for ML embeddings) |
239
- | Releases | npm + GitHub release per tag · semver · provenance attached · channels: `latest` (stable), `beta`, `alpha` |
206
+ | CI | Ubuntu × {Node 20, 22, 24} required + macOS advisory job |
207
+ | Lint | Biome 2 (zero-warning policy) |
208
+ | Language | TypeScript strict + `noUncheckedIndexedAccess` |
209
+ | Runtime deps | 5 mandatory, 3 optional (FTS5 + ML embeddings + PDF parser — markdown-only path stays zero-cost) |
210
+ | Releases | npm + GitHub release per tag · semver · SLSA-3 provenance |
240
211
 
241
- ```bash
242
- git clone https://github.com/oomkapwn/enquire-mcp.git
243
- cd enquire-mcp && npm install
244
- npm test # full suite
245
- npm run lint # zero warnings
246
- npm run build # tsc → dist/
247
- ```
212
+ Full posture: **[SECURITY.md](./SECURITY.md)**. Report vulnerabilities to `oomkapwn@gmail.com`.
248
213
 
249
214
  ---
250
215
 
251
- ## FAQ
216
+ ## FAQ
217
+
218
+ **Do I need Obsidian installed?**
219
+ No. enquire reads `.md` + `.canvas` + `.pdf` files directly. Works against any Obsidian-format vault.
220
+
221
+ **Will this write to my vault?**
222
+ Not unless you start with `--enable-write`. Even then, all 7 write tools are gated by privacy filters and refuse to overwrite without `overwrite: true`. `dry_run` modes available on the destructive ones.
223
+
224
+ **Is my data sent anywhere?**
225
+ Only on `enquire-mcp install-model` (downloads ONNX weights from HuggingFace, one-time). Serve mode itself never makes outbound HTTP. Embeddings + reranker run on CPU locally.
226
+
227
+ **How is this different from Smart Connections?**
228
+ Smart Connections is a paid Obsidian plugin that runs ML embeddings inside Obsidian. enquire is a standalone MCP server: free, MCP-native (works with Claude / Cursor / Codex / any agent), and fuses 3 retrieval signals + cross-encoder reranking for higher recall + precision than embeddings alone. PDFs index too.
229
+
230
+ **Performance on large vaults?**
231
+ Cold-build of FTS5 on a 1k-note vault: ~5s. Warm BM25 top-10: sub-100ms. Embedding build: ~30ms/chunk on M1 (~8min for 8k chunks). Hybrid query latency: <200ms typical. Reranker adds ~30-50ms at top-50. Maintainer dogfoods on a 128-note bilingual vault with all of the above on.
252
232
 
253
- **Q: Do I need Obsidian installed?**
254
- No. enquire reads `.md` + `.canvas` files directly. Works against any Obsidian-format vault.
233
+ **Languages?**
234
+ Default embedding model is `paraphrase-multilingual-MiniLM-L12-v2` (50+ languages). Multilingual cross-encoder reranker (`mxbai-rerank-xsmall-v1`) is the default too. Validated end-to-end on Russian + English bilingual vaults. CJK / Thai / Khmer / Lao tokenization via `Intl.Segmenter` (Node 16+ ICU).
255
235
 
256
- **Q: Will this write to my vault?**
257
- No, unless you explicitly start with `--enable-write`. Even then, all 5 write tools are gated by privacy filters and refuse to overwrite without `overwrite: true`.
236
+ **Can I run it remotely?**
237
+ Yes. `serve-http` exposes the same server over [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) with bearer auth. Front with Tailscale Funnel or Cloudflare Tunnel for HTTPS works with claude.ai web, ChatGPT custom GPT, Cursor HTTP mode, mobile MCP clients. See [docs/http-transport.md](./docs/http-transport.md).
258
238
 
259
- **Q: Is my data sent anywhere?**
260
- Only on `enquire-mcp install-model` (downloads ONNX weights from HuggingFace). The serve mode itself never makes outbound HTTP. Embeddings run on CPU locally.
239
+ ---
261
240
 
262
- **Q: How is this different from Smart Connections?**
263
- Smart Connections is a paid Obsidian plugin that does ML embeddings inside Obsidian. enquire-mcp is a standalone MCP server: free, MCP-native (works with Claude / Cursor / Codex / any agent), and adds hybrid retrieval (BM25 + TF-IDF + embeddings via RRF) for higher recall than embeddings alone.
241
+ ## 🚀 Releases
264
242
 
265
- **Q: Performance on large vaults?**
266
- Cold-build of FTS5 on a 1k-note vault: ~5s. Warm BM25 top-10: sub-100ms. Embedding build: ~30ms/chunk on M1 (8s for 256 chunks, ~8min for 8k chunks). Hybrid query latency: <200ms typical.
243
+ `v2.0.0` (stable) · `v2.5.0` (5-sprint roadmap consolidated) · `v2.6.0` (remote MCP) · `v2.7.0` (PDF read tools) · `v2.8.0` (PDFs blended into hybrid search) · `v2.9.0` (BGE cross-encoder reranking)
267
244
 
268
- **Q: Does it support languages other than English?**
269
- Yes. Default embedding model is `paraphrase-multilingual-MiniLM-L12-v2` (50+ languages). Validated end-to-end on Russian + English bilingual vaults. CJK requires the upcoming Intl.Segmenter pre-pass (v2.1 backlog).
245
+ Channel: `npm install @oomkapwn/enquire-mcp` latest stable. Full changelog: [CHANGELOG.md](./CHANGELOG.md).
270
246
 
271
247
  ---
272
248
 
273
- ## Contributing
249
+ ## 🤝 Contributing
274
250
 
275
- Issues and PRs welcome. Read [CONTRIBUTING.md](./CONTRIBUTING.md). All changes go through PR review per branch protection ruleset.
251
+ ```bash
252
+ git clone https://github.com/oomkapwn/enquire-mcp.git
253
+ cd enquire-mcp && npm install
254
+ npm test # full suite (502 tests, ~5s)
255
+ npm run lint # zero warnings
256
+ npm run build # tsc → dist/
257
+ ```
276
258
 
277
- ## License & credits
259
+ Issues, PRs, and ideas welcome. Branch protection requires PR review on `main`.
278
260
 
279
- [MIT](./LICENSE). Built by Alex — [GitHub `@oomkapwn`](https://github.com/oomkapwn) · [X `@OomkaBear`](https://x.com/OomkaBear).
261
+ ---
280
262
 
281
- Named after [ENQUIRE](https://en.wikipedia.org/wiki/ENQUIRE) Tim Berners-Lee's 1980 hypertext prototype of the World Wide Web.
263
+ ## 📜 License & credits
282
264
 
283
- > **Not affiliated with Obsidian.md.** Obsidian and the Obsidian logo are trademarks of Dynalist Inc. enquire-mcp is an independent open-source project that reads Obsidian-format vaults.
265
+ MIT. Built by [Alex (@OomkaBear)](https://github.com/oomkapwn). Named after [Tim Berners-Lee's 1980 prototype of the WWW](https://en.wikipedia.org/wiki/ENQUIRE) the original hypertext system, before the web. The original spec was that you could ask the system anything; this brings that to your vault.