obsidian-mcp-server 3.7.2 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/AGENTS.md +20 -15
  2. package/CLAUDE.md +20 -15
  3. package/README.md +31 -19
  4. package/changelog/3.8.x/3.8.0.md +38 -0
  5. package/changelog/3.9.x/3.9.0.md +43 -0
  6. package/changelog/template.md +10 -3
  7. package/dist/config/server-config.d.ts +1 -0
  8. package/dist/config/server-config.d.ts.map +1 -1
  9. package/dist/config/server-config.js +30 -19
  10. package/dist/config/server-config.js.map +1 -1
  11. package/dist/index.d.ts +3 -3
  12. package/dist/index.js +27 -15
  13. package/dist/index.js.map +1 -1
  14. package/dist/instructions.d.ts +4 -3
  15. package/dist/instructions.d.ts.map +1 -1
  16. package/dist/instructions.js +23 -12
  17. package/dist/instructions.js.map +1 -1
  18. package/dist/mcp-server/resources/definitions/index.d.ts +2 -2
  19. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts +3 -2
  20. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts.map +1 -1
  21. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js +6 -5
  22. package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js.map +1 -1
  23. package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts +2 -2
  24. package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js +2 -2
  25. package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js.map +1 -1
  26. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts +15 -6
  27. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts.map +1 -1
  28. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js +24 -12
  29. package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js.map +1 -1
  30. package/dist/mcp-server/tools/definitions/index.d.ts +250 -230
  31. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  32. package/dist/mcp-server/tools/definitions/index.js +6 -4
  33. package/dist/mcp-server/tools/definitions/index.js.map +1 -1
  34. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +8 -2
  35. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -1
  36. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +10 -3
  37. package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -1
  38. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +7 -6
  39. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -1
  40. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +7 -7
  41. package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -1
  42. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +2 -2
  43. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -1
  44. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +8 -5
  45. package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -1
  46. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +6 -6
  47. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -1
  48. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +8 -8
  49. package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -1
  50. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts +4 -4
  51. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +7 -7
  52. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -1
  53. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +2 -2
  54. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -1
  55. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +2 -2
  56. package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -1
  57. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts +2 -2
  58. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -1
  59. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +2 -2
  60. package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -1
  61. package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts +2 -2
  62. package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js +3 -3
  63. package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js.map +1 -1
  64. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +8 -2
  65. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts.map +1 -1
  66. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +10 -3
  67. package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -1
  68. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts +2 -2
  69. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +2 -2
  70. package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -1
  71. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +185 -35
  72. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -1
  73. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +145 -82
  74. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -1
  75. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +8 -2
  76. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -1
  77. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +12 -5
  78. package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -1
  79. package/dist/services/obsidian/jsonlogic-evaluator.d.ts +33 -8
  80. package/dist/services/obsidian/jsonlogic-evaluator.d.ts.map +1 -1
  81. package/dist/services/obsidian/jsonlogic-evaluator.js +171 -32
  82. package/dist/services/obsidian/jsonlogic-evaluator.js.map +1 -1
  83. package/dist/services/obsidian/jsonlogic-worker.d.ts +6 -5
  84. package/dist/services/obsidian/jsonlogic-worker.d.ts.map +1 -1
  85. package/dist/services/obsidian/jsonlogic-worker.js +7 -5
  86. package/dist/services/obsidian/jsonlogic-worker.js.map +1 -1
  87. package/dist/services/obsidian/obsidian-service.d.ts +90 -36
  88. package/dist/services/obsidian/obsidian-service.d.ts.map +1 -1
  89. package/dist/services/obsidian/obsidian-service.js +748 -248
  90. package/dist/services/obsidian/obsidian-service.js.map +1 -1
  91. package/dist/services/obsidian/patch-instruction.d.ts +4 -1
  92. package/dist/services/obsidian/patch-instruction.d.ts.map +1 -1
  93. package/dist/services/obsidian/patch-instruction.js +5 -2
  94. package/dist/services/obsidian/patch-instruction.js.map +1 -1
  95. package/dist/services/obsidian/path-policy.d.ts +67 -23
  96. package/dist/services/obsidian/path-policy.d.ts.map +1 -1
  97. package/dist/services/obsidian/path-policy.js +183 -80
  98. package/dist/services/obsidian/path-policy.js.map +1 -1
  99. package/dist/services/obsidian/section-extractor.d.ts +62 -1
  100. package/dist/services/obsidian/section-extractor.d.ts.map +1 -1
  101. package/dist/services/obsidian/section-extractor.js +387 -51
  102. package/dist/services/obsidian/section-extractor.js.map +1 -1
  103. package/dist/services/obsidian/simple-search.d.ts +62 -0
  104. package/dist/services/obsidian/simple-search.d.ts.map +1 -0
  105. package/dist/services/obsidian/simple-search.js +264 -0
  106. package/dist/services/obsidian/simple-search.js.map +1 -0
  107. package/manifest.json +12 -4
  108. package/package.json +9 -9
  109. package/server.json +23 -11
  110. package/skills/obsidian-guide/SKILL.md +61 -0
@@ -5,20 +5,22 @@
5
5
  * separately so the entry point wraps it with `disabledTool()` when either
6
6
  * `OBSIDIAN_ENABLE_COMMANDS=false` or `OBSIDIAN_READ_ONLY=true` — keeping this
7
7
  * module free of eager config reads. `obsidian_search_notes` is exposed as a
