@oomkapwn/enquire-mcp 3.5.8 → 3.5.10

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/docs/api.md CHANGED
@@ -1,17 +1,80 @@
1
1
  # enquire — API
2
2
 
3
- **enquire is an MCP server for Obsidian vaults.** 40 MCP tools (29 always-on read + 4 opt-in read + 7 opt-in write); the 4 opt-ins are: 1 via `--persistent-index` (`obsidian_full_text_search`) + 3 via `--diagnostic-search-tools` (the single-ranker `obsidian_search_text` / `obsidian_semantic_search` / `obsidian_embeddings_search` — gated by default in v2.0+ since `obsidian_search` auto-detects + fuses signals). 2 + 1 opt-in MCP resources, 19 MCP prompts. **v3.1.0+ adds `obsidian_hyde_search`** (HyDE-augmented retrieval, Gao et al 2023; agent supplies a synthetic answer, server embeds it for retrieval) plus the `vault_research` (sub-question decomposition) and `vault_synthesis_page` (Karpathy LLM-Wiki synthesis loop) prompts. v2.6.0+ also speaks Streamable HTTP via `serve-http` (bearer auth + rate-limit + CORS). v2.7.0+ indexes PDFs as a separate read tool surface; **v2.8.0+ blends PDF chunks into `obsidian_search` hybrid retrieval** with `--include-pdfs` — every hit carries a `kind: "md" | "pdf"` flag and PDF snippets include `[page: N]` markers for citation. **v2.9.0+ adds BGE cross-encoder reranking** on top of RRF with `--enable-reranker` — typical +5-10 NDCG@10 retrieval-quality boost. **v2.10.0+ adds Tesseract OCR for image-only / scanned PDFs** via `obsidian_ocr_pdf` — completes the PDF retrieval story.
3
+ **enquire is an MCP server for Obsidian vaults.** 44 MCP tools (33 always-on read + 4 opt-in read + 7 opt-in write); the 4 opt-ins are: 1 via `--persistent-index` (`obsidian_full_text_search`) + 3 via `--diagnostic-search-tools` (the single-ranker `obsidian_search_text` / `obsidian_semantic_search` / `obsidian_embeddings_search` — gated by default in v2.0+ since `obsidian_search` auto-detects + fuses signals). 2 + 1 opt-in MCP resources, 19 MCP prompts. **v3.1.0+ adds `obsidian_hyde_search`** (HyDE-augmented retrieval, Gao et al 2023; agent supplies a synthetic answer, server embeds it for retrieval) plus the `vault_research` (sub-question decomposition) and `vault_synthesis_page` (Karpathy LLM-Wiki synthesis loop) prompts. v2.6.0+ also speaks Streamable HTTP via `serve-http` (bearer auth + rate-limit + CORS). v2.7.0+ indexes PDFs as a separate read tool surface; **v2.8.0+ blends PDF chunks into `obsidian_search` hybrid retrieval** with `--include-pdfs` — every hit carries a `kind: "md" | "pdf"` flag and PDF snippets include `[page: N]` markers for citation. **v2.9.0+ adds BGE cross-encoder reranking** on top of RRF with `--enable-reranker` — typical +5-10 NDCG@10 retrieval-quality boost. **v2.10.0+ adds Tesseract OCR for image-only / scanned PDFs** via `obsidian_ocr_pdf` — completes the PDF retrieval story.
4
4
 
5
5
  > **Channels:** stable v1.x (`@latest` on npm) ships 28 tools — no ML embeddings, no hybrid search. **v2.0 beta** (`@beta` on npm) adds `obsidian_search` (hybrid RRF) + `obsidian_embeddings_search` + the `install-model` / `build-embeddings` / `clear-embeddings` subcommands. This document covers the **v2.0 beta** surface.
6
6
 
7
7
  > Versioned dynamically — see [`CHANGELOG.md`](../CHANGELOG.md) for the current release.
8
8
 
