obsidian-mcp-server 3.5.1 → 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.
Files changed (65) hide show
  1. package/AGENTS.md +7 -6
  2. package/CLAUDE.md +7 -6
  3. package/README.md +7 -5
  4. package/changelog/3.5.x/3.5.2.md +27 -0
  5. package/dist/mcp-server/resources/definitions/index.d.ts +5 -0
  6. package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -1
  7. package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts +11 -4
  8. package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts.map +1 -1
  9. package/dist/mcp-server/resources/definitions/obsidian-status.resource.js +21 -12
  10. package/dist/mcp-server/resources/definitions/obsidian-status.resource.js.map +1 -1
  11. package/dist/mcp-server/tools/definitions/index.d.ts +88 -39
  12. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  13. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +6 -1
  14. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +7 -1
  16. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -1
  17. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +6 -1
  18. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -1
  19. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +7 -1
  20. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -1
  21. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +11 -2
  22. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -1
  23. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +41 -15
  24. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -1
  25. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +8 -3
  26. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +12 -5
  28. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +6 -1
  30. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +7 -1
  32. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -1
  33. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts +8 -3
  34. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -1
  35. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +10 -4
  36. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -1
  37. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +6 -1
  38. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts.map +1 -1
  39. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +7 -1
  40. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -1
  41. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts +6 -1
  42. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts.map +1 -1
  43. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +7 -1
  44. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -1
  45. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +6 -1
  46. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -1
  47. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +7 -1
  48. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -1
  49. package/dist/services/obsidian/frontmatter-ops.d.ts +9 -5
  50. package/dist/services/obsidian/frontmatter-ops.d.ts.map +1 -1
  51. package/dist/services/obsidian/frontmatter-ops.js +70 -16
  52. package/dist/services/obsidian/frontmatter-ops.js.map +1 -1
  53. package/dist/services/obsidian/obsidian-service.d.ts +10 -7
  54. package/dist/services/obsidian/obsidian-service.d.ts.map +1 -1
  55. package/dist/services/obsidian/obsidian-service.js +179 -31
  56. package/dist/services/obsidian/obsidian-service.js.map +1 -1
  57. package/dist/services/obsidian/section-extractor.d.ts +17 -1
  58. package/dist/services/obsidian/section-extractor.d.ts.map +1 -1
  59. package/dist/services/obsidian/section-extractor.js +109 -40
  60. package/dist/services/obsidian/section-extractor.js.map +1 -1
  61. package/dist/services/obsidian/types.d.ts +17 -0
  62. package/dist/services/obsidian/types.d.ts.map +1 -1
  63. package/manifest.json +1 -1
  64. package/package.json +7 -7
  65. package/server.json +3 -3
package/AGENTS.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** obsidian-mcp-server
4
- **Version:** 3.5.1
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.5`
4
+ **Version:** 3.5.2
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.8`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
8
8
  **Zod:** ^4.5.4
@@ -189,7 +189,7 @@ The framework also provides `ctx.state`. It isn't used by this server — Obsidi
189
189
 
190
190
  Handlers throw — the framework catches, classifies, and formats.
191
191
 
192
- **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated) — it's the single source of truth for the recovery hint that flows to the wire. Spread `ctx.recoveryFor('reason')` into `data` to opt the contract recovery onto the wire (the framework mirrors `data.recovery.hint` into `content[]` text). Override with explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
192
+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated) — it's the single source of truth for the recovery hint that flows to the wire. Spread `ctx.recoveryFor('reason')` into `data` to opt the contract recovery onto the wire (the framework mirrors `data.recovery.hint` into `content[]` text). Override with explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
193
193
 
194
194
  ```ts
195
195
  errors: [
@@ -299,8 +299,9 @@ Available skills:
299
299
  | `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
300
300
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
301
301
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
302
- | `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
303
- | `release-and-publish` | Ship a release end-to-end across npm, MCP Registry, GitHub Releases (`.mcpb`), and GHCR |
302
+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
303
+ | `release-pr-review` | Review pass on an open release PR simplifier + correctness review, fixup commits autosquashed into the stack, PR body kept in sync. Release PR mode only |
304
+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
304
305
  | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
305
306
  | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
306
307
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
@@ -380,7 +381,7 @@ security: false # optional — true ONLY for a source
380
381
 
381
382
  `agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
382
383
 
383
- **Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
384
+ **Section order:** the Keep a Changelog sequence — Added, Changed, Deprecated, Removed, Fixed, Security — then `Dependencies` last. Include only sections with entries — don't ship empty headers.
384
385
 