8
- * factory (`buildSearchNotesTool`) because its mode enum is conditional on
9
- * Omnisearch reachability, which is only known after the startup probe runs.
8
+ * factory (`buildSearchNotesTool`) because its mode enum follows
9
+ * `omnisearchAvailability`, which the entry point decides at startup: the
10
+ * `omnisearch` mode is withheld under a read scope and otherwise offered only
11
+ * when the startup probe reaches the plugin.
10
12
  * `obsidian_delete_note` is exposed as a factory (`buildDeleteNoteTool`)
11
13
  * because its description and confirmation round follow
12
14
  * `OBSIDIAN_DELETE_ELICITATION`, which the entry point reads.
13
15
  * @module mcp-server/tools/definitions/index
14
16
  */
15
17
  export { buildDeleteNoteTool } from './obsidian-delete-note.tool.js';
16
- export { buildSearchNotesTool } from './obsidian-search-notes.tool.js';
18
+ export { buildSearchNotesTool, type OmnisearchAvailability, omnisearchAvailability, } from './obsidian-search-notes.tool.js';
17
19
  /**
18
20
  * Tools that don't depend on runtime probes — always registered, even with
19
21
  * `OBSIDIAN_READ_ONLY=true`. `obsidian_search_notes` is constructed separately
20
22
  * in the entry point via `buildSearchNotesTool` so its mode enum can reflect
21
- * Omnisearch reachability.
23
+ * Omnisearch availability.
22
24
  *
23
25
  * `obsidian_open_in_ui` stays here rather than in the write bucket so that
24
26
  * opening a note that already exists keeps working on a read-only vault, which
@@ -113,8 +115,8 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
113
115
  readonly reason: "path_forbidden";
114
116
  readonly thrownBy: "service";
115
117
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
116
- readonly when: "The target path is outside OBSIDIAN_READ_PATHS (and OBSIDIAN_WRITE_PATHS, since write paths imply read access).";
117
- readonly recovery: "Use a path inside the configured read scope. The error data echoes the active scope.";
118
+ readonly when: "The target path is outside OBSIDIAN_READ_PATHS (and OBSIDIAN_WRITE_PATHS, since write paths imply read access), or inside OBSIDIAN_DENY_PATHS.";
119
+ readonly recovery: "Use a path inside the configured read scope. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
118
120
  }, {
119
121
  readonly reason: "note_missing";
120
122
  readonly thrownBy: "service";
@@ -171,6 +173,35 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
171
173
  }], {
172
174
  readonly requestedPath: import("zod").ZodOptional<import("zod").ZodString>;
173
175
  readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
176
+ }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
177
+ nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
178
+ minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
179
+ limit: import("zod").ZodDefault<import("zod").ZodNumber>;
180
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
181
+ tags: import("zod").ZodArray<import("zod").ZodObject<{
182
+ name: import("zod").ZodString;
183
+ count: import("zod").ZodNumber;
184
+ }, import("zod/v4/core").$strip>>;
185
+ appliedFilters: import("zod").ZodObject<{
186
+ nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
187
+ minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
188
+ limit: import("zod").ZodNumber;
189
+ }, import("zod/v4/core").$strip>;
190
+ }, import("zod/v4/core").$strip>, readonly [{
191
+ readonly reason: "regex_invalid";
192
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
193
+ readonly when: "The supplied `nameRegex` is not a valid ECMAScript regex.";
194
+ readonly recovery: "Use a valid ECMAScript regex (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
195
+ }, {
196
+ readonly reason: "regex_unsafe";
197
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
198
+ readonly when: "The supplied `nameRegex` is well-formed but exceeds the 256-character limit or contains nested quantifiers known to cause catastrophic backtracking.";
199
+ readonly recovery: "Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
200
+ }], {
201
+ readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
202
+ readonly truncated: import("zod").ZodOptional<import("zod").ZodBoolean>;
203
+ readonly shown: import("zod").ZodOptional<import("zod").ZodNumber>;
204
+ readonly cap: import("zod").ZodOptional<import("zod").ZodNumber>;
174
205
  }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
175
206
  path: import("zod").ZodOptional<import("zod").ZodString>;
176
207
  extension: import("zod").ZodOptional<import("zod").ZodString>;
@@ -215,8 +246,8 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
215
246
  readonly reason: "path_forbidden";
216
247
  readonly thrownBy: "service";
217
248
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
218
- readonly when: "The supplied `path` is outside OBSIDIAN_READ_PATHS and is not a folder on the way to it. The vault root and the folders leading to a scoped path always list, showing only in-scope entries.";
219
- 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.";
249
+ readonly when: "The supplied `path` is outside OBSIDIAN_READ_PATHS and is not a folder on the way to it, or is inside OBSIDIAN_DENY_PATHS. The vault root and the folders leading to a scoped path always list, showing only in-scope entries.";
250
+ 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; a `denied_path` denial carries none, since the operator withholds that path.";
220
251
  }, {
221
252
  readonly reason: "directory_missing";
222
253
  readonly thrownBy: "service";
@@ -231,35 +262,6 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
231
262
  readonly recovery: "Read the file with obsidian_get_note instead, or list its parent directory to browse alongside it.";
232
263
  }], {
233
264
  readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
234
- }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
235
- nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
236
- minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
237
- limit: import("zod").ZodDefault<import("zod").ZodNumber>;
238
- }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
239
- tags: import("zod").ZodArray<import("zod").ZodObject<{
240
- name: import("zod").ZodString;
241
- count: import("zod").ZodNumber;
242
- }, import("zod/v4/core").$strip>>;
243
- appliedFilters: import("zod").ZodObject<{
244
- nameRegex: import("zod").ZodOptional<import("zod").ZodString>;
245
- minCount: import("zod").ZodOptional<import("zod").ZodNumber>;
246
- limit: import("zod").ZodNumber;
247
- }, import("zod/v4/core").$strip>;
248
- }, import("zod/v4/core").$strip>, readonly [{
249
- readonly reason: "regex_invalid";
250
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
251
- readonly when: "The supplied `nameRegex` is not a valid ECMAScript regex.";
252
- readonly recovery: "Use a valid ECMAScript regex (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
253
- }, {
254
- readonly reason: "regex_unsafe";
255
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
256
- readonly when: "The supplied `nameRegex` is well-formed but exceeds the 256-character limit or contains nested quantifiers known to cause catastrophic backtracking.";
257
- readonly recovery: "Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern (e.g. `^mcp/.*`), or omit nameRegex to disable filtering.";
258
- }], {
259
- readonly notice: import("zod").ZodOptional<import("zod").ZodString>;
260
- readonly truncated: import("zod").ZodOptional<import("zod").ZodBoolean>;
261
- readonly shown: import("zod").ZodOptional<import("zod").ZodNumber>;
262
- readonly cap: import("zod").ZodOptional<import("zod").ZodNumber>;
263
265
  }> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
264
266
  path: import("zod").ZodString;
265
267
  failIfMissing: import("zod").ZodDefault<import("zod").ZodBoolean>;
@@ -272,8 +274,8 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
272
274
  readonly reason: "path_forbidden";
273
275
  readonly thrownBy: "service";
274
276
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
275
- readonly when: "The path is outside OBSIDIAN_READ_PATHS, or — when the file does not exist and `failIfMissing: false` would have Obsidian create it — outside OBSIDIAN_WRITE_PATHS or blocked by OBSIDIAN_READ_ONLY=true.";
276
- readonly recovery: "Open a path inside the configured scope. The error data echoes the active scope and whether read or write access was the one denied.";
277
+ readonly when: "The path is outside OBSIDIAN_READ_PATHS or inside OBSIDIAN_DENY_PATHS, or — when the file does not exist and `failIfMissing: false` would have Obsidian create it — outside OBSIDIAN_WRITE_PATHS or blocked by OBSIDIAN_READ_ONLY=true.";
278
+ readonly recovery: "Open a path inside the configured scope; `op` on the error data names whether read or write access was the one denied. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
277
279
  }, {
278
280
  readonly reason: "note_missing";
279
281
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
@@ -307,11 +309,6 @@ export declare const readToolDefinitions: (import("@cyanheads/mcp-ts-core").Tool
307
309
  * `buildDeleteNoteTool`.
308
310
  */
