obsidian-mcp-server 3.7.2 → 3.9.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/AGENTS.md +20 -15
- package/CLAUDE.md +20 -15
- package/README.md +31 -19
- package/changelog/3.8.x/3.8.0.md +38 -0
- package/changelog/3.9.x/3.9.0.md +43 -0
- package/changelog/template.md +10 -3
- package/dist/config/server-config.d.ts +1 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +30 -19
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +27 -15
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +4 -3
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +23 -12
- package/dist/instructions.js.map +1 -1
- package/dist/mcp-server/resources/definitions/index.d.ts +2 -2
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts +3 -2
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js +6 -5
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js.map +1 -1
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts +2 -2
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js +2 -2
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts +15 -6
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js +24 -12
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +250 -230
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/index.js +6 -4
- package/dist/mcp-server/tools/definitions/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +8 -2
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +10 -3
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +7 -6
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +7 -7
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +8 -5
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +6 -6
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +8 -8
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts +4 -4
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +7 -7
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js +3 -3
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +8 -2
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +10 -3
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +185 -35
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +145 -82
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +8 -2
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +12 -5
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -1
- package/dist/services/obsidian/jsonlogic-evaluator.d.ts +33 -8
- package/dist/services/obsidian/jsonlogic-evaluator.d.ts.map +1 -1
- package/dist/services/obsidian/jsonlogic-evaluator.js +171 -32
- package/dist/services/obsidian/jsonlogic-evaluator.js.map +1 -1
- package/dist/services/obsidian/jsonlogic-worker.d.ts +6 -5
- package/dist/services/obsidian/jsonlogic-worker.d.ts.map +1 -1
- package/dist/services/obsidian/jsonlogic-worker.js +7 -5
- package/dist/services/obsidian/jsonlogic-worker.js.map +1 -1
- package/dist/services/obsidian/obsidian-service.d.ts +90 -36
- package/dist/services/obsidian/obsidian-service.d.ts.map +1 -1
- package/dist/services/obsidian/obsidian-service.js +748 -248
- package/dist/services/obsidian/obsidian-service.js.map +1 -1
- package/dist/services/obsidian/patch-instruction.d.ts +4 -1
- package/dist/services/obsidian/patch-instruction.d.ts.map +1 -1
- package/dist/services/obsidian/patch-instruction.js +5 -2
- package/dist/services/obsidian/patch-instruction.js.map +1 -1
- package/dist/services/obsidian/path-policy.d.ts +67 -23
- package/dist/services/obsidian/path-policy.d.ts.map +1 -1
- package/dist/services/obsidian/path-policy.js +183 -80
- package/dist/services/obsidian/path-policy.js.map +1 -1
- package/dist/services/obsidian/section-extractor.d.ts +62 -1
- package/dist/services/obsidian/section-extractor.d.ts.map +1 -1
- package/dist/services/obsidian/section-extractor.js +387 -51
- package/dist/services/obsidian/section-extractor.js.map +1 -1
- package/dist/services/obsidian/simple-search.d.ts +62 -0
- package/dist/services/obsidian/simple-search.d.ts.map +1 -0
- package/dist/services/obsidian/simple-search.js +264 -0
- package/dist/services/obsidian/simple-search.js.map +1 -0
- package/manifest.json +12 -4
- package/package.json +9 -9
- package/server.json +23 -11
- package/skills/obsidian-guide/SKILL.md +61 -0
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview obsidian_search_notes — text/jsonlogic/omnisearch search with
|
|
3
|
-
* MCP-spec cursor pagination. The `omnisearch` mode is
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* multi-token query's per-token spans) and additionally clip
|
|
7
|
-
* `maxMatchesPerHit` so a single match-heavy note can't blow the
|
|
8
|
-
* budget — clipped hits carry `truncated: true` and `totalMatches`.
|
|
3
|
+
* MCP-spec cursor pagination. The `omnisearch` mode is offered only with reads
|
|
4
|
+
* unscoped and the Omnisearch plugin's HTTP server answering at startup
|
|
5
|
+
* (`omnisearchAvailability`). Text-mode hits carry match locations (the
|
|
6
|
+
* service merges a multi-token query's per-token spans) and additionally clip
|
|
7
|
+
* per file via `maxMatchesPerHit` so a single match-heavy note can't blow the
|
|
8
|
+
* response budget — clipped hits carry `truncated: true` and `totalMatches`.
|
|
9
9
|
* @module mcp-server/tools/definitions/obsidian-search-notes.tool
|
|
10
10
|
*/
|
|
11
11
|
import { z } from '@cyanheads/mcp-ts-core';
|
|
12
12
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
13
|
+
import { type ObsidianService } from '../../../services/obsidian/obsidian-service.js';
|
|
14
|
+
/**
|
|
15
|
+
* Whether `obsidian_search_notes` offers `omnisearch` mode: `offered` when
|
|
16
|
+
* reads are unscoped and the plugin answered the startup probe,
|
|
17
|
+
* `unreachable` when it did not answer, and `withheld` under
|
|
18
|
+
* OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS, where the plugin is never
|
|
19
|
+
* called. Omnisearch ranks and selects its results over the whole vault, so
|
|
20
|
+
* every one of them depends on notes outside a read scope.
|
|
21
|
+
*/
|
|
22
|
+
export type OmnisearchAvailability = 'offered' | 'unreachable' | 'withheld';
|
|
23
|
+
/**
|
|
24
|
+
* Decide {@link OmnisearchAvailability} once, at startup. Under a read scope
|
|
25
|
+
* it is `withheld` with no request, the probe included; otherwise the probe
|
|
26
|
+
* decides.
|
|
27
|
+
*/
|
|
28
|
+
export declare function omnisearchAvailability(svc: ObsidianService): Promise<OmnisearchAvailability>;
|
|
13
29
|
/**
|
|
14
30
|
* Build the `obsidian_search_notes` tool. The `omnisearch` mode is included
|
|
15
|
-
* in the input/output schemas only when `
|
|
16
|
-
* LLM never sees it as an option on a deployment where it
|
|
17
|
-
* requires a server restart.
|
|
31
|
+
* in the input/output schemas and the description only when `omnisearch` is
|
|
32
|
+
* `offered`, so the LLM never sees it as an option on a deployment where it
|
|
33
|
+
* can't run or is withheld. Re-probe requires a server restart.
|
|
18
34
|
*/
|
|
19
|
-
export declare function buildSearchNotesTool({
|
|
20
|
-
|
|
35
|
+
export declare function buildSearchNotesTool({ omnisearch }: {
|
|
36
|
+
omnisearch: OmnisearchAvailability;
|
|
21
37
|
}): import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
22
38
|
mode: z.ZodEnum<{
|
|
23
39
|
jsonlogic: "jsonlogic";
|
|
@@ -99,6 +115,10 @@ export declare function buildSearchNotesTool({ omnisearchReachable }: {
|
|
|
99
115
|
totalCount: z.ZodNumber;
|
|
100
116
|
nextCursor: z.ZodOptional<z.ZodString>;
|
|
101
117
|
truncated: z.ZodBoolean;
|
|
118
|
+
truncatedReason: z.ZodOptional<z.ZodEnum<{
|
|
119
|
+
cap_reached: "cap_reached";
|
|
120
|
+
filtered_after_cap: "filtered_after_cap";
|
|
121
|
+
}>>;
|
|
102
122
|
}, z.core.$strip>], "mode">;
|
|
103
123
|
}, z.core.$strip>, readonly [{
|
|
104
124
|
readonly reason: 'path_prefix_invalid_mode';
|
|
@@ -108,14 +128,14 @@ export declare function buildSearchNotesTool({ omnisearchReachable }: {
|
|
|
108
128
|
}, {
|
|
109
129
|
readonly reason: 'query_required';
|
|
110
130
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
111
|
-
readonly when: '`query` is missing
|
|
112
|
-
readonly recovery:
|
|
131
|
+
readonly when: '`query` is missing in a mode that requires it: every mode but `jsonlogic`.';
|
|
132
|
+
readonly recovery: "Pass `query` as space-separated tokens, all of which a note must contain." | "Pass `query` — space-separated tokens, all required, for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch.";
|
|
113
133
|
}, {
|
|
114
134
|
readonly reason: 'context_length_too_large';
|
|
115
135
|
readonly thrownBy: 'service';
|
|
116
136
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
117
|
-
readonly when:
|
|
118
|
-
readonly recovery: '
|
|
137
|
+
readonly when: "The matches would make a response longer than one can be, so the whole text search failed: the Local REST API runs out of string capacity serializing them, or, under OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS, the readable notes' matches alone would pass the same ceiling. Each match costs its context window plus about 150 characters of offsets and framing.";
|
|
138
|
+
readonly recovery: 'Narrow the query so fewer notes match, and drop very common tokens: every occurrence of each token in a matching note is a match costing about 150 characters beside its window, so a token like `the` can fail even at `contextLength` 0. Lowering `contextLength` helps only while the windows, about twice `contextLength` each, dominate that cost.';
|
|
119
139
|
}, {
|
|
120
140
|
readonly reason: 'logic_required';
|
|
121
141
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
@@ -131,32 +151,95 @@ export declare function buildSearchNotesTool({ omnisearchReachable }: {
|
|
|
131
151
|
readonly reason: 'logic_too_costly';
|
|
132
152
|
readonly thrownBy: 'service';
|
|
133
153
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
134
|
-
readonly when: "With OBSIDIAN_READ_PATHS set, the server evaluates the JSONLogic tree itself, and the tree passed a bound: nesting deeper than 1,000 levels, more than a work budget,
|
|
154
|
+
readonly when: "With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server evaluates the JSONLogic tree itself, and the tree passed a bound: nesting deeper than 1,000 levels, more than a work budget, more than 10 s of evaluation, or more than 45 s for the whole search, reading the readable notes included. One work budget is per note — the size of values built by `merge`, `cat`, `map`, and array literals while evaluating that note, up to the note's own size plus 10,000,000 units — and one is per search: 10,000,000 units across every note for returned results, where a result whose JSON is at most 1,024 characters is free and a larger one pays for what it holds beyond its own note.";
|
|
135
155
|
readonly recovery: "Simplify the tree: nest fewer levels, avoid building arrays or strings many times a note's size with `merge`, `cat`, `map`, or `reduce`, return values drawn from the note rather than large built values or constants, and prefer a simpler `regexp` pattern.";
|
|
156
|
+
}, {
|
|
157
|
+
readonly reason: 'text_search_too_costly';
|
|
158
|
+
readonly thrownBy: 'service';
|
|
159
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
160
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server runs text mode itself, reading every readable note whose path or body holds each token, and reading and matching them took more than 45 s.';
|
|
161
|
+
readonly recovery: 'Add rarer tokens, which every candidate note must contain, so fewer readable notes are read, or ask the operator to narrow OBSIDIAN_READ_PATHS or extend OBSIDIAN_DENY_PATHS.';
|
|
162
|
+
}, {
|
|
163
|
+
readonly reason: 'link_fields_unavailable';
|
|
164
|
+
readonly thrownBy: 'service';
|
|
165
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
166
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the JSONLogic tree names `links`, `backlinks`, or `unresolvedLinks` by a fixed `var`, `missing`, or `missing_some` key. Scoped searches leave these fields out, since Obsidian resolves each link across the whole vault, notes outside the scope included; a computed key reads them as null.';
|
|
167
|
+
readonly recovery: 'Find backlinks by body text: `{"regexp": ["\\\\[\\\\[Target Note(\\\\\\\\?\\\\||#|\\\\]\\\\])", {"var": "content"}]}` matches every readable note that wikilinks or embeds `Target Note`. For a note\'s outgoing links, call `obsidian_get_note` with `includeLinks: true`.';
|
|
136
168
|
}, {
|
|
137
169
|
readonly reason: 'note_too_deep';
|
|
138
170
|
readonly thrownBy: 'service';
|
|
139
171
|
readonly code: JsonRpcErrorCode.SerializationError;
|
|
140
|
-
readonly when: 'With OBSIDIAN_READ_PATHS set, a readable note holds data, usually a frontmatter value, nested deeper than the server can hand to its evaluator (about 3,500 levels under Node, more under Bun).';
|
|
141
|
-
readonly recovery: 'Flatten the deeply nested value, usually a frontmatter field, in the note the message names, or ask the operator to leave that note out of OBSIDIAN_READ_PATHS; no scoped jsonlogic search runs while it is readable.';
|
|
172
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, a readable note holds data, usually a frontmatter value, nested deeper than the server can hand to its evaluator (about 3,500 levels under Node, more under Bun).';
|
|
173
|
+
readonly recovery: 'Flatten the deeply nested value, usually a frontmatter field, in the note the message names, or ask the operator to leave that note out of OBSIDIAN_READ_PATHS or add it to OBSIDIAN_DENY_PATHS; no scoped jsonlogic search runs while it is readable.';
|
|
142
174
|
}, {
|
|
143
175
|
readonly reason: 'omnisearch_unreachable';
|
|
144
176
|
readonly thrownBy: 'service';
|
|
145
177
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
146
|
-
readonly when: 'Omnisearch was reachable at startup but is
|
|
178
|
+
readonly when: 'Omnisearch was reachable at startup but did not answer: it is unreachable (Obsidian quit, plugin disabled, or mobile session), answered with a non-2xx status, or took longer than OBSIDIAN_REQUEST_TIMEOUT_MS. One attempt, never retried.';
|
|
147
179
|
readonly retryable: true;
|
|
148
|
-
readonly recovery: '
|
|
149
|
-
}]
|
|
180
|
+
readonly recovery: 'Confirm Obsidian is running with the Omnisearch plugin enabled and retry, or narrow the query if it was slow. If Omnisearch stays down, restart Obsidian, then restart this MCP server so it re-probes the plugin URL.';
|
|
181
|
+
}] | readonly [{
|
|
182
|
+
readonly reason: 'path_prefix_invalid_mode';
|
|
183
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
184
|
+
readonly when: '`pathPrefix` was provided in a non-text mode (only `text` supports prefix filtering).';
|
|
185
|
+
readonly recovery: 'Drop pathPrefix or switch mode to text for prefix filtering.';
|
|
186
|
+
}, {
|
|
187
|
+
readonly reason: 'query_required';
|
|
188
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
189
|
+
readonly when: '`query` is missing in a mode that requires it: every mode but `jsonlogic`.';
|
|
190
|
+
readonly recovery: "Pass `query` as space-separated tokens, all of which a note must contain." | "Pass `query` — space-separated tokens, all required, for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch.";
|
|
191
|
+
}, {
|
|
192
|
+
readonly reason: 'context_length_too_large';
|
|
193
|
+
readonly thrownBy: 'service';
|
|
194
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
195
|
+
readonly when: "The matches would make a response longer than one can be, so the whole text search failed: the Local REST API runs out of string capacity serializing them, or, under OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS, the readable notes' matches alone would pass the same ceiling. Each match costs its context window plus about 150 characters of offsets and framing.";
|
|
196
|
+
readonly recovery: 'Narrow the query so fewer notes match, and drop very common tokens: every occurrence of each token in a matching note is a match costing about 150 characters beside its window, so a token like `the` can fail even at `contextLength` 0. Lowering `contextLength` helps only while the windows, about twice `contextLength` each, dominate that cost.';
|
|
197
|
+
}, {
|
|
198
|
+
readonly reason: 'logic_required';
|
|
199
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
200
|
+
readonly when: '`logic` is missing for `jsonlogic` mode.';
|
|
201
|
+
readonly recovery: 'Pass a JSONLogic tree as `logic`. `glob` and `regexp` take `[PATTERN, VALUE]` — pattern first, e.g. `{"glob": ["Projects/*.md", {"var": "path"}]}`.';
|
|
202
|
+
}, {
|
|
203
|
+
readonly reason: 'logic_invalid';
|
|
204
|
+
readonly thrownBy: 'service';
|
|
205
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
206
|
+
readonly when: 'The JSONLogic tree failed to evaluate against a note — an unrecognized operator, or an operand that could not be evaluated (an uncompilable `regexp` pattern). A mis-arity operator is not rejected: it evaluates to false and the search returns zero hits.';
|
|
207
|
+
readonly recovery: 'Check the operator names and arity. `glob` and `regexp` take `[PATTERN, VALUE]` — pattern first, e.g. `{"regexp": ["^Inbox/", {"var": "path"}]}`; the reverse order compiles the note field as the pattern.';
|
|
208
|
+
}, {
|
|
209
|
+
readonly reason: 'logic_too_costly';
|
|
210
|
+
readonly thrownBy: 'service';
|
|
211
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
212
|
+
readonly when: "With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server evaluates the JSONLogic tree itself, and the tree passed a bound: nesting deeper than 1,000 levels, more than a work budget, more than 10 s of evaluation, or more than 45 s for the whole search, reading the readable notes included. One work budget is per note — the size of values built by `merge`, `cat`, `map`, and array literals while evaluating that note, up to the note's own size plus 10,000,000 units — and one is per search: 10,000,000 units across every note for returned results, where a result whose JSON is at most 1,024 characters is free and a larger one pays for what it holds beyond its own note.";
|
|
213
|
+
readonly recovery: "Simplify the tree: nest fewer levels, avoid building arrays or strings many times a note's size with `merge`, `cat`, `map`, or `reduce`, return values drawn from the note rather than large built values or constants, and prefer a simpler `regexp` pattern.";
|
|
214
|
+
}, {
|
|
215
|
+
readonly reason: 'text_search_too_costly';
|
|
216
|
+
readonly thrownBy: 'service';
|
|
217
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
218
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server runs text mode itself, reading every readable note whose path or body holds each token, and reading and matching them took more than 45 s.';
|
|
219
|
+
readonly recovery: 'Add rarer tokens, which every candidate note must contain, so fewer readable notes are read, or ask the operator to narrow OBSIDIAN_READ_PATHS or extend OBSIDIAN_DENY_PATHS.';
|
|
220
|
+
}, {
|
|
221
|
+
readonly reason: 'link_fields_unavailable';
|
|
222
|
+
readonly thrownBy: 'service';
|
|
223
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
224
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the JSONLogic tree names `links`, `backlinks`, or `unresolvedLinks` by a fixed `var`, `missing`, or `missing_some` key. Scoped searches leave these fields out, since Obsidian resolves each link across the whole vault, notes outside the scope included; a computed key reads them as null.';
|
|
225
|
+
readonly recovery: 'Find backlinks by body text: `{"regexp": ["\\\\[\\\\[Target Note(\\\\\\\\?\\\\||#|\\\\]\\\\])", {"var": "content"}]}` matches every readable note that wikilinks or embeds `Target Note`. For a note\'s outgoing links, call `obsidian_get_note` with `includeLinks: true`.';
|
|
226
|
+
}, {
|
|
227
|
+
readonly reason: 'note_too_deep';
|
|
228
|
+
readonly thrownBy: 'service';
|
|
229
|
+
readonly code: JsonRpcErrorCode.SerializationError;
|
|
230
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, a readable note holds data, usually a frontmatter value, nested deeper than the server can hand to its evaluator (about 3,500 levels under Node, more under Bun).';
|
|
231
|
+
readonly recovery: 'Flatten the deeply nested value, usually a frontmatter field, in the note the message names, or ask the operator to leave that note out of OBSIDIAN_READ_PATHS or add it to OBSIDIAN_DENY_PATHS; no scoped jsonlogic search runs while it is readable.';
|
|
232
|
+
}, ...never[]], {
|
|
150
233
|
readonly effectiveQuery: z.ZodOptional<z.ZodString>;
|
|
151
234
|
readonly notice: z.ZodOptional<z.ZodString>;
|
|
152
235
|
}>;
|
|
153
236
|
/**
|
|
154
237
|
* Static specimen for the MCP definition linter (which duck-types tool
|
|
155
238
|
* exports out of each `.tool.ts` file) and for existing tests that import
|
|
156
|
-
* the tool directly.
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
239
|
+
* the tool directly. Built without the `omnisearch` mode — the safe baseline
|
|
240
|
+
* that doesn't assume the optional plugin is installed. The entry point
|
|
241
|
+
* (`src/index.ts`) builds the live tool via `buildSearchNotesTool` with the
|
|
242
|
+
* startup `omnisearchAvailability`; this export is not the registered tool.
|
|
160
243
|
* The omnisearch-enabled variant is exercised by tests rather than the
|
|
161
244
|
* linter (two exports under the same tool name would collide on
|
|
162
245
|
* `name-unique`).
|
|
@@ -242,6 +325,10 @@ export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolD
|
|
|
242
325
|
totalCount: z.ZodNumber;
|
|
243
326
|
nextCursor: z.ZodOptional<z.ZodString>;
|
|
244
327
|
truncated: z.ZodBoolean;
|
|
328
|
+
truncatedReason: z.ZodOptional<z.ZodEnum<{
|
|
329
|
+
cap_reached: "cap_reached";
|
|
330
|
+
filtered_after_cap: "filtered_after_cap";
|
|
331
|
+
}>>;
|
|
245
332
|
}, z.core.$strip>], "mode">;
|
|
246
333
|
}, z.core.$strip>, readonly [{
|
|
247
334
|
readonly reason: 'path_prefix_invalid_mode';
|
|
@@ -251,14 +338,14 @@ export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolD
|
|
|
251
338
|
}, {
|
|
252
339
|
readonly reason: 'query_required';
|
|
253
340
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
254
|
-
readonly when: '`query` is missing
|
|
255
|
-
readonly recovery:
|
|
341
|
+
readonly when: '`query` is missing in a mode that requires it: every mode but `jsonlogic`.';
|
|
342
|
+
readonly recovery: "Pass `query` as space-separated tokens, all of which a note must contain." | "Pass `query` — space-separated tokens, all required, for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch.";
|
|
256
343
|
}, {
|
|
257
344
|
readonly reason: 'context_length_too_large';
|
|
258
345
|
readonly thrownBy: 'service';
|
|
259
346
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
260
|
-
readonly when:
|
|
261
|
-
readonly recovery: '
|
|
347
|
+
readonly when: "The matches would make a response longer than one can be, so the whole text search failed: the Local REST API runs out of string capacity serializing them, or, under OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS, the readable notes' matches alone would pass the same ceiling. Each match costs its context window plus about 150 characters of offsets and framing.";
|
|
348
|
+
readonly recovery: 'Narrow the query so fewer notes match, and drop very common tokens: every occurrence of each token in a matching note is a match costing about 150 characters beside its window, so a token like `the` can fail even at `contextLength` 0. Lowering `contextLength` helps only while the windows, about twice `contextLength` each, dominate that cost.';
|
|
262
349
|
}, {
|
|
263
350
|
readonly reason: 'logic_required';
|
|
264
351
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
@@ -274,22 +361,85 @@ export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolD
|
|
|
274
361
|
readonly reason: 'logic_too_costly';
|
|
275
362
|
readonly thrownBy: 'service';
|
|
276
363
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
277
|
-
readonly when: "With OBSIDIAN_READ_PATHS set, the server evaluates the JSONLogic tree itself, and the tree passed a bound: nesting deeper than 1,000 levels, more than a work budget,
|
|
364
|
+
readonly when: "With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server evaluates the JSONLogic tree itself, and the tree passed a bound: nesting deeper than 1,000 levels, more than a work budget, more than 10 s of evaluation, or more than 45 s for the whole search, reading the readable notes included. One work budget is per note — the size of values built by `merge`, `cat`, `map`, and array literals while evaluating that note, up to the note's own size plus 10,000,000 units — and one is per search: 10,000,000 units across every note for returned results, where a result whose JSON is at most 1,024 characters is free and a larger one pays for what it holds beyond its own note.";
|
|
278
365
|
readonly recovery: "Simplify the tree: nest fewer levels, avoid building arrays or strings many times a note's size with `merge`, `cat`, `map`, or `reduce`, return values drawn from the note rather than large built values or constants, and prefer a simpler `regexp` pattern.";
|
|
366
|
+
}, {
|
|
367
|
+
readonly reason: 'text_search_too_costly';
|
|
368
|
+
readonly thrownBy: 'service';
|
|
369
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
370
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server runs text mode itself, reading every readable note whose path or body holds each token, and reading and matching them took more than 45 s.';
|
|
371
|
+
readonly recovery: 'Add rarer tokens, which every candidate note must contain, so fewer readable notes are read, or ask the operator to narrow OBSIDIAN_READ_PATHS or extend OBSIDIAN_DENY_PATHS.';
|
|
372
|
+
}, {
|
|
373
|
+
readonly reason: 'link_fields_unavailable';
|
|
374
|
+
readonly thrownBy: 'service';
|
|
375
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
376
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the JSONLogic tree names `links`, `backlinks`, or `unresolvedLinks` by a fixed `var`, `missing`, or `missing_some` key. Scoped searches leave these fields out, since Obsidian resolves each link across the whole vault, notes outside the scope included; a computed key reads them as null.';
|
|
377
|
+
readonly recovery: 'Find backlinks by body text: `{"regexp": ["\\\\[\\\\[Target Note(\\\\\\\\?\\\\||#|\\\\]\\\\])", {"var": "content"}]}` matches every readable note that wikilinks or embeds `Target Note`. For a note\'s outgoing links, call `obsidian_get_note` with `includeLinks: true`.';
|
|
279
378
|
}, {
|
|
280
379
|
readonly reason: 'note_too_deep';
|
|
281
380
|
readonly thrownBy: 'service';
|
|
282
381
|
readonly code: JsonRpcErrorCode.SerializationError;
|
|
283
|
-
readonly when: 'With OBSIDIAN_READ_PATHS set, a readable note holds data, usually a frontmatter value, nested deeper than the server can hand to its evaluator (about 3,500 levels under Node, more under Bun).';
|
|
284
|
-
readonly recovery: 'Flatten the deeply nested value, usually a frontmatter field, in the note the message names, or ask the operator to leave that note out of OBSIDIAN_READ_PATHS; no scoped jsonlogic search runs while it is readable.';
|
|
382
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, a readable note holds data, usually a frontmatter value, nested deeper than the server can hand to its evaluator (about 3,500 levels under Node, more under Bun).';
|
|
383
|
+
readonly recovery: 'Flatten the deeply nested value, usually a frontmatter field, in the note the message names, or ask the operator to leave that note out of OBSIDIAN_READ_PATHS or add it to OBSIDIAN_DENY_PATHS; no scoped jsonlogic search runs while it is readable.';
|
|
285
384
|
}, {
|
|
286
385
|
readonly reason: 'omnisearch_unreachable';
|
|
287
386
|
readonly thrownBy: 'service';
|
|
288
387
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
289
|
-
readonly when: 'Omnisearch was reachable at startup but is
|
|
388
|
+
readonly when: 'Omnisearch was reachable at startup but did not answer: it is unreachable (Obsidian quit, plugin disabled, or mobile session), answered with a non-2xx status, or took longer than OBSIDIAN_REQUEST_TIMEOUT_MS. One attempt, never retried.';
|
|
290
389
|
readonly retryable: true;
|
|
291
|
-
readonly recovery: '
|
|
292
|
-
}]
|
|
390
|
+
readonly recovery: 'Confirm Obsidian is running with the Omnisearch plugin enabled and retry, or narrow the query if it was slow. If Omnisearch stays down, restart Obsidian, then restart this MCP server so it re-probes the plugin URL.';
|
|
391
|
+
}] | readonly [{
|
|
392
|
+
readonly reason: 'path_prefix_invalid_mode';
|
|
393
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
394
|
+
readonly when: '`pathPrefix` was provided in a non-text mode (only `text` supports prefix filtering).';
|
|
395
|
+
readonly recovery: 'Drop pathPrefix or switch mode to text for prefix filtering.';
|
|
396
|
+
}, {
|
|
397
|
+
readonly reason: 'query_required';
|
|
398
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
399
|
+
readonly when: '`query` is missing in a mode that requires it: every mode but `jsonlogic`.';
|
|
400
|
+
readonly recovery: "Pass `query` as space-separated tokens, all of which a note must contain." | "Pass `query` — space-separated tokens, all required, for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch.";
|
|
401
|
+
}, {
|
|
402
|
+
readonly reason: 'context_length_too_large';
|
|
403
|
+
readonly thrownBy: 'service';
|
|
404
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
405
|
+
readonly when: "The matches would make a response longer than one can be, so the whole text search failed: the Local REST API runs out of string capacity serializing them, or, under OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS, the readable notes' matches alone would pass the same ceiling. Each match costs its context window plus about 150 characters of offsets and framing.";
|
|
406
|
+
readonly recovery: 'Narrow the query so fewer notes match, and drop very common tokens: every occurrence of each token in a matching note is a match costing about 150 characters beside its window, so a token like `the` can fail even at `contextLength` 0. Lowering `contextLength` helps only while the windows, about twice `contextLength` each, dominate that cost.';
|
|
407
|
+
}, {
|
|
408
|
+
readonly reason: 'logic_required';
|
|
409
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
410
|
+
readonly when: '`logic` is missing for `jsonlogic` mode.';
|
|
411
|
+
readonly recovery: 'Pass a JSONLogic tree as `logic`. `glob` and `regexp` take `[PATTERN, VALUE]` — pattern first, e.g. `{"glob": ["Projects/*.md", {"var": "path"}]}`.';
|
|
412
|
+
}, {
|
|
413
|
+
readonly reason: 'logic_invalid';
|
|
414
|
+
readonly thrownBy: 'service';
|
|
415
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
416
|
+
readonly when: 'The JSONLogic tree failed to evaluate against a note — an unrecognized operator, or an operand that could not be evaluated (an uncompilable `regexp` pattern). A mis-arity operator is not rejected: it evaluates to false and the search returns zero hits.';
|
|
417
|
+
readonly recovery: 'Check the operator names and arity. `glob` and `regexp` take `[PATTERN, VALUE]` — pattern first, e.g. `{"regexp": ["^Inbox/", {"var": "path"}]}`; the reverse order compiles the note field as the pattern.';
|
|
418
|
+
}, {
|
|
419
|
+
readonly reason: 'logic_too_costly';
|
|
420
|
+
readonly thrownBy: 'service';
|
|
421
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
422
|
+
readonly when: "With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server evaluates the JSONLogic tree itself, and the tree passed a bound: nesting deeper than 1,000 levels, more than a work budget, more than 10 s of evaluation, or more than 45 s for the whole search, reading the readable notes included. One work budget is per note — the size of values built by `merge`, `cat`, `map`, and array literals while evaluating that note, up to the note's own size plus 10,000,000 units — and one is per search: 10,000,000 units across every note for returned results, where a result whose JSON is at most 1,024 characters is free and a larger one pays for what it holds beyond its own note.";
|
|
423
|
+
readonly recovery: "Simplify the tree: nest fewer levels, avoid building arrays or strings many times a note's size with `merge`, `cat`, `map`, or `reduce`, return values drawn from the note rather than large built values or constants, and prefer a simpler `regexp` pattern.";
|
|
424
|
+
}, {
|
|
425
|
+
readonly reason: 'text_search_too_costly';
|
|
426
|
+
readonly thrownBy: 'service';
|
|
427
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
428
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the server runs text mode itself, reading every readable note whose path or body holds each token, and reading and matching them took more than 45 s.';
|
|
429
|
+
readonly recovery: 'Add rarer tokens, which every candidate note must contain, so fewer readable notes are read, or ask the operator to narrow OBSIDIAN_READ_PATHS or extend OBSIDIAN_DENY_PATHS.';
|
|
430
|
+
}, {
|
|
431
|
+
readonly reason: 'link_fields_unavailable';
|
|
432
|
+
readonly thrownBy: 'service';
|
|
433
|
+
readonly code: JsonRpcErrorCode.ValidationError;
|
|
434
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, the JSONLogic tree names `links`, `backlinks`, or `unresolvedLinks` by a fixed `var`, `missing`, or `missing_some` key. Scoped searches leave these fields out, since Obsidian resolves each link across the whole vault, notes outside the scope included; a computed key reads them as null.';
|
|
435
|
+
readonly recovery: 'Find backlinks by body text: `{"regexp": ["\\\\[\\\\[Target Note(\\\\\\\\?\\\\||#|\\\\]\\\\])", {"var": "content"}]}` matches every readable note that wikilinks or embeds `Target Note`. For a note\'s outgoing links, call `obsidian_get_note` with `includeLinks: true`.';
|
|
436
|
+
}, {
|
|
437
|
+
readonly reason: 'note_too_deep';
|
|
438
|
+
readonly thrownBy: 'service';
|
|
439
|
+
readonly code: JsonRpcErrorCode.SerializationError;
|
|
440
|
+
readonly when: 'With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, a readable note holds data, usually a frontmatter value, nested deeper than the server can hand to its evaluator (about 3,500 levels under Node, more under Bun).';
|
|
441
|
+
readonly recovery: 'Flatten the deeply nested value, usually a frontmatter field, in the note the message names, or ask the operator to leave that note out of OBSIDIAN_READ_PATHS or add it to OBSIDIAN_DENY_PATHS; no scoped jsonlogic search runs while it is readable.';
|
|
442
|
+
}, ...never[]], {
|
|
293
443
|
readonly effectiveQuery: z.ZodOptional<z.ZodString>;
|
|
294
444
|
readonly notice: z.ZodOptional<z.ZodString>;
|
|
295
445
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obsidian-search-notes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-search-notes.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"obsidian-search-notes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-search-notes.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AASjE,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAiBnG;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,CAAC;AAE5E;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,sBAAsB,CAAC,CAGjC;AAqGD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,sBAAsB,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA4J7E,0BAA0B;;mBAE5B,uFAAuF;uBACnF,8DAA8D;;qBAGhE,gBAAgB;;mBAElB,4EAA4E;;;qBAM1E,0BAA0B;uBACxB,SAAS;;mBAEb,wWAAwW;uBAE5W,yVAAyV;;qBAGnV,gBAAgB;;mBAElB,0CAA0C;uBAE9C,qJAAqJ;;qBAG/I,eAAe;uBACb,SAAS;;mBAEb,8PAA8P;uBAElQ,6MAA6M;;qBAGvM,kBAAkB;uBAChB,SAAS;;mBAEb,sqBAAsqB;uBAE1qB,gQAAgQ;;qBAG1P,wBAAwB;uBACtB,SAAS;;mBAEb,4MAA4M;uBAEhN,+KAA+K;;qBAGzK,yBAAyB;uBACvB,SAAS;;mBAEb,qVAAqV;uBAEzV,6QAA6Q;;qBAGvQ,eAAe;uBACb,SAAS;;mBAEb,wNAAwN;uBAE5N,wPAAwP;;qBAnFlP,wBAAwB;uBACtB,SAAS;;mBAEb,6OAA6O;;uBAGjP,wNAAwN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6O/N;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA/OlB,0BAA0B;;mBAE5B,uFAAuF;uBACnF,8DAA8D;;qBAGhE,gBAAgB;;mBAElB,4EAA4E;;;qBAM1E,0BAA0B;uBACxB,SAAS;;mBAEb,wWAAwW;uBAE5W,yVAAyV;;qBAGnV,gBAAgB;;mBAElB,0CAA0C;uBAE9C,qJAAqJ;;qBAG/I,eAAe;uBACb,SAAS;;mBAEb,8PAA8P;uBAElQ,6MAA6M;;qBAGvM,kBAAkB;uBAChB,SAAS;;mBAEb,sqBAAsqB;uBAE1qB,gQAAgQ;;qBAG1P,wBAAwB;uBACtB,SAAS;;mBAEb,4MAA4M;uBAEhN,+KAA+K;;qBAGzK,yBAAyB;uBACvB,SAAS;;mBAEb,qVAAqV;uBAEzV,6QAA6Q;;qBAGvQ,eAAe;uBACb,SAAS;;mBAEb,wNAAwN;uBAE5N,wPAAwP;;qBAnFlP,wBAAwB;uBACtB,SAAS;;mBAEb,6OAA6O;;uBAGjP,wNAAwN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0P1I,CAAC"}
|