385
386
  **Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
386
387
 
package/CLAUDE.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** obsidian-mcp-server
4
- **Version:** 3.5.1
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.5`
4
+ **Version:** 3.5.2
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.8`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
8
8
  **Zod:** ^4.5.4
@@ -189,7 +189,7 @@ The framework also provides `ctx.state`. It isn't used by this server — Obsidi
189
189
 
190
190
  Handlers throw — the framework catches, classifies, and formats.
191
191
 
192
- **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated) — it's the single source of truth for the recovery hint that flows to the wire. Spread `ctx.recoveryFor('reason')` into `data` to opt the contract recovery onto the wire (the framework mirrors `data.recovery.hint` into `content[]` text). Override with explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
192
+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive a typed `ctx.fail(reason, …)` keyed by the declared reason union. TypeScript catches `ctx.fail('typo')` at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. The `recovery` field is required descriptive metadata (≥ 5 words, lint-validated) — it's the single source of truth for the recovery hint that flows to the wire. Spread `ctx.recoveryFor('reason')` into `data` to opt the contract recovery onto the wire (the framework mirrors `data.recovery.hint` into `content[]` text). Override with explicit `{ recovery: { hint: '...' } }` when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
193
193
 
194
194
  ```ts
195
195
  errors: [
@@ -299,8 +299,9 @@ Available skills:
299
299
  | `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
300
300
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
301
301
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
302
- | `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
303
- | `release-and-publish` | Ship a release end-to-end across npm, MCP Registry, GitHub Releases (`.mcpb`), and GHCR |
302
+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
303
+ | `release-pr-review` | Review pass on an open release PR simplifier + correctness review, fixup commits autosquashed into the stack, PR body kept in sync. Release PR mode only |
304
+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
304
305
  | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
305
306
  | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
306
307
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
@@ -380,7 +381,7 @@ security: false # optional — true ONLY for a source
380
381
 
381
382
  `agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
382
383
 
383
- **Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
384
+ **Section order:** the Keep a Changelog sequence — Added, Changed, Deprecated, Removed, Fixed, Security — then `Dependencies` last. Include only sections with entries — don't ship empty headers.
384
385
 
385
386
  **Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
386
387
 
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-3.5.1-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/obsidian-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/obsidian-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/obsidian-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-3.5.2-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/obsidian-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/obsidian-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/obsidian-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -49,7 +49,7 @@ Read a note in one of four projections, addressed by vault path, the active file
49
49
  - `format: "content"` — raw markdown body
50
50
  - `format: "full"` — content, frontmatter, tags, and file metadata; pass `includeLinks: true` to also parse outgoing wiki and markdown link references from the body (vault-internal only — external URLs are filtered)
51
51
  - `format: "document-map"` — catalog of headings, block references, and frontmatter fields
52
- - `format: "section"` — single heading/block/frontmatter section value (requires `section`); heading sections include the full subtree under that heading
52
+ - `format: "section"` — single heading/block/frontmatter section value (requires `section`); heading sections include the full subtree under that heading. The response echoes the locator the read resolved to in `sectionTarget` as a full `Parent::Child` path, and when a bare leaf name matches several headings it lists every colliding path in `candidates` — the read still returns the first match
53
53
 
54
54
  Pair the document-map projection with `obsidian_patch_note` to discover edit targets before patching.
55
55
 
@@ -136,7 +136,9 @@ Add, remove, or list tags on a note. Operates on one of two representations, def
136
136
  - `location: 'inline'` — only inline `#tag` syntax in the body; `add` appends `#tag` at end-of-file
137
137
  - `location: 'both'` — opt-in reconciliation across both representations
138
138
 
139
- `add` ensures the tag is present in the requested location(s); `remove` strips it; `list` ignores the input `tags` array. Inline `#tag` occurrences inside fenced code blocks are intentionally left alone.
139
+ `add` ensures the tag is present in the requested location(s); `remove` strips it; `list` ignores the input `tags` array.
140
+
141
+ Inline `#tag` detection skips code spans (fenced and inline), link spans (`[[...]]`, `[text](...)`, `[text][ref]`) — so a heading anchor, block anchor, or wikilink alias is never read as a tag or rewritten by a removal — and a hash escaped as `\#`. A `#` inside an HTML comment or a math span is still counted. `list` and `remove` run the same detection, so what `list` reports is what `remove` can reach.
140
142
 
