@oomkapwn/enquire-mcp 3.0.1 → 3.1.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 +69 -0
- package/README.md +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +144 -1
- package/dist/index.js.map +1 -1
- package/dist/tools.d.ts +34 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +28 -2
- package/dist/tools.js.map +1 -1
- package/docs/api.md +1 -1
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,75 @@
|
|
|
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
|
+
## [3.1.0] — 2026-05-09
|
|
6
|
+
|
|
7
|
+
**Sprint 18 — agentic retrieval primitives.** First v3.x minor release. Closes the "agentic-RAG" gap surfaced in the v3.0 competitive audit (vs Copilot Plus's autonomous agent + GraphRAG-style sub-question patterns). Three additive surfaces, all opt-in for callers, all backwards compatible.
|
|
8
|
+
|
|
9
|
+
### Added — `obsidian_hyde_search` tool (HyDE retrieval)
|
|
10
|
+
|
|
11
|
+
[Hypothetical Document Embeddings](https://arxiv.org/abs/2212.10496) (Gao et al, 2023) wired into the always-on read tool surface. The calling agent generates a 1-3 sentence synthetic answer to its own question, passes it as `hypothetical_answer`, and the server embeds *that* (not the question) for retrieval. The answer-shaped vector lands in the same neighborhood as real notes, beating raw-query embedding by **+2-5 NDCG@10** on under-specified queries in our internal eval.
|
|
12
|
+
|
|
13
|
+
```jsonc
|
|
14
|
+
{
|
|
15
|
+
"tool": "obsidian_hyde_search",
|
|
16
|
+
"args": {
|
|
17
|
+
"query": "what did I learn about RRF",
|
|
18
|
+
"hypothetical_answer": "RRF (Reciprocal Rank Fusion) combines ranked lists from multiple retrievers by summing 1/(k+rank). Equal weights with k=60 work surprisingly well across domains (Cormack et al, 2009).",
|
|
19
|
+
"limit": 10
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Server stays LLM-free — the agent does the LLM call to produce the hypothetical answer. Response includes a `hyde: true` flag for client-side audit. Falls back to embedding the raw `query` when `hypothetical_answer` is empty/whitespace.
|
|
25
|
+
|
|
26
|
+
Uses the same `.embed.db` as `obsidian_embeddings_search`. Picks up HNSW persistence (v2.16+) automatically when `--use-hnsw` is set.
|
|
27
|
+
|
|
28
|
+
### Added — `vault_research` MCP prompt (sub-question decomposition)
|
|
29
|
+
|
|
30
|
+
Multi-hop research workflow. Agent decomposes a complex question into 3-5 atomic sub-questions, retrieves per-sub (preferring `obsidian_hyde_search` when it has a hypothesis), then synthesizes an answer with cited evidence. Closes the agentic-decomposition gap from the competitive audit — pure prompt-side, no new tools required, agent handles the recursion.
|
|
31
|
+
|
|
32
|
+
Output structure: synthesis paragraph + bulleted "Evidence" section with `[[Path/To/Note.md#L23-L27]]` citations + "Open questions" section listing sub-questions the vault didn't answer (= future ingest gaps).
|
|
33
|
+
|
|
34
|
+
### Added — `vault_synthesis_page` MCP prompt
|
|
35
|
+
|
|
36
|
+
Karpathy LLM-Wiki **synthesis** loop (vs the existing `vault_synth` which is the *ingest* loop). Takes a topic the user already has scattered notes about, surveys via `obsidian_search`, deduplicates + reconciles bullets across hits, produces a single consolidated wiki page with frontmatter `synthesized_from: [...]` and `[[wikilink]]` citations to every contributing source. Run when you have ENOUGH existing notes that a consolidated overview would help.
|
|
37
|
+
|
|
38
|
+
### API additions
|
|
39
|
+
|
|
40
|
+
`src/tools.ts`:
|
|
41
|
+
- `pickEmbedTextForHyde(args): { text, usedHyde }` — exported pure helper that decides whether to embed `query` or `hypothetical_answer`. Unit-tested in isolation (the real `embeddingsSearch` loads the embedder, which is out of scope for fast tests).
|
|
42
|
+
- `EmbedSearchResponse.hyde?: boolean` — present + true when retrieval used HyDE.
|
|
43
|
+
- `embeddingsSearch` accepts `hypothetical_answer` arg (backwards compatible).
|
|
44
|
+
|
|
45
|
+
`src/index.ts`:
|
|
46
|
+
- 1 new always-on read tool registration: `obsidian_hyde_search`.
|
|
47
|
+
- 2 new MCP prompts: `vault_research`, `vault_synthesis_page`.
|
|
48
|
+
|
|
49
|
+
### Tools / prompts surface
|
|
50
|
+
|
|
51
|
+
- **40 production tools** (was 39 in v3.0): 29 always-on read (added `obsidian_hyde_search`) + 1 FTS5 opt-in + 3 diagnostic opt-in + 7 gated writes.
|
|
52
|
+
- **19 MCP prompts** (was 17): added `vault_research` + `vault_synthesis_page`.
|
|
53
|
+
- **3 MCP resources**: unchanged.
|
|
54
|
+
|
|
55
|
+
### Tests
|
|
56
|
+
|
|
57
|
+
612 unit tests pass (was 606 in v3.0.1, +6 new):
|
|
58
|
+
- `pickEmbedTextForHyde` (6): undefined/empty/whitespace fallback to query, trimmed hypothetical takes precedence, query NOT trimmed when not HyDE (preserves whitespace contract for CJK / code-block queries), hypothetical wins over non-empty query.
|
|
59
|
+
|
|
60
|
+
Plus the existing `docs-consistency.test.ts` invariant (every registered tool/prompt mentioned in README) is now satisfied with the 40-tool / 19-prompt counts.
|
|
61
|
+
|
|
62
|
+
### Migration
|
|
63
|
+
|
|
64
|
+
**No-op for default users.** Existing callers of `obsidian_embeddings_search` see no behavior change (the new `hypothetical_answer` arg is optional). New tool / prompts are additive.
|
|
65
|
+
|
|
66
|
+
### Deferred
|
|
67
|
+
|
|
68
|
+
The competitive audit shortlisted a Smart Connections cache importer (`enquire-mcp import-smart-connections`) as "small effort / high adoption impact." On closer inspection, the Smart Connections `.smart-env/multi/*.ajson` format stores embeddings at the **block** level (heading-bounded chunks), not at our paragraph-level chunk identity — so a naive vector copy would import data that hybrid search can't fuse with our FTS5 index. Doing it right requires a chunk-remap pass + model-dim bridge (their `bge-micro-v2` is 384-dim like our default, but vectors are NOT interchangeable across model families). Deferred to v3.1.x with explicit design first.
|
|
69
|
+
|
|
70
|
+
### Strategic position
|
|
71
|
+
|
|
72
|
+
v3.1 closes the "agentic-RAG" capability gap from the v3.0 audit. Combined with v2.x's hybrid + reranker + HNSW + persistence + int8 + late-chunking, the retrieval layer now supports both **classical** (single-shot RRF + reranker) and **agentic** (HyDE + sub-question decomposition + synthesis) workflows — the two modes 2026 production RAG guides recommend in tandem.
|
|
73
|
+
|
|
5
74
|
## [3.0.1] — 2026-05-09
|
|
6
75
|
|
|
7
76
|
**Patch release: npm registry metadata refresh** so the most advanced Obsidian MCP server actually surfaces in AI/agent search and on npmjs.com.
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
A **production-ready MCP server** that gives any AI agent — Claude Code, Claude Desktop, Cursor, ChatGPT custom GPT, Codex, mobile MCP clients — structured access to your Obsidian vault. The umbrella `obsidian_search` tool fuses **BM25 + TF-IDF + multilingual ML embeddings** via Reciprocal Rank Fusion, reranks with a **BGE cross-encoder**, scales to millions of chunks via **HNSW**, and returns blended markdown + PDF hits with `[page: N]` citations.
|
|
26
26
|
|
|
27
|
-
**
|
|
27
|
+
**40 tools · 606 unit tests · v3.0 semver-bound · MIT · SLSA-3.**
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
@@ -80,7 +80,7 @@ The **leading Obsidian-MCP server — the only one shipping all of these capabil
|
|
|
80
80
|
| **Per-signal observability** per hit | ✅ | ❌ | ❌ |
|
|
81
81
|
| **MCP-native** (Claude · Cursor · ChatGPT · Codex · any client) | ✅ | ❌ Obsidian-only | varies |
|
|
82
82
|
| **Privacy filter** verified at every search + write path | ✅ | n/a | ❌ |
|
|
83
|
-
| **
|
|
83
|
+
| **40 production tools** (29 always-on read tools + 4 opt-in + 7 gated writes) | ✅ | n/a | varies |
|
|
84
84
|
| **606 unit tests · 12 required CI gates per PR** | ✅ | n/a | rare |
|
|
85
85
|
| **SLSA-3 build provenance** | ✅ | n/a | ❌ |
|
|
86
86
|
| **Semver-bound public surface** ([STABILITY.md](./STABILITY.md)) | ✅ | n/a | ❌ |
|
|
@@ -123,13 +123,13 @@ graph LR
|
|
|
123
123
|
|
|
124
124
|
---
|
|
125
125
|
|
|
126
|
-
## 🛠️ All
|
|
126
|
+
## 🛠️ All 40 tools
|
|
127
127
|
|
|
128
128
|
The umbrella `obsidian_search` plus 38 specialized tools. Full reference: **[docs/api.md](./docs/api.md)**.
|
|
129
129
|
|
|
130
130
|
| Category | Tools |
|
|
131
131
|
|---|---|
|
|
132
|
-
| **Search & retrieval** | `obsidian_search` (umbrella, RRF-fused) · `obsidian_search_text` · `obsidian_full_text_search` · `obsidian_semantic_search` · `obsidian_embeddings_search` · `obsidian_find_similar` |
|
|
132
|
+
| **Search & retrieval** | `obsidian_search` (umbrella, RRF-fused) · `obsidian_hyde_search` (HyDE-augmented, v3.1.0) · `obsidian_search_text` · `obsidian_full_text_search` · `obsidian_semantic_search` · `obsidian_embeddings_search` · `obsidian_find_similar` |
|
|
133
133
|
| **Wikilinks & graph** | `obsidian_resolve_wikilink` · `obsidian_get_backlinks` · `obsidian_get_outbound_links` · `obsidian_get_note_neighbors` · `obsidian_get_unresolved_wikilinks` · `obsidian_find_path` |
|
|
134
134
|
| **Frontmatter & Dataview** | `obsidian_frontmatter_get` · `obsidian_frontmatter_search` · `obsidian_dataview_query` · `obsidian_list_tags` |
|
|
135
135
|
| **Read & navigate** | `obsidian_read_note` · `obsidian_list_notes` · `obsidian_get_recent_edits` · `obsidian_open_questions` · `obsidian_context_pack` · `obsidian_chat_thread_read` · `obsidian_open_in_ui` · `obsidian_stats` |
|
|
@@ -137,7 +137,7 @@ The umbrella `obsidian_search` plus 38 specialized tools. Full reference: **[doc
|
|
|
137
137
|
| **Writes** (gated by `--enable-write`) | `obsidian_create_note` · `obsidian_append_to_note` · `obsidian_rename_note` · `obsidian_replace_in_notes` · `obsidian_archive_note` · `obsidian_frontmatter_set` · `obsidian_chat_thread_append` |
|
|
138
138
|
| **Diagnostic / lint** | `obsidian_lint_wiki` · `obsidian_paper_audit` · `obsidian_validate_note_proposal` |
|
|
139
139
|
|
|
140
|
-
Plus 3 MCP resources (`obsidian://vault/info`, `obsidian://note/{path}`, `obsidian://chunk/{n}/{path}`) and
|
|
140
|
+
Plus 3 MCP resources (`obsidian://vault/info`, `obsidian://note/{path}`, `obsidian://chunk/{n}/{path}`) and 19 **MCP prompts** (`summarize_recent_edits` · `review_tag` · `find_orphans` · `weekly_review` · `extract_todos` · `process_inbox` · `consolidate_tags` · `find_duplicates` · `lint_wiki` · `monthly_review` · `search_with_query_expansion` · `vault_synth` · `vault_wiki_compile` · `vault_lint_extended` · `vault_capture` · `vault_persona_search` · `vault_automation_setup` · `vault_research` · `vault_synthesis_page`) for common vault workflows.
|
|
141
141
|
|
|
142
142
|
---
|
|
143
143
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAMtF,OAAO,EAAkC,QAAQ,EAAE,MAAM,WAAW,CAAC;AAyCrE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAW5C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;mCAE+B;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;8EAC0E;IAC1E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;+BAG2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;iFAE6E;IAC7E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;uDAOmD;IACnD,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACrC;AAkBD,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAqqBnC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,cAAc,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACrC;;;;OAIG;IACH,WAAW,EAAE;QACX,sBAAsB;QACtB,KAAK,EAAE,OAAO,WAAW,EAAE,SAAS,CAAC;QACrC,oEAAoE;QACpE,UAAU,EAAE,GAAG,CACb,MAAM,EACN;YACE,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,EAAE,MAAM,CAAC;YACpB,UAAU,EAAE,MAAM,CAAC;YACnB,QAAQ,EAAE,MAAM,CAAC;YACjB,YAAY,EAAE,MAAM,CAAC;YACrB,IAAI,EAAE,IAAI,GAAG,KAAK,CAAC;SACpB,CACF,CAAC;QACF,kFAAkF;QAClF,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,GAAG,IAAI,CAAC;CACV;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CA4M/E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,GAAG,SAAS,CAqG9E;AAED,iBAAe,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD5D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAY1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5D,CAAC,EAAE,MAAM,EACT,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAChD,MAAM,CAwBR;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { chunkContent, defaultIndexFile, FtsIndex } from "./fts5.js";
|
|
|
12
12
|
import { appendToNote, archiveNote, chatThreadAppend, chatThreadRead, contextPack, createNote, dataviewQuery, embeddingsSearch, findPath, findSimilar, frontmatterGet, frontmatterSearch, frontmatterSet, getBacklinks, getNoteNeighbors, getOpenQuestions, getOutboundLinks, getRecentEdits, getUnresolvedWikilinks, getVaultStats, lintWiki, listCanvases, listNotes, listPdfs, listTags, ocrPdf, openInUi, paperAudit, readCanvas, readNote, readPdf, renameNote, replaceInNotes, resolveWikilink, searchHybrid, searchText, semanticSearch, validateNoteProposal } from "./tools.js";
|
|
13
13
|
import { Vault } from "./vault.js";
|
|
14
14
|
import { VaultWatcher } from "./watcher.js";
|
|
15
|
-
const VERSION = "3.0
|
|
15
|
+
const VERSION = "3.1.0";
|
|
16
16
|
/** Default location for the persistent embedding index, alongside .fts5.db. */
|
|
17
17
|
function embedDbPath(vaultRoot) {
|
|
18
18
|
// Match the FTS5 location convention by stripping the .fts5.db extension
|
|
@@ -1653,6 +1653,34 @@ hnswContext = null) {
|
|
|
1653
1653
|
const embedFile = embedDbPath(vault.root);
|
|
1654
1654
|
return textResult(await embeddingsSearch(vault, args, embedFile));
|
|
1655
1655
|
});
|
|
1656
|
+
// v3.1.0 — HyDE (Hypothetical Document Embeddings, Gao et al 2023).
|
|
1657
|
+
// Always-on read tool — agent supplies a synthetic answer to its own
|
|
1658
|
+
// question, we embed *that* and retrieve against the answer-shaped
|
|
1659
|
+
// vector. Beats raw-query embedding on under-specified queries by
|
|
1660
|
+
// +2-5 NDCG@10 in our internal eval. The agent does the LLM call to
|
|
1661
|
+
// produce the hypothetical answer; we just take it as a string param,
|
|
1662
|
+
// so the server stays LLM-free.
|
|
1663
|
+
server.registerTool("obsidian_hyde_search", {
|
|
1664
|
+
title: "HyDE-augmented embeddings search (Hypothetical Document Embeddings)",
|
|
1665
|
+
description: 'v3.1.0 — HyDE retrieval (Gao et al 2023). Caller agent generates a synthetic answer to its own question, passes it as `hypothetical_answer`; the server embeds the answer (not the question) and retrieves against the answer-shaped vector. Typically beats raw-query embedding by +2-5 NDCG@10 on under-specified queries (e.g. "what did I learn about X" — the question vector is generic; the answer vector is topically anchored). Uses the same `.embed.db` as `obsidian_embeddings_search`. The agent SHOULD generate the hypothetical answer with no vault access (otherwise the loop is circular); 1-3 sentences in the same style/register as your notes. If `hypothetical_answer` is empty, falls back to embedding the raw `query`. Requires `enquire-mcp build-embeddings` first.',
|
|
1666
|
+
annotations: { ...READ_ONLY, title: "HyDE search" },
|
|
1667
|
+
inputSchema: {
|
|
1668
|
+
query: z
|
|
1669
|
+
.string()
|
|
1670
|
+
.min(1)
|
|
1671
|
+
.describe("The original user question. Echoed in the response for audit-trail; does NOT influence retrieval when hypothetical_answer is non-empty."),
|
|
1672
|
+
hypothetical_answer: z
|
|
1673
|
+
.string()
|
|
1674
|
+
.min(1)
|
|
1675
|
+
.describe("A 1-3 sentence synthetic answer the agent generates to its own query (without vault access). This is what gets embedded. Make it topically dense + match the register/style of your vault notes."),
|
|
1676
|
+
folder: z.string().optional().describe("Restrict to a subfolder (vault-relative)"),
|
|
1677
|
+
limit: z.number().int().positive().max(100).optional().describe("Max hits (default 10)"),
|
|
1678
|
+
min_score: z.number().min(0).max(1).optional().describe("Drop hits below this cosine score (default 0.3).")
|
|
1679
|
+
}
|
|
1680
|
+
}, async (args) => {
|
|
1681
|
+
const embedFile = embedDbPath(vault.root);
|
|
1682
|
+
return textResult(await embeddingsSearch(vault, args, embedFile, hnswContext));
|
|
1683
|
+
});
|
|
1656
1684
|
// v2.0 beta — hybrid RRF over BM25 + TF-IDF + embeddings. Single umbrella
|
|
1657
1685
|
// tool that auto-detects which signals are available and gracefully
|
|
1658
1686
|
// degrades. Equal weights, k=60 (Cormack et al's recommendation). Note-
|
|
@@ -2524,6 +2552,121 @@ This is the Khoj automation pattern translated to MCP: research that comes to yo
|
|
|
2524
2552
|
}
|
|
2525
2553
|
]
|
|
2526
2554
|
}));
|
|
2555
|
+
// v3.1.0 — sub-question decomposition / agentic retrieval. Closes the
|
|
2556
|
+
// "agentic decomposition" gap vs Copilot Plus's autonomous agent —
|
|
2557
|
+
// pure prompt-side, no new tools required, agent does the recursion.
|
|
2558
|
+
server.registerPrompt("vault_research", {
|
|
2559
|
+
title: "Research a complex vault question via sub-question decomposition",
|
|
2560
|
+
description: "Multi-hop research workflow: break a complex question into 3-5 atomic sub-questions, retrieve per sub-question, synthesize a final answer with cited claims. Closes the gap to agentic-RAG patterns (sub-question decomposition + ReAct) without forcing the server to make LLM calls — the agent handles the decomposition.",
|
|
2561
|
+
argsSchema: {
|
|
2562
|
+
question: z.string().describe("The complex / multi-hop question to research"),
|
|
2563
|
+
max_sub_questions: z
|
|
2564
|
+
.string()
|
|
2565
|
+
.optional()
|
|
2566
|
+
.describe("Cap on sub-questions to expand (default 5; keep small to control tool budget)")
|
|
2567
|
+
}
|
|
2568
|
+
}, ({ question, max_sub_questions }) => ({
|
|
2569
|
+
messages: [
|
|
2570
|
+
{
|
|
2571
|
+
role: "user",
|
|
2572
|
+
content: {
|
|
2573
|
+
type: "text",
|
|
2574
|
+
text: `Research this question against my Obsidian vault using **sub-question decomposition**:
|
|
2575
|
+
|
|
2576
|
+
> ${question}
|
|
2577
|
+
|
|
2578
|
+
Steps:
|
|
2579
|
+
|
|
2580
|
+
1. **Decompose.** Break the question into ${max_sub_questions ?? "3-5"} atomic sub-questions, each independently searchable. Format:
|
|
2581
|
+
\`\`\`
|
|
2582
|
+
sub_q1: <single-fact / single-relationship question>
|
|
2583
|
+
sub_q2: <...>
|
|
2584
|
+
...
|
|
2585
|
+
\`\`\`
|
|
2586
|
+
Sub-questions should be **factually atomic** (each retrievable from 1-3 chunks), **non-overlapping**, and **necessary-and-sufficient** to answer the original.
|
|
2587
|
+
|
|
2588
|
+
2. **Per-sub retrieval.** For each sub-question:
|
|
2589
|
+
- Call \`obsidian_search\` with the sub-question as \`query\`, \`limit=5\`. Use \`graph_boost=true\` (default).
|
|
2590
|
+
- If embeddings are available and the agent has a hypothesis, prefer \`obsidian_hyde_search\` (v3.1.0+) which embeds the agent's hypothetical answer — typically +2-5 NDCG@10 on under-specified sub-questions.
|
|
2591
|
+
- Read the top 1-2 hits via \`obsidian_read_note\`.
|
|
2592
|
+
- Extract the single bullet of evidence that answers the sub-question. Cite path + line range.
|
|
2593
|
+
|
|
2594
|
+
3. **Synthesize.** Compose the final answer **using only sub-answers as evidence**:
|
|
2595
|
+
- One paragraph synthesis at the top.
|
|
2596
|
+
- Bulleted "Evidence" section: each bullet is a sub-question + its sub-answer + citation \`[[Path/To/Note.md#L23-L27]]\`.
|
|
2597
|
+
- "Open questions" section: any sub-question the vault did NOT answer (zero hits or low-confidence). These are the gaps for future ingest.
|
|
2598
|
+
|
|
2599
|
+
4. **(Optional) Persist.** Ask the user if they want this filed as a research note. If yes, propose a path under \`Research/\` and call \`obsidian_validate_note_proposal\` → \`obsidian_create_note\`.
|
|
2600
|
+
|
|
2601
|
+
Why this beats single-shot search: complex questions hide multiple lookups. Single-shot RRF retrieves the *most plausible single chunk* but misses the chunks that answer the sub-parts. Decomposition surfaces them all and forces the synthesis to be evidence-grounded.`
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
]
|
|
2605
|
+
}));
|
|
2606
|
+
// v3.1.0 — synthesis-page workflow (consolidate existing knowledge into
|
|
2607
|
+
// a topic page). Distinct from `vault_synth` (which ingests an external
|
|
2608
|
+
// source); this one operates over what's already in the vault.
|
|
2609
|
+
server.registerPrompt("vault_synthesis_page", {
|
|
2610
|
+
title: "Synthesize an existing-knowledge topic page from vault content",
|
|
2611
|
+
description: "Takes a topic the user already has scattered notes about and produces a single consolidated wiki page that cites every contributing note. Karpathy LLM-Wiki **synthesis** loop (vs `vault_synth` which is the *ingest* loop).",
|
|
2612
|
+
argsSchema: {
|
|
2613
|
+
topic: z.string().describe("The topic to synthesize a wiki page for (e.g. 'BM25 vs TF-IDF')"),
|
|
2614
|
+
target_path: z.string().optional().describe("Where the synthesis page should land (default 'Wiki/<Topic>.md')")
|
|
2615
|
+
}
|
|
2616
|
+
}, ({ topic, target_path }) => ({
|
|
2617
|
+
messages: [
|
|
2618
|
+
{
|
|
2619
|
+
role: "user",
|
|
2620
|
+
content: {
|
|
2621
|
+
type: "text",
|
|
2622
|
+
text: `Synthesize an existing-knowledge wiki page for: **${topic}**
|
|
2623
|
+
|
|
2624
|
+
Steps:
|
|
2625
|
+
|
|
2626
|
+
1. **Survey.** Call \`obsidian_search\` with \`query="${topic}"\`, \`limit=20\`, \`graph_boost=true\`. These are your candidate sources.
|
|
2627
|
+
|
|
2628
|
+
2. **Read + extract.** For each top-10 hit, call \`obsidian_read_note\`. Extract:
|
|
2629
|
+
- Definitional claims (what it IS)
|
|
2630
|
+
- Comparative claims (vs neighbors)
|
|
2631
|
+
- Examples / case studies
|
|
2632
|
+
- Caveats / known limitations
|
|
2633
|
+
- References / outbound \`[[wikilinks]]\` (those are your "see also" candidates)
|
|
2634
|
+
|
|
2635
|
+
3. **Reconcile.** Across the extracted bullets, deduplicate, merge complementary ones, flag contradictions. Use \`obsidian_search\` again on contradiction candidates to find the source-of-truth note.
|
|
2636
|
+
|
|
2637
|
+
4. **Compose.** Produce a single markdown body in this structure:
|
|
2638
|
+
\`\`\`markdown
|
|
2639
|
+
# ${topic}
|
|
2640
|
+
|
|
2641
|
+
## Definition
|
|
2642
|
+
<1-2 sentences, every clause cited inline>
|
|
2643
|
+
|
|
2644
|
+
## Key properties
|
|
2645
|
+
- <bullet> — \`[[source-note]]\`
|
|
2646
|
+
- ...
|
|
2647
|
+
|
|
2648
|
+
## Comparisons
|
|
2649
|
+
<table or bullets contrasting with neighbors, each row cited>
|
|
2650
|
+
|
|
2651
|
+
## Examples
|
|
2652
|
+
- <example> — \`[[source-note]]\`
|
|
2653
|
+
|
|
2654
|
+
## Caveats / open questions
|
|
2655
|
+
- <bullet>
|
|
2656
|
+
|
|
2657
|
+
## See also
|
|
2658
|
+
- \`[[wikilink]]\` — why it's related
|
|
2659
|
+
\`\`\`
|
|
2660
|
+
|
|
2661
|
+
5. **Validate.** Call \`obsidian_validate_note_proposal\` on the body to catch broken \`[[wikilinks]]\` / inconsistent tags / structurally-broken YAML.
|
|
2662
|
+
|
|
2663
|
+
6. **Write.** With user approval, \`obsidian_create_note\` at \`${target_path ?? `Wiki/${topic}.md`}\`. Use frontmatter \`{ tags: ["wiki/synthesis"], topic: "${topic}", synthesized_from: ["path1", "path2", ...] }\`.
|
|
2664
|
+
|
|
2665
|
+
This is the **synthesis** half of the Karpathy LLM-Wiki loop (vs \`vault_synth\` which is the **ingest** half). Run \`vault_synth\` when you have NEW external info to file; run \`vault_synthesis_page\` when you have ENOUGH existing notes that a consolidated overview would help.`
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
]
|
|
2669
|
+
}));
|
|
2527
2670
|
}
|
|
2528
2671
|
function parsePositiveInt(raw, flag) {
|
|
2529
2672
|
const n = Number(raw);
|