obsidian-mcp-server 3.1.11 → 3.2.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 (40) hide show
  1. package/CLAUDE.md +2 -2
  2. package/README.md +14 -13
  3. package/changelog/3.2.x/3.2.0.md +49 -0
  4. package/dist/config/server-config.d.ts +1 -0
  5. package/dist/config/server-config.d.ts.map +1 -1
  6. package/dist/config/server-config.js +6 -0
  7. package/dist/config/server-config.js.map +1 -1
  8. package/dist/index.d.ts +4 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +26 -9
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/_shared/regex-safety.d.ts +18 -0
  13. package/dist/mcp-server/tools/definitions/_shared/regex-safety.d.ts.map +1 -0
  14. package/dist/mcp-server/tools/definitions/_shared/regex-safety.js +34 -0
  15. package/dist/mcp-server/tools/definitions/_shared/regex-safety.js.map +1 -0
  16. package/dist/mcp-server/tools/definitions/_shared/schemas.js +1 -1
  17. package/dist/mcp-server/tools/definitions/_shared/schemas.js.map +1 -1
  18. package/dist/mcp-server/tools/definitions/index.d.ts +30 -844
  19. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  20. package/dist/mcp-server/tools/definitions/index.js +10 -6
  21. package/dist/mcp-server/tools/definitions/index.js.map +1 -1
  22. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts +21 -2
  23. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts.map +1 -1
  24. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js +59 -6
  25. package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js.map +1 -1
  26. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +2 -22
  28. package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +175 -19
  30. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +249 -134
  32. package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -1
  33. package/dist/services/obsidian/obsidian-service.d.ts +22 -2
  34. package/dist/services/obsidian/obsidian-service.d.ts.map +1 -1
  35. package/dist/services/obsidian/obsidian-service.js +143 -12
  36. package/dist/services/obsidian/obsidian-service.js.map +1 -1
  37. package/dist/services/obsidian/types.d.ts +25 -1
  38. package/dist/services/obsidian/types.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/server.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiBH,gFAAgF;AAChF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAM/B,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAQhC,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAAoD,CAAC;AAErF,mHAAmH;AACnH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;+CAAiD,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;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAK/B,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAQhC,CAAC;AAEF,mHAAmH;AACnH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAAiD,CAAC"}
@@ -4,7 +4,9 @@
4
4
  * when `OBSIDIAN_READ_ONLY=true`. The command-palette pair is exported
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
- * module free of eager config reads.
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
10
  * @module mcp-server/tools/definitions/index
9
11
  */
10
12
  import { obsidianAppendToNote } from './obsidian-append-to-note.tool.js';
@@ -19,14 +21,18 @@ import { obsidianManageTags } from './obsidian-manage-tags.tool.js';
19
21
  import { obsidianOpenInUi } from './obsidian-open-in-ui.tool.js';
20
22
  import { obsidianPatchNote } from './obsidian-patch-note.tool.js';
21
23
  import { obsidianReplaceInNote } from './obsidian-replace-in-note.tool.js';
22
- import { obsidianSearchNotes } from './obsidian-search-notes.tool.js';
23
24
  import { obsidianWriteNote } from './obsidian-write-note.tool.js';
24
- /** Read-only tools — always registered, even with `OBSIDIAN_READ_ONLY=true`. */
25
+ export { buildSearchNotesTool } from './obsidian-search-notes.tool.js';
26
+ /**
27
+ * Read-only tools that don't depend on runtime probes — always registered,
28
+ * even with `OBSIDIAN_READ_ONLY=true`. `obsidian_search_notes` is constructed
29
+ * separately in the entry point via `buildSearchNotesTool` so its mode enum
30
+ * can reflect Omnisearch reachability.
31
+ */
25
32
  export const readToolDefinitions = [
26
33
  obsidianGetNote,
27
34
  obsidianListNotes,
28
35
  obsidianListTags,
29
- obsidianSearchNotes,
30
36
  obsidianOpenInUi,
31
37
  ];