9
+ ## Tool index
10
+
11
+ Canonical list of every registered MCP tool. The `Kind` column splits read/write; `Gating` calls out CLI flags required to register the tool (else `always`). The new-tool invariant in `tests/docs-consistency.test.ts` parses this table and fails CI if any registered tool is missing.
12
+
13
+ ### Read tools — always registered
14
+
15
+ | Tool | Kind | Gating | Summary |
16
+ |---|---|---|---|
17
+ | `obsidian_list_notes` | read | always | List markdown notes filtered by tag / folder / mtime — newest-first. |
18
+ | `obsidian_read_note` | read | always | Read a note by `path` or `title` (full body or heading-only map). |
19
+ | `obsidian_resolve_wikilink` | read | always | Resolve `[[wikilink]]` (alias / section / block / relative) to a vault file. |
20
+ | `obsidian_get_recent_edits` | read | always | List notes ordered by most recent modification. |
21
+ | `obsidian_get_backlinks` | read | always | List every note that links (or embeds) the target note, ranked. |
22
+ | `obsidian_list_tags` | read | always | List unique tags with frontmatter / inline usage counts. |
23
+ | `obsidian_dataview_query` | read | always | Run a Dataview-style `LIST` / `TABLE` query (subset DSL). |
24
+ | `obsidian_get_unresolved_wikilinks` | read | always | Find every `[[wikilink]]` whose target does not resolve to a real file. |
25
+ | `obsidian_get_outbound_links` | read | always | List every outbound wikilink / embed in a note with resolution status. |
26
+ | `obsidian_validate_note_proposal` | read | always | Lint a draft note BEFORE writing — YAML / wikilinks / tags / collisions. |
27
+ | `obsidian_find_similar` | read | always | Lexical-hybrid similarity (tags / 3-grams / shared outbound / co-backlinks). |
28
+ | `obsidian_get_note_neighbors` | read | always | Return a note + its 1-hop graph neighborhood (outbound / inbound / tag siblings). |
29
+ | `obsidian_stats` | read | always | Vault dashboard — totals, recent edits, orphans, broken links, top tags. |
30
+ | `obsidian_lint_wiki` | read | always | Karpathy LLM-Wiki lint — orphans / broken / stubs / stale / concept candidates. |
31
+ | `obsidian_open_questions` | read | always | Surface deferred-thinking markers (`Open question:` / `Q:` / `TODO?` / `??`) across notes. |
32
+ | `obsidian_paper_audit` | read | always | Flag `#paper` notes missing a citable identifier (arxiv / doi / url / isbn). |
33
+ | `obsidian_find_path` | read | always | BFS shortest wikilink path between two notes (with alternatives). |
34
+ | `obsidian_open_in_ui` | read | always | Generate an `obsidian://open` URI for hand-off to the desktop app. |
35
+ | `obsidian_list_canvases` | read | always | List `.canvas` files (whiteboard format) with node + edge counts. |
36
+ | `obsidian_read_canvas` | read | always | Parse one `.canvas` file into typed nodes (text / file / link / group) + edges. |
37
+ | `obsidian_get_communities` | read | always | Detect wikilink-graph communities via greedy modularity (GraphRAG-light). |
38
+ | `obsidian_list_bases` | read | always | List `.base` files (Obsidian's structured-query primitive) with view counts. |
39
+ | `obsidian_read_base` | read | always | Parse a `.base` file into structured JSON (filters / formulas / properties / views). |
40
+ | `obsidian_query_base` | read | always | Execute a `.base` file's filter against the vault, returning matching notes. |
41
+ | `obsidian_list_pdfs` | read | always | List `.pdf` files in the vault with size + mtime. |
42
+ | `obsidian_read_pdf` | read | always | Extract per-page text + `full_text` + doc-level metadata from a PDF. |
43
+ | `obsidian_ocr_pdf` | read | always | Tesseract OCR for image-only / scanned PDFs (multilingual via `lang`). |
44
+ | `obsidian_hyde_search` | read | always | HyDE retrieval — agent supplies a synthetic answer; server embeds it for retrieval. |
45
+ | `obsidian_search` | read | always | Hybrid retrieval — BM25 + TF-IDF + embeddings fused via RRF (v2.0 default). |
46
+ | `obsidian_chat_thread_read` | read | always | Parse a note's `## Chat: <title>` block into structured messages. |
47
+ | `obsidian_context_pack` | read | always | Retrieve + pack vault context for a question to a token budget. |
48
+ | `obsidian_frontmatter_get` | read | always | Read parsed YAML frontmatter (full object or single key). |
49
+ | `obsidian_frontmatter_search` | read | always | Find notes where `frontmatter.<key>` matches `equals` / `exists` / `contains`. |
50
+
51
+ ### Read tools — opt-in (diagnostic / index-gated)
52
+
53
+ | Tool | Kind | Gating | Summary |
54
+ |---|---|---|---|
55
+ | `obsidian_full_text_search` | read | `--persistent-index` (+ `--diagnostic-search-tools`) | BM25-ranked search over a SQLite FTS5 inverted index. |
56
+ | `obsidian_search_text` | read | `--diagnostic-search-tools` | Case-insensitive token search (AND / OR / phrase modes). |
57
+ | `obsidian_semantic_search` | read | `--diagnostic-search-tools` | Pure-JS TF-IDF cosine retrieval (no model download). |
58
+ | `obsidian_embeddings_search` | read | `--diagnostic-search-tools` | ML-embedding retrieval via `@huggingface/transformers` (persistent vector index). |
59
+
60
+ ### Write tools — opt-in (`--enable-write`)
61
+
62
+ | Tool | Kind | Gating | Summary |
63
+ |---|---|---|---|
64
+ | `obsidian_create_note` | write | `--enable-write` | Create a new note (refuses to overwrite unless `overwrite=true`). |
65
+ | `obsidian_append_to_note` | write | `--enable-write` | Append a markdown block to the end of an existing note. |
66
+ | `obsidian_rename_note` | write | `--enable-write` | Atomically rename a note AND rewrite every `[[wikilink]]` / `![[embed]]` pointing at it (code-fence-aware). |
67
+ | `obsidian_replace_in_notes` | write | `--enable-write` | Bulk find/replace across notes outside fenced code blocks. |
68
+ | `obsidian_archive_note` | write | `--enable-write` | Move a note into `Archive/` and rewrite backlinks (`rename_note` wrapper). |
69
+ | `obsidian_chat_thread_append` | write | `--enable-write` | Append a user/assistant/system message to a note's `## Chat: <title>` block. |
70
+ | `obsidian_frontmatter_set` | write | `--enable-write` | Set or unset frontmatter keys atomically (pass `null` to delete). |
71
+
9
72
  ## CLI flags
10
73
 
11
74
  | Flag | Default | Notes |
12
75
  |------------------------|---------|--------------------------------------------|
13
76
  | `--vault <path>` | (required) | Path to the Obsidian vault root. |
14
- | `--enable-write` | off | Register the five write tools. |
77
+ | `--enable-write` | off | Register the seven write tools. |
15
78
  | `--max-file-bytes <n>` | 5 MB | Max size for any single file read/write. |
16
79
  | `--cache-size <n>` | 1024 | LRU cap for parsed-note cache. |
17
80
  | `--persistent-cache` | off | Persist parsed-note cache to disk so cold starts skip re-parsing. **Stores full note bodies — see [Cache & privacy](../README.md#cache--privacy).** |
@@ -476,9 +539,155 @@ If the index is missing, the tool returns a clean error pointing at `enquire-mcp
476
539
 
477
540
  **Why prefer this over the per-ranker tools?** Single tool surface for agents → consistent recall regardless of vault setup. Per-ranker tools (`obsidian_search_text`, `obsidian_full_text_search`, `obsidian_semantic_search`, `obsidian_embeddings_search`) remain available as diagnostic surfaces for tuning / debugging.
478
541
 
542
+ ## `obsidian_hyde_search` _(v3.1.0)_
543
+
544
+ HyDE retrieval (Gao et al 2023). The caller agent generates a 1–3 sentence synthetic answer to its own query (without vault access); 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. Uses the same `.embed.db` as `obsidian_embeddings_search`. Requires `enquire-mcp build-embeddings` first; if `hypothetical_answer` is empty, falls back to embedding the raw `query`.
545
+
546
+ | Argument | Type | Notes |
547
+ |------------------------|------------------|----------------------------------------------------------------------------------------|
548
+ | `query` | `string` | Required. The original user question; echoed for audit-trail. Does NOT influence retrieval when `hypothetical_answer` is non-empty. |
549
+ | `hypothetical_answer` | `string` | Required. The 1–3 sentence synthetic answer the agent generates. This is what gets embedded. |
550
+ | `folder` | `string?` | Restrict to a subfolder. |
551
+ | `limit` | `number?` (≤ 100)| Max hits. Default 10. |
552
+ | `min_score` | `number?` (0–1) | Drop hits below this cosine score. Default 0.3. |
553
+
554
+ **Returns:** Same shape as `obsidian_embeddings_search` plus an `applied_hyde: true` echo so the caller can confirm which branch ran.
555
+
556
+ ## `obsidian_context_pack`
557
+
558
+ Given a question, retrieves top-relevant notes (via `obsidian_search`), gathers backlink summaries + optional recent dailies, deduplicates, packs to a token budget, and returns a single ready-to-paste markdown bundle. Saves ~5 separate tool calls; produces a coherent context blob you can paste into any AI chat.
559
+
560
+ | Argument | Type | Notes |
561
+ |---------------------|-------------------|------------------------------------------------------------------------|
562
+ | `query` | `string` | Required. Topic or question to gather context for. |
563
+ | `budget_tokens` | `number?` (≤ 32000)| Approximate token budget. Default 4000 (~4 chars/token). |
564
+ | `folder` | `string?` | Restrict retrieval to a folder. |
565
+ | `include_backlinks` | `boolean?` | Include 1-line backlink summaries for top-3 notes. Default `true`. |
566
+ | `recent_dailies` | `number?` (0–30) | Include the last N daily-format notes (`YYYY-MM-DD` basenames). Default 0. |
567
+
568
+ **Returns:** `{ query, budget_tokens, included_notes: [{ path, title, reason }], markdown }`. `markdown` is the packed bundle, ready to paste.
569
+
570
+ ## `obsidian_chat_thread_read`
571
+
572
+ Parse a note's `## Chat: <title>` block into structured messages with role / timestamp / content / line-range. Non-chat content in the same note is ignored.
573
+
574
+ | Argument | Type | Notes |
575
+ |-------------|----------|------------------------------------------------------|
576
+ | `note_path` | `string` | Required. Vault-relative path to the note hosting the thread. |
577
+
578
+ **Returns:** `{ note_path, threads: Array<{ title, messages: Array<{ role, content, timestamp?, line_start, line_end }> }> }`.
579
+
580
+ ## `obsidian_frontmatter_get`
581
+
582
+ Return parsed YAML frontmatter for a note. With `key`, returns just that field's value; without `key`, returns the whole frontmatter object.
583
+
584
+ | Argument | Type | Notes |
585
+ |----------|-----------|------------------------------------------------------|
586
+ | `path` | `string?` | Vault-relative path. |
587
+ | `title` | `string?` | Note title (filename without `.md`; periodic aliases accepted). |
588
+ | `key` | `string?` | Single key to read; omit for full frontmatter. |
589
+
590
+ **Returns:** `{ path, title, frontmatter }` (full mode) or `{ path, title, key, value }` (single-key mode). `value` is `null` when the key is absent.
591
+
592
+ ## `obsidian_frontmatter_search`
593
+
594
+ Find every note where `frontmatter.<key>` matches a predicate. Useful as a precursor to bulk `frontmatter_set`: *find all notes with `status: draft` and set their status to `published`*. Predicates are exclusive — pass exactly one of `equals` / `exists` / `contains`.
595
+
596
+ | Argument | Type | Notes |
597
+ |------------|--------------------|----------------------------------------------------------------------|
598
+ | `key` | `string` | Required. Frontmatter key to test. |
599
+ | `equals` | `unknown?` | Strict equality predicate (`JSON.stringify` comparison). |
600
+ | `exists` | `boolean?` | Predicate: key must exist (any value). |
601
+ | `contains` | `unknown?` | For array values, value must be a member. |
602
+ | `folder` | `string?` | Restrict search to a folder. |
603
+ | `limit` | `number?` (≤ 1000) | Max matches. Default 100. |
604
+
605
+ **Returns:** `Array<{ path, title, value, mtime }>`.
606
+
607
+ ## `obsidian_get_communities` _(v3.4.0)_
608
+
609
+ GraphRAG-light. Builds an undirected wikilink graph and partitions notes into structural communities via greedy modularity optimization (single-phase Louvain). Pure structural — no embeddings, no LLM calls. The agent can summarize a community by reading its `representative` (the highest-in-community-degree note) + a sample of members.
610
+
611
+ | Argument | Type | Notes |
612
+ |-------------|------------------|-----------------------------------------------------------------------|
613
+ | `min_size` | `number?` (≤ 1000)| Drop communities with fewer than N members. Default 1 (keep singletons). |
614
+ | `limit` | `number?` (≤ 500)| Max communities to return (size-desc sort). Default 50. |
615
+
616
+ **Returns:** `{ community_count, modularity, iterations, node_count, communities: [{ id, size, members: string[], representative }], membership: Record<string, number> }`. `modularity` ∈ [-0.5, 1] — higher = stronger structure. NOT cached server-side; call once per session and reuse.
617
+
618
+ ## `obsidian_list_bases` _(v3.2.0)_
619
+
620
+ Lists `.base` files (Obsidian's structured-query primitive — YAML files defining filters/views over the vault) with each base's view count and view names. Honors `--exclude-glob` and `--read-paths`. Sorted newest-first by mtime.
621
+
622
+ | Argument | Type | Notes |
623
+ |----------|------------------|---------------------------------------------|
624
+ | `folder` | `string?` | Restrict the listing to a subfolder. |
625
+ | `limit` | `number?` (≤ 500)| Max bases to return. Default 100. |
626
+
627
+ **Returns:** `Array<{ path, name, size_bytes, mtime, view_count, view_names: string[] }>`.
628
+
629
+ ## `obsidian_read_base` _(v3.2.0)_
630
+
631
+ Parses a `.base` file into structured JSON (filters, formulas, properties, summaries, views). Does NOT execute the query — use `obsidian_query_base` for that. Useful when an agent wants to introspect a base before deciding which view to run.
632
+
633
+ | Argument | Type | Notes |
634
+ |----------|----------|--------------------------------------------------------|
635
+ | `path` | `string` | Required. Vault-relative path of the `.base` file (`.base` extension auto-appended). |
636
+
637
+ **Returns:** `{ path, name, size_bytes, mtime, filters, formulas, properties, summaries, views }`.
638
+
639
+ ## `obsidian_query_base` _(v3.2.0, extended in v3.5.0)_
640
+
641
+ Runs a `.base` file's filter against the vault's markdown notes, returning matching paths + the frontmatter values that contributed to the match. Supported DSL: `tag == "x"`, `taggedWith(file.file, "x")`, `linksTo(file.file, "Target")` (basename-resolved), `path startsWith / contains "X"`, `file.name == "X"`, `<frontmatter_key> == / != / contains <value>`, plus `and` / `or` / `not`. Anything else (formula evaluation, date arithmetic, summaries) is treated as `true` and surfaced in `unevaluated_predicates`.
642
+
643
+ | Argument | Type | Notes |
644
+ |----------|------------------|-------------------------------------------------------------------------------------------------------------|
645
+ | `path` | `string` | Required. Vault-relative path of the `.base` file. |
646
+ | `view` | `string?` | Optional view name; the view's filters are concat'd with the global filter via AND (matching Obsidian semantics). |
647
+ | `folder` | `string?` | Extra folder scope on top of the base's filters. |
648
+ | `limit` | `number?` (≤ 500)| Max matches to return. Default 50. |
649
+
650
+ **Returns:** `{ path, view, matches: Array<{ path, title, frontmatter_subset }>, unevaluated_predicates: string[] }`. Pair with `obsidian_search` for retrieval-quality search; this tool is for explicit saved queries.
651
+
652
+ ## `obsidian_list_pdfs` _(v2.7.0)_
653
+
654
+ Lists `.pdf` files in the vault with size + last-modified timestamp. Honors `--exclude-glob` and `--read-paths`. Use as the discovery entry point before calling `obsidian_read_pdf`. Sorted newest-first by mtime.
655
+
656
+ | Argument | Type | Notes |
657
+ |----------|------------------|---------------------------------------------|
658
+ | `folder` | `string?` | Restrict the listing to a subfolder. |
659
+ | `limit` | `number?` (≤ 500)| Max PDFs to return. Default 100. |
660
+
661
+ **Returns:** `Array<{ path, name, size_bytes, mtime }>`.
662
+
663
+ ## `obsidian_read_pdf` _(v2.7.0)_
664
+
665
+ Extracts plain text from one PDF, returning per-page text + a `full_text` join + doc-level metadata (title / author / subject / etc). Image-only / scanned PDFs surface `has_text: false` so agents can detect-and-recommend `obsidian_ocr_pdf`. Powered by Mozilla's PDF.js (Apache-2.0).
666
+
667
+ | Argument | Type | Notes |
668
+ |--------------------|-------------------------------------|----------------------------------------------------------------------|
669
+ | `path` | `string` | Required. Vault-relative path of the `.pdf` file. |
670
+ | `pages` | `[number, number]?` | Optional 1-indexed inclusive page range, e.g. `[2, 5]`. |
671
+ | `include_metadata` | `boolean?` | Include doc-level metadata. Default `true`. |
672
+
673
+ **Returns:** `{ path, page_count, pages: Array<{ index, text }>, full_text, has_text, metadata? }`. `has_text: false` indicates an image-only PDF — call `obsidian_ocr_pdf` instead.
674
+
675
+ ## `obsidian_ocr_pdf` _(v2.10.0)_
676
+
677
+ Runs Tesseract OCR over each page of an image-only / scanned PDF, returning per-page text + per-page confidence + mean confidence + the same shape as `obsidian_read_pdf`. Multilingual via `lang` (default `'eng'`; multi-lang via `'+'`, e.g. `'eng+rus'`). ~1–2s per page on M1 CPU. Powered by Tesseract.js (Apache-2.0; trained-data files download on first use into the local cache, ~10 MB per language) + `@napi-rs/canvas` for PDF→bitmap rendering. Both gated to `optionalDependencies` so the markdown-only path stays zero-cost.
678
+
679
+ | Argument | Type | Notes |
680
+ |----------|-------------------------------------|---------------------------------------------------------------------------------------------|
681
+ | `path` | `string` | Required. Vault-relative path of the `.pdf` file. |
682
+ | `lang` | `string?` | Tesseract language pack(s). Default `'eng'`. Multi-lang via `'+'`: `'eng+rus'`. Common: `'eng'`, `'rus'`, `'jpn'`, `'chi_sim'`, `'fra'`, `'deu'`. |
683
+ | `pages` | `[number, number]?` | Optional 1-indexed inclusive page range. |
684
+ | `scale` | `number?` (0.5–4) | Render scale (DPI multiplier). Default 2 (~150 DPI). Higher = better OCR on small text but slower. |
685
+
686
+ **Returns:** Same shape as `obsidian_read_pdf` plus `{ mean_confidence, pages: Array<{ index, text, confidence }> }`.
687
+
479
688
  ## Write tools (opt-in)
480
689
 
481
- All five write tools are **only registered when the server is started with `--enable-write`**. Without that flag the tools are not advertised to the client at all.
690
+ All seven write tools are **only registered when the server is started with `--enable-write`**. Without that flag the tools are not advertised to the client at all.
482
691
 
483
692
  ### `obsidian_create_note`
484
693
 
@@ -554,6 +763,32 @@ Convenience wrapper around `obsidian_rename_note` for the common archive workflo
554
763
 
555
764
  **Bare-vs-qualified backlinks.** Bare wikilinks (`[[Foo]]`) stay bare and continue to resolve via `findBestMatch`'s basename search — they don't need rewriting. Path-qualified wikilinks (`[[Inbox/Foo]]`) are updated to point at the new path.
556
765
 
766
+ ### `obsidian_chat_thread_append`
767
+
768
+ Add a user / assistant / system message to a note's `## Chat: <title>` block. Creates the note + heading if absent. Threads are stored as markdown so they're searchable, version-controllable, and survive across sessions / clients. Pair with `obsidian_chat_thread_read` to load past context.
769
+
770
+ | Argument | Type | Notes |
771
+ |----------------|-------------------------------------|----------------------------------------------------------------------|
772
+ | `note_path` | `string` | Required. Vault-relative path to the note hosting the thread. |
773
+ | `role` | `"user" \| "assistant" \| "system"` | Required. Role of the message being appended. |
774
+ | `content` | `string` | Required. Message body (markdown allowed). |
775
+ | `thread_title` | `string?` | Optional thread title — used when the note is created from scratch. |
776
+
777
+ **Returns:** `{ note_path, thread_title, role, line_start, line_end, appended_bytes }`.
778
+
779
+ ### `obsidian_frontmatter_set`
780
+
781
+ Surgical YAML manipulation: set one or more frontmatter keys, or remove them by passing `null` as the value. Round-trips through gray-matter (the same parser used at write time) so YAML formatting / quoting / type-coercion stays consistent.
782
+
783
+ | Argument | Type | Notes |
784
+ |-----------|----------------------------|-----------------------------------------------------------------------------|
785
+ | `path` | `string?` | Vault-relative path. |
786
+ | `title` | `string?` | Note title (filename without `.md`). |
787
+ | `set` | `Record<string, unknown>` | Required. Keys to set. Pass `null` as value to delete a key (e.g. `{status: "published", draft: null}`). |
788
+ | `dry_run` | `boolean?` | Preview the diff without writing. Default `false`. |
789
+
790
+ **Returns:** `{ path, before: object, after: object, changed_keys: string[], dry_run }`.
791
+
557
792
  ## MCP resources
558
793
 
559
794
  | URI | Type | Description |
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@oomkapwn/enquire-mcp",
4
- "version": "3.5.8",
5
- "description": "The most advanced MCP server for Obsidian vaults. Hybrid retrieval (BM25 + TF-IDF + multilingual ML embeddings, RRF-fused) with BGE cross-encoder reranking, HNSW vector index, int8 quantization, late-chunking, HyDE-augmented retrieval, sub-question decomposition, PDFs (with OCR), Bases (.base query execution, standalone — no Obsidian needed), GraphRAG-light (Louvain wikilink community detection), wikilinks, backlinks, Dataview, frontmatter, canvas. 44 tools, 19 MCP prompts, 5 cross-encoder reranker models, 664 tests, SLSA-3, semver-bound. Works with Claude Code, Claude Desktop, Cursor, ChatGPT custom GPT, Codex, and any MCP client.",
4
+ "version": "3.5.10",
5
+ "description": "The most advanced MCP server for Obsidian vaults. Hybrid retrieval (BM25 + TF-IDF + multilingual ML embeddings, RRF-fused) with BGE cross-encoder reranking, HNSW vector index, int8 quantization, late-chunking, HyDE-augmented retrieval, sub-question decomposition, PDFs (with OCR), Bases (.base query execution, standalone — no Obsidian needed), GraphRAG-light (Louvain wikilink community detection), wikilinks, backlinks, Dataview, frontmatter, canvas. 44 tools, 19 MCP prompts, 5 cross-encoder reranker models, 711 tests, SLSA-3, semver-bound. Works with Claude Code, Claude Desktop, Cursor, ChatGPT custom GPT, Codex, and any MCP client.",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "enquire-mcp": "dist/index.js"