141
143
  Inline mode reads and writes the note body only — a `#` inside a YAML scalar is frontmatter, so it is neither listed as an inline tag nor rewritten by a removal. Removing an inline tag takes exactly one adjacent horizontal space with it — the one before the tag, or the one after when no space precedes it; every other byte survives, including nested list indentation, four-space indented code blocks, trailing two-space hard line breaks, and table cell padding.
142
144
 
@@ -210,7 +212,7 @@ Obsidian-specific:
210
212
 
211
213
  - Wraps the [Obsidian Local REST API](https://github.com/coddingtonbear/obsidian-local-rest-api) plugin — typed client, deterministic error mapping
212
214
  - Section-aware editing across headings, block references, and frontmatter fields via `PATCH`-with-target operations
213
- - Tag reconciliation across both representations: frontmatter `tags:` array and inline `#tag` syntax (skipping fenced code blocks)
215
+ - Tag reconciliation across both representations: frontmatter `tags:` array and inline `#tag` syntax (skipping code spans, link spans, and hashes escaped as `\#`)
214
216
  - Search across up to three modes: text, JSONLogic, and (when the plugin is reachable) BM25-ranked Omnisearch — cursor-paginated per the MCP 2025-11-25 spec, with per-file match clipping in text mode
215
217
  - Required human-in-the-loop confirmation for destructive deletes — a multi-round-trip `input_required` round served on both protocol revisions, with no unconfirmed path through the tool
216
218
  - Folder-scoped read/write permissions via `OBSIDIAN_READ_PATHS` / `OBSIDIAN_WRITE_PATHS` and a global `OBSIDIAN_READ_ONLY` kill switch — denies are typed `path_forbidden` with the active scope echoed back in the error data
@@ -268,7 +270,7 @@ MCP_TRANSPORT_TYPE=http OBSIDIAN_API_KEY=... bun run start:http
268
270
 
269
271
  - [Bun v1.3.0](https://bun.sh/) or higher (or Node.js v24+).
270
272
  - The [Obsidian Local REST API](https://github.com/coddingtonbear/obsidian-local-rest-api) plugin, **v4.0.0 through v5.x**, installed and enabled in your vault. Generate an API key in **Settings → Community Plugins → Local REST API** and copy it into `OBSIDIAN_API_KEY`. Plugin v6.0 removes the markdown-patch 1.x wire format this server pins for section-targeted writes and the document map.
271
- - Periodic-note targets (`target: { "type": "periodic" }`) additionally need plugin **v5.0.1 or earlier** v5.0.2 removed the built-in `/periodic/` routes. Every other target type is unaffected.
273
+ - Periodic-note targets (`target: { "type": "periodic" }`) work across that whole range: natively on plugin **v5.0.1 and earlier**, and on **v5.0.2 and later** — which moved the `/periodic/` routes out of the plugin — once the companion [periodic-notes API extension](https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes) is installed. Without that extension on v5.0.2+, periodic targets fail with a `periodic_unsupported` error naming it; `obsidian://status` lists the registered extensions if you want to check first. Every other target type is unaffected.
272
274
  - An MCP client that can answer an input request (elicitation). `obsidian_delete_note` always asks for confirmation before deleting, so a client without that support can read and write notes but cannot delete one.
273
275
  - This server defaults to `http://127.0.0.1:27123` for simplicity. Enable **"Non-encrypted (HTTP) Server"** in the plugin settings to use it. To use the always-on HTTPS port instead, set `OBSIDIAN_BASE_URL=https://127.0.0.1:27124`; the plugin's self-signed cert is handled by `OBSIDIAN_VERIFY_SSL=false` (the default).
274
276
 
@@ -0,0 +1,27 @@
1
+ ---
2
+ summary: "Frontmatter boundary, inline tag detection, and section reads now share one consistent boundary and resolution logic; list_notes reports directory_missing/path_is_file instead of a generic note_missing; periodic-note targets get a periodic_unsupported error naming the missing plugin extension instead of a bare 404."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 3.5.2 — 2026-09-09
8
+
9
+ ## Changed
10
+
11
+ - **`obsidian_get_note` section reads echo the resolved heading path** — a bare leaf target reports back the full `Parent::Child` path it matched in `sectionTarget`, and every colliding heading in `candidates` when more than one shares that leaf; the read still returns the first match ([#107](https://github.com/cyanheads/obsidian-mcp-server/issues/107)).
12
+ - **`obsidian_manage_tags` inline `#tag` detection skips link spans and escaped hashes** — `[[wikilink#Heading]]`, `[text](url#frag)`, and `[text][ref]` are no longer read or rewritten as tags, and a `\#` is recognized as an escaped literal hash; `list` and `remove` share the same detection so they agree on what a tag is ([#119](https://github.com/cyanheads/obsidian-mcp-server/issues/119)).
13
+ - **`obsidian_list_notes` reports `directory_missing` and `path_is_file` in place of `note_missing`** — a folder that doesn't exist or holds no files gets `directory_missing`; a `path` that names a file gets `path_is_file` ([#105](https://github.com/cyanheads/obsidian-mcp-server/issues/105)).
14
+
15
+ ## Fixed
16
+
17
+ - **The frontmatter fence regex now agrees with `splice()` on an empty properties block** — `section-extractor`'s line-scan boundary previously used a different rule than `frontmatter-ops`'s splice, so a note with an empty `---\n---` block or a fence carrying trailing whitespace could hide body content from section reads; both now resolve the boundary through the same `splice()` call ([#118](https://github.com/cyanheads/obsidian-mcp-server/issues/118)).
18
+ - **Periodic-note targets get a typed `periodic_unsupported` error on Local REST API v5.0.2+ without the companion extension** — that plugin version removed the built-in `/periodic/` routes, and every periodic-target tool previously surfaced the resulting 404 as `periodic_not_found`, indistinguishable from "no note for that period." A memoized capability probe against `GET /`'s `apiExtensions` now tells the two apart and names `local-rest-api-periodic-notes` in the recovery hint ([#103](https://github.com/cyanheads/obsidian-mcp-server/issues/103)).
19
+
20
+ ## Dependencies
21
+
22
+ - `@cyanheads/mcp-ts-core` ^0.12.5 → ^0.12.8
23
+ - `undici` ^8.10.1 → ^8.10.2
24
+ - `@biomejs/biome` ^2.5.11 → ^2.5.12 (dev)
25
+ - `@types/node` ^26.4.0 → ^26.4.1 (dev)
26
+ - `tsc-alias` ^1.9.3 → ^1.9.4 (dev)
27
+ - `vitest` ^4.1.11 → ^5.0.0 (dev)
@@ -47,5 +47,10 @@ export declare const allResourceDefinitions: (import("@cyanheads/mcp-ts-core").R
47
47
  name: import("zod").ZodString;
48
48
  version: import("zod").ZodString;
49
49
  }, import("zod/v4/core").$strip>>;
50
+ apiExtensions: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
51
+ id: import("zod").ZodString;
52
+ name: import("zod").ZodOptional<import("zod").ZodString>;
53
+ version: import("zod").ZodOptional<import("zod").ZodString>;
54
+ }, import("zod/v4/core").$strip>>>;
50
55
  }, import("zod/v4/core").$strip>, undefined>)[];
51
56
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAAoD,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAAoD,CAAC"}
@@ -1,8 +1,10 @@
1
1
  /**
2
- * @fileoverview obsidian://status — reachability + auth check for the Obsidian
3
- * Local REST API plugin. Issues an anonymous probe (so the resource still
4
- * works when the key is wrong) and a separate authenticated probe so the
5
- * `authenticated` field reflects whether the configured key is accepted.
2
+ * @fileoverview obsidian://status — reachability, auth, and capability report
3
+ * for the Obsidian Local REST API plugin. One authenticated `GET /`: the route
4
+ * answers `200` whatever the key is and self-reports whether it was accepted,
5
+ * so a misconfigured key still yields the full reachability payload, and
6
+ * `apiExtensions` — which the plugin omits from the unauthenticated response —
7
+ * comes back on the same request.
6
8
  * @module mcp-server/resources/definitions/obsidian-status.resource
7
9
  */
8
10
  import { z } from '@cyanheads/mcp-ts-core';
@@ -19,5 +21,10 @@ export declare const obsidianStatus: import("@cyanheads/mcp-ts-core").ResourceDe
19
21
  name: z.ZodString;
20
22
  version: z.ZodString;
21
23
  }, z.core.$strip>>;
24
+ apiExtensions: z.ZodOptional<z.ZodArray<z.ZodObject<{
25
+ id: z.ZodString;
26
+ name: z.ZodOptional<z.ZodString>;
27
+ version: z.ZodOptional<z.ZodString>;
28
+ }, z.core.$strip>>>;
22
29
  }, z.core.$strip>, undefined>;
23
30
  //# sourceMappingURL=obsidian-status.resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-status.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-status.resource.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGrD,eAAO,MAAM,cAAc;;;;;;;;;;;;;6BAwCzB,CAAC"}
1
+ {"version":3,"file":"obsidian-status.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-status.resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGrD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;6BAmDzB,CAAC"}
@@ -1,15 +1,17 @@
1
1
  /**
2
- * @fileoverview obsidian://status — reachability + auth check for the Obsidian
3
- * Local REST API plugin. Issues an anonymous probe (so the resource still
4
- * works when the key is wrong) and a separate authenticated probe so the
5
- * `authenticated` field reflects whether the configured key is accepted.
2
+ * @fileoverview obsidian://status — reachability, auth, and capability report
3
+ * for the Obsidian Local REST API plugin. One authenticated `GET /`: the route
4
+ * answers `200` whatever the key is and self-reports whether it was accepted,
5
+ * so a misconfigured key still yields the full reachability payload, and
6
+ * `apiExtensions` — which the plugin omits from the unauthenticated response —
7
+ * comes back on the same request.
6
8
  * @module mcp-server/resources/definitions/obsidian-status.resource
7
9
  */
8
10
  import { resource, z } from '@cyanheads/mcp-ts-core';
9
11
  import { getObsidianService } from '../../../services/obsidian/obsidian-service.js';
10
12
  export const obsidianStatus = resource('obsidian://status', {
11
13
  name: 'obsidian-status',
12
- description: 'Server reachability, plugin version, and auth status of the Obsidian Local REST API. Reports the unauthenticated reachability info even when the API key is misconfigured; `authenticated` reflects whether the configured key is accepted by an authenticated probe.',
14
+ description: 'Server reachability, plugin version, auth status, and registered API extensions of the Obsidian Local REST API. Still reports reachability when the API key is misconfigured; `authenticated` reflects whether the plugin accepted the configured key. Check `apiExtensions` before using a `periodic` note target — on plugin v5.0.2 and later those routes are served only when `local-rest-api-periodic-notes` is registered.',
13
15
  mimeType: 'application/json',
14
16
  params: z.object({}),
15
17
  output: z.object({
@@ -17,7 +19,7 @@ export const obsidianStatus = resource('obsidian://status', {
17
19
  service: z.string().describe('Service identifier returned by the plugin.'),
18
20
  authenticated: z
19
21
  .boolean()
20
- .describe('True when the configured OBSIDIAN_API_KEY is accepted by an authenticated request to the vault listing.'),
22
+ .describe('True when the plugin accepted the configured OBSIDIAN_API_KEY on this request.'),
21
23
  versions: z
22
24
  .object({
23
25
  obsidian: z.string().optional().describe('Obsidian app version, when reported.'),
@@ -33,15 +35,22 @@ export const obsidianStatus = resource('obsidian://status', {
33
35
  })
34
36
  .optional()
35
37
  .describe('Plugin manifest, when reported.'),
38
+ apiExtensions: z
39
+ .array(z
40
+ .object({
41
+ id: z
42
+ .string()
43
+ .describe('Extension manifest ID, e.g. `local-rest-api-periodic-notes` for the extension that serves `/periodic/` routes on plugin v5.0.2 and later.'),
44
+ name: z.string().optional().describe('Extension display name, when reported.'),
45
+ version: z.string().optional().describe('Extension version, when reported.'),
46
+ })
47
+ .describe('One registered API extension.'))
48
+ .optional()
49
+ .describe('API extensions registered against the plugin. An empty array means none are registered; the field is absent when the plugin did not report it at all, which it does only for a request whose API key was accepted.'),
36
50
  }),
37
51
  auth: ['resource:obsidian-status:read'],
38
52
  async handler(_params, ctx) {
39
- const svc = getObsidianService();
40
- const [status, authenticated] = await Promise.all([
41
- svc.getStatus(ctx),
42
- svc.probeAuthenticated(ctx),
43
- ]);
44
- return { ...status, authenticated };
53
+ return await getObsidianService().getStatus(ctx);
45
54
  },
46
55
  });
47
56
  //# sourceMappingURL=obsidian-status.resource.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-status.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-status.resource.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAE7E,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,mBAAmB,EAAE;IAC1D,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,uQAAuQ;IACzQ,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC/D,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;QAC1E,aAAa,EAAE,CAAC;aACb,OAAO,EAAE;aACT,QAAQ,CACP,yGAAyG,CAC1G;QACH,QAAQ,EAAE,CAAC;aACR,MAAM,CAAC;YACN,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;YAChF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;SACtF,CAAC;aACD,QAAQ,EAAE;aACV,QAAQ,CAAC,oDAAoD,CAAC;QACjE,QAAQ,EAAE,CAAC;aACR,MAAM,CAAC;YACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;YAC9C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YACjD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;SAChD,CAAC;aACD,QAAQ,EAAE;aACV,QAAQ,CAAC,iCAAiC,CAAC;KAC/C,CAAC;IACF,IAAI,EAAE,CAAC,+BAA+B,CAAC;IAEvC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG;QACxB,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;QACjC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAChD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC;YAClB,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC;SAC5B,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,MAAM,EAAE,aAAa,EAAE,CAAC;IACtC,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"obsidian-status.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-status.resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAE7E,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,mBAAmB,EAAE;IAC1D,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,kaAAka;IACpa,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC/D,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;QAC1E,aAAa,EAAE,CAAC;aACb,OAAO,EAAE;aACT,QAAQ,CAAC,gFAAgF,CAAC;QAC7F,QAAQ,EAAE,CAAC;aACR,MAAM,CAAC;YACN,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;YAChF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;SACtF,CAAC;aACD,QAAQ,EAAE;aACV,QAAQ,CAAC,oDAAoD,CAAC;QACjE,QAAQ,EAAE,CAAC;aACR,MAAM,CAAC;YACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;YAC9C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YACjD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;SAChD,CAAC;aACD,QAAQ,EAAE;aACV,QAAQ,CAAC,iCAAiC,CAAC;QAC9C,aAAa,EAAE,CAAC;aACb,KAAK,CACJ,CAAC;aACE,MAAM,CAAC;YACN,EAAE,EAAE,CAAC;iBACF,MAAM,EAAE;iBACR,QAAQ,CACP,2IAA2I,CAC5I;YACH,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;YAC9E,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;SAC7E,CAAC;aACD,QAAQ,CAAC,+BAA+B,CAAC,CAC7C;aACA,QAAQ,EAAE;aACV,QAAQ,CACP,oNAAoN,CACrN;KACJ,CAAC;IACF,IAAI,EAAE,CAAC,+BAA+B,CAAC;IAEvC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG;QACxB,OAAO,MAAM,kBAAkB,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC;CACF,CAAC,CAAC"}
@@ -95,6 +95,8 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
95
95
  }>;
96
96
  target: import("zod").ZodString;
97
97
  }, import("zod/v4/core").$strip>;
98
+ sectionTarget: import("zod").ZodOptional<import("zod").ZodString>;
99
+ candidates: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
98
100
  valueText: import("zod").ZodOptional<import("zod").ZodString>;
99
101
  valueJson: import("zod").ZodOptional<import("zod").ZodUnknown>;
100
102
  }, import("zod/v4/core").$strip>], "format">;
@@ -123,10 +125,15 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
123
125
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
124
126
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
125
127
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
128
+ }, {
129
+ readonly reason: "periodic_unsupported";
130
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
131
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
132
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
126
133
  }, {
127
134
  readonly reason: "periodic_not_found";
128
135
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
129
- readonly when: "Target was `periodic` but no matching periodic note exists.";
136
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
130
137
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
131
138
  }, {
132
139
  readonly reason: "periodic_disabled";
@@ -148,7 +155,38 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
148
155
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
149
156
  readonly when: "The path contains a `.` or `..` segment, which is rejected to prevent vault escape.";
150
157
  readonly recovery: "Supply a vault-relative path with no `.` or `..` segments, e.g. \"Projects/Note.md\". Use obsidian_list_notes to browse the vault.";
151
- }], undefined> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
158
+ }], {
159
+ readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
160
+ }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
161
+ nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
162
+ minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
163
+ limit: import("zod").ZodDefault<import("zod").ZodNumber>;
164
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
165
+ tags: import("zod").ZodArray<import("zod").ZodObject<{
166
+ name: import("zod").ZodString;
167
+ count: import("zod").ZodNumber;
168
+ }, import("zod/v4/core").$strip>>;
169
+ appliedFilters: import("zod").ZodObject<{
170
+ nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
171
+ minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
172
+ limit: import("zod").ZodNumber;
173
+ }, import("zod/v4/core").$strip>;
174
+ }, import("zod/v4/core").$strip>, readonly [{
175
+ readonly reason: "regex_invalid";
176
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
177
+ readonly when: "The supplied `nameRegex` is not a valid ECMAScript regex.";
178
+ readonly recovery: "Use a valid ECMAScript regex (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
179
+ }, {
180
+ readonly reason: "regex_unsafe";
181
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
182
+ readonly when: "The supplied `nameRegex` is well-formed but exceeds the 256-character limit or contains nested quantifiers known to cause catastrophic backtracking.";
183
+ readonly recovery: "Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
184
+ }], {
185
+ readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
186
+ readonly truncated: import("zod").ZodOptional<import("zod").ZodBoolean>;
187
+ readonly shown: import("zod").ZodOptional<import("zod").ZodNumber>;
188
+ readonly cap: import("zod").ZodOptional<import("zod").ZodNumber>;
189
+ }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
152
190
  path: import("zod").ZodOptional<import("zod").ZodString>;
