@noteplanco/noteplan-mcp 1.1.6 → 1.1.8

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 (98) hide show
  1. package/dist/noteplan/embeddings.d.ts +8 -0
  2. package/dist/noteplan/embeddings.d.ts.map +1 -1
  3. package/dist/noteplan/embeddings.js +3 -3
  4. package/dist/noteplan/embeddings.js.map +1 -1
  5. package/dist/noteplan/file-reader.d.ts +6 -0
  6. package/dist/noteplan/file-reader.d.ts.map +1 -1
  7. package/dist/noteplan/file-reader.js +15 -0
  8. package/dist/noteplan/file-reader.js.map +1 -1
  9. package/dist/noteplan/file-writer.d.ts.map +1 -1
  10. package/dist/noteplan/file-writer.js +86 -17
  11. package/dist/noteplan/file-writer.js.map +1 -1
  12. package/dist/noteplan/file-writer.test.d.ts +2 -0
  13. package/dist/noteplan/file-writer.test.d.ts.map +1 -0
  14. package/dist/noteplan/file-writer.test.js +896 -0
  15. package/dist/noteplan/file-writer.test.js.map +1 -0
  16. package/dist/noteplan/filter-store.d.ts.map +1 -1
  17. package/dist/noteplan/filter-store.js +13 -1
  18. package/dist/noteplan/filter-store.js.map +1 -1
  19. package/dist/noteplan/frontmatter-parser.d.ts +10 -1
  20. package/dist/noteplan/frontmatter-parser.d.ts.map +1 -1
  21. package/dist/noteplan/frontmatter-parser.js +59 -6
  22. package/dist/noteplan/frontmatter-parser.js.map +1 -1
  23. package/dist/noteplan/frontmatter-parser.test.js +576 -1
  24. package/dist/noteplan/frontmatter-parser.test.js.map +1 -1
  25. package/dist/noteplan/markdown-parser.d.ts +6 -1
  26. package/dist/noteplan/markdown-parser.d.ts.map +1 -1
  27. package/dist/noteplan/markdown-parser.js +25 -46
  28. package/dist/noteplan/markdown-parser.js.map +1 -1
  29. package/dist/noteplan/markdown-parser.test.d.ts +2 -0
  30. package/dist/noteplan/markdown-parser.test.d.ts.map +1 -0
  31. package/dist/noteplan/markdown-parser.test.js +690 -0
  32. package/dist/noteplan/markdown-parser.test.js.map +1 -0
  33. package/dist/noteplan/template-docs.d.ts +35 -0
  34. package/dist/noteplan/template-docs.d.ts.map +1 -0
  35. package/dist/noteplan/template-docs.js +184 -0
  36. package/dist/noteplan/template-docs.js.map +1 -0
  37. package/dist/noteplan/unified-store.d.ts +2 -0
  38. package/dist/noteplan/unified-store.d.ts.map +1 -1
  39. package/dist/noteplan/unified-store.js +22 -6
  40. package/dist/noteplan/unified-store.js.map +1 -1
  41. package/dist/server.d.ts.map +1 -1
  42. package/dist/server.js +661 -241
  43. package/dist/server.js.map +1 -1
  44. package/dist/tools/attachments.d.ts +151 -0
  45. package/dist/tools/attachments.d.ts.map +1 -0
  46. package/dist/tools/attachments.js +421 -0
  47. package/dist/tools/attachments.js.map +1 -0
  48. package/dist/tools/attachments.test.d.ts +2 -0
  49. package/dist/tools/attachments.test.d.ts.map +1 -0
  50. package/dist/tools/attachments.test.js +561 -0
  51. package/dist/tools/attachments.test.js.map +1 -0
  52. package/dist/tools/calendar.d.ts +7 -7
  53. package/dist/tools/notes.d.ts +148 -48
  54. package/dist/tools/notes.d.ts.map +1 -1
  55. package/dist/tools/notes.js +366 -29
  56. package/dist/tools/notes.js.map +1 -1
  57. package/dist/tools/notes.test.d.ts +2 -0
  58. package/dist/tools/notes.test.d.ts.map +1 -0
  59. package/dist/tools/notes.test.js +800 -0
  60. package/dist/tools/notes.test.js.map +1 -0
  61. package/dist/tools/plugins.d.ts.map +1 -1
  62. package/dist/tools/plugins.js +1 -0
  63. package/dist/tools/plugins.js.map +1 -1
  64. package/dist/tools/reminders.d.ts +4 -4
  65. package/dist/tools/search.d.ts +2 -2
  66. package/dist/tools/search.d.ts.map +1 -1
  67. package/dist/tools/search.js +32 -4
  68. package/dist/tools/search.js.map +1 -1
  69. package/dist/tools/tasks.d.ts +10 -10
  70. package/dist/tools/tasks.d.ts.map +1 -1
  71. package/dist/tools/tasks.js +14 -27
  72. package/dist/tools/tasks.js.map +1 -1
  73. package/dist/tools/templates.d.ts +130 -0
  74. package/dist/tools/templates.d.ts.map +1 -0
  75. package/dist/tools/templates.js +217 -0
  76. package/dist/tools/templates.js.map +1 -0
  77. package/dist/tools/templates.test.d.ts +2 -0
  78. package/dist/tools/templates.test.d.ts.map +1 -0
  79. package/dist/tools/templates.test.js +48 -0
  80. package/dist/tools/templates.test.js.map +1 -0
  81. package/dist/tools/ui.d.ts +2 -0
  82. package/dist/tools/ui.d.ts.map +1 -1
  83. package/dist/tools/ui.js +24 -0
  84. package/dist/tools/ui.js.map +1 -1
  85. package/dist/utils/applescript.d.ts.map +1 -1
  86. package/dist/utils/applescript.js +21 -0
  87. package/dist/utils/applescript.js.map +1 -1
  88. package/dist/utils/confirmation-tokens.test.d.ts +2 -0
  89. package/dist/utils/confirmation-tokens.test.d.ts.map +1 -0
  90. package/dist/utils/confirmation-tokens.test.js +159 -0
  91. package/dist/utils/confirmation-tokens.test.js.map +1 -0
  92. package/dist/utils/version.d.ts +3 -0
  93. package/dist/utils/version.d.ts.map +1 -1
  94. package/dist/utils/version.js +71 -23
  95. package/dist/utils/version.js.map +1 -1
  96. package/docs/templates.db.gz +0 -0
  97. package/docs/x-callback-url.md +318 -0
  98. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -19,12 +19,18 @@ import * as memoryTools from './tools/memory.js';
19
19
  import * as uiTools from './tools/ui.js';
20
20
  import * as pluginTools from './tools/plugins.js';
21
21
  import * as themeTools from './tools/themes.js';
22
+ import * as templateTools from './tools/templates.js';
23
+ import * as attachmentTools from './tools/attachments.js';
22
24
  import { parseFlexibleDate } from './utils/date-utils.js';
23
- import { upgradeMessage, getNotePlanVersion, getMcpServerVersion, MIN_BUILD_ADVANCED_FEATURES } from './utils/version.js';
25
+ import { upgradeMessage, getNotePlanVersion, getMcpServerVersion, MIN_BUILD_ADVANCED_FEATURES, MIN_BUILD_CREATE_BACKUP } from './utils/version.js';
24
26
  import { initSqlite } from './noteplan/sqlite-loader.js';
25
27
  const __filename = fileURLToPath(import.meta.url);
26
28
  const __dirname = path.dirname(__filename);
27
29
  const PLUGIN_API_DOCS_DIR = path.join(__dirname, '../docs/plugin-api');
