@noteplanco/noteplan-mcp 1.1.7 → 1.1.9

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 (79) 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 +13 -1
  11. package/dist/noteplan/file-writer.js.map +1 -1
  12. package/dist/noteplan/file-writer.test.js +4 -0
  13. package/dist/noteplan/file-writer.test.js.map +1 -1
  14. package/dist/noteplan/frontmatter-parser.d.ts +4 -4
  15. package/dist/noteplan/frontmatter-parser.d.ts.map +1 -1
  16. package/dist/noteplan/frontmatter-parser.js +11 -14
  17. package/dist/noteplan/frontmatter-parser.js.map +1 -1
  18. package/dist/noteplan/frontmatter-parser.test.js +92 -0
  19. package/dist/noteplan/frontmatter-parser.test.js.map +1 -1
  20. package/dist/noteplan/markdown-parser.d.ts.map +1 -1
  21. package/dist/noteplan/markdown-parser.js +4 -2
  22. package/dist/noteplan/markdown-parser.js.map +1 -1
  23. package/dist/noteplan/markdown-parser.test.js +129 -0
  24. package/dist/noteplan/markdown-parser.test.js.map +1 -1
  25. package/dist/noteplan/sqlite-loader.d.ts +11 -2
  26. package/dist/noteplan/sqlite-loader.d.ts.map +1 -1
  27. package/dist/noteplan/sqlite-loader.js +143 -15
  28. package/dist/noteplan/sqlite-loader.js.map +1 -1
  29. package/dist/noteplan/sqlite-writer.d.ts.map +1 -1
  30. package/dist/noteplan/sqlite-writer.js +3 -0
  31. package/dist/noteplan/sqlite-writer.js.map +1 -1
  32. package/dist/noteplan/template-docs.d.ts +35 -0
  33. package/dist/noteplan/template-docs.d.ts.map +1 -0
  34. package/dist/noteplan/template-docs.js +184 -0
  35. package/dist/noteplan/template-docs.js.map +1 -0
  36. package/dist/noteplan/unified-store.d.ts +2 -0
  37. package/dist/noteplan/unified-store.d.ts.map +1 -1
  38. package/dist/noteplan/unified-store.js +30 -7
  39. package/dist/noteplan/unified-store.js.map +1 -1
  40. package/dist/server.d.ts.map +1 -1
  41. package/dist/server.js +307 -65
  42. package/dist/server.js.map +1 -1
  43. package/dist/tools/calendar.d.ts +2 -2
  44. package/dist/tools/events.d.ts +16 -16
  45. package/dist/tools/events.d.ts.map +1 -1
  46. package/dist/tools/events.js +17 -2
  47. package/dist/tools/events.js.map +1 -1
  48. package/dist/tools/notes.d.ts +327 -45
  49. package/dist/tools/notes.d.ts.map +1 -1
  50. package/dist/tools/notes.js +391 -51
  51. package/dist/tools/notes.js.map +1 -1
  52. package/dist/tools/notes.test.js +959 -1
  53. package/dist/tools/notes.test.js.map +1 -1
  54. package/dist/tools/plugins.d.ts.map +1 -1
  55. package/dist/tools/plugins.js +1 -0
  56. package/dist/tools/plugins.js.map +1 -1
  57. package/dist/tools/search.d.ts +2 -2
  58. package/dist/tools/search.d.ts.map +1 -1
  59. package/dist/tools/search.js +32 -4
  60. package/dist/tools/search.js.map +1 -1
  61. package/dist/tools/tasks.d.ts +86 -10
  62. package/dist/tools/tasks.d.ts.map +1 -1
  63. package/dist/tools/tasks.js +84 -25
  64. package/dist/tools/tasks.js.map +1 -1
  65. package/dist/tools/templates.d.ts +64 -3
  66. package/dist/tools/templates.d.ts.map +1 -1
  67. package/dist/tools/templates.js +73 -1
  68. package/dist/tools/templates.js.map +1 -1
  69. package/dist/tools/ui-automation.d.ts +74 -0
  70. package/dist/tools/ui-automation.d.ts.map +1 -0
  71. package/dist/tools/ui-automation.js +209 -0
  72. package/dist/tools/ui-automation.js.map +1 -0
  73. package/dist/utils/version.d.ts +1 -0
  74. package/dist/utils/version.d.ts.map +1 -1
  75. package/dist/utils/version.js +89 -27
  76. package/dist/utils/version.js.map +1 -1
  77. package/docs/templates.db.gz +0 -0
  78. package/docs/x-callback-url.md +318 -0
  79. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -24,9 +24,14 @@ import * as attachmentTools from './tools/attachments.js';
24
24
  import { parseFlexibleDate } from './utils/date-utils.js';
25
25
  import { upgradeMessage, getNotePlanVersion, getMcpServerVersion, MIN_BUILD_ADVANCED_FEATURES, MIN_BUILD_CREATE_BACKUP } from './utils/version.js';
26
26
  import { initSqlite } from './noteplan/sqlite-loader.js';
27
+ import { getDatabase, getDatabasePath, listSpaces as listSpacesFromDb } from './noteplan/sqlite-reader.js';
27
28
  const __filename = fileURLToPath(import.meta.url);
