obsidian-mcp-server 3.5.0 → 3.5.2
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 +8 -7
- package/CLAUDE.md +8 -7
- package/Dockerfile +3 -1
- package/README.md +8 -5
- package/changelog/3.5.x/3.5.1.md +27 -0
- package/changelog/3.5.x/3.5.2.md +27 -0
- package/dist/config/server-config.d.ts +4 -4
- package/dist/mcp-server/resources/definitions/index.d.ts +5 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts +11 -4
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.js +21 -12
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +88 -39
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +6 -1
- 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 +7 -1
- 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 +6 -1
- 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 -1
- 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 +11 -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 +41 -15
- 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 +8 -3
- 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 +12 -5
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +6 -1
- 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 +7 -1
- 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 +8 -3
- 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 +10 -4
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +6 -1
- 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 +7 -1
- 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 +6 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +7 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +6 -1
- 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 +7 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -1
- package/dist/services/obsidian/frontmatter-ops.d.ts +9 -5
- package/dist/services/obsidian/frontmatter-ops.d.ts.map +1 -1
- package/dist/services/obsidian/frontmatter-ops.js +70 -16
- package/dist/services/obsidian/frontmatter-ops.js.map +1 -1
- package/dist/services/obsidian/obsidian-service.d.ts +10 -7
- package/dist/services/obsidian/obsidian-service.d.ts.map +1 -1
- package/dist/services/obsidian/obsidian-service.js +193 -38
- package/dist/services/obsidian/obsidian-service.js.map +1 -1
- package/dist/services/obsidian/section-extractor.d.ts +17 -1
- package/dist/services/obsidian/section-extractor.d.ts.map +1 -1
- package/dist/services/obsidian/section-extractor.js +109 -40
- package/dist/services/obsidian/section-extractor.js.map +1 -1
- package/dist/services/obsidian/types.d.ts +17 -0
- package/dist/services/obsidian/types.d.ts.map +1 -1
- package/manifest.json +1 -1
- package/package.json +10 -10
- package/server.json +3 -3
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
* @module services/obsidian/frontmatter-ops
|
|
6
6
|
*/
|
|
7
7
|
import { isMap, parseDocument } from 'yaml';
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The one frontmatter boundary: an opening `---` alone on the first line, YAML,
|
|
10
|
+
* then a `---` that starts its own line. The YAML span and the newline that
|
|
11
|
+
* separates it from the closing fence are one atomic optional unit, so an empty
|
|
12
|
+
* block (`---` immediately followed by `---`) matches while a `---` sitting
|
|
13
|
+
* mid-scalar still cannot close the block — making the separator newline
|
|
14
|
+
* independently optional would close `key: a---b` at the `---` inside it.
|
|
15
|
+
*/
|
|
16
|
+
const FM_RE = /^(---\r?\n)(?:([\s\S]*?)\r?\n)?(?:---\r?\n?)/;
|
|
9
17
|
/**
|
|
10
18
|
* Split a note into its raw frontmatter prefix and its body. `raw + body`
|
|
11
19
|
* reconstructs the input byte for byte, which is what makes this the primitive
|
|
@@ -15,20 +23,31 @@ const FM_RE = /^(---\r?\n)([\s\S]*?)(\r?\n---\r?\n?)/;
|
|
|
15
23
|
*
|
|
16
24
|
* A file whose fence is never closed, whose `---` sits below the first line, or
|
|
17
25
|
* whose opening `---` carries trailing whitespace has no frontmatter — the whole
|
|
18
|
-
* thing is body, which is also how Obsidian reads each of those shapes.
|
|
26
|
+
* thing is body, which is also how Obsidian reads each of those shapes. An empty
|
|
27
|
+
* block is a block: `---` immediately followed by `---` splits with an empty
|
|
28
|
+
* `yamlText`, matching the properties block Obsidian reads there.
|
|
19
29
|
*/
|
|
20
30
|
export function splice(content) {
|
|
21
31
|
const m = FM_RE.exec(content);
|
|
22
32
|
if (!m) {
|
|
23
33
|
return { hasFrontmatter: false, raw: '', open: '', yamlText: '', close: '', body: content };
|
|
24
34
|
}
|
|
35
|
+
const raw = m[0];
|
|
36
|
+
const open = m[1] ?? '';
|
|
37
|
+
const yamlText = m[2] ?? '';
|
|
25
38
|
return {
|
|
26
39
|
hasFrontmatter: true,
|
|
27
|
-
raw
|
|
28
|
-
open
|
|
29
|
-
yamlText
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
raw,
|
|
41
|
+
open,
|
|
42
|
+
yamlText,
|
|
43
|
+
/**
|
|
44
|
+
* Sliced rather than taken from the closing-fence capture: the separator
|
|
45
|
+
* newline belongs to `close` when there is YAML and does not exist at all
|
|
46
|
+
* for an empty block, and slicing keeps `open + yamlText + close === raw`
|
|
47
|
+
* in both shapes.
|
|
48
|
+
*/
|
|
49
|
+
close: raw.slice(open.length + yamlText.length),
|
|
50
|
+
body: content.slice(raw.length),
|
|
32
51
|
};
|
|
33
52
|
}
|
|
34
53
|
/**
|
|
@@ -90,8 +109,8 @@ export function deleteFrontmatterKey(content, key) {
|
|
|
90
109
|
}
|
|
91
110
|
/**
|
|
92
111
|
* Add or remove tags across frontmatter (`tags:` array) and inline `#tag`
|
|
93
|
-
* syntax. Inline
|
|
94
|
-
*
|
|
112
|
+
* syntax. Inline detection skips code spans, link spans, and a hash escaped as
|
|
113
|
+
* `\#` — see `splitProtectedSegments` and `TAG_LEFT_BOUNDARY`.
|
|
95
114
|
*/
|
|
96
115
|
export function reconcileTags(content, tags, operation, location) {
|
|
97
116
|
const norm = (t) => t.replace(/^#+/, '').trim();
|
|
@@ -166,8 +185,32 @@ function normalizeTagList(value) {
|
|
|
166
185
|
}
|
|
167
186
|
return [];
|
|
168
187
|
}
|
|
169
|
-
const FENCED_CODE_BLOCK =
|
|
170
|
-
const INLINE_CODE =
|
|
188
|
+
const FENCED_CODE_BLOCK = /```[\s\S]*?```|~~~[\s\S]*?~~~/;
|
|
189
|
+
const INLINE_CODE = /`[^`\n]+`/;
|
|
190
|
+
/**
|
|
191
|
+
* `[[Target#Heading|Alias]]`. The `#` in a wikilink opens a heading anchor and
|
|
192
|
+
* the text after `|` is display text — neither is a tag. Protecting the whole
|
|
193
|
+
* span rather than tightening the tag's left boundary is what makes the result
|
|
194
|
+
* independent of what the linked note is named: `[[Note#Overview]]` only escapes
|
|
195
|
+
* a boundary rule because the name happens to end in a word character, while
|
|
196
|
+
* `[[Note (Draft)#Overview]]` does not. Obsidian rejects `[` and `]` inside a
|
|
197
|
+
* link target, so a span never nests.
|
|
198
|
+
*/
|
|
199
|
+
const WIKILINK = /\[\[[^[\]\n]*\]\]/;
|
|
200
|
+
/** `[text](destination)` — a `#` in the link text or in a URL fragment is link syntax. */
|
|
201
|
+
const MARKDOWN_LINK = /\[[^[\]\n]*\]\([^()\n]*\)/;
|
|
202
|
+
/**
|
|
203
|
+
* `[text][label]` and `[text][]` — the reference-style forms of the same link.
|
|
204
|
+
* The label must follow the text immediately; a bracketed phrase on its own
|
|
205
|
+
* (`[note #work]`) is not a link and its `#` stays a tag.
|
|
206
|
+
*/
|
|
207
|
+
const REFERENCE_LINK = /\[[^[\]\n]+\]\[[^[\]\n]*\]/;
|
|
208
|
+
/**
|
|
209
|
+
* A tag's left boundary: the start of a segment, or a character that is neither
|
|
210
|
+
* part of a tag nor the `\` that escapes one. Obsidian documents `\#` as an
|
|
211
|
+
* escaped hashtag — a literal `#` carrying no formatting.
|
|
212
|
+
*/
|
|
213
|
+
const TAG_LEFT_BOUNDARY = '(^|[^\\w/\\\\])';
|
|
171
214
|
/**
|
|
172
215
|
* Inline `#tag` syntax lives in the body. The frontmatter block is spliced off
|
|
173
216
|
* first and re-attached verbatim, so a `#` inside a YAML scalar is neither read
|
|
@@ -261,10 +304,18 @@ function removeAt(full, leading, trailing, offset, whole) {
|
|
|
261
304
|
return leading;
|
|
262
305
|
return trailing;
|
|
263
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Split the body into stretches a tag may live in and stretches it may not:
|
|
309
|
+
* code, where a `#` is code, and link syntax, where a `#` is a heading anchor
|
|
310
|
+
* or link text. Both the read and the write path run over the result, so
|
|
311
|
+
* `list` and `remove` agree on what counts as a tag.
|
|
312
|
+
*/
|
|
264
313
|
function splitProtectedSegments(content) {
|
|
265
314
|
const segments = [];
|
|
266
315
|
let cursor = 0;
|
|
267
|
-
const re = new RegExp(
|
|
316
|
+
const re = new RegExp([FENCED_CODE_BLOCK, INLINE_CODE, WIKILINK, MARKDOWN_LINK, REFERENCE_LINK]
|
|
317
|
+
.map((r) => r.source)
|
|
318
|
+
.join('|'), 'g');
|
|
268
319
|
for (;;) {
|
|
269
320
|
const m = re.exec(content);
|
|
270
321
|
if (!m)
|
|
@@ -284,21 +335,24 @@ function splitProtectedSegments(content) {
|
|
|
284
335
|
/** Captures the character before the tag and the single horizontal space after it, if any. */
|
|
285
336
|
function makeInlineTagRegex(tag) {
|
|
286
337
|
const escaped = tag.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
|
|
287
|
-
return new RegExp(
|
|
338
|
+
return new RegExp(`${TAG_LEFT_BOUNDARY}#${escaped}(?![\\w/-])([ \\t]?)`, 'g');
|
|
288
339
|
}
|
|
289
340
|
/**
|
|
290
341
|
* Read-only helpers for `obsidian_manage_tags list`. Inline tags are read from
|
|
291
|
-
* the body only,
|
|
292
|
-
*
|
|
342
|
+
* the body only, and through the same protected-segment split and left boundary
|
|
343
|
+
* a removal uses, so what `list` reports is exactly what `remove` can reach — a
|
|
344
|
+
* `#` inside a YAML scalar, a code span, a link span, or escaped as `\#` is
|
|
345
|
+
* none of them.
|
|
293
346
|
*/
|
|
294
347
|
export function listTagsFromContent(content, frontmatter) {
|
|
295
348
|
const fmTags = normalizeTagList(frontmatter.tags);
|
|
296
349
|
const inline = [];
|
|
297
350
|
const seen = new Set();
|
|
351
|
+
/** Each segment is scanned to exhaustion, which resets `lastIndex` between them. */
|
|
352
|
+
const re = new RegExp(`${TAG_LEFT_BOUNDARY}#([a-zA-Z][\\w/-]*)`, 'g');
|
|
298
353
|
for (const seg of splitProtectedSegments(splice(content).body)) {
|
|
299
354
|
if (seg.protected)
|
|
300
355
|
continue;
|
|
301
|
-
const re = /(^|[^\w/])#([a-zA-Z][\w/-]*)/g;
|
|
302
356
|
for (;;) {
|
|
303
357
|
const m = re.exec(seg.text);
|
|
304
358
|
if (!m)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter-ops.js","sourceRoot":"","sources":["../../../src/services/obsidian/frontmatter-ops.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAiB,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE3D,MAAM,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"frontmatter-ops.js","sourceRoot":"","sources":["../../../src/services/obsidian/frontmatter-ops.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,15 +32,18 @@ export declare class ObsidianService {
|
|
|
32
32
|
get policy(): PathPolicy;
|
|
33
33
|
/** Resolved Omnisearch URL (derived from baseUrl or OBSIDIAN_OMNISEARCH_URL override). */
|
|
34
34
|
get omnisearchUrl(): string;
|
|
35
|
-
getStatus(ctx: Context): Promise<VaultStatus>;
|
|
36
35
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
36
|
+
* The plugin's root capability report: reachability, versions, manifest, and
|
|
37
|
+
* the registered API extensions.
|
|
38
|
+
*
|
|
39
|
+
* Sent authenticated. `GET /` never 401s — it answers `200` either way and
|
|
40
|
+
* self-reports `authenticated`, which is a direct string comparison against
|
|
41
|
+
* the configured key upstream — so the misconfigured-key case still returns
|
|
42
|
+
* the full reachability payload with `authenticated: false` rather than
|
|
43
|
+
* throwing. `apiExtensions` appears only on the authenticated response,
|
|
44
|
+
* which is why the request carries the key at all.
|
|
42
45
|
*/
|
|
43
|
-
|
|
46
|
+
getStatus(ctx: Context): Promise<VaultStatus>;
|
|
44
47
|
getNoteContent(ctx: Context, target: NoteTarget): Promise<string>;
|
|
45
48
|
getNoteJson(ctx: Context, target: NoteTarget): Promise<NoteJson>;
|
|
46
49
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obsidian-service.d.ts","sourceRoot":"","sources":["../../../src/services/obsidian/obsidian-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAYtD,OAAO,EAAS,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,QAAQ,CAAC;AACxF,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"obsidian-service.d.ts","sourceRoot":"","sources":["../../../src/services/obsidian/obsidian-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,16 @@ import { getServerConfig } from '../../config/server-config.js';
|
|
|
11
11
|
import { PathPolicy } from './path-policy.js';
|
|
12
12
|
/** Per-call timeout for the startup probe — covers the 4-tuple TCP handshake + a tiny GET. */
|
|
13
13
|
const OMNISEARCH_PROBE_TIMEOUT_MS = 500;
|
|
14
|
+
/**
|
|
15
|
+
* Ceiling on the capability probe. It runs only *after* an upstream error has
|
|
16
|
+
* already come back, so it delays a response the caller is waiting on — a
|
|
17
|
+
* diagnostic is not worth another full request timeout.
|
|
18
|
+
*/
|
|
19
|
+
const CAPABILITY_PROBE_TIMEOUT_MS = 2_000;
|
|
20
|
+
/** Manifest id of the extension that re-adds `/periodic/` on plugin v5.0.2+. */
|
|
21
|
+
const PERIODIC_EXTENSION_ID = 'local-rest-api-periodic-notes';
|
|
22
|
+
/** First Local REST API version that ships without the built-in `/periodic/` routes. */
|
|
23
|
+
const PERIODIC_ROUTES_REMOVED_IN = [5, 0, 2];
|
|
14
24
|
const OMNISEARCH_DEFAULT_PORT = '51361';
|
|
15
25
|
const NOTE_JSON_ACCEPT = 'application/vnd.olrapi.note+json';
|
|
16
26
|
const DOCUMENT_MAP_ACCEPT = 'application/vnd.olrapi.document-map+json';
|
|
@@ -46,6 +56,13 @@ export class ObsidianService {
|
|
|
46
56
|
#fetch;
|
|
47
57
|
#policy;
|
|
48
58
|
#omnisearchUrl;
|
|
59
|
+
/**
|
|
60
|
+
* Memoized capability probe, consulted only when an error response is
|
|
61
|
+
* ambiguous without it. Holds the in-flight promise so concurrent failures
|
|
62
|
+
* share one request; a failed probe is evicted so a later failure can try
|
|
63
|
+
* again rather than inheriting a transient outage forever.
|
|
64
|
+
*/
|
|
65
|
+
#capabilities;
|
|
49
66
|
/**
|
|
50
67
|
* @param config - Validated server config (api key, base URL, TLS, timeouts).
|
|
51
68
|
* @param fetchImpl - Optional fetch override for tests. Defaults to undici's
|
|
@@ -82,32 +99,20 @@ export class ObsidianService {
|
|
|
82
99
|
return this.#omnisearchUrl;
|
|
83
100
|
}
|
|
84
101
|
// ── Status ───────────────────────────────────────────────────────────────
|
|
85
|
-
async getStatus(ctx) {
|
|
86
|
-
const res = await this.#request(ctx, '/', { method: 'GET', skipAuth: true });
|
|
87
|
-
return (await res.json());
|
|
88
|
-
}
|
|
89
102
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
103
|
+
* The plugin's root capability report: reachability, versions, manifest, and
|
|
104
|
+
* the registered API extensions.
|
|
105
|
+
*
|
|
106
|
+
* Sent authenticated. `GET /` never 401s — it answers `200` either way and
|
|
107
|
+
* self-reports `authenticated`, which is a direct string comparison against
|
|
108
|
+
* the configured key upstream — so the misconfigured-key case still returns
|
|
109
|
+
* the full reachability payload with `authenticated: false` rather than
|
|
110
|
+
* throwing. `apiExtensions` appears only on the authenticated response,
|
|
111
|
+
* which is why the request carries the key at all.
|
|
95
112
|
*/
|
|
96
|
-
async
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
method: 'GET',
|
|
100
|
-
headers: { Authorization: `Bearer ${this.#config.apiKey}` },
|
|
101
|
-
dispatcher: this.#dispatcher,
|
|
102
|
-
signal: ctx.signal,
|
|
103
|
-
});
|
|
104
|
-
return res.ok;
|
|
105
|
-
}
|
|
106
|
-
catch (err) {
|
|
107
|
-
if (ctx.signal?.aborted)
|
|
108
|
-
throw err;
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
113
|
+
async getStatus(ctx) {
|
|
114
|
+
const res = await this.#request(ctx, '/', { method: 'GET' });
|
|
115
|
+
return normalizeVaultStatus((await res.json()));
|
|
111
116
|
}
|
|
112
117
|
// ── Notes ────────────────────────────────────────────────────────────────
|
|
113
118
|
async getNoteContent(ctx, target) {
|
|
@@ -287,7 +292,7 @@ export class ObsidianService {
|
|
|
287
292
|
if (normalized) {
|
|
288
293
|
this.#policy.assertReadable(normalized);
|
|
289
294
|
}
|
|
290
|
-
const res = await this.#request(ctx, url, { method: 'GET' });
|
|
295
|
+
const res = await this.#request(ctx, url, { method: 'GET', listRead: true });
|
|
291
296
|
return (await res.json());
|
|
292
297
|
}
|
|
293
298
|
async listTags(ctx) {
|
|
@@ -575,10 +580,10 @@ export class ObsidianService {
|
|
|
575
580
|
}
|
|
576
581
|
#request(ctx, pathAndQuery, init) {
|
|
577
582
|
const url = `${this.#config.baseUrl}${pathAndQuery}`;
|
|
578
|
-
const headers = {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
583
|
+
const headers = {
|
|
584
|
+
...(init.headers ?? {}),
|
|
585
|
+
Authorization: `Bearer ${this.#config.apiKey}`,
|
|
586
|
+
};
|
|
582
587
|
const exec = async () => {
|
|
583
588
|
const res = await this.#fetch(url, {
|
|
584
589
|
method: init.method,
|
|
@@ -588,11 +593,14 @@ export class ObsidianService {
|
|
|
588
593
|
signal: ctx.signal,
|
|
589
594
|
});
|
|
590
595
|
if (!res.ok) {
|
|
591
|
-
await this.#throwForStatus(res, pathAndQuery, ctx);
|
|
596
|
+
await this.#throwForStatus(res, pathAndQuery, ctx, { listRead: init.listRead });
|
|
592
597
|
}
|
|
593
598
|
if (init.noteRead) {
|
|
594
599
|
this.#assertNotDirectory(res, pathAndQuery, ctx);
|
|
595
600
|
}
|
|
601
|
+
if (init.listRead) {
|
|
602
|
+
this.#assertNotFile(res, pathAndQuery, ctx);
|
|
603
|
+
}
|
|
596
604
|
return res;
|
|
597
605
|
};
|
|
598
606
|
if (!RETRY_SAFE_METHODS.has(init.method.toUpperCase())) {
|
|
@@ -644,6 +652,65 @@ export class ObsidianService {
|
|
|
644
652
|
...ctx.recoveryFor('path_is_directory'),
|
|
645
653
|
});
|
|
646
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* Reject a `2xx` folder listing that actually served a file. The inverse of
|
|
657
|
+
* `#assertNotDirectory`, read off the same header: a trailing slash is a
|
|
658
|
+
* no-op upstream, so `GET /vault/<file>/` answers `200`
|
|
659
|
+
* with the file's own body and `Content-Disposition: attachment`. Without
|
|
660
|
+
* this guard the body reaches `res.json()`, which throws an untyped
|
|
661
|
+
* `SyntaxError` on markdown and — worse — succeeds on a vault `.json` file,
|
|
662
|
+
* handing the walk a listing with no `files` array.
|
|
663
|
+
*/
|
|
664
|
+
#assertNotFile(res, path, ctx) {
|
|
665
|
+
if (!path.startsWith('/vault/'))
|
|
666
|
+
return;
|
|
667
|
+
if (res.headers.get('content-disposition') === null)
|
|
668
|
+
return;
|
|
669
|
+
const display = displayPath(path);
|
|
670
|
+
throw validationError(`${display} is a file, not a directory.`, {
|
|
671
|
+
path: display,
|
|
672
|
+
reason: 'path_is_file',
|
|
673
|
+
...ctx.recoveryFor('path_is_file'),
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* The plugin's capability report, fetched at most once per service instance
|
|
678
|
+
* and only when a failure is ambiguous without it — never as a startup probe.
|
|
679
|
+
*
|
|
680
|
+
* Deliberately not routed through `#request`: this runs while a caller is
|
|
681
|
+
* already waiting on a failed request, so it takes neither the retry budget
|
|
682
|
+
* nor the full request timeout. Every failure resolves to `undefined`, which
|
|
683
|
+
* callers must read as "unknown" and fall back on — a diagnostic must never
|
|
684
|
+
* sink the response it was issued to diagnose.
|
|
685
|
+
*
|
|
686
|
+
* The cache never expires, and does not need to: it is read only to word an
|
|
687
|
+
* error on a call that already failed. Installing the missing extension
|
|
688
|
+
* mid-session makes the route resolve, so no 404 arrives to be classified.
|
|
689
|
+
*/
|
|
690
|
+
async #vaultCapabilities(ctx) {
|
|
691
|
+
this.#capabilities ??= this.#probeCapabilities(ctx);
|
|
692
|
+
const capabilities = await this.#capabilities;
|
|
693
|
+
if (!capabilities)
|
|
694
|
+
this.#capabilities = undefined;
|
|
695
|
+
return capabilities;
|
|
696
|
+
}
|
|
697
|
+
async #probeCapabilities(ctx) {
|
|
698
|
+
const deadline = AbortSignal.timeout(CAPABILITY_PROBE_TIMEOUT_MS);
|
|
699
|
+
try {
|
|
700
|
+
const res = await this.#fetch(`${this.#config.baseUrl}/`, {
|
|
701
|
+
method: 'GET',
|
|
702
|
+
headers: { Authorization: `Bearer ${this.#config.apiKey}` },
|
|
703
|
+
dispatcher: this.#dispatcher,
|
|
704
|
+
signal: ctx.signal ? AbortSignal.any([ctx.signal, deadline]) : deadline,
|
|
705
|
+
});
|
|
706
|
+
if (!res.ok)
|
|
707
|
+
return;
|
|
708
|
+
return normalizeVaultStatus((await res.json()));
|
|
709
|
+
}
|
|
710
|
+
catch {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
647
714
|
/**
|
|
648
715
|
* Classify an upstream error response and throw.
|
|
649
716
|
*
|
|
@@ -677,7 +744,7 @@ export class ObsidianService {
|
|
|
677
744
|
*
|
|
678
745
|
* Issues #104 and #116.
|
|
679
746
|
*/
|
|
680
|
-
async #throwForStatus(res, path, ctx) {
|
|
747
|
+
async #throwForStatus(res, path, ctx, opts = {}) {
|
|
681
748
|
const text = await this.#readBodySafe(res);
|
|
682
749
|
const body = parseJsonObject(text);
|
|
683
750
|
const display = displayPath(path);
|
|
@@ -698,6 +765,18 @@ export class ObsidianService {
|
|
|
698
765
|
throw notFound('No file is currently active in Obsidian — open a file in the app first.', data('no_active_file'), { cause });
|
|
699
766
|
}
|
|
700
767
|
if (path.startsWith('/periodic/')) {
|
|
768
|
+
/**
|
|
769
|
+
* Two different failures answer identically here. Local REST API
|
|
770
|
+
* v5.0.2 removed the built-in `/periodic/` routes — a request against
|
|
771
|
+
* a version past that, with no companion extension registered to
|
|
772
|
+
* re-add them, gets Express's route-miss 404, byte-identical to the
|
|
773
|
+
* plugin's own "that note does not exist". Only the capability report
|
|
774
|
+
* separates them, and an unreadable one keeps the older, weaker
|
|
775
|
+
* reading rather than asserting a cause it cannot see.
|
|
776
|
+
*/
|
|
777
|
+
if (periodicRoutesAbsent(await this.#vaultCapabilities(ctx))) {
|
|
778
|
+
throw notFound(`This vault's Local REST API serves no /periodic/ routes: plugin v5.0.2 removed them, and the companion extension that restores them (${PERIODIC_EXTENSION_ID}) is not registered here.`, data('periodic_unsupported'), { cause });
|
|
779
|
+
}
|
|
701
780
|
const dateMatch = /\/(\d{4})\/(\d{2})\/(\d{2})\/?$/.exec(path);
|
|
702
781
|
const suffix = dateMatch ? ` for ${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}` : '';
|
|
703
782
|
throw notFound(`No ${periodOf(path)} note found${suffix}. Check that the Periodic Notes plugin is enabled and the note exists.`, data('periodic_not_found'), { cause });
|
|
@@ -705,6 +784,17 @@ export class ObsidianService {
|
|
|
705
784
|
if (path.startsWith('/commands/')) {
|
|
706
785
|
throw notFound(`Unknown Obsidian command: ${display}. Use \`obsidian_list_commands\` to discover valid command IDs.`, data('command_unknown'), { cause });
|
|
707
786
|
}
|
|
787
|
+
/**
|
|
788
|
+
* A folder listing 404s for two reasons the upstream does not
|
|
789
|
+
* separate: the folder is not there, or it is there and holds no
|
|
790
|
+
* files — the plugin computes the listing from loaded vault files, so
|
|
791
|
+
* an empty folder never reports. The recovery names both; the reason
|
|
792
|
+
* at least keeps the object right, where `note_missing` sent a caller
|
|
793
|
+
* who just listed a folder off to look for a note.
|
|
794
|
+
*/
|
|
795
|
+
if (opts.listRead) {
|
|
796
|
+
throw notFound(`Directory not found: ${display}`, data('directory_missing'), { cause });
|
|
797
|
+
}
|
|
708
798
|
throw notFound(`Not found: ${display}`, data('note_missing'), { cause });
|
|
709
799
|
}
|
|
710
800
|
case 405:
|
|
@@ -755,21 +845,28 @@ export class ObsidianService {
|
|
|
755
845
|
default: {
|
|
756
846
|
/**
|
|
757
847
|
* Unhandled 4xx and all 5xx. `httpStatusToErrorCode` supplies the same
|
|
758
|
-
* canonical mapping `httpErrorFromResponse` would (
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
*
|
|
848
|
+
* canonical mapping `httpErrorFromResponse` would (the whole 5xx range
|
|
849
|
+
* except 504 → ServiceUnavailable, 504 → Timeout) without that
|
|
850
|
+
* helper's response-derived extras: it builds its message from the
|
|
851
|
+
* upstream's reason phrase and mirrors the body onto
|
|
762
852
|
* `data.body`/`data.responseBody`, which is the #104 leak.
|
|
763
853
|
*
|
|
764
|
-
* `
|
|
765
|
-
*
|
|
766
|
-
*
|
|
854
|
+
* Two `data` fields are re-attached by hand because both steer
|
|
855
|
+
* `withRetry` and neither carries vault content:
|
|
856
|
+
*
|
|
857
|
+
* - `retryAfter` is a duration the backoff paces itself against, so
|
|
858
|
+
* dropping it with the rest would quietly change retry timing.
|
|
859
|
+
* - `retryable: false` on a 501 is the in-band opt-out. 501 shares
|
|
860
|
+
* ServiceUnavailable's transient code, so without the flag a method
|
|
861
|
+
* the plugin does not implement would burn the full retry budget on
|
|
862
|
+
* a result that cannot change.
|
|
767
863
|
*/
|
|
768
864
|
const retryAfter = res.headers.get('retry-after');
|
|
769
865
|
throw new McpError(httpStatusToErrorCode(res.status) ?? JsonRpcErrorCode.InternalError, `Obsidian Local REST API returned HTTP ${res.status}.`, {
|
|
770
866
|
...data(),
|
|
771
867
|
status: res.status,
|
|
772
868
|
...(retryAfter !== null ? { retryAfter } : {}),
|
|
869
|
+
...(res.status === 501 ? { retryable: false } : {}),
|
|
773
870
|
}, { cause });
|
|
774
871
|
}
|
|
775
872
|
}
|
|
@@ -896,6 +993,64 @@ function upstreamTextOf(err) {
|
|
|
896
993
|
function isStringCapacityOverflow(err) {
|
|
897
994
|
return /invalid string length/i.test(upstreamTextOf(err) ?? '');
|
|
898
995
|
}
|
|
996
|
+
/**
|
|
997
|
+
* Normalize the upstream `GET /` payload. `apiExtensions` entries arrive as a
|
|
998
|
+
* whole `PluginManifest` spread plus the extension's `routes` and `mcpTools`;
|
|
999
|
+
* only the identity trio is kept, since that is all either the capability
|
|
1000
|
+
* check or a calling agent reads.
|
|
1001
|
+
*/
|
|
1002
|
+
function normalizeVaultStatus(raw) {
|
|
1003
|
+
const extensions = raw.apiExtensions?.flatMap((e) => typeof e.id === 'string'
|
|
1004
|
+
? [
|
|
1005
|
+
{
|
|
1006
|
+
id: e.id,
|
|
1007
|
+
...(typeof e.name === 'string' ? { name: e.name } : {}),
|
|
1008
|
+
...(typeof e.version === 'string' ? { version: e.version } : {}),
|
|
1009
|
+
},
|
|
1010
|
+
]
|
|
1011
|
+
: []);
|
|
1012
|
+
return {
|
|
1013
|
+
/** Absent means the plugin did not accept the key, which is not authenticated. */
|
|
1014
|
+
authenticated: raw.authenticated === true,
|
|
1015
|
+
service: raw.service,
|
|
1016
|
+
status: raw.status,
|
|
1017
|
+
...(raw.versions ? { versions: raw.versions } : {}),
|
|
1018
|
+
...(raw.manifest ? { manifest: raw.manifest } : {}),
|
|
1019
|
+
...(extensions ? { apiExtensions: extensions } : {}),
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* True when this plugin build cannot serve `/periodic/` at all: version past
|
|
1024
|
+
* the removal, and the companion extension absent from a capability report
|
|
1025
|
+
* that actually listed its extensions.
|
|
1026
|
+
*
|
|
1027
|
+
* The version is the deciding factor, not extension presence — `apiExtensions`
|
|
1028
|
+
* predates the removal and reads empty on older builds that still route
|
|
1029
|
+
* `/periodic/` natively. A missing `apiExtensions` key means the report was
|
|
1030
|
+
* unauthenticated (the plugin omits it there) and so says nothing either way.
|
|
1031
|
+
*/
|
|
1032
|
+
function periodicRoutesAbsent(capabilities) {
|
|
1033
|
+
const extensions = capabilities?.apiExtensions;
|
|
1034
|
+
if (!extensions)
|
|
1035
|
+
return false;
|
|
1036
|
+
if (!atLeastVersion(capabilities.versions?.self, PERIODIC_ROUTES_REMOVED_IN))
|
|
1037
|
+
return false;
|
|
1038
|
+
return !extensions.some((e) => e.id === PERIODIC_EXTENSION_ID);
|
|
1039
|
+
}
|
|
1040
|
+
/** Dotted-version comparison against a numeric floor. Unparseable reads as "below". */
|
|
1041
|
+
function atLeastVersion(version, floor) {
|
|
1042
|
+
if (!version)
|
|
1043
|
+
return false;
|
|
1044
|
+
const parts = version.split('.').map((p) => Number.parseInt(p, 10));
|
|
1045
|
+
for (const [i, bound] of floor.entries()) {
|
|
1046
|
+
const part = parts[i];
|
|
1047
|
+
if (part === undefined || Number.isNaN(part))
|
|
1048
|
+
return false;
|
|
1049
|
+
if (part !== bound)
|
|
1050
|
+
return part > bound;
|
|
1051
|
+
}
|
|
1052
|
+
return true;
|
|
1053
|
+
}
|
|
899
1054
|
/** The period segment of a `/periodic/…` request path, for message copy. */
|
|
900
1055
|
function periodOf(urlPath) {
|
|
901
1056
|
return /^\/periodic\/(daily|weekly|monthly|quarterly|yearly)\//.exec(urlPath)?.[1] ?? 'periodic';
|