30
+ const DOCS_DIR = path.join(__dirname, '../docs');
31
+ const GENERAL_DOC_RESOURCES = [
32
+ { file: 'x-callback-url.md', name: 'x-callback-url Reference', desc: 'NotePlan URL scheme — all actions (openNote, addText, addNote, addQuickTask, search, runPlugin, etc.), parameters, and encoding examples' },
33
+ ];
28
34
  const PLUGIN_API_RESOURCES = [
29
35
  { file: 'plugin-api-condensed.md', name: 'Plugin API Reference (Condensed)', desc: 'Complete NotePlan plugin API — all signatures, types, and patterns in one reference. Read this first.' },
30
36
  { file: 'getting-started.md', name: 'Getting Started Guide', desc: 'How to create NotePlan plugins — structure, setup, first plugin, testing, HTML views' },
@@ -200,6 +206,8 @@ function getToolOutputSchema(toolName) {
200
206
  case 'noteplan_plugins':
201
207
  case 'noteplan_themes':
202
208
  case 'noteplan_embeddings':
209
+ case 'noteplan_templates':
210
+ case 'noteplan_attachments':
203
211
  return GENERIC_TOOL_OUTPUT_SCHEMA;
204
212
  default:
205
213
  return GENERIC_TOOL_OUTPUT_SCHEMA;
@@ -371,6 +379,7 @@ function getToolAnnotations(toolName) {
371
379
  'noteplan_plugins',
372
380
  'noteplan_themes',
373
381
  'noteplan_embeddings',
382
+ 'noteplan_attachments',
374
383
  ]);
375
384
  const nonIdempotentTools = new Set([
376
385
  'noteplan_manage_note',
@@ -384,11 +393,14 @@ function getToolAnnotations(toolName) {
384
393
  'noteplan_plugins',
385
394
  'noteplan_themes',
386
395
  'noteplan_embeddings',
396
+ 'noteplan_templates',
397
+ 'noteplan_attachments',
387
398
  ]);
388
399
  const openWorldTools = new Set([
389
400
  'noteplan_eventkit',
390
401
  'noteplan_embeddings',
391
402
  'noteplan_plugins',
403
+ 'noteplan_templates',
392
404
  ]);
393
405
  return {
394
406
  readOnlyHint: readOnlyTools.has(toolName),
@@ -459,6 +471,12 @@ function getToolSearchAliases(toolName) {
459
471
  case 'noteplan_embeddings':
460
472
  aliases.push('embeddings', 'semantic search', 'vector search', 'similarity');
461
473
  break;
474
+ case 'noteplan_templates':
475
+ aliases.push('template', 'templates', 'render template', 'list templates', 'template types', 'meeting template', 'project template', 'debug template', 'test template');
476
+ break;
477
+ case 'noteplan_attachments':
478
+ aliases.push('attachment', 'attachments', 'image', 'file', 'upload', 'add image', 'add file', 'add attachment', 'list attachments', 'get attachment', 'base64', 'photo', 'screenshot');
479
+ break;
462
480
  }
463
481
  return aliases;
464
482
  }
@@ -516,7 +534,7 @@ function searchToolDefinitions(tools, query, limit) {
516
534
  function inferToolErrorMeta(toolName, errorMessage, registeredToolNames) {
517
535
  const message = errorMessage.toLowerCase();
518
536
  if (message.includes('unknown tool')) {
519
- const toolList = registeredToolNames?.join(', ') ?? 'noteplan_get_notes, noteplan_search, noteplan_manage_note, noteplan_edit_content, noteplan_paragraphs, noteplan_folders, noteplan_filters, noteplan_eventkit, noteplan_memory';
537
+ const toolList = registeredToolNames?.join(', ') ?? 'noteplan_get_notes, noteplan_search, noteplan_manage_note, noteplan_edit_content, noteplan_paragraphs, noteplan_folders, noteplan_filters, noteplan_eventkit, noteplan_memory, noteplan_templates, noteplan_attachments';
520
538
  return {
521
539
  code: 'ERR_UNKNOWN_TOOL',
522
540
  hint: `Check tool name spelling. Available tools: ${toolList}.`,
@@ -737,6 +755,12 @@ function withSuggestedNextTools(result, toolName, availableToolNames) {
737
755
  case 'noteplan_themes':
738
756
  suggestedNextTools = ['noteplan_themes'];
739
757
  break;
758
+ case 'noteplan_templates':
759
+ suggestedNextTools = ['noteplan_templates', 'noteplan_manage_note', 'noteplan_edit_content'];
760
+ break;
761
+ case 'noteplan_attachments':
762
+ suggestedNextTools = ['noteplan_attachments', 'noteplan_edit_content', 'noteplan_get_notes'];
763
+ break;
740
764
  default:
741
765
  suggestedNextTools = [];
742
766
  }
@@ -798,12 +822,59 @@ function withDuration(result, durationMs, includeTiming) {
798
822
  export function createServer() {
799
823
  const server = new Server({
800
824
  name: 'NotePlan',
801
- version: '1.1.4',
825
+ version: '1.1.7',
802
826
  }, {
803
827
  capabilities: {
804
828
  tools: {},
805
829
  resources: {},
806
830
  },
831
+ instructions: [
832
+ 'You have access to NotePlan — a markdown-based note-taking and task management app for macOS/iOS.',
833
+ 'All tools use action-based dispatch: one tool per domain, with an `action` parameter to select the operation.',
834
+ '',
835
+ '## Workflow',
836
+ '',
837
+ 'Prefer granular edits over full-note rewrites to avoid large context payloads and accidental data loss.',
838
+ '',
839
+ '1. **Find** the note: `noteplan_get_notes` (by id/title/filename/date) or `noteplan_search`',
840
+ '2. **Inspect** content: `noteplan_paragraphs(action: get)` for line metadata, `noteplan_paragraphs(action: search)` for text lookup',
841
+ '3. **Edit** with targeted mutations:',
842
+ ' - `noteplan_edit_content(action: edit_line)` — single-line change',
843
+ ' - `noteplan_edit_content(action: insert/append)` — add content at a position or heading',
844
+ ' - `noteplan_edit_content(action: delete_lines)` — remove lines',
845
+ '4. Only use `noteplan_manage_note(action: update)` for intentional full-note rewrites',
846
+ '',
847
+ '## Tasks',
848
+ '',
849
+ '- Add tasks via `noteplan_paragraphs(action: add)` — the server auto-formats the task marker to match the user\'s NotePlan settings. Never write raw markers like `- [ ]` or `* [ ]`; just pass the task text.',
850
+ '- Find tasks: `noteplan_paragraphs(action: search)` in one note, or `search_global` across all notes',
851
+ '- Complete/update: `noteplan_paragraphs(action: complete/update)`',
852
+ '- Use `heading` parameter to target a specific section (e.g., heading: "Tasks")',
853
+ '',
854
+ '## Destructive Operations',
855
+ '',
856
+ 'Delete, move, rename, and restore use a 2-step safety flow:',
857
+ '1. Call with `dryRun=true` → get a preview and `confirmationToken`',
858
+ '2. Call again with that `confirmationToken` to execute',
859
+ '',
860
+ '## Key Behaviors',
861
+ '',
862
+ '- Calendar notes (daily/weekly/etc.) are auto-created when targeted by date — no need to create them first',
863
+ '- Notes can be targeted by `id`, `filename`, `title`, `date`, or `query` — prefer `id` or `filename` when available for precision',
864
+ '- Errors include a `hint` and often a `suggestedTool` to guide recovery',
865
+ '- The `noteplan_memory` tool stores user preferences (formatting, style, workflow) persistently across sessions',
866
+ '',
867
+ '## Templates',
868
+ '',
869
+ '- IMPORTANT: Before writing or editing templates, ALWAYS search the built-in documentation first: `noteplan_templates(action: "search_docs", query: "your topic")`',
870
+ '- Use `get_doc` to read the full text of a doc chunk returned by `search_docs`',
871
+ '- Then use `render` to test/debug your template code',
872
+ '',
873
+ '## Action Discovery',
874
+ '',
875
+ '- Every tool supports `action: "list_actions"` — call it to get a list of all available actions with descriptions',
876
+ '- Use `list_actions` when you are unsure what a tool can do or need to discover capabilities',
877
+ ].join('\n'),
807
878
  });
808
879
  let embeddingsToolsEnabled = false;
809
880
  try {
@@ -977,14 +1048,14 @@ export function createServer() {
977
1048
  },
978
1049
  {
979
1050
  name: 'noteplan_manage_note',
980
- description: 'Manage notes: create, update, delete, move, restore, rename, or manage frontmatter properties.\n\nActions:\n- create: Create a project note (requires title)\n- update: Replace note content (requires filename, content, fullReplace + confirmationToken)\n- delete/move/restore/rename: Lifecycle ops (requires id or filename + dryRun/confirmationToken)\n- set_property/remove_property: Frontmatter (requires filename + key)',
1051
+ description: 'Manage notes: create, update, delete, move, restore, rename, or manage frontmatter properties.\n\nActions:\n- create: Create a project note (requires title). Set noteType="template" to create in @Templates with proper frontmatter. After creating a template, verify it with noteplan_templates(action: "render").\n- update: Replace note content (requires filename, content, fullReplace + confirmationToken)\n- delete/move/restore: Lifecycle ops (requires id or filename + dryRun/confirmationToken)\n- rename: Rename a note (accepts id, filename, title, or query to find the note + newTitle for the new name + dryRun/confirmationToken)\n- set_property/remove_property: Frontmatter (requires filename + key)',
981
1052
  inputSchema: {
982
1053
  type: 'object',
983
1054
  properties: {
984
1055
  action: {
985
1056
  type: 'string',
986
- enum: ['create', 'update', 'delete', 'move', 'restore', 'rename', 'set_property', 'remove_property'],
987
- description: 'Action to perform',
1057
+ enum: ['create', 'update', 'delete', 'move', 'restore', 'rename', 'set_property', 'remove_property', 'list_actions'],
1058
+ description: 'Action: create | update | delete | move | restore | rename | set_property | remove_property | list_actions (discover all actions)',
988
1059
  },
989
1060
  id: {
990
1061
  type: 'string',
@@ -996,7 +1067,11 @@ export function createServer() {
996
1067
  },
997
1068
  title: {
998
1069
  type: 'string',
999
- description: 'Note title — required for create',
1070
+ description: 'Note title — required for create. Also used by rename to find the note by title (fuzzy matched).',
1071
+ },
1072
+ query: {
1073
+ type: 'string',
1074
+ description: 'Fuzzy search query to find the note — used by rename',
1000
1075
  },
1001
1076
  content: {
1002
1077
  type: 'string',
@@ -1010,6 +1085,16 @@ export function createServer() {
1010
1085
  type: 'boolean',
1011
1086
  description: 'Bypass smart matching and create exact folder name — used by create',
1012
1087
  },
1088
+ noteType: {
1089
+ type: 'string',
1090
+ enum: ['note', 'template'],
1091
+ description: 'Type of note to create. Use "template" to create in @Templates with proper frontmatter — used by create',
1092
+ },
1093
+ templateTypes: {
1094
+ type: 'array',
1095
+ items: { type: 'string', enum: ['empty-note', 'meeting-note', 'project-note', 'calendar-note'] },
1096
+ description: 'Template type tags — used by create with noteType="template"',
1097
+ },
1013
1098
  space: {
1014
1099
  type: 'string',
1015
1100
  description: 'Space name or ID scope',
@@ -1036,11 +1121,11 @@ export function createServer() {
1036
1121
  },
1037
1122
  newFilename: {
1038
1123
  type: 'string',
1039
- description: 'New filename for local notes — used by rename',
1124
+ description: 'New filename for local notes — used by rename. Prefer newTitle instead.',
1040
1125
  },
1041
1126
  newTitle: {
1042
1127
  type: 'string',
1043
- description: 'New title for TeamSpace notes — used by rename',
1128
+ description: 'New title for the note — used by rename. Works for both local and TeamSpace notes. For local notes this renames the file and updates the # heading.',
1044
1129
  },
1045
1130
  keepExtension: {
1046
1131
  type: 'boolean',
@@ -1060,14 +1145,14 @@ export function createServer() {
1060
1145
  },
1061
1146
  {
1062
1147
  name: 'noteplan_edit_content',
1063
- description: 'Edit note content: insert, append, delete lines, edit a line, or replace a range of lines.\n\nActions:\n- insert: Insert at position (start/end/after-heading/in-section/at-line)\n- append: Append to end (shorthand for insert at end). Use date="today" to append to today\'s daily note (replaces noteplan_add_to_today).\n- delete_lines: Delete line range (requires startLine, endLine + dryRun/confirmationToken)\n- edit_line: Edit one line (requires line + content)\n- replace_lines: Replace line range (requires startLine, endLine, content + dryRun/confirmationToken)\n\nTarget note via id, filename, title, date, or query. Calendar notes (date param) are auto-created if they don\'t exist yet — no need to create them first. Always use tab characters for indentation.\n\nNotePlan syntax: Tasks use "- [ ] text" (open), "- [x]" (done), "- [-]" (cancelled), "- [>]" (scheduled). The task marker (*/- with or without checkbox) follows user settings prefer noteplan_paragraphs(action=add) for auto-formatted tasks. Schedule tasks to dates with >YYYY-MM-DD. Link to notes with [[Note Name]]. Never add block IDs (^id) — only the NotePlan app creates these.\n\nUse scheduleDate to auto-append >YYYY-MM-DD scheduling to content.',
1148
+ description: 'Edit note content. IMPORTANT: action values use snake_case.\n\nValid actions (exactly these strings):\n- "insert": Insert at position. Combine any position with heading="Section Name" to scope insertion to that section. position="start" + heading inserts right after the heading. position="end" + heading appends at end of the heading\'s section. position="after-heading" inserts right after a heading. position="in-section" appends at end of a heading\'s section. position="start" (no heading) inserts after frontmatter. position="at-line" inserts at a specific line number. position="end" (no heading) appends to note.\n- "append": Shorthand for insert at end. Supports heading="Section Name" to append at end of that section. Use date="today" to append to today\'s daily note.\n- "delete_lines": Delete a line range. Requires startLine + endLine (1-indexed). Use dryRun=true first, then confirmationToken to execute.\n- "edit_line": Edit a single line. Requires line (1-indexed) + content. Set content="" to clear a line.\n- "replace_lines": Replace a line range. Requires startLine + endLine + content. Use dryRun=true first, then confirmationToken to execute.\n\nTarget note via id, filename, title, date, or query. Calendar notes (date param) are auto-created if they don\'t exist yet. Always use tab characters for indentation.\n\nAdding tasks: When inserting a task, set type="task" and pass only the task text as content (e.g. content="Buy groceries", type="task"). Do NOT include raw markers like "* [ ]" or "- [ ]" in content the type parameter handles formatting to match the user\'s configured style. For checklists use type="checklist". For task lifecycle (complete, update, search), use noteplan_paragraphs instead.\n\nSchedule tasks with >YYYY-MM-DD. Link to notes with [[Note Name]]. Never add block IDs (^id).\n\nUse scheduleDate to auto-append >YYYY-MM-DD to content.',
1064
1149
  inputSchema: {
1065
1150
  type: 'object',
1066
1151
  properties: {
1067
1152
  action: {
1068
1153
  type: 'string',
1069
- enum: ['insert', 'append', 'delete_lines', 'edit_line', 'replace_lines'],
1070
- description: 'Action to perform',
1154
+ enum: ['insert', 'append', 'delete_lines', 'edit_line', 'replace_lines', 'list_actions'],
1155
+ description: 'Action: insert | append | delete_lines | edit_line | replace_lines | list_actions (discover all actions)',
1071
1156
  },
1072
1157
  id: {
1073
1158
  type: 'string',
@@ -1095,16 +1180,16 @@ export function createServer() {
1095
1180
  },
1096
1181
  content: {
1097
1182
  type: 'string',
1098
- description: 'Content to insert/append/replace, or new line content for edit_line',
1183
+ description: 'Content to insert/append/replace, or new line content for edit_line. NEVER include task markers like "- [ ]", "* [ ]", or "* " — instead set type="task" and pass only the task text (e.g. content="Buy groceries", NOT content="- [ ] Buy groceries").',
1099
1184
  },
1100
1185
  position: {
1101
1186
  type: 'string',
1102
1187
  enum: ['start', 'end', 'after-heading', 'at-line', 'in-section'],
1103
- description: 'Where to insert: start (after frontmatter), end, after-heading (right after heading/marker line), in-section (at end of section, before next heading/marker), or at-line — used by insert',
1188
+ description: 'Where to insert. With heading: start=right after heading, end/in-section=end of section. Without heading: start=after frontmatter, end=bottom of note. at-line=specific line number — used by insert',
1104
1189
  },
1105
1190
  heading: {
1106
1191
  type: 'string',
1107
- description: 'Heading or section marker text (required for after-heading and in-section; matches both ## headings and **bold:** section markers) — used by insert',
1192
+ description: 'Heading or section marker text (required for after-heading and in-section; matches both ## headings and **bold:** section markers) — used by insert, append',
1108
1193
  },
1109
1194
  line: {
1110
1195
  type: 'number',
@@ -1167,14 +1252,14 @@ export function createServer() {
1167
1252
  },
1168
1253
  {
1169
1254
  name: 'noteplan_paragraphs',
1170
- description: 'Paragraph and task operations on notes.\n\nParagraph actions:\n- get: Get note lines with metadata (requires filename). Returns line, lineIndex, content, type, etc.\n- search: Search for matching lines in a note (requires query + note ref via id/filename/title/date)\n\nTask actions:\n- search_global: Search tasks across all notes (requires query, supports "*" wildcard)\n- add: Add a task (requires target + content). Task marker format auto-matches user settings. Use scheduleDate to auto-append >YYYY-MM-DD scheduling to content, [[Note Name]] to link, #tag for tags, @person for mentions.\n- complete: Mark task done (requires filename + lineIndex or line)\n- update: Update task content/status (requires filename + lineIndex or line)',
1255
+ description: 'Task lifecycle and paragraph inspection.\n\nParagraph actions:\n- get: Get note lines with metadata (requires filename). Returns line, lineIndex, content, type, etc.\n- search: Search for matching lines in a note (requires query + note ref via id/filename/title/date)\n\nTask actions:\n- search_global: Search tasks across all notes (requires query, supports "*" wildcard)\n- add: Add a task (requires target + content). Target is a date ("today", "tomorrow", "YYYY-MM-DD") for daily notes or a filename for project notes. Pass only the task text as content — formatting auto-matches user settings. Position+heading combos: position="start"+heading inserts right after the heading, position="end"+heading appends at end of that section, position="after-heading" inserts right after heading, position="in-section" appends at end of section. Default position is "end" (bottom of note). Use scheduleDate for >YYYY-MM-DD, [[Note Name]] to link, #tag for tags, @person for mentions.\n- complete: Mark task done (requires filename + lineIndex or line)\n- update: Update task content/status (requires filename + lineIndex or line)',
1171
1256
  inputSchema: {
1172
1257
  type: 'object',
1173
1258
  properties: {
1174
1259
  action: {
1175
1260
  type: 'string',
1176
- enum: ['get', 'search', 'search_global', 'add', 'complete', 'update'],
1177
- description: 'Action to perform',
1261
+ enum: ['get', 'search', 'search_global', 'add', 'complete', 'update', 'list_actions'],
1262
+ description: 'Action: get | search | search_global | add | complete | update | list_actions (discover all actions)',
1178
1263
  },
1179
1264
  id: {
1180
1265
  type: 'string',
@@ -1236,16 +1321,16 @@ export function createServer() {
1236
1321
  },
1237
1322
  target: {
1238
1323
  type: 'string',
1239
- description: 'Target for add: date (today/tomorrow/YYYY-MM-DD) for daily notes or filename for project notes',
1324
+ description: 'Target note for add: use a date string (today, tomorrow, yesterday, YYYY-MM-DD, YYYYMMDD) for daily/calendar notes, or a filename path for project notes. Daily notes are auto-created if they don\'t exist.',
1240
1325
  },
1241
1326
  position: {
1242
1327
  type: 'string',
1243
1328
  enum: ['start', 'end', 'after-heading', 'in-section'],
1244
- description: 'Where to add task (default: end) — used by add',
1329
+ description: 'Where to add task (default: end). With heading: start=right after heading, end/in-section=end of section. Without heading: start=top of note, end=bottom of note — used by add',
1245
1330
  },
1246
1331
  heading: {
1247
1332
  type: 'string',
1248
- description: 'Heading or section marker text (matches both ## headings and **bold:** section markers) — used by add',
1333
+ description: 'Heading or section marker text to scope insertion (matches both ## headings and **bold:** section markers). Combine with position to control placement — used by add',
1249
1334
  },
1250
1335
  lineIndex: {
1251
1336
  type: 'number',
@@ -1320,8 +1405,8 @@ export function createServer() {
1320
1405
  properties: {
1321
1406
  action: {
1322
1407
  type: 'string',
1323
- enum: ['list', 'find', 'resolve', 'create', 'move', 'rename', 'delete', 'list_spaces'],
1324
- description: 'Action to perform',
1408
+ enum: ['list', 'find', 'resolve', 'create', 'move', 'rename', 'delete', 'list_spaces', 'list_actions'],
1409
+ description: 'Action: list | find | resolve | create | move | rename | delete | list_spaces | list_actions (discover all actions)',
1325
1410
  },
1326
1411
  path: {
1327
1412
  type: 'string',
@@ -1423,8 +1508,8 @@ export function createServer() {
1423
1508
  properties: {
1424
1509
  action: {
1425
1510
  type: 'string',
1426
- enum: ['list', 'get', 'get_tasks', 'list_parameters', 'save', 'rename'],
1427
- description: 'Action to perform',
1511
+ enum: ['list', 'get', 'get_tasks', 'list_parameters', 'save', 'rename', 'list_actions'],
1512
+ description: 'Action: list | get | get_tasks | list_parameters | save | rename | list_actions (discover all actions)',
1428
1513
  },
1429
1514
  name: {
1430
1515
  type: 'string',
@@ -1508,8 +1593,8 @@ export function createServer() {
1508
1593
  },
1509
1594
  action: {
1510
1595
  type: 'string',
1511
- enum: ['get_events', 'list_calendars', 'create_event', 'update_event', 'delete_event', 'get', 'list_lists', 'create', 'complete', 'update', 'delete'],
1512
- description: 'Action to perform',
1596
+ enum: ['get_events', 'list_calendars', 'create_event', 'update_event', 'delete_event', 'get', 'list_lists', 'create', 'complete', 'update', 'delete', 'list_actions'],
1597
+ description: 'Action: get_events | list_calendars | create_event | update_event | delete_event | get | list_lists | create | complete | update | delete | list_actions (discover all actions)',
1513
1598
  },
1514
1599
  // Calendar params
1515
1600
  eventId: {
@@ -1610,8 +1695,8 @@ export function createServer() {
1610
1695
  properties: {
1611
1696
  action: {
1612
1697
  type: 'string',
1613
- enum: ['list', 'save', 'update', 'delete'],
1614
- description: 'Action to perform',
1698
+ enum: ['list', 'save', 'update', 'delete', 'list_actions'],
1699
+ description: 'Action: list | save | update | delete | list_actions (discover all actions)',
1615
1700
  },
1616
1701
  id: {
1617
1702
  type: 'string',
@@ -1657,8 +1742,8 @@ export function createServer() {
1657
1742
  properties: {
1658
1743
  action: {
1659
1744
  type: 'string',
1660
- enum: ['search', 'list_tags'],
1661
- description: 'Action to perform (default: search)',
1745
+ enum: ['search', 'list_tags', 'list_actions'],
1746
+ description: 'Action to perform (default: search) | list_actions (discover all actions)',
1662
1747
  },
1663
1748
  query: {
1664
1749
  type: 'string',
@@ -1746,8 +1831,8 @@ export function createServer() {
1746
1831
  properties: {
1747
1832
  action: {
1748
1833
  type: 'string',
1749
- enum: ['status', 'search', 'sync', 'reset'],
1750
- description: 'Action to perform',
1834
+ enum: ['status', 'search', 'sync', 'reset', 'list_actions'],
1835
+ description: 'Action: status | search | sync | reset | list_actions (discover all actions)',
1751
1836
  },
1752
1837
  query: {
1753
1838
  type: 'string',
@@ -1824,196 +1909,282 @@ export function createServer() {
1824
1909
  },
1825
1910
  });
1826
1911
  }
1827
- if (advancedFeaturesEnabled) {
1828
- toolDefinitions.push({
1829
- name: 'noteplan_ui',
1830
- description: 'NotePlan UI control via AppleScript.\n\nActions:\n- open_note: Open a note (title or filename)\n- open_today: Open today\'s note\n- search: Search in UI\n- run_plugin: Run a plugin command (requires pluginId + command)\n- open_view: Open a named view\n- toggle_sidebar: Toggle sidebar visibility\n- close_plugin_window: Close plugin window (by windowID/title, or omit both to close all)\n- list_plugin_windows: List open plugin windows',
1831
- inputSchema: {
1832
- type: 'object',
1833
- properties: {
1834
- action: {
1835
- type: 'string',
1836
- enum: ['open_note', 'open_today', 'search', 'run_plugin', 'open_view', 'toggle_sidebar', 'close_plugin_window', 'list_plugin_windows'],
1837
- description: 'Action to perform',
1838
- },
1839
- title: {
1840
- type: 'string',
1841
- description: 'Note title — used by open_note; window title — used by close_plugin_window',
1842
- },
1843
- filename: {
1844
- type: 'string',
1845
- description: 'Filename — used by open_note',
1846
- },
1847
- inNewWindow: {
1848
- type: 'boolean',
1849
- description: 'Open in new window — used by open_note',
1850
- },
1851
- inSplitView: {
1852
- type: 'boolean',
1853
- description: 'Open in split view — used by open_note',
1854
- },
1855
- query: {
1856
- type: 'string',
1857
- description: 'Search text — used by search',
1858
- },
1859
- pluginId: {
1860
- type: 'string',
1861
- description: 'Plugin ID — used by run_plugin',
1862
- },
1863
- command: {
1864
- type: 'string',
1865
- description: 'Command name — used by run_plugin',
1866
- },
1867
- arguments: {
1868
- type: 'string',
1869
- description: 'JSON arguments string — used by run_plugin',
1870
- },
1871
- name: {
1872
- type: 'string',
1873
- description: 'View name — used by open_view',
1874
- },
1875
- windowID: {
1876
- type: 'string',
1877
- description: 'Window ID — used by close_plugin_window',
1878
- },
1912
+ // noteplan_ui is always available — basic AppleScript commands work on all NotePlan versions
1913
+ toolDefinitions.push({
1914
+ name: 'noteplan_ui',
1915
+ description: 'NotePlan UI control via AppleScript.\n\nActions:\n- open_note: Open a note (title or filename)\n- open_today: Open today\'s note\n- search: Search in UI\n- run_plugin: Run a plugin command (requires pluginId + command)\n- open_view: Open a named view\n- toggle_sidebar: Toggle sidebar visibility\n- close_plugin_window: Close plugin window (by windowID/title, or omit both to close all)\n- list_plugin_windows: List open plugin windows\n- backup: Create a full backup of all notes, calendars, themes, filters, and plugin data. Old backups are pruned automatically.',
1916
+ inputSchema: {
1917
+ type: 'object',
1918
+ properties: {
1919
+ action: {
1920
+ type: 'string',
1921
+ enum: ['open_note', 'open_today', 'search', 'run_plugin', 'open_view', 'toggle_sidebar', 'close_plugin_window', 'list_plugin_windows', 'backup', 'list_actions'],
1922
+ description: 'Action: open_note | open_today | search | run_plugin | open_view | toggle_sidebar | close_plugin_window | list_plugin_windows | backup | list_actions (discover all actions)',
1879
1923
  },
1880
- required: ['action'],
1881
- },
1882
- }, {
1883
- name: 'noteplan_plugins',
1884
- description: 'Plugin management: list, create, delete, install, read source/log, update HTML, screenshot.\n\nActions:\n- list: List installed plugins\n- list_available: List plugins from online repository\n- create: Create plugin with HTML view (requires pluginId, pluginName, commandName, html)\n- delete: Delete plugin (requires pluginId + confirmationToken)\n- install: Install from repository (requires pluginId)\n- log: Read plugin console log (requires pluginId)\n- source: Read plugin source (requires pluginId)\n- update_html: Apply find/replace patches (requires pluginId + patches)\n- screenshot: Capture plugin WebView screenshot (requires pluginId)',
1885
- inputSchema: {
1886
- type: 'object',
1887
- properties: {
1888
- action: {
1889
- type: 'string',
1890
- enum: ['list', 'list_available', 'create', 'delete', 'install', 'log', 'source', 'update_html', 'screenshot'],
1891
- description: 'Action to perform',
1892
- },
1893
- pluginId: {
1894
- type: 'string',
1895
- description: 'Plugin ID — used by create, delete, install, log, source, update_html, screenshot',
1896
- },
1897
- pluginName: {
1898
- type: 'string',
1899
- description: 'Display name — used by create',
1900
- },
1901
- commandName: {
1902
- type: 'string',
1903
- description: 'Command name — used by create',
1904
- },
1905
- html: {
1906
- type: 'string',
1907
- description: 'HTML content — used by create',
1908
- },
1909
- icon: {
1910
- type: 'string',
1911
- description: 'Font Awesome icon name — used by create',
1912
- },
1913
- iconColor: {
1914
- type: 'string',
1915
- description: 'Tailwind color — used by create',
1916
- },
1917
- displayMode: {
1918
- type: 'string',
1919
- enum: ['main', 'split', 'window'],
1920
- description: 'Display mode (default: main) — used by create',
1921
- },
1922
- autoLaunch: {
1923
- type: 'boolean',
1924
- description: 'Auto-reload and run — used by create, update_html',
1925
- },
1926
- patches: {
1927
- type: 'array',
1928
- items: {
1929
- type: 'object',
1930
- properties: {
1931
- find: { type: 'string' },
1932
- replace: { type: 'string' },
1933
- },
1934
- required: ['find', 'replace'],
1935
- },
1936
- description: 'Find/replace patches — used by update_html',
1937
- },
1938
- query: {
1939
- type: 'string',
1940
- description: 'Filter/search — used by list, list_available, source',
1941
- },
1942
- includeBeta: {
1943
- type: 'boolean',
1944
- description: 'Include beta plugins — used by list_available',
1945
- },
1946
- tail: {
1947
- type: 'integer',
1948
- description: 'Return last N lines — used by log',
1949
- },
1950
- clear: {
1951
- type: 'boolean',
1952
- description: 'Clear log after reading — used by log',
1953
- },
1954
- startLine: {
1955
- type: 'integer',
1956
- description: 'Start line (1-based) — used by source',
1957
- },
1958
- endLine: {
1959
- type: 'integer',
1960
- description: 'End line (1-based) — used by source',
1961
- },
1962
- contextLines: {
1963
- type: 'integer',
1964
- description: 'Context lines around matches — used by source',
1965
- },
1966
- confirmationToken: {
1967
- type: 'string',
1968
- description: 'Token for confirmation — used by delete',
1969
- },
1924
+ title: {
1925
+ type: 'string',
1926
+ description: 'Note title — used by open_note; window title — used by close_plugin_window',
1927
+ },
1928
+ filename: {
1929
+ type: 'string',
1930
+ description: 'Filename — used by open_note',
1931
+ },
1932
+ inNewWindow: {
1933
+ type: 'boolean',
1934
+ description: 'Open in new window used by open_note',
1935
+ },
1936
+ inSplitView: {
1937
+ type: 'boolean',
1938
+ description: 'Open in split view — used by open_note',
1939
+ },
1940
+ query: {
1941
+ type: 'string',
1942
+ description: 'Search text — used by search',
1943
+ },
1944
+ pluginId: {
1945
+ type: 'string',
1946
+ description: 'Plugin ID — used by run_plugin',
1947
+ },
1948
+ command: {
1949
+ type: 'string',
1950
+ description: 'Command name — used by run_plugin',
1951
+ },
1952
+ arguments: {
1953
+ type: 'string',
1954
+ description: 'JSON arguments string — used by run_plugin',
1955
+ },
1956
+ name: {
1957
+ type: 'string',
1958
+ description: 'View name — used by open_view',
1959
+ },
1960
+ windowID: {
1961
+ type: 'string',
1962
+ description: 'Window ID — used by close_plugin_window',
1970
1963
  },
1971
- required: ['action'],
1972
1964
  },
1973
- }, {
1974
- name: 'noteplan_themes',
1975
- description: 'Theme management: list, get, save, set active.\n\nActions:\n- list: List all themes and active theme names\n- get: Read a custom theme JSON (requires filename)\n- save: Create/update a custom theme (requires filename + theme)\n- set_active: Activate a theme (requires name)',
1976
- inputSchema: {
1977
- type: 'object',
1978
- properties: {
1979
- action: {
1980
- type: 'string',
1981
- enum: ['list', 'get', 'save', 'set_active'],
1982
- description: 'Action to perform',
1983
- },
1984
- filename: {
1985
- type: 'string',
1986
- description: 'Theme filename used by get, save',
1987
- },
1988
- name: {
1989
- type: 'string',
1990
- description: 'Theme name — used by set_active',
1991
- },
1992
- theme: {
1965
+ required: ['action'],
1966
+ },
1967
+ });
1968
+ // Always register all tools — if NotePlan is too old for a specific action,
1969
+ // the action handler returns a helpful upgrade message instead of hiding the tool entirely.
1970
+ toolDefinitions.push({
1971
+ name: 'noteplan_plugins',
1972
+ description: 'Plugin management: list, create, delete, install, read source/log, update HTML, screenshot.\n\nActions:\n- list: List installed plugins\n- list_available: List plugins from online repository\n- create: Create plugin with HTML view (requires pluginId, pluginName, commandName, html)\n- delete: Delete plugin (requires pluginId + confirmationToken)\n- install: Install from repository (requires pluginId)\n- log: Read plugin console log (requires pluginId)\n- source: Read plugin source (requires pluginId)\n- update_html: Apply find/replace patches (requires pluginId + patches)\n- screenshot: Capture plugin WebView screenshot (requires pluginId)',
1973
+ inputSchema: {
1974
+ type: 'object',
1975
+ properties: {
1976
+ action: {
1977
+ type: 'string',
1978
+ enum: ['list', 'list_available', 'create', 'delete', 'install', 'log', 'source', 'update_html', 'screenshot', 'list_actions'],
1979
+ description: 'Action: list | list_available | create | delete | install | log | source | update_html | screenshot | list_actions (discover all actions)',
1980
+ },
1981
+ pluginId: {
1982
+ type: 'string',
1983
+ description: 'Plugin ID — used by create, delete, install, log, source, update_html, screenshot',
1984
+ },
1985
+ pluginName: {
1986
+ type: 'string',
1987
+ description: 'Display name — used by create',
1988
+ },
1989
+ commandName: {
1990
+ type: 'string',
1991
+ description: 'Command name — used by create',
1992
+ },
1993
+ html: {
1994
+ type: 'string',
1995
+ description: 'HTML content — used by create',
1996
+ },
1997
+ icon: {
1998
+ type: 'string',
1999
+ description: 'Font Awesome icon name — used by create',
2000
+ },
2001
+ iconColor: {
2002
+ type: 'string',
2003
+ description: 'Tailwind color — used by create',
2004
+ },
2005
+ displayMode: {
2006
+ type: 'string',
2007
+ enum: ['main', 'split', 'window'],
2008
+ description: 'Display mode (default: main) — used by create',
2009
+ },
2010
+ autoLaunch: {
2011
+ type: 'boolean',
2012
+ description: 'Auto-reload and run — used by create, update_html',
2013
+ },
2014
+ patches: {
2015
+ type: 'array',
2016
+ items: {
1993
2017
  type: 'object',
1994
- description: 'Theme object — used by save',
1995
2018
  properties: {
1996
- name: { type: 'string' },
1997
- style: { type: 'string', enum: ['Light', 'Dark'] },
1998
- author: { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string' } } },
1999
- editor: { type: 'object' },
2000
- styles: { type: 'object' },
2019
+ find: { type: 'string' },
2020
+ replace: { type: 'string' },
2001
2021
  },
2022
+ required: ['find', 'replace'],
2002
2023
  },
2003
- setActive: {
2004
- type: 'boolean',
2005
- description: 'Apply theme immediately — used by save (default: true)',
2006
- },
2007
- mode: {
2008
- type: 'string',
2009
- enum: ['light', 'dark', 'auto'],
2010
- description: 'Mode to apply for — used by save, set_active',
2024
+ description: 'Find/replace patches — used by update_html',
2025
+ },
2026
+ query: {
2027
+ type: 'string',
2028
+ description: 'Filter/search — used by list, list_available, source',
2029
+ },
2030
+ includeBeta: {
2031
+ type: 'boolean',
2032
+ description: 'Include beta plugins — used by list_available',
2033
+ },
2034
+ tail: {
2035
+ type: 'integer',
2036
+ description: 'Return last N lines — used by log',
2037
+ },
2038
+ clear: {
2039
+ type: 'boolean',
2040
+ description: 'Clear log after reading — used by log',
2041
+ },
2042
+ startLine: {
2043
+ type: 'integer',
2044
+ description: 'Start line (1-based) — used by source',
2045
+ },
2046
+ endLine: {
2047
+ type: 'integer',
2048
+ description: 'End line (1-based) — used by source',
2049
+ },
2050
+ contextLines: {
2051
+ type: 'integer',
2052
+ description: 'Context lines around matches — used by source',
2053
+ },
2054
+ confirmationToken: {
2055
+ type: 'string',
2056
+ description: 'Token for confirmation — used by delete',
2057
+ },
2058
+ },
2059
+ required: ['action'],
2060
+ },
2061
+ }, {
2062
+ name: 'noteplan_themes',
2063
+ description: 'Theme management: list, get, save, set active.\n\nActions:\n- list: List all themes and active theme names\n- get: Read a custom theme JSON (requires filename)\n- save: Create/update a custom theme (requires filename + theme)\n- set_active: Activate a theme (requires name)',
2064
+ inputSchema: {
2065
+ type: 'object',
2066
+ properties: {
2067
+ action: {
2068
+ type: 'string',
2069
+ enum: ['list', 'get', 'save', 'set_active', 'list_actions'],
2070
+ description: 'Action: list | get | save | set_active | list_actions (discover all actions)',
2071
+ },
2072
+ filename: {
2073
+ type: 'string',
2074
+ description: 'Theme filename — used by get, save',
2075
+ },
2076
+ name: {
2077
+ type: 'string',
2078
+ description: 'Theme name — used by set_active',
2079
+ },
2080
+ theme: {
2081
+ type: 'object',
2082
+ description: 'Theme object — used by save',
2083
+ properties: {
2084
+ name: { type: 'string' },
2085
+ style: { type: 'string', enum: ['Light', 'Dark'] },
2086
+ author: { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string' } } },
2087
+ editor: { type: 'object' },
2088
+ styles: { type: 'object' },
2011
2089
  },
2012
2090
  },
2013
- required: ['action'],
2091
+ setActive: {
2092
+ type: 'boolean',
2093
+ description: 'Apply theme immediately — used by save (default: true)',
2094
+ },
2095
+ mode: {
2096
+ type: 'string',
2097
+ enum: ['light', 'dark', 'auto'],
2098
+ description: 'Mode to apply for — used by save, set_active',
2099
+ },
2014
2100
  },
2015
- });
2016
- }
2101
+ required: ['action'],
2102
+ },
2103
+ }, {
2104
+ name: 'noteplan_templates',
2105
+ description: 'Template operations: list available templates, render a template, or search template documentation.\n\nActions:\n- list: List templates from @Templates folder with their types and preview\n- render: Render a template by title (saved template) or raw content string (for debugging). Rendering requires a recent NotePlan build.\n- search_docs: Semantic search over bundled NotePlan template documentation. Requires query param. Uses embeddings API or NotePlan built-in embedding. Returns matched doc chunks ranked by relevance — use this to look up template syntax, helpers, and examples before writing templates.\n- get_doc: Retrieve the full text of a doc chunk by noteTitle and chunkIndex (from search_docs results). Use this to read complete method signatures, format tokens, and examples that may be truncated in search_docs previews.\n\nDebugging workflow: After creating or editing a template, use render with its title or raw content to verify the output. Check variables, date formatting, and logic. If rendering fails or produces unexpected output, read the plugin log via noteplan_plugins(action: "log", pluginId: "np.Templating") for error details.\n\nTemplate syntax: <%- expr %> (output), <% code %> (logic), <%= expr %> (escaped output). Common helpers: date.now("YYYY-MM-DD"), web.weather(), date.tomorrow("format").',
2106
+ inputSchema: {
2107
+ type: 'object',
2108
+ properties: {
2109
+ action: {
2110
+ type: 'string',
2111
+ enum: ['list', 'render', 'search_docs', 'get_doc', 'list_actions'],
2112
+ description: 'Action: list | render | search_docs | get_doc | list_actions (discover all actions)',
2113
+ },
2114
+ templateTitle: {
2115
+ type: 'string',
2116
+ description: 'Template title — used by render (loads a saved template by title)',
2117
+ },
2118
+ content: {
2119
+ type: 'string',
2120
+ description: 'Raw template content string — used by render (renders arbitrary template code for debugging)',
2121
+ },
2122
+ folder: {
2123
+ type: 'string',
2124
+ description: 'Template subfolder — used by list (default: @Templates)',
2125
+ },
2126
+ limit: {
2127
+ type: 'number',
2128
+ description: 'Maximum results — used by list',
2129
+ },
2130
+ offset: {
2131
+ type: 'number',
2132
+ description: 'Pagination offset — used by list',
2133
+ },
2134
+ cursor: {
2135
+ type: 'string',
2136
+ description: 'Cursor from previous page — used by list',
2137
+ },
2138
+ query: {
2139
+ type: 'string',
2140
+ description: 'Search query — used by search_docs',
2141
+ },
2142
+ includeContent: {
2143
+ type: 'boolean',
2144
+ description: 'Include full chunk text in results — used by search_docs',
2145
+ },
2146
+ noteTitle: {
2147
+ type: 'string',
2148
+ description: 'Doc note title — used by get_doc (from search_docs results)',
2149
+ },
2150
+ chunkIndex: {
2151
+ type: 'number',
2152
+ description: 'Chunk index — used by get_doc (from search_docs results, default 0)',
2153
+ },
2154
+ },
2155
+ required: ['action'],
2156
+ },
2157
+ }, {
2158
+ name: 'noteplan_attachments',
2159
+ description: 'Attachment operations: add files/images to notes, list attachments, get attachment data, or move between notes.\n\nActions:\n- add: Write a file to the note\'s _attachments folder. Requires data (base64) + attachmentFilename. Returns markdownLink — use noteplan_edit_content to place it in the note (insertLink defaults to false).\n- list: List all attachments for a note with filenames, sizes, and markdown links.\n- get: Get attachment metadata. Set includeData=true for base64 content. Use maxDataSize to cap large images.\n- move: Move an attachment between notes. Source note via id/filename/title/date, destination via destinationId/destinationFilename/destinationTitle/destinationDate. Moves file, removes old link from source, returns new markdownLink.\n\nAttachments are stored in {notename}_attachments/ sibling folder. Images: ![image](path), files: ![file](path). Use attachmentFilename for both add, get, and move.',
2160
+ inputSchema: {
2161
+ type: 'object',
2162
+ properties: {
2163
+ action: {
2164
+ type: 'string',
2165
+ enum: ['add', 'list', 'get', 'move', 'list_actions'],
2166
+ description: 'Action: add | list | get | move | list_actions (discover all actions)',
2167
+ },
2168
+ id: { type: 'string', description: 'Source note ID — used by all actions' },
2169
+ filename: { type: 'string', description: 'Source note filename/path — used by all actions' },
2170
+ title: { type: 'string', description: 'Source note title — used by all actions' },
2171
+ date: { type: 'string', description: 'Source calendar note date (YYYYMMDD or YYYY-MM-DD) — used by all actions' },
2172
+ query: { type: 'string', description: 'Search query to find the source note' },
2173
+ space: { type: 'string', description: 'Space name or ID' },
2174
+ data: { type: 'string', description: 'Base64-encoded file data — required for add' },
2175
+ attachmentFilename: { type: 'string', description: 'Attachment filename (e.g. "photo.png") — required for add, get, and move' },
2176
+ mimeType: { type: 'string', description: 'MIME type hint (e.g. "image/png") — used by add' },
2177
+ insertLink: { type: 'boolean', description: 'Auto-insert markdown link into note — used by add (default: false). Prefer placing links yourself via noteplan_edit_content.' },
2178
+ destinationId: { type: 'string', description: 'Destination note ID — used by move' },
2179
+ destinationFilename: { type: 'string', description: 'Destination note filename — used by move' },
2180
+ destinationTitle: { type: 'string', description: 'Destination note title — used by move' },
2181
+ destinationDate: { type: 'string', description: 'Destination calendar note date — used by move' },
2182
+ includeData: { type: 'boolean', description: 'Include base64 data in response — used by get (default: false)' },
2183
+ maxDataSize: { type: 'number', description: 'Max file size in bytes for data inclusion — used by get. Files exceeding this are skipped.' },
2184
+ },
2185
+ required: ['action'],
2186
+ },
2187
+ });
2017
2188
  const annotatedToolDefinitions = toolDefinitions.map((tool) => ({
2018
2189
  ...tool,
2019
2190
  inputSchema: withDebugTimingsInputSchema(tool.inputSchema),
@@ -2030,42 +2201,178 @@ export function createServer() {
2030
2201
  });
2031
2202
  // Register resource listing handler
2032
2203
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
2033
- return {
2034
- resources: PLUGIN_API_RESOURCES.map((r) => ({
2035
- uri: `noteplan://plugin-api/${r.file}`,
2036
- name: r.name,
2037
- description: r.desc,
2038
- mimeType: r.file.endsWith('.md') ? 'text/markdown' : r.file.endsWith('.json') ? 'application/json' : 'text/javascript',
2039
- })),
2040
- };
2204
+ const pluginResources = PLUGIN_API_RESOURCES.map((r) => ({
2205
+ uri: `noteplan://plugin-api/${r.file}`,
2206
+ name: r.name,
2207
+ description: r.desc,
2208
+ mimeType: r.file.endsWith('.md') ? 'text/markdown' : r.file.endsWith('.json') ? 'application/json' : 'text/javascript',
2209
+ }));
2210
+ const generalResources = GENERAL_DOC_RESOURCES.map((r) => ({
2211
+ uri: `noteplan://docs/${r.file}`,
2212
+ name: r.name,
2213
+ description: r.desc,
2214
+ mimeType: 'text/markdown',
2215
+ }));
2216
+ return { resources: [...pluginResources, ...generalResources] };
2041
2217
  });
2042
2218
  // Register resource read handler
2043
2219
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2044
2220
  const uri = request.params.uri;
2045
- const prefix = 'noteplan://plugin-api/';
2046
- if (!uri.startsWith(prefix)) {
2047
- throw new Error(`Unknown resource URI: ${uri}`);
2221
+ const pluginPrefix = 'noteplan://plugin-api/';
2222
+ const docsPrefix = 'noteplan://docs/';
2223
+ let filePath;
2224
+ if (uri.startsWith(pluginPrefix)) {
2225
+ const filename = uri.slice(pluginPrefix.length);
2226
+ const entry = PLUGIN_API_RESOURCES.find((r) => r.file === filename);
2227
+ if (!entry) {
2228
+ throw new Error(`Unknown resource: ${filename}. Available: ${PLUGIN_API_RESOURCES.map((r) => r.file).join(', ')}`);
2229
+ }
2230
+ filePath = path.join(PLUGIN_API_DOCS_DIR, entry.file);
2231
+ }
2232
+ else if (uri.startsWith(docsPrefix)) {
2233
+ const filename = uri.slice(docsPrefix.length);
2234
+ const entry = GENERAL_DOC_RESOURCES.find((r) => r.file === filename);
2235
+ if (!entry) {
2236
+ throw new Error(`Unknown resource: ${filename}. Available: ${GENERAL_DOC_RESOURCES.map((r) => r.file).join(', ')}`);
2237
+ }
2238
+ filePath = path.join(DOCS_DIR, entry.file);
2048
2239
  }
2049
- const filename = uri.slice(prefix.length);
2050
- const entry = PLUGIN_API_RESOURCES.find((r) => r.file === filename);
2051
- if (!entry) {
2052
- throw new Error(`Unknown resource: ${filename}. Available: ${PLUGIN_API_RESOURCES.map((r) => r.file).join(', ')}`);
2240
+ else {
2241
+ throw new Error(`Unknown resource URI: ${uri}`);
2053
2242
  }
2054
- const filePath = path.join(PLUGIN_API_DOCS_DIR, entry.file);
2055
2243
  if (!fs.existsSync(filePath)) {
2056
2244
  throw new Error(`Resource file not found on disk: ${filePath}`);
2057
2245
  }
2058
2246
  const content = fs.readFileSync(filePath, 'utf-8');
2247
+ const mimeType = filePath.endsWith('.md') ? 'text/markdown' : filePath.endsWith('.json') ? 'application/json' : 'text/javascript';
2059
2248
  return {
2060
- contents: [
2061
- {
2062
- uri,
2063
- mimeType: entry.file.endsWith('.md') ? 'text/markdown' : entry.file.endsWith('.json') ? 'application/json' : 'text/javascript',
2064
- text: content,
2065
- },
2066
- ],
2249
+ contents: [{ uri, mimeType, text: content }],
2067
2250
  };
2068
2251
  });
2252
+ // ── Action registry for list_actions discovery ──
2253
+ const TOOL_ACTIONS = {
2254
+ noteplan_get_notes: [
2255
+ { action: 'get', description: 'Get a single note by id, filename, title, date, or query' },
2256
+ { action: 'list', description: 'List notes with optional folder/type/date filtering' },
2257
+ { action: 'resolve', description: 'Resolve a reference (title, filename, query) to a single note' },
2258
+ { action: 'today', description: 'Get today\'s daily note' },
2259
+ { action: 'calendar', description: 'Get a calendar note by date' },
2260
+ { action: 'periodic', description: 'Get a periodic note (week, month, quarter, year)' },
2261
+ { action: 'range', description: 'Get calendar notes in a date range' },
2262
+ ],
2263
+ noteplan_search: [
2264
+ { action: 'search', description: 'Full-text or metadata search across notes' },
2265
+ { action: 'list_tags', description: 'List all tags/hashtags with optional filtering' },
2266
+ ],
2267
+ noteplan_manage_note: [
2268
+ { action: 'create', description: 'Create a project note (requires title)' },
2269
+ { action: 'update', description: 'Replace note content (requires filename, content, fullReplace + confirmationToken)' },
2270
+ { action: 'delete', description: 'Delete a note (requires dryRun/confirmationToken)' },
2271
+ { action: 'move', description: 'Move a note to another folder (requires dryRun/confirmationToken)' },
2272
+ { action: 'restore', description: 'Restore a trashed note (requires dryRun/confirmationToken)' },
2273
+ { action: 'rename', description: 'Rename a note (requires newTitle)' },
2274
+ { action: 'set_property', description: 'Set a frontmatter property (requires key + value)' },
2275
+ { action: 'remove_property', description: 'Remove a frontmatter property (requires key)' },
2276
+ ],
2277
+ noteplan_edit_content: [
2278
+ { action: 'insert', description: 'Insert content at a position. Use heading param to scope to a section' },
2279
+ { action: 'append', description: 'Append content at end of note or section' },
2280
+ { action: 'delete_lines', description: 'Delete a line range (requires startLine + endLine, 1-indexed)' },
2281
+ { action: 'edit_line', description: 'Edit a single line (requires line + content)' },
2282
+ { action: 'replace_lines', description: 'Replace a line range (requires startLine + endLine + content)' },
2283
+ ],
2284
+ noteplan_paragraphs: [
2285
+ { action: 'get', description: 'Get note lines with metadata (requires filename)' },
2286
+ { action: 'search', description: 'Search for matching lines in a note (requires query + note ref)' },
2287
+ { action: 'search_global', description: 'Search tasks across all notes (requires query, supports "*" wildcard)' },
2288
+ { action: 'add', description: 'Add a task (requires target + content)' },
2289
+ { action: 'complete', description: 'Mark task done (requires filename + lineIndex or line)' },
2290
+ { action: 'update', description: 'Update task content/status (requires filename + lineIndex or line)' },
2291
+ ],
2292
+ noteplan_folders: [
2293
+ { action: 'list', description: 'List folders with optional filtering' },
2294
+ { action: 'find', description: 'Find folder matches for exploration' },
2295
+ { action: 'resolve', description: 'Resolve to one canonical folder path' },
2296
+ { action: 'create', description: 'Create a folder' },
2297
+ { action: 'move', description: 'Move a folder (requires dryRun/confirmationToken)' },
2298
+ { action: 'rename', description: 'Rename a folder (requires dryRun/confirmationToken)' },
2299
+ { action: 'delete', description: 'Delete folder to trash (requires dryRun/confirmationToken)' },
2300
+ { action: 'list_spaces', description: 'List spaces/workspaces' },
2301
+ ],
2302
+ noteplan_filters: [
2303
+ { action: 'list', description: 'List saved filters' },
2304
+ { action: 'get', description: 'Get one filter with parsed params (requires name)' },
2305
+ { action: 'get_tasks', description: 'Execute a filter against tasks (requires name)' },
2306
+ { action: 'list_parameters', description: 'List supported filter parameter keys' },
2307
+ { action: 'save', description: 'Create or update a filter (requires name + items)' },
2308
+ { action: 'rename', description: 'Rename a filter (requires oldName + newName)' },
2309
+ ],
2310
+ noteplan_eventkit: [
2311
+ { action: 'get_events', description: 'Get events for a date/range (source: calendar)' },
2312
+ { action: 'list_calendars', description: 'List all calendars (source: calendar)' },
2313
+ { action: 'create_event', description: 'Create event (source: calendar, requires title + startDate)' },
2314
+ { action: 'update_event', description: 'Update event (source: calendar, requires eventId)' },
2315
+ { action: 'delete_event', description: 'Delete event (source: calendar, requires eventId + dryRun/confirmationToken)' },
2316
+ { action: 'get', description: 'Get reminders (source: reminders, optional list/query filter)' },
2317
+ { action: 'list_lists', description: 'List reminder lists (source: reminders)' },
2318
+ { action: 'create', description: 'Create reminder (source: reminders, requires title)' },
2319
+ { action: 'complete', description: 'Mark reminder done (source: reminders, requires reminderId)' },
2320
+ { action: 'update', description: 'Update reminder (source: reminders, requires reminderId)' },
2321
+ { action: 'delete', description: 'Delete reminder (source: reminders, requires reminderId + dryRun/confirmationToken)' },
2322
+ ],
2323
+ noteplan_memory: [
2324
+ { action: 'list', description: 'List/search stored memories' },
2325
+ { action: 'save', description: 'Save a new memory (requires content)' },
2326
+ { action: 'update', description: 'Update memory content/tags (requires id)' },
2327
+ { action: 'delete', description: 'Delete a memory (requires id)' },
2328
+ ],
2329
+ noteplan_ui: [
2330
+ { action: 'open_note', description: 'Open a note by title or filename' },
2331
+ { action: 'open_today', description: 'Open today\'s note' },
2332
+ { action: 'search', description: 'Search in UI' },
2333
+ { action: 'run_plugin', description: 'Run a plugin command (requires pluginId + command)' },
2334
+ { action: 'open_view', description: 'Open a named view' },
2335
+ { action: 'toggle_sidebar', description: 'Toggle sidebar visibility' },
2336
+ { action: 'close_plugin_window', description: 'Close plugin window (by windowID/title, or omit both to close all)' },
2337
+ { action: 'list_plugin_windows', description: 'List open plugin windows' },
2338
+ { action: 'backup', description: 'Create a full backup of all notes, calendars, themes, filters, and plugin data' },
2339
+ ],
2340
+ noteplan_plugins: [
2341
+ { action: 'list', description: 'List installed plugins' },
2342
+ { action: 'list_available', description: 'List plugins from online repository' },
2343
+ { action: 'create', description: 'Create plugin with HTML view (requires pluginId, pluginName, commandName, html)' },
2344
+ { action: 'delete', description: 'Delete plugin (requires pluginId + confirmationToken)' },
2345
+ { action: 'install', description: 'Install from repository (requires pluginId)' },
2346
+ { action: 'log', description: 'Read plugin console log (requires pluginId)' },
2347
+ { action: 'source', description: 'Read plugin source (requires pluginId)' },
2348
+ { action: 'update_html', description: 'Apply find/replace patches (requires pluginId + patches)' },
2349
+ { action: 'screenshot', description: 'Capture plugin WebView screenshot (requires pluginId)' },
2350
+ ],
2351
+ noteplan_themes: [
2352
+ { action: 'list', description: 'List all themes and active theme names' },
2353
+ { action: 'get', description: 'Read a custom theme JSON (requires filename)' },
2354
+ { action: 'save', description: 'Create/update a custom theme (requires filename + theme)' },
2355
+ { action: 'set_active', description: 'Activate a theme (requires name)' },
2356
+ ],
2357
+ noteplan_embeddings: [
2358
+ { action: 'status', description: 'Get embeddings config and index status' },
2359
+ { action: 'search', description: 'Semantic search over indexed notes (requires query)' },
2360
+ { action: 'sync', description: 'Build/refresh embeddings index' },
2361
+ { action: 'reset', description: 'Delete index rows (requires dryRun/confirmationToken)' },
2362
+ ],
2363
+ noteplan_templates: [
2364
+ { action: 'list', description: 'List templates from @Templates folder with types and preview' },
2365
+ { action: 'render', description: 'Render a template by title or raw content string' },
2366
+ { action: 'search_docs', description: 'Semantic search over bundled template documentation (requires query). Use this to look up template syntax, helpers, and examples before writing templates' },
2367
+ { action: 'get_doc', description: 'Get full text of a doc chunk by noteTitle + chunkIndex (from search_docs results)' },
2368
+ ],
2369
+ noteplan_attachments: [
2370
+ { action: 'add', description: 'Write a file to the note\'s _attachments folder (requires data + attachmentFilename)' },
2371
+ { action: 'list', description: 'List all attachments for a note' },
2372
+ { action: 'get', description: 'Get attachment metadata. Set includeData=true for base64 content' },
2373
+ { action: 'move', description: 'Move an attachment between notes' },
2374
+ ],
2375
+ };
2069
2376
  // Register tool call handler
2070
2377
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
2071
2378
  const { name, arguments: args } = request.params;
@@ -2074,6 +2381,12 @@ export function createServer() {
2074
2381
  const startTime = Date.now();
2075
2382
  try {
2076
2383
  let result;
2384
+ // ── Intercept list_actions for any tool with an action registry ──
2385
+ if (args?.action === 'list_actions' && TOOL_ACTIONS[normalizedName]) {
2386
+ result = { success: true, tool: normalizedName, actions: TOOL_ACTIONS[normalizedName] };
2387
+ const resultWithDuration = withDuration(result, Date.now() - startTime, includeTiming);
2388
+ return { content: [{ type: 'text', text: JSON.stringify(resultWithDuration, null, 2) }] };
2389
+ }
2077
2390
  switch (normalizedName) {
2078
2391
  // ── Primary consolidated tools ──
2079
2392
  case 'noteplan_get_notes':
@@ -2121,7 +2434,7 @@ export function createServer() {
2121
2434
  case 'remove_property':
2122
2435
  result = noteTools.removeProperty(args);
2123
2436
  break;
2124
- default: throw new Error(`Unknown action: ${action}`);
2437
+ default: throw new Error(`Unknown action: "${action}". Valid actions: create, update, delete, move, restore, rename, set_property, remove_property`);
2125
2438
  }
2126
2439
  break;
2127
2440
  }
@@ -2152,7 +2465,7 @@ export function createServer() {
2152
2465
  case 'replace_lines':
2153
2466
  result = noteTools.replaceLines(a);
2154
2467
  break;
2155
- default: throw new Error(`Unknown action: ${action}`);
2468
+ default: throw new Error(`Unknown action: "${action}". Valid actions: insert, append, delete_lines, edit_line, replace_lines (snake_case required)`);
2156
2469
  }
2157
2470
  break;
2158
2471
  }
@@ -2166,6 +2479,10 @@ export function createServer() {
2166
2479
  if (a.scheduleDate && a.content) {
2167
2480
  a.content = appendScheduleDate(a.content, a.scheduleDate);
2168
2481
  }
2482
+ // Derive `target` for addTaskToNote from date/filename when not provided
2483
+ if (!a.target && (a.date || a.filename)) {
2484
+ a.target = a.date || a.filename;
2485
+ }
2169
2486
  const action = a?.action;
2170
2487
  switch (action) {
2171
2488
  case 'get':
@@ -2175,7 +2492,7 @@ export function createServer() {
2175
2492
  result = noteTools.searchParagraphs(a);
2176
2493
  break;
2177
2494
  case 'search_global':
2178
- result = taskTools.searchTasksGlobal(a);
2495
+ result = noteTools.searchParagraphsGlobal(a);
2179
2496
  break;
2180
2497
  case 'add':
2181
2498
  result = taskTools.addTaskToNote(a);
@@ -2346,7 +2663,67 @@ export function createServer() {
2346
2663
  case 'list_plugin_windows':
2347
2664
  result = uiTools.listPluginWindows(args);
2348
2665
  break;
2349
- default: throw new Error(`Unknown action: ${action}`);
2666
+ case 'backup': {
2667
+ if (versionInfo.build < MIN_BUILD_CREATE_BACKUP) {
2668
+ result = { success: false, error: `Backup requires NotePlan build ${MIN_BUILD_CREATE_BACKUP}+. Current: ${versionInfo.build}.`, code: 'ERR_VERSION_GATE' };
2669
+ }
2670
+ else {
2671
+ result = uiTools.createBackup(args);
2672
+ }
2673
+ break;
2674
+ }
2675
+ default: {
2676
+ // Help the LLM recover from common mistakes (camelCase, missing underscore, etc.)
2677
+ const UI_ACTION_ALIASES = {
2678
+ open: 'open_note', opennote: 'open_note', open_notes: 'open_note',
2679
+ opentoday: 'open_today', today: 'open_today',
2680
+ openview: 'open_view', view: 'open_view',
2681
+ runplugin: 'run_plugin', plugin: 'run_plugin',
2682
+ togglesidebar: 'toggle_sidebar', sidebar: 'toggle_sidebar',
2683
+ closepluginwindow: 'close_plugin_window',
2684
+ listpluginwindows: 'list_plugin_windows',
2685
+ navigate: 'open_note', show: 'open_note', select: 'open_note',
2686
+ };
2687
+ const normalized = (action || '').toLowerCase().replace(/[\s_-]/g, '');
2688
+ const resolved = UI_ACTION_ALIASES[normalized];
2689
+ if (resolved) {
2690
+ args.action = resolved;
2691
+ // Re-dispatch with corrected action
2692
+ switch (resolved) {
2693
+ case 'open_note':
2694
+ result = uiTools.openNote(args);
2695
+ break;
2696
+ case 'open_today':
2697
+ result = uiTools.openToday(args);
2698
+ break;
2699
+ case 'search':
2700
+ result = uiTools.searchNotes(args);
2701
+ break;
2702
+ case 'run_plugin':
2703
+ result = uiTools.runPlugin(args);
2704
+ break;
2705
+ case 'open_view':
2706
+ result = uiTools.openView(args);
2707
+ break;
2708
+ case 'toggle_sidebar':
2709
+ result = uiTools.toggleSidebar(args);
2710
+ break;
2711
+ case 'close_plugin_window':
2712
+ result = uiTools.closePluginWindow(args);
2713
+ break;
2714
+ case 'list_plugin_windows':
2715
+ result = uiTools.listPluginWindows(args);
2716
+ break;
2717
+ case 'backup':
2718
+ result = uiTools.createBackup(args);
2719
+ break;
2720
+ default: throw new Error(`Unknown action: ${action}. Valid actions: open_note, open_today, search, run_plugin, open_view, toggle_sidebar, close_plugin_window, list_plugin_windows, backup`);
2721
+ }
2722
+ }
2723
+ else {
2724
+ throw new Error(`Unknown action: "${action}". Valid actions: open_note, open_today, search, run_plugin, open_view, toggle_sidebar, close_plugin_window, list_plugin_windows, backup`);
2725
+ }
2726
+ }
2350
2727
  }
2351
2728
  break;
2352
2729
  }
@@ -2422,6 +2799,44 @@ export function createServer() {
2422
2799
  }
2423
2800
  break;
2424
2801
  }
2802
+ case 'noteplan_templates': {
2803
+ const action = args?.action;
2804
+ switch (action) {
2805
+ case 'list':
2806
+ result = templateTools.listTemplates(args);
2807
+ break;
2808
+ case 'render':
2809
+ result = templateTools.renderTemplate(args);
2810
+ break;
2811
+ case 'search_docs':
2812
+ result = await templateTools.searchDocs(args);
2813
+ break;
2814
+ case 'get_doc':
2815
+ result = templateTools.getDoc(args);
2816
+ break;
2817
+ default: throw new Error(`Unknown action: ${action}`);
2818
+ }
2819
+ break;
2820
+ }
2821
+ case 'noteplan_attachments': {
2822
+ const action = args?.action;
2823
+ switch (action) {
2824
+ case 'add':
2825
+ result = attachmentTools.addAttachment(args);
2826
+ break;
2827
+ case 'list':
2828
+ result = attachmentTools.listAttachments(args);
2829
+ break;
2830
+ case 'get':
2831
+ result = attachmentTools.getAttachment(args);
2832
+ break;
2833
+ case 'move':
2834
+ result = attachmentTools.moveAttachment(args);
2835
+ break;
2836
+ default: throw new Error(`Unknown action: ${action}. Valid actions: add, list, get, move`);
2837
+ }
2838
+ break;
2839
+ }
2425
2840
  default:
2426
2841
  throw new Error(`Unknown tool: ${name} (normalized: ${normalizedName})`);
2427
2842
  }
@@ -2462,7 +2877,12 @@ export function createServer() {
2462
2877
  };
2463
2878
  }
2464
2879
  catch (error) {
2465
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
2880
+ let errorMessage = error instanceof Error ? error.message : 'Unknown error';
2881
+ // Append list_actions hint for unknown action errors
2882
+ if (errorMessage.includes('Unknown action') && TOOL_ACTIONS[normalizedName]) {
2883
+ const validActions = TOOL_ACTIONS[normalizedName].map(a => a.action).join(', ');
2884
+ errorMessage += `. Valid actions: ${validActions}. Tip: use action "list_actions" to discover all actions with descriptions.`;
2885
+ }
2466
2886
  const meta = inferToolErrorMeta(normalizedName, errorMessage, registeredToolNames);
2467
2887
  const errorResult = {
2468
2888
  success: false,