309
311
  export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
310
- operation: import("zod").ZodEnum<{
311
- delete: "delete";
312
- get: "get";
313
- set: "set";
314
- }>;
315
312
  target: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
316
313
  type: import("zod").ZodLiteral<"path">;
317
314
  path: import("zod").ZodString;
@@ -328,46 +325,52 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
328
325
  }>;
329
326
  date: import("zod").ZodOptional<import("zod").ZodString>;
330
327
  }, import("zod/v4/core").$strip>], "type">;
331
- key: import("zod").ZodString;
332
- value: import("zod").ZodOptional<import("zod").ZodUnknown>;
328
+ section: import("zod").ZodObject<{
329
+ type: import("zod").ZodEnum<{
330
+ block: "block";
331
+ frontmatter: "frontmatter";
332
+ heading: "heading";
333
+ }>;
334
+ target: import("zod").ZodString;
335
+ }, import("zod/v4/core").$strip>;
336
+ operation: import("zod").ZodEnum<{
337
+ append: "append";
338
+ prepend: "prepend";
339
+ replace: "replace";
340
+ }>;
341
+ content: import("zod").ZodString;
342
+ contentType: import("zod").ZodDefault<import("zod").ZodEnum<{
343
+ json: "json";
344
+ markdown: "markdown";
345
+ }>>;
346
+ patchOptions: import("zod").ZodOptional<import("zod").ZodObject<{
347
+ createTargetIfMissing: import("zod").ZodDefault<import("zod").ZodBoolean>;
348
+ applyIfContentPreexists: import("zod").ZodDefault<import("zod").ZodBoolean>;
349
+ trimTargetWhitespace: import("zod").ZodDefault<import("zod").ZodBoolean>;
350
+ }, import("zod/v4/core").$strip>>;
333
351
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
334
- result: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
335
- operation: import("zod").ZodLiteral<"get">;
336
- path: import("zod").ZodString;
337
- key: import("zod").ZodString;
338
- exists: import("zod").ZodBoolean;
339
- value: import("zod").ZodUnknown;
340
- }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
341
- operation: import("zod").ZodLiteral<"set">;
342
- path: import("zod").ZodString;
343
- key: import("zod").ZodString;
344
- frontmatter: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>;
345
- previousSizeInBytes: import("zod").ZodNumber;
346
- currentSizeInBytes: import("zod").ZodNumber;
347
- }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
348
- operation: import("zod").ZodLiteral<"delete">;
349
- path: import("zod").ZodString;
350
- key: import("zod").ZodString;
351
- frontmatter: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>;
352
- previousSizeInBytes: import("zod").ZodNumber;
353
- currentSizeInBytes: import("zod").ZodNumber;
354
- }, import("zod/v4/core").$strip>], "operation">;
352
+ path: import("zod").ZodString;
353
+ section: import("zod").ZodObject<{
354
+ type: import("zod").ZodEnum<{
355
+ block: "block";
356
+ frontmatter: "frontmatter";
357
+ heading: "heading";
358
+ }>;
359
+ target: import("zod").ZodString;
360
+ }, import("zod/v4/core").$strip>;
361
+ operation: import("zod").ZodEnum<{
362
+ append: "append";
363
+ prepend: "prepend";
364
+ replace: "replace";
365
+ }>;
366
+ previousSizeInBytes: import("zod").ZodNumber;
367
+ currentSizeInBytes: import("zod").ZodNumber;
355
368
  }, import("zod/v4/core").$strip>, readonly [{
356
369
  readonly reason: "path_forbidden";
357
370
  readonly thrownBy: "service";
358
371
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
359
- readonly when: "`get` requires the path to be readable; `set`/`delete` require it to be inside OBSIDIAN_WRITE_PATHS, with OBSIDIAN_READ_ONLY=false.";
360
- readonly recovery: "Use a path inside the configured scope. The error data echoes the active scope.";
361
- }, {
362
- readonly reason: "value_required";
363
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
364
- readonly when: "`operation` is \"set\" but no `value` was supplied.";
365
- readonly recovery: "Pass `value` as any JSON-typed value: string, number, boolean, array, or object (e.g. `\"draft\"`, `42`, `true`, `[\"a\",\"b\"]`).";
366
- }, {
367
- readonly reason: "frontmatter_invalid";
368
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
369
- readonly when: "`operation` is \"delete\" and the note's existing frontmatter does not parse as a mapping of properties, or removing the key would leave YAML that cannot be re-emitted (an alias whose anchor went with it). Nothing is written — the note keeps its original bytes.";
370
- readonly recovery: "Read the block with obsidian_get_note (format \"content\"), repair the YAML between the `---` fences, then retry the delete.";
372
+ readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS or inside OBSIDIAN_DENY_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes.";
373
+ readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
371
374
  }, {
372
375
  readonly reason: "note_missing";
373
376
  readonly thrownBy: "service";
@@ -399,110 +402,41 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
399
402
  readonly when: "Target was `periodic` but the requested period is not enabled in Obsidian's Periodic Notes plugin settings.";
400
403
  readonly recovery: "Pass an explicit path target — the requested period is disabled in the operator's Periodic Notes plugin.";
401
404
  }, {
402
- readonly reason: "path_is_directory";
403
- readonly thrownBy: "service";
404
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
405
- readonly when: "The supplied path names a folder rather than a note file.";
406
- readonly recovery: "Call obsidian_list_notes with this path to list the folder, then retry with the full path of one of the files it returns.";
407
- }, {
408
- readonly reason: "path_traversal";
409
- readonly thrownBy: "service";
410
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
411
- readonly when: "The path contains a `.` or `..` segment, which is rejected to prevent vault escape.";
412
- readonly recovery: "Supply a vault-relative path with no `.` or `..` segments, e.g. \"Projects/Note.md\". Use obsidian_list_notes to browse the vault.";
413
- }], undefined> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
414
- target: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
415
- type: import("zod").ZodLiteral<"path">;
416
- path: import("zod").ZodString;
417
- }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
418
- type: import("zod").ZodLiteral<"active">;
419
- }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
420
- type: import("zod").ZodLiteral<"periodic">;
421
- period: import("zod").ZodEnum<{
422
- daily: "daily";
423
- monthly: "monthly";
424
- quarterly: "quarterly";
425
- weekly: "weekly";
426
- yearly: "yearly";
427
- }>;
428
- date: import("zod").ZodOptional<import("zod").ZodString>;
429
- }, import("zod/v4/core").$strip>], "type">;
430
- scope: import("zod").ZodDefault<import("zod").ZodEnum<{
431
- body: "body";
432
- both: "both";
433
- frontmatter: "frontmatter";
434
- }>>;
435
- replacements: import("zod").ZodArray<import("zod").ZodObject<{
436
- search: import("zod").ZodString;
437
- replace: import("zod").ZodString;
438
- useRegex: import("zod").ZodDefault<import("zod").ZodBoolean>;
439
- caseSensitive: import("zod").ZodDefault<import("zod").ZodBoolean>;
440
- wholeWord: import("zod").ZodDefault<import("zod").ZodBoolean>;
441
- flexibleWhitespace: import("zod").ZodDefault<import("zod").ZodBoolean>;
442
- replaceAll: import("zod").ZodDefault<import("zod").ZodBoolean>;
443
- }, import("zod/v4/core").$strip>>;
444
- }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
445
- path: import("zod").ZodString;
446
- totalReplacements: import("zod").ZodNumber;
447
- perReplacement: import("zod").ZodArray<import("zod").ZodObject<{
448
- search: import("zod").ZodString;
449
- count: import("zod").ZodNumber;
450
- bodyCount: import("zod").ZodNumber;
451
- frontmatterCount: import("zod").ZodNumber;
452
- }, import("zod/v4/core").$strip>>;
453
- previousSizeInBytes: import("zod").ZodNumber;
454
- currentSizeInBytes: import("zod").ZodNumber;
455
- }, import("zod/v4/core").$strip>, readonly [{
456
- readonly reason: "path_forbidden";
405
+ readonly reason: "section_target_missing";
457
406
  readonly thrownBy: "service";
458
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
459
- readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes. The pre-read also requires the path to be readable.";
460
- readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope.";
461
- }, {
462
- readonly reason: "regex_invalid";
463
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
464
- readonly when: "A `useRegex: true` replacement supplied a `search` pattern that is not a valid ECMAScript regex.";
465
- readonly recovery: "Use a valid ECMAScript regex, or set useRegex to false to match `search` as a literal string.";
466
- }, {
467
- readonly reason: "regex_unsafe";
468
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
469
- readonly when: "A `useRegex: true` replacement supplied a `search` pattern that is well-formed but exceeds the 1024-character limit or contains nested quantifiers known to cause catastrophic backtracking against the note body.";
470
- readonly recovery: "Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern, or set useRegex to false to match `search` as a literal string.";
471
- }, {
472
- readonly reason: "frontmatter_invalid";
473
407
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
474
- readonly when: "A `scope` of \"frontmatter\" or \"both\" produced YAML that no longer parses as a mapping of properties, or that parses but cannot be re-emitted (an alias whose anchor the replacement removed). Nothing is written — the note keeps its original bytes. The check reads the rewritten YAML only, so a replacement that renames a key or changes a scalar's type while still parsing is not caught by it.";
475
- readonly recovery: "Narrow the search so it cannot match inside the YAML, or leave scope at \"body\" and edit the property with obsidian_manage_frontmatter.";
408
+ readonly when: "The named heading/block/frontmatter field does not exist in the note. Use `obsidian_get_note` with `format: \"document-map\"` to discover available targets.";
409
+ readonly recovery: "Call obsidian_get_note with format document-map to discover the available targets.";
476
410
  }, {
477
- readonly reason: "note_missing";
411
+ readonly reason: "ambiguous_section";
478
412
  readonly thrownBy: "service";
479
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
480
- readonly when: "The vault path does not resolve to an existing note.";
481
- readonly recovery: "Verify the path with obsidian_list_notes or use obsidian_search_notes to locate the note.";
413
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Conflict;
414
+ readonly when: "A bare heading leaf name matches more than one heading in the note, or the resolved full heading path occurs more than once, so the write target is undetermined.";
415
+ readonly recovery: "Retry with a distinct full Parent::Child heading path from `candidates` on the error data; when every candidate is the same path, rename the repeated headings first.";
482
416
  }, {
483
- readonly reason: "no_active_file";
417
+ readonly reason: "content_preexists";
484
418
  readonly thrownBy: "service";
485
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
486
- readonly when: "Target was `active` but no file is currently open in Obsidian.";
487
- readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
419
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
420
+ readonly when: "The supplied content already appears at the target — the patch was rejected to keep retries idempotent (the default).";
421
+ readonly recovery: "Pass `patchOptions.applyIfContentPreexists: true` to force-apply over preexisting content, or change the content to something not already present.";
488
422
  }, {
489
- readonly reason: "periodic_unsupported";
423
+ readonly reason: "heading_outside_section";
490
424
  readonly thrownBy: "service";
491
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
492
- 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.";
493
- 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.";
425
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
426
+ readonly when: "On Local REST API v5.0 and later, the content of a heading write carries a heading at or above the target section’s own level, which would have to sit outside the section.";
427
+ readonly recovery: "Append the heading at the parent section or at the end of the note with obsidian_append_to_note, or write it one level below the target section or deeper.";
494
428
  }, {
495
- readonly reason: "periodic_not_found";
429
+ readonly reason: "repeated_link_definition";
496
430
  readonly thrownBy: "service";
497
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
498
- readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
499
- readonly recovery: "Create the periodic note first or pass an explicit path target.";
431
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Conflict;
432
+ readonly when: "A link reference definition that repeats a label defined earlier in the note sits above where the target heading’s section ends (for the last section, above its heading); the Local REST API’s markdown parser drops it and misplaces every heading below it, so the write would land outside the section.";
433
+ readonly recovery: "Use obsidian_replace_in_note to place the content directly, or to remove or rename the repeated definition at `line`, then retry the section write.";
500
434
  }, {
501
- readonly reason: "periodic_disabled";
435
+ readonly reason: "patch_rejected";
502
436
  readonly thrownBy: "service";
503
437
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
504
- readonly when: "Target was `periodic` but the requested period is not enabled in Obsidian's Periodic Notes plugin settings.";
505
- readonly recovery: "Pass an explicit path target — the requested period is disabled in the operator's Periodic Notes plugin.";
438
+ readonly when: "The target exists but the plugin refused the content for it — table rows for a block that is not a table, a row with the wrong cell count, a frontmatter value that cannot merge, or content of the wrong shape.";
439
+ readonly recovery: "Read the target with obsidian_get_note format section, then send content that fits it — rows matching the table columns, or a value of the field’s own type.";
506
440
  }, {
507
441
  readonly reason: "path_is_directory";
508
442
  readonly thrownBy: "service";
@@ -567,8 +501,8 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
567
501
  readonly reason: "path_forbidden";
568
502
  readonly thrownBy: "service";
569
503
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
570
- readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes.";
571
- readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope.";
504
+ readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS or inside OBSIDIAN_DENY_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes.";
505
+ readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
572
506
  }, {
573
507
  readonly reason: "note_missing";
574
508
  readonly thrownBy: "service";
@@ -617,6 +551,12 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
617
551
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
618
552
  readonly when: "On Local REST API v5.0 and later, the new body of a heading section carries a heading at or above that section’s own level, which would have to sit outside the section.";
619
553
  readonly recovery: "Append the heading at the parent section or at the end of the note with obsidian_append_to_note, or write it one level below the target section or deeper.";
554
+ }, {
555
+ readonly reason: "repeated_link_definition";
556
+ readonly thrownBy: "service";
557
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Conflict;
558
+ readonly when: "A link reference definition that repeats a label defined earlier in the note sits above where the target heading’s section ends (for the last section, above its heading); the Local REST API’s markdown parser drops it and misplaces every heading below it, so the replacement would land outside the section.";
559
+ readonly recovery: "Use obsidian_replace_in_note to place the new body directly, or to remove or rename the repeated definition at `line`, then retry the section write.";
620
560
  }, {
621
561
  readonly reason: "patch_rejected";
622
562
  readonly thrownBy: "service";
@@ -683,8 +623,8 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
683
623
  readonly reason: "path_forbidden";
684
624
  readonly thrownBy: "service";
685
625
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
686
- readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes.";
687
- readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope.";
626
+ readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS or inside OBSIDIAN_DENY_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes.";
627
+ readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
688
628
  }, {
689
629
  readonly reason: "note_missing";
690
630
  readonly thrownBy: "service";
@@ -739,6 +679,12 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
739
679
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
740
680
  readonly when: "On Local REST API v5.0 and later, content appended to a heading carries a heading at or above that section’s own level, which would have to sit outside the section.";
741
681
  readonly recovery: "Append the heading at the parent section or at the end of the note (no `section`), or write it one level below the target section or deeper.";
682
+ }, {
683
+ readonly reason: "repeated_link_definition";
684
+ readonly thrownBy: "service";
685
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Conflict;
686
+ readonly when: "A link reference definition that repeats a label defined earlier in the note sits above where the target heading’s section ends (for the last section, above its heading); the Local REST API’s markdown parser drops it and misplaces every heading below it, so the append would land outside the section.";
687
+ readonly recovery: "Use obsidian_replace_in_note to place the content directly, or to remove or rename the repeated definition at `line`, then retry the section append.";
742
688
  }, {
743
689
  readonly reason: "patch_rejected";
744
690
  readonly thrownBy: "service";
@@ -774,52 +720,52 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
774
720
  }>;
775
721
  date: import("zod").ZodOptional<import("zod").ZodString>;
776
722
  }, import("zod/v4/core").$strip>], "type">;
