@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 +102 -0
- package/README.md +177 -195
- package/dist/embeddings.d.ts +34 -0
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +79 -0
- package/dist/embeddings.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -6
- package/dist/index.js.map +1 -1
- package/dist/ocr.d.ts +66 -0
- package/dist/ocr.d.ts.map +1 -0
- package/dist/ocr.js +199 -0
- package/dist/ocr.js.map +1 -0
- package/dist/tools.d.ts +78 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +110 -1
- package/dist/tools.js.map +1 -1
- package/docs/api.md +1 -1
- package/package.json +4 -2
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 —
|
|
5
|
+
# enquire — give your AI a search engine for your Obsidian vault
|
|
6
6
|
|
|
7
|
-
**Hybrid retrieval
|
|
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
|
[](https://github.com/oomkapwn/enquire-mcp/actions/workflows/ci.yml)
|
|
10
|
-
[](https://www.npmjs.com/package/@oomkapwn/enquire-mcp)
|
|
11
|
-
[](#engineering)
|
|
13
|
-
[](./LICENSE)
|
|
14
|
-
[](https://modelcontextprotocol.io/)
|
|
12
|
+
[](https://www.npmjs.com/package/@oomkapwn/enquire-mcp)
|
|
13
|
+
[](#trust)
|
|
15
14
|
[](https://slsa.dev/spec/v1.0/levels#build-l3)
|
|
15
|
+
[](https://modelcontextprotocol.io/)
|
|
16
|
+
[](./LICENSE)
|
|
17
|
+
[](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
|
|
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
|
-
##
|
|
55
|
+
## 🎯 The only Obsidian-MCP with…
|
|
29
56
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
##
|
|
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
|
|
76
|
+
S --> BM25[BM25 / FTS5<br/>--persistent-index]
|
|
59
77
|
S --> TFIDF[TF-IDF<br/>always on]
|
|
60
|
-
S --> EMB[ML embeddings<br/>
|
|
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 -->
|
|
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).
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
128
|
+
## 🛠️ All 39 tools at a glance
|
|
106
129
|
|
|
107
|
-
|
|
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
|
-
|
|
132
|
+
<details>
|
|
133
|
+
<summary><b>28 always-on read tools</b> — click to expand</summary>
|
|
114
134
|
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
+
</details>
|
|
132
145
|
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 |
|
|
163
|
+
| Flag | Default | What it does |
|
|
193
164
|
|---|---|---|
|
|
194
|
-
| `--vault <path>` | required | Path to Obsidian vault root
|
|
195
|
-
| `--
|
|
196
|
-
| `--
|
|
197
|
-
| `--
|
|
198
|
-
| `--
|
|
199
|
-
| `--
|
|
200
|
-
| `--
|
|
201
|
-
| `--
|
|
202
|
-
| `--
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
##
|
|
190
|
+
## 🛡️ Trust
|
|
214
191
|
|
|
215
|
-
- **Read-only by default.**
|
|
216
|
-
- **Path traversal blocked.** Realpath check on every read+write target. Symlinks
|
|
217
|
-
- **Privacy boundary verified across all paths
|
|
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
|
|
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
|
-
|
|
|
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
|
-
|
|
|
239
|
-
|
|
|
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
|
-
|
|
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
|
-
**
|
|
254
|
-
|
|
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
|
-
**
|
|
257
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
259
|
+
Issues, PRs, and ideas welcome. Branch protection requires PR review on `main`.
|
|
278
260
|
|
|
279
|
-
|
|
261
|
+
---
|
|
280
262
|
|
|
281
|
-
|
|
263
|
+
## 📜 License & credits
|
|
282
264
|
|
|
283
|
-
|
|
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.
|