28
29
  const __dirname = path.dirname(__filename);
29
30
  const PLUGIN_API_DOCS_DIR = path.join(__dirname, '../docs/plugin-api');
31
+ const DOCS_DIR = path.join(__dirname, '../docs');
32
+ const GENERAL_DOC_RESOURCES = [
33
+ { 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' },
34
+ ];
30
35
  const PLUGIN_API_RESOURCES = [
31
36
  { 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.' },
32
37
  { file: 'getting-started.md', name: 'Getting Started Guide', desc: 'How to create NotePlan plugins — structure, setup, first plugin, testing, HTML views' },
@@ -818,12 +823,59 @@ function withDuration(result, durationMs, includeTiming) {
818
823
  export function createServer() {
819
824
  const server = new Server({
820
825
  name: 'NotePlan',
821
- version: '1.1.4',
826
+ version: '1.1.7',
822
827
  }, {
823
828
  capabilities: {
824
829
  tools: {},
825
830
  resources: {},
826
831
  },
832
+ instructions: [
833
+ 'You have access to NotePlan — a markdown-based note-taking and task management app for macOS/iOS.',
834
+ 'All tools use action-based dispatch: one tool per domain, with an `action` parameter to select the operation.',
835
+ '',
836
+ '## Workflow',
837
+ '',
838
+ 'Prefer granular edits over full-note rewrites to avoid large context payloads and accidental data loss.',
839
+ '',
840
+ '1. **Find** the note: `noteplan_get_notes` (by id/title/filename/date) or `noteplan_search`',
841
+ '2. **Inspect** content: `noteplan_paragraphs(action: get)` for line metadata, `noteplan_paragraphs(action: search)` for text lookup',
842
+ '3. **Edit** with targeted mutations:',
843
+ ' - `noteplan_edit_content(action: edit_line)` — single-line change',
844
+ ' - `noteplan_edit_content(action: insert/append)` — add content at a position or heading',
845
+ ' - `noteplan_edit_content(action: delete_lines)` — remove lines',
846
+ '4. Only use `noteplan_manage_note(action: update)` for intentional full-note rewrites',
847
+ '',
848
+ '## Tasks',
849
+ '',
850
+ '- 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.',
851
+ '- Find tasks: `noteplan_paragraphs(action: search)` in one note, or `search_global` across all notes',
852
+ '- Complete/update: `noteplan_paragraphs(action: complete/update)`',
853
+ '- Use `heading` parameter to target a specific section (e.g., heading: "Tasks")',
854
+ '',
855
+ '## Destructive Operations',
856
+ '',
857
+ 'Delete, move, rename, and restore use a 2-step safety flow:',
858
+ '1. Call with `dryRun=true` → get a preview and `confirmationToken`',
859
+ '2. Call again with that `confirmationToken` to execute',
860
+ '',
861
+ '## Key Behaviors',
862
+ '',
863
+ '- Calendar notes (daily/weekly/etc.) are auto-created when targeted by date — no need to create them first',
864
+ '- Notes can be targeted by `id`, `filename`, `title`, `date`, or `query` — prefer `id` or `filename` when available for precision',
865
+ '- Errors include a `hint` and often a `suggestedTool` to guide recovery',
866
+ '- The `noteplan_memory` tool stores user preferences (formatting, style, workflow) persistently across sessions',
867
+ '',
868
+ '## Templates',
869
+ '',
870
+ '- IMPORTANT: Before writing or editing templates, ALWAYS search the built-in documentation first: `noteplan_templates(action: "search_docs", query: "your topic")`',
871
+ '- Use `get_doc` to read the full text of a doc chunk returned by `search_docs`',
872
+ '- Then use `render` to test/debug your template code',
873
+ '',
874
+ '## Action Discovery',
875
+ '',
876
+ '- Every tool supports `action: "list_actions"` — call it to get a list of all available actions with descriptions',
877
+ '- Use `list_actions` when you are unsure what a tool can do or need to discover capabilities',
878
+ ].join('\n'),
827
879
  });
828
880
  let embeddingsToolsEnabled = false;
829
881
  try {
@@ -842,6 +894,19 @@ export function createServer() {
842
894
  console.error('[noteplan-mcp] Failed to detect NotePlan version:', err);
843
895
  }
844
896
  console.error(`[noteplan-mcp] Detected NotePlan ${versionInfo.version} (build ${versionInfo.build}, source: ${versionInfo.source}). Advanced features: ${advancedFeaturesEnabled ? 'enabled' : 'disabled'}.`);
897
+ // Startup diagnostics: HOME, database, spaces
898
+ console.error(`[noteplan-mcp] HOME: ${process.env.HOME ?? '(unset)'}`);
899
+ const dbPath = getDatabasePath();
900
+ if (dbPath) {
901
+ const database = getDatabase();
902
+ const writable = database ? 'read-write' : 'unavailable';
903
+ console.error(`[noteplan-mcp] Space database: ${dbPath} (${writable})`);
904
+ const spaces = listSpacesFromDb();
905
+ console.error(`[noteplan-mcp] Spaces discovered: ${spaces.length}`);
906
+ }
907
+ else {
908
+ console.error('[noteplan-mcp] Space database: not found');
909
+ }
845
910
  const toolDefinitions = [
846
911
  // ── Consolidated tools (16 — action/param-based dispatch) ──
847
912
  {
@@ -997,18 +1062,18 @@ export function createServer() {
997
1062
  },
998
1063
  {
999
1064
  name: 'noteplan_manage_note',
1000
- 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)',
1065
+ 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 note ref via id/filename/title/date/query, 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 note ref via id/filename/title/date/query + key)',
1001
1066
  inputSchema: {
1002
1067
  type: 'object',
1003
1068
  properties: {
1004
1069
  action: {
1005
1070
  type: 'string',
1006
- enum: ['create', 'update', 'delete', 'move', 'restore', 'rename', 'set_property', 'remove_property'],
1007
- description: 'Action: create | update | delete | move | restore | rename | set_property | remove_property',
1071
+ enum: ['create', 'update', 'delete', 'move', 'restore', 'rename', 'set_property', 'remove_property', 'list_actions'],
1072
+ description: 'Action: create | update | delete | move | restore | rename | set_property | remove_property | list_actions (discover all actions)',
1008
1073
  },
1009
1074
  id: {
1010
1075
  type: 'string',
1011
- description: 'Note ID — used by delete, move, rename, restore (preferred for TeamSpace notes)',
1076
+ description: 'Note ID — used by delete, move, rename, restore, set_property, remove_property (preferred for TeamSpace notes)',
1012
1077
  },
1013
1078
  filename: {
1014
1079
  type: 'string',
@@ -1016,11 +1081,15 @@ export function createServer() {
1016
1081
  },
1017
1082
  title: {
1018
1083
  type: 'string',
1019
- description: 'Note title — required for create. Also used by rename to find the note by title (fuzzy matched).',
1084
+ description: 'Note title — required for create. Also used by rename, move, set_property, remove_property to find the note by title (fuzzy matched).',
1085
+ },
1086
+ date: {
1087
+ type: 'string',
1088
+ description: 'Calendar note date (YYYYMMDD, YYYY-MM-DD, today, tomorrow, yesterday) — used by set_property, remove_property, move',
1020
1089
  },
1021
1090
  query: {
1022
1091
  type: 'string',
1023
- description: 'Fuzzy search query to find the note — used by rename',
1092
+ description: 'Fuzzy search query to find the note — used by rename, move, set_property, remove_property',
1024
1093
  },
1025
1094
  content: {
1026
1095
  type: 'string',
@@ -1100,8 +1169,8 @@ export function createServer() {
1100
1169
  properties: {
1101
1170
  action: {
1102
1171
  type: 'string',
1103
- enum: ['insert', 'append', 'delete_lines', 'edit_line', 'replace_lines'],
1104
- description: 'Action: insert | append | delete_lines | edit_line | replace_lines',
1172
+ enum: ['insert', 'append', 'delete_lines', 'edit_line', 'replace_lines', 'list_actions'],
1173
+ description: 'Action: insert | append | delete_lines | edit_line | replace_lines | list_actions (discover all actions)',
1105
1174
  },
1106
1175
  id: {
1107
1176
  type: 'string',
@@ -1201,18 +1270,18 @@ export function createServer() {
1201
1270
  },
1202
1271
  {
1203
1272
  name: 'noteplan_paragraphs',
1204
- 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)',
1273
+ 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 note ref via id/filename/title/date + lineIndex/line or taskQuery to find by text)\n- update: Update task content/status (requires note ref via id/filename/title/date + lineIndex or line)',
1205
1274
  inputSchema: {
1206
1275
  type: 'object',
1207
1276
  properties: {
1208
1277
  action: {
1209
1278
  type: 'string',
1210
- enum: ['get', 'search', 'search_global', 'add', 'complete', 'update'],
1211
- description: 'Action: get | search | search_global | add | complete | update',
1279
+ enum: ['get', 'search', 'search_global', 'add', 'complete', 'update', 'list_actions'],
1280
+ description: 'Action: get | search | search_global | add | complete | update | list_actions (discover all actions)',
1212
1281
  },
1213
1282
  id: {
1214
1283
  type: 'string',
1215
- description: 'Note ID — used by search',
1284
+ description: 'Note ID — used by get, search, complete, update',
1216
1285
  },
1217
1286
  filename: {
1218
1287
  type: 'string',
@@ -1220,11 +1289,11 @@ export function createServer() {
1220
1289
  },
1221
1290
  title: {
1222
1291
  type: 'string',
1223
- description: 'Note title — used by search',
1292
+ description: 'Note title — used by get, search, complete, update',
1224
1293
  },
1225
1294
  date: {
1226
1295
  type: 'string',
1227
- description: 'Calendar date — used by search',
1296
+ description: 'Calendar date (e.g. today, YYYY-MM-DD) — used by get, search, complete, update',
1228
1297
  },
1229
1298
  space: {
1230
1299
  type: 'string',
@@ -1289,6 +1358,10 @@ export function createServer() {
1289
1358
  type: 'number',
1290
1359
  description: 'Task line number (1-based) — used by complete, update',
1291
1360
  },
1361
+ taskQuery: {
1362
+ type: 'string',
1363
+ description: 'Find task by content text instead of line number (completes first matching open task) — used by complete',
1364
+ },
1292
1365
  priority: {
1293
1366
  type: 'number',
1294
1367
  description: 'Priority 1-3 — used by add',
@@ -1354,8 +1427,8 @@ export function createServer() {
1354
1427
  properties: {
1355
1428
  action: {
1356
1429
  type: 'string',
1357
- enum: ['list', 'find', 'resolve', 'create', 'move', 'rename', 'delete', 'list_spaces'],
1358
- description: 'Action: list | find | resolve | create | move | rename | delete | list_spaces',
1430
+ enum: ['list', 'find', 'resolve', 'create', 'move', 'rename', 'delete', 'list_spaces', 'list_actions'],
1431
+ description: 'Action: list | find | resolve | create | move | rename | delete | list_spaces | list_actions (discover all actions)',
1359
1432
  },
1360
1433
  path: {
1361
1434
  type: 'string',
@@ -1457,8 +1530,8 @@ export function createServer() {
1457
1530
  properties: {
1458
1531
  action: {
1459
1532
  type: 'string',
1460
- enum: ['list', 'get', 'get_tasks', 'list_parameters', 'save', 'rename'],
1461
- description: 'Action: list | get | get_tasks | list_parameters | save | rename',
1533
+ enum: ['list', 'get', 'get_tasks', 'list_parameters', 'save', 'rename', 'list_actions'],
1534
+ description: 'Action: list | get | get_tasks | list_parameters | save | rename | list_actions (discover all actions)',
1462
1535
  },
1463
1536
  name: {
1464
1537
  type: 'string',
@@ -1542,8 +1615,8 @@ export function createServer() {
1542
1615
  },
1543
1616
  action: {
1544
1617
  type: 'string',
1545
- enum: ['get_events', 'list_calendars', 'create_event', 'update_event', 'delete_event', 'get', 'list_lists', 'create', 'complete', 'update', 'delete'],
1546
- description: 'Action: get_events | list_calendars | create_event | update_event | delete_event | get | list_lists | create | complete | update | delete',
1618
+ enum: ['get_events', 'list_calendars', 'create_event', 'update_event', 'delete_event', 'get', 'list_lists', 'create', 'complete', 'update', 'delete', 'list_actions'],
1619
+ description: 'Action: get_events | list_calendars | create_event | update_event | delete_event | get | list_lists | create | complete | update | delete | list_actions (discover all actions)',
1547
1620
  },
1548
1621
  // Calendar params
1549
1622
  eventId: {
@@ -1644,8 +1717,8 @@ export function createServer() {
1644
1717
  properties: {
1645
1718
  action: {
1646
1719
  type: 'string',
1647
- enum: ['list', 'save', 'update', 'delete'],
1648
- description: 'Action: list | save | update | delete',
1720
+ enum: ['list', 'save', 'update', 'delete', 'list_actions'],
1721
+ description: 'Action: list | save | update | delete | list_actions (discover all actions)',
1649
1722
  },
1650
1723
  id: {
1651
1724
  type: 'string',
@@ -1691,8 +1764,8 @@ export function createServer() {
1691
1764
  properties: {
1692
1765
  action: {
1693
1766
  type: 'string',
1694
- enum: ['search', 'list_tags'],
1695
- description: 'Action to perform (default: search)',
1767
+ enum: ['search', 'list_tags', 'list_actions'],
1768
+ description: 'Action to perform (default: search) | list_actions (discover all actions)',
1696
1769
  },
1697
1770
  query: {
1698
1771
  type: 'string',
@@ -1780,8 +1853,8 @@ export function createServer() {
1780
1853
  properties: {
1781
1854
  action: {
1782
1855
  type: 'string',
1783
- enum: ['status', 'search', 'sync', 'reset'],
1784
- description: 'Action: status | search | sync | reset',
1856
+ enum: ['status', 'search', 'sync', 'reset', 'list_actions'],
1857
+ description: 'Action: status | search | sync | reset | list_actions (discover all actions)',
1785
1858
  },
1786
1859
  query: {
1787
1860
  type: 'string',
@@ -1861,14 +1934,14 @@ export function createServer() {
1861
1934
  // noteplan_ui is always available — basic AppleScript commands work on all NotePlan versions
1862
1935
  toolDefinitions.push({
1863
1936
  name: 'noteplan_ui',
1864
- 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.',
1937
+ 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',
1865
1938
  inputSchema: {
1866
1939
  type: 'object',
1867
1940
  properties: {
1868
1941
  action: {
1869
1942
  type: 'string',
1870
- enum: ['open_note', 'open_today', 'search', 'run_plugin', 'open_view', 'toggle_sidebar', 'close_plugin_window', 'list_plugin_windows', 'backup'],
1871
- description: 'Action: open_note | open_today | search | run_plugin | open_view | toggle_sidebar | close_plugin_window | list_plugin_windows | backup',
1943
+ enum: ['open_note', 'open_today', 'search', 'run_plugin', 'open_view', 'toggle_sidebar', 'close_plugin_window', 'list_plugin_windows', 'backup', 'list_actions'],
1944
+ 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)',
1872
1945
  },
1873
1946
  title: {
1874
1947
  type: 'string',
@@ -1924,8 +1997,8 @@ export function createServer() {
1924
1997
  properties: {
1925
1998
  action: {
1926
1999
  type: 'string',
1927
- enum: ['list', 'list_available', 'create', 'delete', 'install', 'log', 'source', 'update_html', 'screenshot'],
1928
- description: 'Action: list | list_available | create | delete | install | log | source | update_html | screenshot',
2000
+ enum: ['list', 'list_available', 'create', 'delete', 'install', 'log', 'source', 'update_html', 'screenshot', 'list_actions'],
2001
+ description: 'Action: list | list_available | create | delete | install | log | source | update_html | screenshot | list_actions (discover all actions)',
1929
2002
  },
1930
2003
  pluginId: {
1931
2004
  type: 'string',
@@ -2015,8 +2088,8 @@ export function createServer() {
2015
2088
  properties: {
2016
2089
  action: {
2017
2090
  type: 'string',
2018
- enum: ['list', 'get', 'save', 'set_active'],
2019
- description: 'Action: list | get | save | set_active',
2091
+ enum: ['list', 'get', 'save', 'set_active', 'list_actions'],
2092
+ description: 'Action: list | get | save | set_active | list_actions (discover all actions)',
2020
2093
  },
2021
2094
  filename: {
2022
2095
  type: 'string',
@@ -2051,14 +2124,14 @@ export function createServer() {
2051
2124
  },
2052
2125
  }, {
2053
2126
  name: 'noteplan_templates',
2054
- description: 'Template operations: list available templates or render a template.\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\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").',
2127
+ 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").',
2055
2128
  inputSchema: {
2056
2129
  type: 'object',
2057
2130
  properties: {
2058
2131
  action: {
2059
2132
  type: 'string',
2060
- enum: ['list', 'render'],
2061
- description: 'Action: list | render',
2133
+ enum: ['list', 'render', 'search_docs', 'get_doc', 'list_actions'],
2134
+ description: 'Action: list | render | search_docs | get_doc | list_actions (discover all actions)',
2062
2135
  },
2063
2136
  templateTitle: {
2064
2137
  type: 'string',
@@ -2084,6 +2157,22 @@ export function createServer() {
2084
2157
  type: 'string',
2085
2158
  description: 'Cursor from previous page — used by list',
2086
2159
  },
2160
+ query: {
2161
+ type: 'string',
2162
+ description: 'Search query — used by search_docs',
2163
+ },
2164
+ includeContent: {
2165
+ type: 'boolean',
2166
+ description: 'Include full chunk text in results — used by search_docs',
2167
+ },
2168
+ noteTitle: {
2169
+ type: 'string',
2170
+ description: 'Doc note title — used by get_doc (from search_docs results)',
2171
+ },
2172
+ chunkIndex: {
2173
+ type: 'number',
2174
+ description: 'Chunk index — used by get_doc (from search_docs results, default 0)',
2175
+ },
2087
2176
  },
2088
2177
  required: ['action'],
2089
2178
  },
@@ -2095,8 +2184,8 @@ export function createServer() {
2095
2184
  properties: {
2096
2185
  action: {
2097
2186
  type: 'string',
2098
- enum: ['add', 'list', 'get', 'move'],
2099
- description: 'Action: add | list | get | move',
2187
+ enum: ['add', 'list', 'get', 'move', 'list_actions'],
2188
+ description: 'Action: add | list | get | move | list_actions (discover all actions)',
2100
2189
  },
2101
2190
  id: { type: 'string', description: 'Source note ID — used by all actions' },
2102
2191
  filename: { type: 'string', description: 'Source note filename/path — used by all actions' },
@@ -2134,42 +2223,178 @@ export function createServer() {
2134
2223
  });
2135
2224
  // Register resource listing handler
2136
2225
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
2137
- return {
2138
- resources: PLUGIN_API_RESOURCES.map((r) => ({
2139
- uri: `noteplan://plugin-api/${r.file}`,
2140
- name: r.name,
2141
- description: r.desc,
2142
- mimeType: r.file.endsWith('.md') ? 'text/markdown' : r.file.endsWith('.json') ? 'application/json' : 'text/javascript',
2143
- })),
2144
- };
2226
+ const pluginResources = PLUGIN_API_RESOURCES.map((r) => ({
2227
+ uri: `noteplan://plugin-api/${r.file}`,
2228
+ name: r.name,
2229
+ description: r.desc,
2230
+ mimeType: r.file.endsWith('.md') ? 'text/markdown' : r.file.endsWith('.json') ? 'application/json' : 'text/javascript',
2231
+ }));
2232
+ const generalResources = GENERAL_DOC_RESOURCES.map((r) => ({
2233
+ uri: `noteplan://docs/${r.file}`,
2234
+ name: r.name,
2235
+ description: r.desc,
2236
+ mimeType: 'text/markdown',
2237
+ }));
2238
+ return { resources: [...pluginResources, ...generalResources] };
2145
2239
  });
2146
2240
  // Register resource read handler
2147
2241
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2148
2242
  const uri = request.params.uri;
2149
- const prefix = 'noteplan://plugin-api/';
2150
- if (!uri.startsWith(prefix)) {
2151
- throw new Error(`Unknown resource URI: ${uri}`);
2243
+ const pluginPrefix = 'noteplan://plugin-api/';
2244
+ const docsPrefix = 'noteplan://docs/';
2245
+ let filePath;
2246
+ if (uri.startsWith(pluginPrefix)) {
2247
+ const filename = uri.slice(pluginPrefix.length);
2248
+ const entry = PLUGIN_API_RESOURCES.find((r) => r.file === filename);
2249
+ if (!entry) {
2250
+ throw new Error(`Unknown resource: ${filename}. Available: ${PLUGIN_API_RESOURCES.map((r) => r.file).join(', ')}`);
2251
+ }
2252
+ filePath = path.join(PLUGIN_API_DOCS_DIR, entry.file);
2152
2253
  }
2153
- const filename = uri.slice(prefix.length);
2154
- const entry = PLUGIN_API_RESOURCES.find((r) => r.file === filename);
2155
- if (!entry) {
2156
- throw new Error(`Unknown resource: ${filename}. Available: ${PLUGIN_API_RESOURCES.map((r) => r.file).join(', ')}`);
2254
+ else if (uri.startsWith(docsPrefix)) {
2255
+ const filename = uri.slice(docsPrefix.length);
2256
+ const entry = GENERAL_DOC_RESOURCES.find((r) => r.file === filename);
2257
+ if (!entry) {
2258
+ throw new Error(`Unknown resource: ${filename}. Available: ${GENERAL_DOC_RESOURCES.map((r) => r.file).join(', ')}`);
2259
+ }
2260
+ filePath = path.join(DOCS_DIR, entry.file);
2261
+ }
2262
+ else {
2263
+ throw new Error(`Unknown resource URI: ${uri}`);
2157
2264
  }
2158
- const filePath = path.join(PLUGIN_API_DOCS_DIR, entry.file);
2159
2265
  if (!fs.existsSync(filePath)) {
2160
2266
  throw new Error(`Resource file not found on disk: ${filePath}`);
2161
2267
  }
2162
2268
  const content = fs.readFileSync(filePath, 'utf-8');
2269
+ const mimeType = filePath.endsWith('.md') ? 'text/markdown' : filePath.endsWith('.json') ? 'application/json' : 'text/javascript';
2163
2270
  return {
2164
- contents: [
2165
- {
2166
- uri,
2167
- mimeType: entry.file.endsWith('.md') ? 'text/markdown' : entry.file.endsWith('.json') ? 'application/json' : 'text/javascript',
2168
- text: content,
2169
- },
2170
- ],
2271
+ contents: [{ uri, mimeType, text: content }],
2171
2272
  };
2172
2273
  });
2274
+ // ── Action registry for list_actions discovery ──
2275
+ const TOOL_ACTIONS = {
2276
+ noteplan_get_notes: [
2277
+ { action: 'get', description: 'Get a single note by id, filename, title, date, or query' },
2278
+ { action: 'list', description: 'List notes with optional folder/type/date filtering' },
2279
+ { action: 'resolve', description: 'Resolve a reference (title, filename, query) to a single note' },
2280
+ { action: 'today', description: 'Get today\'s daily note' },
2281
+ { action: 'calendar', description: 'Get a calendar note by date' },
2282
+ { action: 'periodic', description: 'Get a periodic note (week, month, quarter, year)' },
2283
+ { action: 'range', description: 'Get calendar notes in a date range' },
2284
+ ],
2285
+ noteplan_search: [
2286
+ { action: 'search', description: 'Full-text or metadata search across notes' },
2287
+ { action: 'list_tags', description: 'List all tags/hashtags with optional filtering' },
2288
+ ],
2289
+ noteplan_manage_note: [
2290
+ { action: 'create', description: 'Create a project note (requires title)' },
2291
+ { action: 'update', description: 'Replace note content (requires filename, content, fullReplace + confirmationToken)' },
2292
+ { action: 'delete', description: 'Delete a note (requires dryRun/confirmationToken)' },
2293
+ { action: 'move', description: 'Move a note to another folder (requires dryRun/confirmationToken)' },
2294
+ { action: 'restore', description: 'Restore a trashed note (requires dryRun/confirmationToken)' },
2295
+ { action: 'rename', description: 'Rename a note (requires newTitle)' },
2296
+ { action: 'set_property', description: 'Set a frontmatter property (requires key + value)' },
2297
+ { action: 'remove_property', description: 'Remove a frontmatter property (requires key)' },
2298
+ ],
2299
+ noteplan_edit_content: [
2300
+ { action: 'insert', description: 'Insert content at a position. Use heading param to scope to a section' },
2301
+ { action: 'append', description: 'Append content at end of note or section' },
2302
+ { action: 'delete_lines', description: 'Delete a line range (requires startLine + endLine, 1-indexed)' },
2303
+ { action: 'edit_line', description: 'Edit a single line (requires line + content)' },
2304
+ { action: 'replace_lines', description: 'Replace a line range (requires startLine + endLine + content)' },
2305
+ ],
2306
+ noteplan_paragraphs: [
2307
+ { action: 'get', description: 'Get note lines with metadata (requires filename)' },
2308
+ { action: 'search', description: 'Search for matching lines in a note (requires query + note ref)' },
2309
+ { action: 'search_global', description: 'Search tasks across all notes (requires query, supports "*" wildcard)' },
2310
+ { action: 'add', description: 'Add a task (requires target + content)' },
2311
+ { action: 'complete', description: 'Mark task done (requires filename + lineIndex or line)' },
2312
+ { action: 'update', description: 'Update task content/status (requires filename + lineIndex or line)' },
2313
+ ],
2314
+ noteplan_folders: [
2315
+ { action: 'list', description: 'List folders with optional filtering' },
2316
+ { action: 'find', description: 'Find folder matches for exploration' },
2317
+ { action: 'resolve', description: 'Resolve to one canonical folder path' },
2318
+ { action: 'create', description: 'Create a folder' },
2319
+ { action: 'move', description: 'Move a folder (requires dryRun/confirmationToken)' },
2320
+ { action: 'rename', description: 'Rename a folder (requires dryRun/confirmationToken)' },
2321
+ { action: 'delete', description: 'Delete folder to trash (requires dryRun/confirmationToken)' },
2322
+ { action: 'list_spaces', description: 'List spaces/workspaces' },
2323
+ ],
2324
+ noteplan_filters: [
2325
+ { action: 'list', description: 'List saved filters' },
2326
+ { action: 'get', description: 'Get one filter with parsed params (requires name)' },
2327
+ { action: 'get_tasks', description: 'Execute a filter against tasks (requires name)' },
2328
+ { action: 'list_parameters', description: 'List supported filter parameter keys' },
2329
+ { action: 'save', description: 'Create or update a filter (requires name + items)' },
2330
+ { action: 'rename', description: 'Rename a filter (requires oldName + newName)' },
2331
+ ],
2332
+ noteplan_eventkit: [
2333
+ { action: 'get_events', description: 'Get events for a date/range (source: calendar)' },
2334
+ { action: 'list_calendars', description: 'List all calendars (source: calendar)' },
2335
+ { action: 'create_event', description: 'Create event (source: calendar, requires title + startDate)' },
2336
+ { action: 'update_event', description: 'Update event (source: calendar, requires eventId)' },
2337
+ { action: 'delete_event', description: 'Delete event (source: calendar, requires eventId + dryRun/confirmationToken)' },
2338
+ { action: 'get', description: 'Get reminders (source: reminders, optional list/query filter)' },
2339
+ { action: 'list_lists', description: 'List reminder lists (source: reminders)' },
2340
+ { action: 'create', description: 'Create reminder (source: reminders, requires title)' },
2341
+ { action: 'complete', description: 'Mark reminder done (source: reminders, requires reminderId)' },
2342
+ { action: 'update', description: 'Update reminder (source: reminders, requires reminderId)' },
2343
+ { action: 'delete', description: 'Delete reminder (source: reminders, requires reminderId + dryRun/confirmationToken)' },
2344
+ ],
2345
+ noteplan_memory: [
2346
+ { action: 'list', description: 'List/search stored memories' },
2347
+ { action: 'save', description: 'Save a new memory (requires content)' },
2348
+ { action: 'update', description: 'Update memory content/tags (requires id)' },
2349
+ { action: 'delete', description: 'Delete a memory (requires id)' },
2350
+ ],
2351
+ noteplan_ui: [
2352
+ { action: 'open_note', description: 'Open a note by title or filename' },
2353
+ { action: 'open_today', description: 'Open today\'s note' },
2354
+ { action: 'search', description: 'Search in UI' },
2355
+ { action: 'run_plugin', description: 'Run a plugin command (requires pluginId + command)' },
2356
+ { action: 'open_view', description: 'Open a named view' },
2357
+ { action: 'toggle_sidebar', description: 'Toggle sidebar visibility' },
2358
+ { action: 'close_plugin_window', description: 'Close plugin window (by windowID/title, or omit both to close all)' },
2359
+ { action: 'list_plugin_windows', description: 'List open plugin windows' },
2360
+ { action: 'backup', description: 'Create a full backup of all notes, calendars, themes, filters, and plugin data' },
2361
+ ],
2362
+ noteplan_plugins: [
2363
+ { action: 'list', description: 'List installed plugins' },
2364
+ { action: 'list_available', description: 'List plugins from online repository' },
2365
+ { action: 'create', description: 'Create plugin with HTML view (requires pluginId, pluginName, commandName, html)' },
2366
+ { action: 'delete', description: 'Delete plugin (requires pluginId + confirmationToken)' },
2367
+ { action: 'install', description: 'Install from repository (requires pluginId)' },
2368
+ { action: 'log', description: 'Read plugin console log (requires pluginId)' },
2369
+ { action: 'source', description: 'Read plugin source (requires pluginId)' },
2370
+ { action: 'update_html', description: 'Apply find/replace patches (requires pluginId + patches)' },
2371
+ { action: 'screenshot', description: 'Capture plugin WebView screenshot (requires pluginId)' },
2372
+ ],
2373
+ noteplan_themes: [
2374
+ { action: 'list', description: 'List all themes and active theme names' },
2375
+ { action: 'get', description: 'Read a custom theme JSON (requires filename)' },
2376
+ { action: 'save', description: 'Create/update a custom theme (requires filename + theme)' },
2377
+ { action: 'set_active', description: 'Activate a theme (requires name)' },
2378
+ ],
2379
+ noteplan_embeddings: [
2380
+ { action: 'status', description: 'Get embeddings config and index status' },
2381
+ { action: 'search', description: 'Semantic search over indexed notes (requires query)' },
2382
+ { action: 'sync', description: 'Build/refresh embeddings index' },
2383
+ { action: 'reset', description: 'Delete index rows (requires dryRun/confirmationToken)' },
2384
+ ],
2385
+ noteplan_templates: [
2386
+ { action: 'list', description: 'List templates from @Templates folder with types and preview' },
2387
+ { action: 'render', description: 'Render a template by title or raw content string' },
2388
+ { 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' },
2389
+ { action: 'get_doc', description: 'Get full text of a doc chunk by noteTitle + chunkIndex (from search_docs results)' },
2390
+ ],
2391
+ noteplan_attachments: [
2392
+ { action: 'add', description: 'Write a file to the note\'s _attachments folder (requires data + attachmentFilename)' },
2393
+ { action: 'list', description: 'List all attachments for a note' },
2394
+ { action: 'get', description: 'Get attachment metadata. Set includeData=true for base64 content' },
2395
+ { action: 'move', description: 'Move an attachment between notes' },
2396
+ ],
2397
+ };
2173
2398
  // Register tool call handler
2174
2399
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
2175
2400
  const { name, arguments: args } = request.params;
@@ -2178,6 +2403,12 @@ export function createServer() {
2178
2403
  const startTime = Date.now();
2179
2404
  try {
2180
2405
  let result;
2406
+ // ── Intercept list_actions for any tool with an action registry ──
2407
+ if (args?.action === 'list_actions' && TOOL_ACTIONS[normalizedName]) {
2408
+ result = { success: true, tool: normalizedName, actions: TOOL_ACTIONS[normalizedName] };
2409
+ const resultWithDuration = withDuration(result, Date.now() - startTime, includeTiming);
2410
+ return { content: [{ type: 'text', text: JSON.stringify(resultWithDuration, null, 2) }] };
2411
+ }
2181
2412
  switch (normalizedName) {
2182
2413
  // ── Primary consolidated tools ──
2183
2414
  case 'noteplan_get_notes':
@@ -2283,7 +2514,7 @@ export function createServer() {
2283
2514
  result = noteTools.searchParagraphs(a);
2284
2515
  break;
2285
2516
  case 'search_global':
2286
- result = taskTools.searchTasksGlobal(a);
2517
+ result = noteTools.searchParagraphsGlobal(a);
2287
2518
  break;
2288
2519
  case 'add':
2289
2520
  result = taskTools.addTaskToNote(a);
@@ -2599,6 +2830,12 @@ export function createServer() {
2599
2830
  case 'render':
2600
2831
  result = templateTools.renderTemplate(args);
2601
2832
  break;
2833
+ case 'search_docs':
2834
+ result = await templateTools.searchDocs(args);
2835
+ break;
2836
+ case 'get_doc':
2837
+ result = templateTools.getDoc(args);
2838
+ break;
2602
2839
  default: throw new Error(`Unknown action: ${action}`);
2603
2840
  }
2604
2841
  break;
@@ -2662,7 +2899,12 @@ export function createServer() {
2662
2899
  };
2663
2900
  }
2664
2901
  catch (error) {
2665
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
2902
+ let errorMessage = error instanceof Error ? error.message : 'Unknown error';
2903
+ // Append list_actions hint for unknown action errors
2904
+ if (errorMessage.includes('Unknown action') && TOOL_ACTIONS[normalizedName]) {
2905
+ const validActions = TOOL_ACTIONS[normalizedName].map(a => a.action).join(', ');
2906
+ errorMessage += `. Valid actions: ${validActions}. Tip: use action "list_actions" to discover all actions with descriptions.`;
2907
+ }
2666
2908
  const meta = inferToolErrorMeta(normalizedName, errorMessage, registeredToolNames);
2667
2909
  const errorResult = {
2668
2910
  success: false,
@@ -2690,7 +2932,7 @@ export function createServer() {
2690
2932
  }
2691
2933
  // Start the server with stdio transport
2692
2934
  export async function startServer() {
2693
- console.error(`[noteplan-mcp] Starting v${getMcpServerVersion()} (Node ${process.version}, ${process.platform} ${process.arch})`);
2935
+ console.error(`[noteplan-mcp] Starting v${getMcpServerVersion()} (Node ${process.version}, ${process.platform} ${process.arch}, pid ${process.pid})`);
2694
2936
  await initSqlite();
2695
2937
  const server = createServer();
2696
2938
  const transport = new StdioServerTransport();