153
191
  extension: import("zod").ZodOptional<import("zod").ZodString>;
154
192
  nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
@@ -194,41 +232,17 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
194
232
  readonly when: "The supplied `path` is outside OBSIDIAN_READ_PATHS (root listings always pass; specific subdirectories must be readable).";
195
233
  readonly recovery: "List a directory inside the configured read scope, or omit `path` to list from the vault root. The error data echoes the active scope.";
196
234
  }, {
197
- readonly reason: "note_missing";
235
+ readonly reason: "directory_missing";
198
236
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
199
- readonly when: "The supplied `path` does not exist in the vault. Sub-directories that disappear mid-walk are silently skipped only the root path surfaces this error.";
200
- readonly recovery: "List a parent directory to find the correct casing or check the spelling.";
201
- }], {
202
- readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
203
- }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
204
- nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
205
- minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
206
- limit: import("zod").ZodDefault<import("zod").ZodNumber>;
207
- }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
208
- tags: import("zod").ZodArray<import("zod").ZodObject<{
209
- name: import("zod").ZodString;
210
- count: import("zod").ZodNumber;
211
- }, import("zod/v4/core").$strip>>;
212
- appliedFilters: import("zod").ZodObject<{
213
- nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
214
- minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
215
- limit: import("zod").ZodNumber;
216
- }, import("zod/v4/core").$strip>;
217
- }, import("zod/v4/core").$strip>, readonly [{
218
- readonly reason: "regex_invalid";
219
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
220
- readonly when: "The supplied `nameRegex` is not a valid ECMAScript regex.";
221
- readonly recovery: "Use a valid ECMAScript regex (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
237
+ readonly when: "No listable directory at the supplied `path` — either it does not exist, or it exists and currently holds no files. Sub-directories that disappear mid-walk are silently skipped, so only the root path surfaces this error.";
238
+ readonly recovery: "List the parent directory to check the spelling and casing. A folder that exists but holds no files reports the same way, since the Local REST API omits empty folders from its listings — add a file to it, or confirm from the parent that the folder is there.";
222
239
  }, {
223
- readonly reason: "regex_unsafe";
240
+ readonly reason: "path_is_file";
224
241
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
225
- readonly when: "The supplied `nameRegex` is well-formed but exceeds the 256-character limit or contains nested quantifiers known to cause catastrophic backtracking.";
226
- readonly recovery: "Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
242
+ readonly when: "The supplied `path` names a file rather than a directory.";
243
+ readonly recovery: "Read the file with obsidian_get_note instead, or list its parent directory to browse alongside it.";
227
244
  }], {
228
245
  readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
229
- readonly truncated: import("zod").ZodOptional<import("zod").ZodBoolean>;
230
- readonly shown: import("zod").ZodOptional<import("zod").ZodNumber>;
231
- readonly cap: import("zod").ZodOptional<import("zod").ZodNumber>;
232
246
  }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