32
38
  /** Write tools — wrapped with `disabledTool()` when `OBSIDIAN_READ_ONLY=true`. */
@@ -39,8 +45,6 @@ export const writeToolDefinitions = [
39
45
  obsidianManageTags,
40
46
  obsidianDeleteNote,
41
47
  ];
42
- /** Combined base set — preserves the previous registration order. */
43
- export const baseToolDefinitions = [...readToolDefinitions, ...writeToolDefinitions];
44
48
  /** Command-palette tools — opt-in via `OBSIDIAN_ENABLE_COMMANDS=true`; suppressed by `OBSIDIAN_READ_ONLY=true`. */
45
49
  export const commandToolDefinitions = [obsidianListCommands, obsidianExecuteCommand];
46
50
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,eAAe;IACf,iBAAiB;IACjB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;CACjB,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,iBAAiB;IACjB,oBAAoB;IACpB,iBAAiB;IACjB,qBAAqB;IACrB,yBAAyB;IACzB,kBAAkB;IAClB,kBAAkB;CACnB,CAAC;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAG,mBAAmB,EAAE,GAAG,oBAAoB,CAAC,CAAC;AAErF,mHAAmH;AACnH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,oBAAoB,EAAE,sBAAsB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,eAAe;IACf,iBAAiB;IACjB,gBAAgB;IAChB,gBAAgB;CACjB,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,iBAAiB;IACjB,oBAAoB;IACpB,iBAAiB;IACjB,qBAAqB;IACrB,yBAAyB;IACzB,kBAAkB;IAClB,kBAAkB;CACnB,CAAC;AAEF,mHAAmH;AACnH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,oBAAoB,EAAE,sBAAsB,CAAC,CAAC"}
@@ -1,13 +1,32 @@
1
1
  /**
2
2
  * @fileoverview obsidian_list_commands — list Obsidian command-palette commands.
3
3
  * Gated behind `OBSIDIAN_ENABLE_COMMANDS=true` alongside `obsidian_execute_command`.
4
+ * Optional `nameRegex` post-filters the upstream payload before returning, since
5
+ * vaults with Templater/Dataview/QuickAdd/Excalidraw routinely register 300+
6
+ * commands and the LLM-bound listing is otherwise mostly noise.
4
7
  * @module mcp-server/tools/definitions/obsidian-list-commands.tool
5
8
  */
6
9
  import { z } from '@cyanheads/mcp-ts-core';
7
- export declare const obsidianListCommands: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
10
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
11
+ export declare const obsidianListCommands: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
12
+ nameRegex: z.ZodOptional<z.ZodString>;
13
+ }, z.core.$strip>, z.ZodObject<{
8
14
  commands: z.ZodArray<z.ZodObject<{
9
15
  id: z.ZodString;
10
16
  name: z.ZodString;
11
17
  }, z.core.$strip>>;
12
- }, z.core.$strip>, undefined>;
18
+ appliedFilters: z.ZodOptional<z.ZodObject<{
19
+ nameRegex: z.ZodOptional<z.ZodString>;
20
+ }, z.core.$strip>>;
21
+ }, z.core.$strip>, readonly [{
22
+ readonly reason: "regex_invalid";
23
+ readonly code: JsonRpcErrorCode.ValidationError;
24
+ readonly when: "The supplied `nameRegex` is not a valid ECMAScript regex.";
25
+ readonly recovery: "Use a valid ECMAScript regex (e.g. `^Templater`), or omit nameRegex to disable filtering.";
26
+ }, {
27
+ readonly reason: "regex_unsafe";
28
+ readonly code: JsonRpcErrorCode.ValidationError;
29
+ readonly when: "The supplied `nameRegex` is well-formed but exceeds the 256-character limit or contains nested quantifiers known to cause catastrophic backtracking.";
30
+ readonly recovery: "Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern (e.g. `^Templater`), or omit nameRegex to disable filtering.";
31
+ }]>;
13
32
  //# sourceMappingURL=obsidian-list-commands.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-list-commands.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-commands.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGjD,eAAO,MAAM,oBAAoB;;;;;6BAiC/B,CAAC"}