777
- section: import("zod").ZodObject<{
778
- type: import("zod").ZodEnum<{
779
- block: "block";
780
- frontmatter: "frontmatter";
781
- heading: "heading";
782
- }>;
783
- target: import("zod").ZodString;
784
- }, import("zod/v4/core").$strip>;
785
- operation: import("zod").ZodEnum<{
786
- append: "append";
787
- prepend: "prepend";
788
- replace: "replace";
789
- }>;
790
- content: import("zod").ZodString;
791
- contentType: import("zod").ZodDefault<import("zod").ZodEnum<{
792
- json: "json";
793
- markdown: "markdown";
723
+ scope: import("zod").ZodDefault<import("zod").ZodEnum<{
724
+ body: "body";
725
+ both: "both";
726
+ frontmatter: "frontmatter";
794
727
  }>>;
795
- patchOptions: import("zod").ZodOptional<import("zod").ZodObject<{
796
- createTargetIfMissing: import("zod").ZodDefault<import("zod").ZodBoolean>;
797
- applyIfContentPreexists: import("zod").ZodDefault<import("zod").ZodBoolean>;
798
- trimTargetWhitespace: import("zod").ZodDefault<import("zod").ZodBoolean>;
728
+ replacements: import("zod").ZodArray<import("zod").ZodObject<{
729
+ search: import("zod").ZodString;
730
+ replace: import("zod").ZodString;
731
+ useRegex: import("zod").ZodDefault<import("zod").ZodBoolean>;
732
+ caseSensitive: import("zod").ZodDefault<import("zod").ZodBoolean>;
733
+ wholeWord: import("zod").ZodDefault<import("zod").ZodBoolean>;
734
+ flexibleWhitespace: import("zod").ZodDefault<import("zod").ZodBoolean>;
735
+ replaceAll: import("zod").ZodDefault<import("zod").ZodBoolean>;
799
736
  }, import("zod/v4/core").$strip>>;
800
737
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
801
738
  path: import("zod").ZodString;
802
- section: import("zod").ZodObject<{
803
- type: import("zod").ZodEnum<{
804
- block: "block";
805
- frontmatter: "frontmatter";
806
- heading: "heading";
807
- }>;
808
- target: import("zod").ZodString;
809
- }, import("zod/v4/core").$strip>;
810
- operation: import("zod").ZodEnum<{
811
- append: "append";
812
- prepend: "prepend";
813
- replace: "replace";
814
- }>;
739
+ totalReplacements: import("zod").ZodNumber;
740
+ perReplacement: import("zod").ZodArray<import("zod").ZodObject<{
741
+ search: import("zod").ZodString;
742
+ count: import("zod").ZodNumber;
743
+ bodyCount: import("zod").ZodNumber;
744
+ frontmatterCount: import("zod").ZodNumber;
745
+ }, import("zod/v4/core").$strip>>;
815
746
  previousSizeInBytes: import("zod").ZodNumber;
816
747
  currentSizeInBytes: import("zod").ZodNumber;
817
748
  }, import("zod/v4/core").$strip>, readonly [{
818
749
  readonly reason: "path_forbidden";
819
750
  readonly thrownBy: "service";
820
751
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
821
- readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes.";
822
- readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope.";
752
+ readonly when: "The target path is outside OBSIDIAN_WRITE_PATHS or inside OBSIDIAN_DENY_PATHS, or OBSIDIAN_READ_ONLY=true denies all writes. The pre-read also requires the path to be readable.";
753
+ readonly recovery: "Use a path inside the configured write scope. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
754
+ }, {
755
+ readonly reason: "regex_invalid";
756
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
757
+ readonly when: "A `useRegex: true` replacement supplied a `search` pattern that is not a valid ECMAScript regex.";
758
+ readonly recovery: "Use a valid ECMAScript regex, or set useRegex to false to match `search` as a literal string.";
759
+ }, {
760
+ readonly reason: "regex_unsafe";
761
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
762
+ readonly when: "A `useRegex: true` replacement supplied a `search` pattern that is well-formed but exceeds the 1024-character limit or contains nested quantifiers known to cause catastrophic backtracking against the note body.";
763
+ readonly recovery: "Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern, or set useRegex to false to match `search` as a literal string.";
764
+ }, {
765
+ readonly reason: "frontmatter_invalid";
766
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
767
+ readonly when: "A `scope` of \"frontmatter\" or \"both\" produced YAML that no longer parses as a mapping of properties, or that parses but cannot be re-emitted (an alias whose anchor the replacement removed). Nothing is written — the note keeps its original bytes. The check reads the rewritten YAML only, so a replacement that renames a key or changes a scalar's type while still parsing is not caught by it.";
768
+ readonly recovery: "Narrow the search so it cannot match inside the YAML, or leave scope at \"body\" and edit the property with obsidian_manage_frontmatter.";
823
769
  }, {
824
770
  readonly reason: "note_missing";
825
771
  readonly thrownBy: "service";
@@ -851,35 +797,109 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
851
797
  readonly when: "Target was `periodic` but the requested period is not enabled in Obsidian's Periodic Notes plugin settings.";
852
798
  readonly recovery: "Pass an explicit path target — the requested period is disabled in the operator's Periodic Notes plugin.";
853
799
  }, {
854
- readonly reason: "section_target_missing";
800
+ readonly reason: "path_is_directory";
855
801
  readonly thrownBy: "service";
856
802
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
857
- readonly when: "The named heading/block/frontmatter field does not exist in the note. Use `obsidian_get_note` with `format: \"document-map\"` to discover available targets.";
858
- readonly recovery: "Call obsidian_get_note with format document-map to discover the available targets.";
803
+ readonly when: "The supplied path names a folder rather than a note file.";
804
+ readonly recovery: "Call obsidian_list_notes with this path to list the folder, then retry with the full path of one of the files it returns.";
859
805
  }, {
860
- readonly reason: "ambiguous_section";
806
+ readonly reason: "path_traversal";
861
807
  readonly thrownBy: "service";
862
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Conflict;
863
- readonly when: "A bare heading leaf name matches more than one heading in the note, or the resolved full heading path occurs more than once, so the write target is undetermined.";
864
- readonly recovery: "Retry with a distinct full Parent::Child heading path from `candidates` on the error data; when every candidate is the same path, rename the repeated headings first.";
865
- }, {
866
- readonly reason: "content_preexists";
808
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
809
+ readonly when: "The path contains a `.` or `..` segment, which is rejected to prevent vault escape.";
810
+ readonly recovery: "Supply a vault-relative path with no `.` or `..` segments, e.g. \"Projects/Note.md\". Use obsidian_list_notes to browse the vault.";
811
+ }], undefined> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
812
+ operation: import("zod").ZodEnum<{
813
+ delete: "delete";
814
+ get: "get";
815
+ set: "set";
816
+ }>;
817
+ target: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
818
+ type: import("zod").ZodLiteral<"path">;
819
+ path: import("zod").ZodString;
820
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
821
+ type: import("zod").ZodLiteral<"active">;
822
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
823
+ type: import("zod").ZodLiteral<"periodic">;
824
+ period: import("zod").ZodEnum<{
825
+ daily: "daily";
826
+ monthly: "monthly";
827
+ quarterly: "quarterly";
828
+ weekly: "weekly";
829
+ yearly: "yearly";
830
+ }>;
831
+ date: import("zod").ZodOptional<import("zod").ZodString>;
832
+ }, import("zod/v4/core").$strip>], "type">;
833
+ key: import("zod").ZodString;
834
+ value: import("zod").ZodOptional<import("zod").ZodUnknown>;
835
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
836
+ result: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
837
+ operation: import("zod").ZodLiteral<"get">;
838
+ path: import("zod").ZodString;
839
+ key: import("zod").ZodString;
840
+ exists: import("zod").ZodBoolean;
841
+ value: import("zod").ZodUnknown;
842
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
843
+ operation: import("zod").ZodLiteral<"set">;
844
+ path: import("zod").ZodString;
845
+ key: import("zod").ZodString;
846
+ frontmatter: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>;
847
+ previousSizeInBytes: import("zod").ZodNumber;
848
+ currentSizeInBytes: import("zod").ZodNumber;
849
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
850
+ operation: import("zod").ZodLiteral<"delete">;
851
+ path: import("zod").ZodString;
852
+ key: import("zod").ZodString;
853
+ frontmatter: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>;
854
+ previousSizeInBytes: import("zod").ZodNumber;
855
+ currentSizeInBytes: import("zod").ZodNumber;
856
+ }, import("zod/v4/core").$strip>], "operation">;
857
+ }, import("zod/v4/core").$strip>, readonly [{
858
+ readonly reason: "path_forbidden";
867
859
  readonly thrownBy: "service";
860
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
861
+ readonly when: "`get` requires the path to be readable; `set`/`delete` require it to be inside OBSIDIAN_WRITE_PATHS, with OBSIDIAN_READ_ONLY=false. A path inside OBSIDIAN_DENY_PATHS is neither.";
862
+ readonly recovery: "Use a path inside the configured scope. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
863
+ }, {
864
+ readonly reason: "value_required";
868
865
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
869
- readonly when: "The supplied content already appears at the target — the patch was rejected to keep retries idempotent (the default).";
870
- readonly recovery: "Pass `patchOptions.applyIfContentPreexists: true` to force-apply over preexisting content, or change the content to something not already present.";
866
+ readonly when: "`operation` is \"set\" but no `value` was supplied.";
867
+ readonly recovery: "Pass `value` as any JSON-typed value: string, number, boolean, array, or object (e.g. `\"draft\"`, `42`, `true`, `[\"a\",\"b\"]`).";
871
868
  }, {
872
- readonly reason: "heading_outside_section";
873
- readonly thrownBy: "service";
869
+ readonly reason: "frontmatter_invalid";
874
870
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
875
- readonly when: "On Local REST API v5.0 and later, the content of a heading write carries a heading at or above the target section’s own level, which would have to sit outside the section.";
876
- readonly recovery: "Append the heading at the parent section or at the end of the note with obsidian_append_to_note, or write it one level below the target section or deeper.";
871
+ readonly when: "`operation` is \"delete\" and the note's existing frontmatter does not parse as a mapping of properties, or removing the key would leave YAML that cannot be re-emitted (an alias whose anchor went with it). Nothing is written — the note keeps its original bytes.";
872
+ readonly recovery: "Read the block with obsidian_get_note (format \"content\"), repair the YAML between the `---` fences, then retry the delete.";
877
873
  }, {
878
- readonly reason: "patch_rejected";
874
+ readonly reason: "note_missing";
875
+ readonly thrownBy: "service";
876
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
877
+ readonly when: "The vault path does not resolve to an existing note.";
878
+ readonly recovery: "Verify the path with obsidian_list_notes or use obsidian_search_notes to locate the note.";
879
+ }, {
880
+ readonly reason: "no_active_file";
881
+ readonly thrownBy: "service";
882
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
883
+ readonly when: "Target was `active` but no file is currently open in Obsidian.";
884
+ readonly recovery: "Call obsidian_open_in_ui to focus a file, or pass an explicit path target instead.";
885
+ }, {
886
+ readonly reason: "periodic_unsupported";
887
+ readonly thrownBy: "service";
888
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
889
+ 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.";
890
+ 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.";
891
+ }, {
892
+ readonly reason: "periodic_not_found";
893
+ readonly thrownBy: "service";
894
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
895
+ readonly when: "Target was `periodic`, the `/periodic/` routes are served on this vault, and no note exists for the requested period.";
896
+ readonly recovery: "Create the periodic note first or pass an explicit path target.";
897
+ }, {
898
+ readonly reason: "periodic_disabled";
879
899
  readonly thrownBy: "service";
880
900
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
881
- readonly when: "The target exists but the plugin refused the content for it — table rows for a block that is not a table, a row with the wrong cell count, a frontmatter value that cannot merge, or content of the wrong shape.";
882
- readonly recovery: "Read the target with obsidian_get_note format section, then send content that fits it — rows matching the table columns, or a value of the field’s own type.";
901
+ readonly when: "Target was `periodic` but the requested period is not enabled in Obsidian's Periodic Notes plugin settings.";
902
+ readonly recovery: "Pass an explicit path target — the requested period is disabled in the operator's Periodic Notes plugin.";
883
903
  }, {
884
904
  readonly reason: "path_is_directory";
885
905
  readonly thrownBy: "service";
@@ -950,8 +970,8 @@ export declare const writeToolDefinitions: (import("@cyanheads/mcp-ts-core").Too
950
970
  readonly reason: "path_forbidden";
951
971
  readonly thrownBy: "service";
952
972
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Forbidden;
953
- readonly when: "`list` requires the path to be readable; `add`/`remove` require it to be inside OBSIDIAN_WRITE_PATHS, with OBSIDIAN_READ_ONLY=false.";
954
- readonly recovery: "Use a path inside the configured scope. The error data echoes the active scope.";
973
+ readonly when: "`list` requires the path to be readable; `add`/`remove` require it to be inside OBSIDIAN_WRITE_PATHS, with OBSIDIAN_READ_ONLY=false. A path inside OBSIDIAN_DENY_PATHS is neither.";
974
+ readonly recovery: "Use a path inside the configured scope. The error data echoes the active scope; a `denied_path` denial carries none, since the operator withholds that path.";
955
975
  }, {
956
976
  readonly reason: "tags_required";
957
977
  readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;