233
247
  path: import("zod").ZodString;
234
248
  failIfMissing: import("zod").ZodDefault<import("zod").ZodBoolean>;
@@ -336,10 +350,15 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
336
350
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
337
351
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
338
352
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
353
+ }, {
354
+ readonly reason: "periodic_unsupported";
355
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
356
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
357
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
339
358
  }, {
340
359
  readonly reason: "periodic_not_found";
341
360
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
342
- readonly when: "Target was `periodic` but no matching periodic note exists.";
361
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
343
362
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
344
363
  }, {
345
364
  readonly reason: "periodic_disabled";
@@ -429,10 +448,15 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
429
448
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
430
449
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
431
450
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
451
+ }, {
452
+ readonly reason: "periodic_unsupported";
453
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
454
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
455
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
432
456
  }, {
433
457
  readonly reason: "periodic_not_found";
434
458
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
435
- readonly when: "Target was `periodic` but no matching periodic note exists.";
459
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
436
460
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
437
461
  }, {
438
462
  readonly reason: "periodic_disabled";
@@ -512,10 +536,15 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
512
536
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
513
537
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
514
538
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
539
+ }, {
540
+ readonly reason: "periodic_unsupported";
541
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
542
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
543
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
515
544
  }, {
516
545
  readonly reason: "periodic_not_found";
517
546
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
518
- readonly when: "Target was `periodic` but no matching periodic note exists.";
547
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
519
548
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
520
549
  }, {
521
550
  readonly reason: "periodic_disabled";
@@ -619,10 +648,15 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
619
648
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
620
649
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
621
650
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
651
+ }, {
652
+ readonly reason: "periodic_unsupported";
653
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
654
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
655
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
622
656
  }, {
623
657
  readonly reason: "periodic_not_found";
624
658
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
625
- readonly when: "Target was `periodic` but no matching periodic note exists.";
659
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
626
660
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
627
661
  }, {
628
662
  readonly reason: "periodic_disabled";
@@ -705,10 +739,15 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
705
739
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
706
740
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
707
741
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
742
+ }, {
743
+ readonly reason: "periodic_unsupported";
744
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
745
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
746
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
708
747
  }, {
709
748
  readonly reason: "periodic_not_found";
710
749
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
711
- readonly when: "Target was `periodic` but no matching periodic note exists.";
750
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
712
751
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
713
752
  }, {
714
753
  readonly reason: "periodic_disabled";
@@ -799,10 +838,15 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
799
838
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
800
839
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
801
840
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
841
+ }, {
842
+ readonly reason: "periodic_unsupported";
843
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
844
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
845
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
802
846
  }, {
803
847
  readonly reason: "periodic_not_found";
804
848
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
805
- readonly when: "Target was `periodic` but no matching periodic note exists.";
849
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
806
850
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
807
851
  }, {
808
852
  readonly reason: "periodic_disabled";
@@ -861,10 +905,15 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
861
905
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
862
906
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
863
907
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
908
+ }, {
909
+ readonly reason: "periodic_unsupported";
910
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
911
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
912
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
864
913
  }, {
865
914
  readonly reason: "periodic_not_found";
866
915
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
867
- readonly when: "Target was `periodic` but no matching periodic note exists.";
916
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
868
917
  readonly recovery: "Pass an explicit path target — periodic notes must already exist.";
869
918
  }, {
870
919
  readonly reason: "periodic_disabled";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgBH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAK/B,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQhC,CAAC;AAEF,mHAAmH;AACnH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAiD,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgBH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAK/B,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQhC,CAAC;AAEF,mHAAmH;AACnH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAiD,CAAC"}
@@ -61,10 +61,15 @@ export declare const obsidianAppendToNote: import("@cyanheads/mcp-ts-core").Tool
61
61
  readonly code: JsonRpcErrorCode.NotFound;
62
62
  readonly when: "Target was `active` but no file is currently open in Obsidian.";
63
63
  readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
64
+ }, {
65
+ readonly reason: "periodic_unsupported";
66
+ readonly code: JsonRpcErrorCode.NotFound;
67
+ readonly when: "Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.";
68
+ readonly recovery: "Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.";
64
69
  }, {
65
70
  readonly reason: "periodic_not_found";
66
71
  readonly code: JsonRpcErrorCode.NotFound;
67
- readonly when: "Target was `periodic` but no matching periodic note exists.";
72
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
68
73
  readonly recovery: "Create the periodic note first or pass an explicit path target.";
69
74
  }, {
70
75
  readonly reason: "periodic_disabled";
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-append-to-note.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-append-to-note.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAIjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiL/B,CAAC"}
1
+ {"version":3,"file":"obsidian-append-to-note.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-append-to-note.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAIjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAwL/B,CAAC"}
@@ -62,10 +62,16 @@ export const obsidianAppendToNote = tool('obsidian_append_to_note', {
62
62
  when: 'Target was `active` but no file is currently open in Obsidian.',
63
63
  recovery: 'Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.',
64
64
  },
65
+ {
66
+ reason: 'periodic_unsupported',
67
+ code: JsonRpcErrorCode.NotFound,
68
+ when: 'Target was `periodic` and this vault runs Local REST API v5.0.2 or later without the companion periodic-notes extension, so the `/periodic/` routes are not served at all.',
69
+ recovery: 'Install the periodic-notes extension from https://github.com/coddingtonbear/obsidian-local-rest-api-periodic-notes, or address the note by an explicit vault path.',
70
+ },
65
71
  {
66
72
  reason: 'periodic_not_found',
67
73
  code: JsonRpcErrorCode.NotFound,
68
- when: 'Target was `periodic` but no matching periodic note exists.',
74
+ when: 'Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.',
69
75
  recovery: 'Create the periodic note first or pass an explicit path target.',
70
76
  },
71
77
  {