1
+ {"version":3,"file":"obsidian-list-commands.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-commands.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;;;;;;;;;;;;;;;;;;;;GA2F/B,CAAC"}
@@ -1,14 +1,24 @@
1
1
  /**
2
2
  * @fileoverview obsidian_list_commands — list Obsidian command-palette commands.
3
3
  * Gated behind `OBSIDIAN_ENABLE_COMMANDS=true` alongside `obsidian_execute_command`.
4
+ * Optional `nameRegex` post-filters the upstream payload before returning, since
5
+ * vaults with Templater/Dataview/QuickAdd/Excalidraw routinely register 300+
6
+ * commands and the LLM-bound listing is otherwise mostly noise.
4
7
  * @module mcp-server/tools/definitions/obsidian-list-commands.tool
5
8
  */
6
9
  import { tool, z } from '@cyanheads/mcp-ts-core';
10
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
11
  import { getObsidianService } from '../../../services/obsidian/obsidian-service.js';
12
+ import { nameRegexSafetyIssue } from './_shared/regex-safety.js';
8
13
  export const obsidianListCommands = tool('obsidian_list_commands', {
9
- description: 'List the Obsidian command-palette commands available in the active vault, with their IDs and display names.',
14
+ description: 'List the Obsidian command-palette commands available in the active vault, with their IDs and display names. Filter to a subset with the optional `nameRegex` matched against the display name.',
10
15
  annotations: { readOnlyHint: true, idempotentHint: true },
11
- input: z.object({}),
16
+ input: z.object({
17
+ nameRegex: z
18
+ .string()
19
+ .optional()
20
+ .describe('Optional ECMAScript regex (no flags, ≤256 chars, no nested quantifiers like `(a+)+`) matched against the command display name (the field agents search by, not the slug-shaped `id`). Use character classes (`[Tt]emplater`) for case-insensitivity.'),
21
+ }),
12
22
  output: z.object({
13
23
  commands: z
14
24
  .array(z
@@ -18,18 +28,61 @@ export const obsidianListCommands = tool('obsidian_list_commands', {
18
28
  })
19
29
  .describe('An Obsidian command-palette entry.'))
20
30
  .describe('All commands registered in the active Obsidian instance.'),
31
+ appliedFilters: z
32
+ .object({
33
+ nameRegex: z.string().optional().describe('nameRegex filter applied to this listing.'),
34
+ })
35
+ .optional()
36
+ .describe('Active filters that produced this listing. Absent when no filter was applied.'),
21
37
  }),
22
38
  auth: ['tool:obsidian_list_commands:read'],
23
- async handler(_input, ctx) {
39
+ errors: [
40
+ {
41
+ reason: 'regex_invalid',
42
+ code: JsonRpcErrorCode.ValidationError,
43
+ when: 'The supplied `nameRegex` is not a valid ECMAScript regex.',
44
+ recovery: 'Use a valid ECMAScript regex (e.g. `^Templater`), or omit nameRegex to disable filtering.',
45
+ },
46
+ {
47
+ reason: 'regex_unsafe',
48
+ code: JsonRpcErrorCode.ValidationError,
49
+ when: 'The supplied `nameRegex` is well-formed but exceeds the 256-character limit or contains nested quantifiers known to cause catastrophic backtracking.',
50
+ recovery: 'Avoid nested quantifiers like `(a+)+` or `(.*)*`. Use a simpler pattern (e.g. `^Templater`), or omit nameRegex to disable filtering.',
51
+ },
52
+ ],
53
+ async handler(input, ctx) {
54
+ let regex;
55
+ if (input.nameRegex) {
56
+ const safetyIssue = nameRegexSafetyIssue(input.nameRegex);
57
+ if (safetyIssue) {
58
+ throw ctx.fail('regex_unsafe', `Unsafe nameRegex: ${safetyIssue}`, {
59
+ nameRegex: input.nameRegex,
60
+ ...ctx.recoveryFor('regex_unsafe'),
61
+ });
62
+ }
63
+ try {
64
+ regex = new RegExp(input.nameRegex);
65
+ }
66
+ catch (err) {
67
+ throw ctx.fail('regex_invalid', `Invalid nameRegex: ${err.message}`, { nameRegex: input.nameRegex, ...ctx.recoveryFor('regex_invalid') }, { cause: err });
68
+ }
69
+ }
24
70
  const svc = getObsidianService();
25
71
  const commands = await svc.listCommands(ctx);
26
- return { commands };
72
+ const filtered = regex ? commands.filter((c) => regex.test(c.name)) : commands;
73
+ return {
74
+ commands: filtered,
75
+ ...(input.nameRegex ? { appliedFilters: { nameRegex: input.nameRegex } } : {}),
76
+ };
27
77
  },
28
78
  format: (result) => {
79
+ const activeRegex = result.appliedFilters?.nameRegex;
29
80
  if (result.commands.length === 0) {
30
- return [{ type: 'text', text: '_No commands available._' }];
81
+ const filterNote = activeRegex ? ` matching \`${activeRegex}\`` : '';
82
+ return [{ type: 'text', text: `_No commands available${filterNote}._` }];
31
83
  }
32
- const lines = [`**${result.commands.length} commands**`, ''];
84
+ const filterSuffix = activeRegex ? ` · nameRegex=\`${activeRegex}\`` : '';
85
+ const lines = [`**${result.commands.length} commands**${filterSuffix}`, ''];
33
86
  for (const c of result.commands)
34
87
  lines.push(`- \`${c.id}\` — ${c.name}`);
35
88
  return [{ type: 'text', text: lines.join('\n') }];
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-list-commands.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-commands.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAE7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,wBAAwB,EAAE;IACjE,WAAW,EACT,6GAA6G;IAC/G,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;IACzD,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACnB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,QAAQ,EAAE,CAAC;aACR,KAAK,CACJ,CAAC;aACE,MAAM,CAAC;YACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;YAC5E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;SAC1D,CAAC;aACD,QAAQ,CAAC,oCAAoC,CAAC,CAClD;aACA,QAAQ,CAAC,0DAA0D,CAAC;KACxE,CAAC;IACF,IAAI,EAAE,CAAC,kCAAkC,CAAC;IAE1C,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC7C,OAAO,EAAE,QAAQ,EAAE,CAAC;IACtB,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QACjB,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,MAAM,aAAa,EAAE,EAAE,CAAC,CAAC;QAC7D,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACzE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"obsidian-list-commands.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-commands.tool.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-list-tags.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-tags.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA2BjE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;GA2F3B,CAAC"}
1
+ {"version":3,"file":"obsidian-list-tags.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-tags.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAIjE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;GA2F3B,CAAC"}
@@ -8,29 +8,9 @@
8
8
  import { tool, z } from '@cyanheads/mcp-ts-core';
9
9
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
10
  import { getObsidianService } from '../../../services/obsidian/obsidian-service.js';
11
- /** Maximum allowed pattern length — bounds compile cost and AST surface. */
12
- const NAME_REGEX_MAX_LENGTH = 256;
13
- /**
14
- * Detects the canonical catastrophic-backtracking shape: a `+`/`*`/`{N,}`
15
- * quantifier immediately following a `)` whose interior already ends in a
16
- * `+`/`*`/`}` quantifier (e.g. `(a+)+`, `(.*)*`, `(a{2,})*`). Not exhaustive —
17
- * patterns with overlapping alternation like `(a|a)*` still slip through —
18
- * but eliminates the textbook ReDoS vector at zero runtime cost. JavaScript's
19
- * RegExp engine has no native execution timeout, so this static check is the
20
- * only defense short of running matches in a worker.
21
- */
22
- const NESTED_QUANTIFIER = /[+*}]\)[*+{]/;
23
- function nameRegexSafetyIssue(pattern) {
24
- if (pattern.length > NAME_REGEX_MAX_LENGTH) {
25
- return `pattern exceeds ${NAME_REGEX_MAX_LENGTH}-character limit`;
26
- }
27
- if (NESTED_QUANTIFIER.test(pattern)) {
28
- return 'pattern contains nested quantifiers (catastrophic-backtracking risk)';
29
- }
30
- return;
31
- }
11
+ import { nameRegexSafetyIssue } from './_shared/regex-safety.js';
32
12
  export const obsidianListTags = tool('obsidian_list_tags', {
33
- description: 'List every tag found across the vault, with usage counts. Includes hierarchical parents — `work/tasks` contributes to both `work` and `work/tasks`. Filter to a subset with the optional `nameRegex`. To find notes by tag, use `obsidian_search_notes` in dataview mode (e.g. `TABLE FROM #work`).',
13
+ description: 'List every tag found across the vault, with usage counts. Includes hierarchical parents — `work/tasks` contributes to both `work` and `work/tasks`. Filter to a subset with the optional `nameRegex`. To find notes by tag, use `obsidian_search_notes` in jsonlogic mode (e.g. `{"in": ["work", {"var": "tags"}]}`).',
34
14
  annotations: { readOnlyHint: true, idempotentHint: true },
35
15
  input: z.object({
36
16
  nameRegex: z
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-list-tags.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-tags.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"obsidian-list-tags.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-list-tags.tool.ts"],"names":[],"mappings":"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"}
@@ -1,24 +1,34 @@
1
1
  /**
2
- * @fileoverview obsidian_search_notes — text/dataview/jsonlogic search.
3
- * Caps results at 100 hits and surfaces an `excluded` indicator when more
4
- * were returned upstream. Text-mode hits are additionally capped per file
5
- * via `maxMatchesPerHit` so a single match-heavy note can't blow the
6
- * response budget — clipped hits carry `truncated: true` and `totalMatches`.
2
+ * @fileoverview obsidian_search_notes — text/jsonlogic/omnisearch search with
3
+ * MCP-spec cursor pagination. The `omnisearch` mode is added conditionally by
4
+ * the entry point only when the Omnisearch plugin's HTTP server is reachable
5
+ * at startup. Text-mode hits additionally clip per file via `maxMatchesPerHit`
6
+ * so a single match-heavy note can't blow the response budget — clipped hits
7
+ * carry `truncated: true` and `totalMatches`.
7
8
  * @module mcp-server/tools/definitions/obsidian-search-notes.tool
8
9
  */
9
10
  import { z } from '@cyanheads/mcp-ts-core';
10
11
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
11
- export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
12
+ /**
13
+ * Build the `obsidian_search_notes` tool. The `omnisearch` mode is included
14
+ * in the input/output schemas only when `omnisearchReachable` is true so the
15
+ * LLM never sees it as an option on a deployment where it can't run. Re-probe
16
+ * requires a server restart.
17
+ */
18
+ export declare function buildSearchNotesTool({ omnisearchReachable }: {
19
+ omnisearchReachable: boolean;
20
+ }): import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
12
21
  mode: z.ZodEnum<{
13
22
  text: "text";
14
- dataview: "dataview";
15
23
  jsonlogic: "jsonlogic";
24
+ omnisearch: "omnisearch";
16
25
  }>;
17
26
  query: z.ZodOptional<z.ZodString>;
18
27
  logic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
19
28
  contextLength: z.ZodDefault<z.ZodNumber>;
20
29
  pathPrefix: z.ZodOptional<z.ZodString>;
21
30
  maxMatchesPerHit: z.ZodDefault<z.ZodNumber>;
31
+ cursor: z.ZodOptional<z.ZodString>;
22
32
  }, z.core.$strip>, z.ZodObject<{
23
33
  result: z.ZodDiscriminatedUnion<[z.ZodObject<{
24
34
  mode: z.ZodLiteral<"text">;
@@ -35,30 +45,170 @@ export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolD
35
45
  totalMatches: z.ZodOptional<z.ZodNumber>;
36
46
  truncated: z.ZodOptional<z.ZodBoolean>;
37
47
  }, z.core.$strip>>;
38
- excluded: z.ZodOptional<z.ZodObject<{
39
- count: z.ZodNumber;
40
- hint: z.ZodString;
48
+ totalCount: z.ZodNumber;
49
+ nextCursor: z.ZodOptional<z.ZodString>;
50
+ }, z.core.$strip>, z.ZodObject<{
51
+ mode: z.ZodLiteral<"jsonlogic">;
52
+ hits: z.ZodArray<z.ZodObject<{
53
+ filename: z.ZodString;
54
+ result: z.ZodUnknown;
41
55
  }, z.core.$strip>>;
56
+ totalCount: z.ZodNumber;
57
+ nextCursor: z.ZodOptional<z.ZodString>;
42
58
  }, z.core.$strip>, z.ZodObject<{
43
- mode: z.ZodLiteral<"dataview">;
59
+ mode: z.ZodLiteral<"omnisearch">;
60
+ hits: z.ZodArray<z.ZodObject<{
61
+ filename: z.ZodString;
62
+ basename: z.ZodString;
63
+ score: z.ZodNumber;
64
+ foundWords: z.ZodArray<z.ZodString>;
65
+ matches: z.ZodArray<z.ZodObject<{
66
+ match: z.ZodString;
67
+ offset: z.ZodNumber;
68
+ }, z.core.$strip>>;
69
+ excerpt: z.ZodString;
70
+ }, z.core.$strip>>;
71
+ totalCount: z.ZodNumber;
72
+ nextCursor: z.ZodOptional<z.ZodString>;
73
+ truncated: z.ZodBoolean;
74
+ }, z.core.$strip>] | [z.ZodObject<{
75
+ mode: z.ZodLiteral<"text">;
76
+ hits: z.ZodArray<z.ZodObject<{
77
+ filename: z.ZodString;
78
+ score: z.ZodOptional<z.ZodNumber>;
79
+ matches: z.ZodArray<z.ZodObject<{
80
+ context: z.ZodString;
81
+ match: z.ZodObject<{
82
+ start: z.ZodNumber;
83
+ end: z.ZodNumber;
84
+ }, z.core.$strip>;
85
+ }, z.core.$strip>>;
86
+ totalMatches: z.ZodOptional<z.ZodNumber>;
87
+ truncated: z.ZodOptional<z.ZodBoolean>;
88
+ }, z.core.$strip>>;
89
+ totalCount: z.ZodNumber;
90
+ nextCursor: z.ZodOptional<z.ZodString>;
91
+ }, z.core.$strip>, z.ZodObject<{
92
+ mode: z.ZodLiteral<"jsonlogic">;
44
93
  hits: z.ZodArray<z.ZodObject<{
45
94
  filename: z.ZodString;
46
95
  result: z.ZodUnknown;
47
96
  }, z.core.$strip>>;
48
- excluded: z.ZodOptional<z.ZodObject<{
49
- count: z.ZodNumber;
50
- hint: z.ZodString;
97
+ totalCount: z.ZodNumber;
98
+ nextCursor: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$strip>], "mode">;
100
+ }, z.core.$strip>, readonly [{
101
+ readonly reason: "path_prefix_invalid_mode";
102
+ readonly code: JsonRpcErrorCode.ValidationError;
103
+ readonly when: "`pathPrefix` was provided in a non-text mode (only `text` supports prefix filtering).";
104
+ readonly recovery: "Drop pathPrefix or switch mode to text for prefix filtering.";
105
+ }, {
106
+ readonly reason: "query_required";
107
+ readonly code: JsonRpcErrorCode.ValidationError;
108
+ readonly when: "`query` is missing for `text` or `omnisearch` mode (required for both).";
109
+ readonly recovery: "Pass `query` — substring for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch.";
110
+ }, {
111
+ readonly reason: "logic_required";
112
+ readonly code: JsonRpcErrorCode.ValidationError;
113
+ readonly when: "`logic` is missing for `jsonlogic` mode.";
114
+ readonly recovery: "Pass a JSONLogic tree as `logic`, e.g. `{\"glob\": [{\"var\": \"path\"}, \"Projects/*.md\"]}`.";
115
+ }, {
116
+ readonly reason: "omnisearch_unreachable";
117
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
118
+ readonly when: "Omnisearch was reachable at startup but is now unreachable (Obsidian quit, plugin disabled, or mobile session).";
119
+ readonly retryable: true;
120
+ readonly recovery: "Restart Obsidian with the Omnisearch plugin enabled, then restart this MCP server so it re-probes the plugin URL.";
121
+ }]>;
122
+ /**
123
+ * Static specimen for the MCP definition linter (which duck-types tool
124
+ * exports out of each `.tool.ts` file) and for existing tests that import
125
+ * the tool directly. Defaults to `omnisearchReachable: false` — the safe
126
+ * baseline that doesn't assume the optional plugin is installed. The entry
127
+ * point (`src/index.ts`) builds the live tool via `buildSearchNotesTool`
128
+ * with the actual probe result; this export is not the registered tool.
129
+ * The omnisearch-enabled variant is exercised by tests rather than the
130
+ * linter (two exports under the same tool name would collide on
131
+ * `name-unique`).
132
+ */
133
+ export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
134
+ mode: z.ZodEnum<{
135
+ text: "text";
136
+ jsonlogic: "jsonlogic";
137
+ omnisearch: "omnisearch";
138
+ }>;
139
+ query: z.ZodOptional<z.ZodString>;
140
+ logic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
141
+ contextLength: z.ZodDefault<z.ZodNumber>;
142
+ pathPrefix: z.ZodOptional<z.ZodString>;
143
+ maxMatchesPerHit: z.ZodDefault<z.ZodNumber>;
144
+ cursor: z.ZodOptional<z.ZodString>;
145
+ }, z.core.$strip>, z.ZodObject<{
146
+ result: z.ZodDiscriminatedUnion<[z.ZodObject<{
147
+ mode: z.ZodLiteral<"text">;
148
+ hits: z.ZodArray<z.ZodObject<{
149
+ filename: z.ZodString;
150
+ score: z.ZodOptional<z.ZodNumber>;
151
+ matches: z.ZodArray<z.ZodObject<{
152
+ context: z.ZodString;
153
+ match: z.ZodObject<{
154
+ start: z.ZodNumber;
155
+ end: z.ZodNumber;
156
+ }, z.core.$strip>;
157
+ }, z.core.$strip>>;
158
+ totalMatches: z.ZodOptional<z.ZodNumber>;
159
+ truncated: z.ZodOptional<z.ZodBoolean>;
51
160
  }, z.core.$strip>>;
161
+ totalCount: z.ZodNumber;
162
+ nextCursor: z.ZodOptional<z.ZodString>;
52
163
  }, z.core.$strip>, z.ZodObject<{
53
164
  mode: z.ZodLiteral<"jsonlogic">;
54
165
  hits: z.ZodArray<z.ZodObject<{
55
166
  filename: z.ZodString;
56
167
  result: z.ZodUnknown;
57
168
  }, z.core.$strip>>;
58
- excluded: z.ZodOptional<z.ZodObject<{
59
- count: z.ZodNumber;
60
- hint: z.ZodString;
169
+ totalCount: z.ZodNumber;
170
+ nextCursor: z.ZodOptional<z.ZodString>;
171
+ }, z.core.$strip>, z.ZodObject<{
172
+ mode: z.ZodLiteral<"omnisearch">;
173
+ hits: z.ZodArray<z.ZodObject<{
174
+ filename: z.ZodString;
175
+ basename: z.ZodString;
176
+ score: z.ZodNumber;
177
+ foundWords: z.ZodArray<z.ZodString>;
178
+ matches: z.ZodArray<z.ZodObject<{
179
+ match: z.ZodString;
180
+ offset: z.ZodNumber;
181
+ }, z.core.$strip>>;
182
+ excerpt: z.ZodString;
183
+ }, z.core.$strip>>;
184
+ totalCount: z.ZodNumber;
185
+ nextCursor: z.ZodOptional<z.ZodString>;
186
+ truncated: z.ZodBoolean;
187
+ }, z.core.$strip>] | [z.ZodObject<{
188
+ mode: z.ZodLiteral<"text">;
189
+ hits: z.ZodArray<z.ZodObject<{
190
+ filename: z.ZodString;
191
+ score: z.ZodOptional<z.ZodNumber>;
192
+ matches: z.ZodArray<z.ZodObject<{
193
+ context: z.ZodString;
194
+ match: z.ZodObject<{
195
+ start: z.ZodNumber;
196
+ end: z.ZodNumber;
197
+ }, z.core.$strip>;
198
+ }, z.core.$strip>>;
199
+ totalMatches: z.ZodOptional<z.ZodNumber>;
200
+ truncated: z.ZodOptional<z.ZodBoolean>;
201
+ }, z.core.$strip>>;
202
+ totalCount: z.ZodNumber;
203
+ nextCursor: z.ZodOptional<z.ZodString>;
204
+ }, z.core.$strip>, z.ZodObject<{
205
+ mode: z.ZodLiteral<"jsonlogic">;
206
+ hits: z.ZodArray<z.ZodObject<{
207
+ filename: z.ZodString;
208
+ result: z.ZodUnknown;
61
209
  }, z.core.$strip>>;
210
+ totalCount: z.ZodNumber;
211
+ nextCursor: z.ZodOptional<z.ZodString>;
62
212
  }, z.core.$strip>], "mode">;
63
213
  }, z.core.$strip>, readonly [{
64
214
  readonly reason: "path_prefix_invalid_mode";
@@ -68,12 +218,18 @@ export declare const obsidianSearchNotes: import("@cyanheads/mcp-ts-core").ToolD
68
218
  }, {
69
219
  readonly reason: "query_required";
70
220
  readonly code: JsonRpcErrorCode.ValidationError;
71
- readonly when: "`query` is missing for `text` or `dataview` mode (required for both).";
72
- readonly recovery: "Pass `query` — search terms for text mode (e.g. \"TODO\"), or DQL like \"TABLE WHERE file.mtime > date(today)\" for dataview mode.";
221
+ readonly when: "`query` is missing for `text` or `omnisearch` mode (required for both).";
222
+ readonly recovery: "Pass `query` — substring for text mode, or BM25 query syntax (quoted phrases, `-exclusion`, `path:` / `ext:` filters) for omnisearch.";
73
223
  }, {
74
224
  readonly reason: "logic_required";
75
225
  readonly code: JsonRpcErrorCode.ValidationError;
76
226
  readonly when: "`logic` is missing for `jsonlogic` mode.";
77
227
  readonly recovery: "Pass a JSONLogic tree as `logic`, e.g. `{\"glob\": [{\"var\": \"path\"}, \"Projects/*.md\"]}`.";
228
+ }, {
229
+ readonly reason: "omnisearch_unreachable";
230
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
231
+ readonly when: "Omnisearch was reachable at startup but is now unreachable (Obsidian quit, plugin disabled, or mobile session).";
232
+ readonly retryable: true;
233
+ readonly recovery: "Restart Obsidian with the Omnisearch plugin enabled, then restart this MCP server so it re-probes the plugin URL.";
78
234
  }]>;
79
235
  //# sourceMappingURL=obsidian-search-notes.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"obsidian-search-notes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-search-notes.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAwDjE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2L9B,CAAC"}
1
+ {"version":3,"file":"obsidian-search-notes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/obsidian-search-notes.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAuFjE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,EAAE;IAAE,mBAAmB,EAAE,OAAO,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAkR7F;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAuD,CAAC"}