@noteplanco/noteplan-mcp 1.1.23 → 1.1.24

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 (164) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +6 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/noteplan/attachments-paths.d.ts +13 -0
  5. package/dist/noteplan/attachments-paths.d.ts.map +1 -0
  6. package/dist/noteplan/attachments-paths.js +27 -0
  7. package/dist/noteplan/attachments-paths.js.map +1 -0
  8. package/dist/noteplan/embeddings.js +1 -1
  9. package/dist/noteplan/embeddings.js.map +1 -1
  10. package/dist/noteplan/file-reader.d.ts +37 -46
  11. package/dist/noteplan/file-reader.d.ts.map +1 -1
  12. package/dist/noteplan/file-reader.js +200 -202
  13. package/dist/noteplan/file-reader.js.map +1 -1
  14. package/dist/noteplan/file-reader.test.d.ts +2 -0
  15. package/dist/noteplan/file-reader.test.d.ts.map +1 -0
  16. package/dist/noteplan/file-reader.test.js +67 -0
  17. package/dist/noteplan/file-reader.test.js.map +1 -0
  18. package/dist/noteplan/file-writer.d.ts +35 -31
  19. package/dist/noteplan/file-writer.d.ts.map +1 -1
  20. package/dist/noteplan/file-writer.js +280 -164
  21. package/dist/noteplan/file-writer.js.map +1 -1
  22. package/dist/noteplan/file-writer.test.js +704 -191
  23. package/dist/noteplan/file-writer.test.js.map +1 -1
  24. package/dist/noteplan/filter-store.d.ts +5 -5
  25. package/dist/noteplan/filter-store.d.ts.map +1 -1
  26. package/dist/noteplan/filter-store.js +94 -79
  27. package/dist/noteplan/filter-store.js.map +1 -1
  28. package/dist/noteplan/ripgrep-search.d.ts +25 -2
  29. package/dist/noteplan/ripgrep-search.d.ts.map +1 -1
  30. package/dist/noteplan/ripgrep-search.js +75 -2
  31. package/dist/noteplan/ripgrep-search.js.map +1 -1
  32. package/dist/noteplan/space-row-utils.d.ts +20 -0
  33. package/dist/noteplan/space-row-utils.d.ts.map +1 -0
  34. package/dist/noteplan/space-row-utils.js +78 -0
  35. package/dist/noteplan/space-row-utils.js.map +1 -0
  36. package/dist/noteplan/space-row-utils.test.d.ts +2 -0
  37. package/dist/noteplan/space-row-utils.test.d.ts.map +1 -0
  38. package/dist/noteplan/space-row-utils.test.js +123 -0
  39. package/dist/noteplan/space-row-utils.test.js.map +1 -0
  40. package/dist/noteplan/sqlite-reader.d.ts +12 -27
  41. package/dist/noteplan/sqlite-reader.d.ts.map +1 -1
  42. package/dist/noteplan/sqlite-reader.js +315 -221
  43. package/dist/noteplan/sqlite-reader.js.map +1 -1
  44. package/dist/noteplan/sqlite-writer.d.ts +1 -1
  45. package/dist/noteplan/sqlite-writer.d.ts.map +1 -1
  46. package/dist/noteplan/sqlite-writer.js +2 -2
  47. package/dist/noteplan/sqlite-writer.js.map +1 -1
  48. package/dist/noteplan/unified-store.d.ts +41 -30
  49. package/dist/noteplan/unified-store.d.ts.map +1 -1
  50. package/dist/noteplan/unified-store.js +257 -159
  51. package/dist/noteplan/unified-store.js.map +1 -1
  52. package/dist/server.d.ts.map +1 -1
  53. package/dist/server.js +142 -61
  54. package/dist/server.js.map +1 -1
  55. package/dist/tools/attachments.d.ts +9 -9
  56. package/dist/tools/attachments.d.ts.map +1 -1
  57. package/dist/tools/attachments.js +74 -83
  58. package/dist/tools/attachments.js.map +1 -1
  59. package/dist/tools/attachments.test.js +170 -129
  60. package/dist/tools/attachments.test.js.map +1 -1
  61. package/dist/tools/calendar.d.ts +16 -13
  62. package/dist/tools/calendar.d.ts.map +1 -1
  63. package/dist/tools/calendar.js +17 -16
  64. package/dist/tools/calendar.js.map +1 -1
  65. package/dist/tools/embeddings.d.ts +6 -6
  66. package/dist/tools/embeddings.d.ts.map +1 -1
  67. package/dist/tools/embeddings.js +6 -6
  68. package/dist/tools/embeddings.js.map +1 -1
  69. package/dist/tools/events.d.ts +7 -3
  70. package/dist/tools/events.d.ts.map +1 -1
  71. package/dist/tools/events.js +51 -16
  72. package/dist/tools/events.js.map +1 -1
  73. package/dist/tools/filters.d.ts +28 -33
  74. package/dist/tools/filters.d.ts.map +1 -1
  75. package/dist/tools/filters.js +42 -105
  76. package/dist/tools/filters.js.map +1 -1
  77. package/dist/tools/notes.d.ts +80 -218
  78. package/dist/tools/notes.d.ts.map +1 -1
  79. package/dist/tools/notes.js +180 -177
  80. package/dist/tools/notes.js.map +1 -1
  81. package/dist/tools/notes.test.js +242 -21
  82. package/dist/tools/notes.test.js.map +1 -1
  83. package/dist/tools/search.d.ts +4 -3
  84. package/dist/tools/search.d.ts.map +1 -1
  85. package/dist/tools/search.js +9 -5
  86. package/dist/tools/search.js.map +1 -1
  87. package/dist/tools/search.test.d.ts +2 -0
  88. package/dist/tools/search.test.d.ts.map +1 -0
  89. package/dist/tools/search.test.js +37 -0
  90. package/dist/tools/search.test.js.map +1 -0
  91. package/dist/tools/spaces.d.ts +20 -20
  92. package/dist/tools/spaces.d.ts.map +1 -1
  93. package/dist/tools/spaces.js +28 -28
  94. package/dist/tools/spaces.js.map +1 -1
  95. package/dist/tools/tasks.d.ts +22 -22
  96. package/dist/tools/tasks.d.ts.map +1 -1
  97. package/dist/tools/tasks.js +22 -22
  98. package/dist/tools/tasks.js.map +1 -1
  99. package/dist/tools/templates.d.ts +7 -7
  100. package/dist/tools/templates.d.ts.map +1 -1
  101. package/dist/tools/templates.js +4 -4
  102. package/dist/tools/templates.js.map +1 -1
  103. package/dist/tools/themes.js +1 -1
  104. package/dist/tools/themes.js.map +1 -1
  105. package/dist/transport/bridge-availability.d.ts +5 -0
  106. package/dist/transport/bridge-availability.d.ts.map +1 -0
  107. package/dist/transport/bridge-availability.js +92 -0
  108. package/dist/transport/bridge-availability.js.map +1 -0
  109. package/dist/transport/bridge-cascade.d.ts +18 -0
  110. package/dist/transport/bridge-cascade.d.ts.map +1 -0
  111. package/dist/transport/bridge-cascade.js +78 -0
  112. package/dist/transport/bridge-cascade.js.map +1 -0
  113. package/dist/transport/bridge-cascade.test.d.ts +2 -0
  114. package/dist/transport/bridge-cascade.test.d.ts.map +1 -0
  115. package/dist/transport/bridge-cascade.test.js +160 -0
  116. package/dist/transport/bridge-cascade.test.js.map +1 -0
  117. package/dist/transport/bridge-client.d.ts +197 -0
  118. package/dist/transport/bridge-client.d.ts.map +1 -0
  119. package/dist/transport/bridge-client.js +288 -0
  120. package/dist/transport/bridge-client.js.map +1 -0
  121. package/dist/transport/bridge-client.test.d.ts +2 -0
  122. package/dist/transport/bridge-client.test.d.ts.map +1 -0
  123. package/dist/transport/bridge-client.test.js +384 -0
  124. package/dist/transport/bridge-client.test.js.map +1 -0
  125. package/dist/transport/bridge-context.d.ts +10 -0
  126. package/dist/transport/bridge-context.d.ts.map +1 -0
  127. package/dist/transport/bridge-context.js +18 -0
  128. package/dist/transport/bridge-context.js.map +1 -0
  129. package/dist/transport/bridge-fs.d.ts +25 -0
  130. package/dist/transport/bridge-fs.d.ts.map +1 -0
  131. package/dist/transport/bridge-fs.js +129 -0
  132. package/dist/transport/bridge-fs.js.map +1 -0
  133. package/dist/utils/date-utils.d.ts +24 -0
  134. package/dist/utils/date-utils.d.ts.map +1 -1
  135. package/dist/utils/date-utils.js +55 -0
  136. package/dist/utils/date-utils.js.map +1 -1
  137. package/dist/utils/date-utils.test.d.ts +2 -0
  138. package/dist/utils/date-utils.test.d.ts.map +1 -0
  139. package/dist/utils/date-utils.test.js +109 -0
  140. package/dist/utils/date-utils.test.js.map +1 -0
  141. package/dist/utils/folder-access.d.ts +23 -0
  142. package/dist/utils/folder-access.d.ts.map +1 -0
  143. package/dist/utils/folder-access.js +131 -0
  144. package/dist/utils/folder-access.js.map +1 -0
  145. package/dist/utils/folder-access.test.d.ts +2 -0
  146. package/dist/utils/folder-access.test.d.ts.map +1 -0
  147. package/dist/utils/folder-access.test.js +182 -0
  148. package/dist/utils/folder-access.test.js.map +1 -0
  149. package/dist/utils/folder-matcher.d.ts.map +1 -1
  150. package/dist/utils/folder-matcher.js +16 -0
  151. package/dist/utils/folder-matcher.js.map +1 -1
  152. package/dist/utils/folder-matcher.test.js +42 -0
  153. package/dist/utils/folder-matcher.test.js.map +1 -1
  154. package/dist/utils/server-config.d.ts +10 -2
  155. package/dist/utils/server-config.d.ts.map +1 -1
  156. package/dist/utils/server-config.js +16 -2
  157. package/dist/utils/server-config.js.map +1 -1
  158. package/dist/utils/version.d.ts +2 -0
  159. package/dist/utils/version.d.ts.map +1 -1
  160. package/dist/utils/version.js +5 -1
  161. package/dist/utils/version.js.map +1 -1
  162. package/package.json +4 -3
  163. package/scripts/calendar-helper +0 -0
  164. package/scripts/reminders-helper +0 -0
package/dist/server.js CHANGED
@@ -24,9 +24,11 @@ import * as templateTools from './tools/templates.js';
24
24
  import * as attachmentTools from './tools/attachments.js';
25
25
  import { parseFlexibleDate } from './utils/date-utils.js';
26
26
  import { upgradeMessage, getNotePlanVersion, getMcpServerVersion, MIN_BUILD_ADVANCED_FEATURES, MIN_BUILD_CREATE_BACKUP } from './utils/version.js';
27
- import { isReadOnly, isSkipDryRun } from './utils/server-config.js';
27
+ import { isReadOnly, isSkipDryRun, shouldAutoLaunchNotePlan } from './utils/server-config.js';
28
28
  import { initSqlite } from './noteplan/sqlite-loader.js';
29
29
  import { getDatabase, getDatabasePath, listSpaces as listSpacesFromDb } from './noteplan/sqlite-reader.js';
30
+ import { getBridgeClient } from './transport/bridge-availability.js';
31
+ import { withBackendTracking, getCurrentBackends } from './transport/bridge-context.js';
30
32
  const __filename = fileURLToPath(import.meta.url);
31
33
  const __dirname = path.dirname(__filename);
32
34
  const PLUGIN_API_DOCS_DIR = path.join(__dirname, '../docs/plugin-api');
@@ -236,7 +238,7 @@ const PERIOD_TYPE_MAP = {
236
238
  quarter: 'quarterly',
237
239
  year: 'yearly',
238
240
  };
239
- function dispatchGetNotes(args) {
241
+ async function dispatchGetNotes(args) {
240
242
  const { resolve, resolveQuery, id, title, filename, date, period, count, rangePeriod, startDate, endDate, folder, space, types, query, limit, offset, cursor, minScore, ambiguityDelta,
241
243
  // periodic note params
242
244
  week, month, quarter, year, fromDate, includeContent, includeMissing, maxLookback,
@@ -839,19 +841,35 @@ function withDuration(result, durationMs, includeTiming) {
839
841
  durationMs,
840
842
  };
841
843
  }
842
- const SERVER_VERSION = '1.1.15';
844
+ /** Attach `backends` (e.g. `["bridge"]` or `["fallback"]` or both) to a tool
845
+ * response when the tool's work passed through bridgeOrFallback. Reads the
846
+ * request-scoped tracker set up by withBackendTracking in the dispatcher.
847
+ * No-op for results that aren't plain objects. */
848
+ function withBackend(result) {
849
+ const backends = getCurrentBackends();
850
+ if (backends.length === 0)
851
+ return result;
852
+ if (result && typeof result === 'object' && !Array.isArray(result)) {
853
+ return {
854
+ ...result,
855
+ backends,
856
+ };
857
+ }
858
+ return result;
859
+ }
843
860
  // Create the server
844
861
  export function createServer() {
862
+ const mcpServerVersion = getMcpServerVersion();
845
863
  const server = new Server({
846
864
  name: 'NotePlan',
847
- version: SERVER_VERSION,
865
+ version: mcpServerVersion,
848
866
  }, {
849
867
  capabilities: {
850
868
  tools: { listChanged: true },
851
869
  resources: {},
852
870
  },
853
871
  instructions: [
854
- `You have access to NotePlan — a markdown-based note-taking and task management app for macOS/iOS. (MCP server v${SERVER_VERSION})`,
872
+ `You have access to NotePlan — a markdown-based note-taking and task management app for macOS/iOS. (MCP server v${mcpServerVersion})`,
855
873
  'All tools use action-based dispatch: one tool per domain, with an `action` parameter to select the operation.',
856
874
  '',
857
875
  '## Workflow',
@@ -923,8 +941,9 @@ export function createServer() {
923
941
  const database = getDatabase();
924
942
  const writable = database ? 'read-write' : 'unavailable';
925
943
  console.error(`[noteplan-mcp] Space database: ${dbPath} (${writable})`);
926
- const spaces = listSpacesFromDb();
927
- console.error(`[noteplan-mcp] Spaces discovered: ${spaces.length}`);
944
+ listSpacesFromDb()
945
+ .then((spaces) => console.error(`[noteplan-mcp] Spaces discovered: ${spaces.length}`))
946
+ .catch((err) => console.error('[noteplan-mcp] Failed to list spaces:', err));
928
947
  }
929
948
  else {
930
949
  console.error('[noteplan-mcp] Space database: not found');
@@ -938,6 +957,9 @@ export function createServer() {
938
957
  if (skipDryRun) {
939
958
  console.error('[noteplan-mcp] Skip dry-run: ENABLED (confirmation tokens not required)');
940
959
  }
960
+ if (!shouldAutoLaunchNotePlan()) {
961
+ console.error('[noteplan-mcp] Auto-launch: DISABLED (NOTEPLAN_MCP_AUTOLAUNCH=false; bridge probe stays passive when NotePlan is closed)');
962
+ }
941
963
  const toolDefinitions = [
942
964
  // ── Consolidated tools (16 — action/param-based dispatch) ──
943
965
  {
@@ -2472,7 +2494,7 @@ export function createServer() {
2472
2494
  ],
2473
2495
  };
2474
2496
  // Register tool call handler
2475
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
2497
+ server.setRequestHandler(CallToolRequestSchema, async (request) => withBackendTracking(async () => {
2476
2498
  const { name, arguments: args } = request.params;
2477
2499
  const normalizedName = normalizeToolName(name);
2478
2500
  const includeTiming = isDebugTimingsEnabled(args);
@@ -2485,7 +2507,8 @@ export function createServer() {
2485
2507
  if (args?.action === 'list_actions' && TOOL_ACTIONS[normalizedName]) {
2486
2508
  result = { success: true, tool: normalizedName, actions: TOOL_ACTIONS[normalizedName] };
2487
2509
  const resultWithDuration = withDuration(result, Date.now() - startTime, includeTiming);
2488
- return { content: [{ type: 'text', text: JSON.stringify(resultWithDuration, null, 2) }] };
2510
+ const finalResult = withBackend(resultWithDuration);
2511
+ return { content: [{ type: 'text', text: JSON.stringify(finalResult, null, 2) }] };
2489
2512
  }
2490
2513
  // ── Read-only mode: reject write actions ──
2491
2514
  if (readOnly) {
@@ -2495,9 +2518,10 @@ export function createServer() {
2495
2518
  const errorResult = { success: false, error: `Read-only mode is enabled (NOTEPLAN_READ_ONLY=true). Write action "${action}" on ${normalizedName} is not allowed.`, code: 'ERR_READ_ONLY' };
2496
2519
  const hasOutputSchema = !!toolDefinitions.find(t => t.name === normalizedName)?.outputSchema;
2497
2520
  const resultWithDuration = withDuration(errorResult, Date.now() - startTime, includeTiming);
2521
+ const finalResult = withBackend(resultWithDuration);
2498
2522
  return {
2499
- content: [{ type: 'text', text: JSON.stringify(resultWithDuration, null, 2) }],
2500
- ...(hasOutputSchema ? { structuredContent: resultWithDuration } : {}),
2523
+ content: [{ type: 'text', text: JSON.stringify(finalResult, null, 2) }],
2524
+ ...(hasOutputSchema ? { structuredContent: finalResult } : {}),
2501
2525
  isError: true,
2502
2526
  };
2503
2527
  }
@@ -2505,12 +2529,12 @@ export function createServer() {
2505
2529
  switch (normalizedName) {
2506
2530
  // ── Primary consolidated tools ──
2507
2531
  case 'noteplan_get_notes':
2508
- result = dispatchGetNotes((args ?? {}));
2532
+ result = await dispatchGetNotes((args ?? {}));
2509
2533
  break;
2510
2534
  case 'noteplan_search': {
2511
2535
  const searchAction = args?.action;
2512
2536
  if (searchAction === 'list_tags') {
2513
- result = spaceTools.listTags(args);
2537
+ result = await spaceTools.listTags(args);
2514
2538
  }
2515
2539
  else {
2516
2540
  result = await searchTools.searchNotes(args);
@@ -2526,28 +2550,28 @@ export function createServer() {
2526
2550
  }
2527
2551
  switch (action) {
2528
2552
  case 'create':
2529
- result = noteTools.createNote(args);
2553
+ result = await noteTools.createNote(args);
2530
2554
  break;
2531
2555
  case 'update':
2532
- result = noteTools.updateNote(args);
2556
+ result = await noteTools.updateNote(args);
2533
2557
  break;
2534
2558
  case 'delete':
2535
- result = noteTools.deleteNote(args);
2559
+ result = await noteTools.deleteNote(args);
2536
2560
  break;
2537
2561
  case 'move':
2538
- result = noteTools.moveNote(args);
2562
+ result = await noteTools.moveNote(args);
2539
2563
  break;
2540
2564
  case 'restore':
2541
- result = noteTools.restoreNote(args);
2565
+ result = await noteTools.restoreNote(args);
2542
2566
  break;
2543
2567
  case 'rename':
2544
- result = noteTools.renameNoteFile(args);
2568
+ result = await noteTools.renameNoteFile(args);
2545
2569
  break;
2546
2570
  case 'set_property':
2547
- result = noteTools.setProperty(args);
2571
+ result = await noteTools.setProperty(args);
2548
2572
  break;
2549
2573
  case 'remove_property':
2550
- result = noteTools.removeProperty(args);
2574
+ result = await noteTools.removeProperty(args);
2551
2575
  break;
2552
2576
  default: throw new Error(`Unknown action: "${action}". Valid actions: create, update, delete, move, restore, rename, set_property, remove_property`);
2553
2577
  }
@@ -2566,19 +2590,19 @@ export function createServer() {
2566
2590
  const action = a?.action;
2567
2591
  switch (action) {
2568
2592
  case 'insert':
2569
- result = noteTools.insertContent(a);
2593
+ result = await noteTools.insertContent(a);
2570
2594
  break;
2571
2595
  case 'append':
2572
- result = noteTools.appendContent(a);
2596
+ result = await noteTools.appendContent(a);
2573
2597
  break;
2574
2598
  case 'delete_lines':
2575
- result = noteTools.deleteLines(a);
2599
+ result = await noteTools.deleteLines(a);
2576
2600
  break;
2577
2601
  case 'edit_line':
2578
- result = noteTools.editLine(a);
2602
+ result = await noteTools.editLine(a);
2579
2603
  break;
2580
2604
  case 'replace_lines':
2581
- result = noteTools.replaceLines(a);
2605
+ result = await noteTools.replaceLines(a);
2582
2606
  break;
2583
2607
  default: throw new Error(`Unknown action: "${action}". Valid actions: insert, append, delete_lines, edit_line, replace_lines (snake_case required)`);
2584
2608
  }
@@ -2601,25 +2625,25 @@ export function createServer() {
2601
2625
  const action = a?.action;
2602
2626
  switch (action) {
2603
2627
  case 'get':
2604
- result = noteTools.getParagraphs(a);
2628
+ result = await noteTools.getParagraphs(a);
2605
2629
  break;
2606
2630
  case 'search':
2607
- result = noteTools.searchParagraphs(a);
2631
+ result = await noteTools.searchParagraphs(a);
2608
2632
  break;
2609
2633
  case 'search_global':
2610
- result = noteTools.searchParagraphsGlobal(a);
2634
+ result = await noteTools.searchParagraphsGlobal(a);
2611
2635
  break;
2612
2636
  case 'add':
2613
- result = taskTools.addTaskToNote(a);
2637
+ result = await taskTools.addTaskToNote(a);
2614
2638
  break;
2615
2639
  case 'complete':
2616
- result = taskTools.completeTask(a);
2640
+ result = await taskTools.completeTask(a);
2617
2641
  break;
2618
2642
  case 'update':
2619
- result = taskTools.updateTask(a);
2643
+ result = await taskTools.updateTask(a);
2620
2644
  break;
2621
2645
  case 'delete_recurring':
2622
- result = taskTools.deleteRecurringTask(a);
2646
+ result = await taskTools.deleteRecurringTask(a);
2623
2647
  break;
2624
2648
  default: throw new Error(`Unknown action: ${action}`);
2625
2649
  }
@@ -2634,28 +2658,28 @@ export function createServer() {
2634
2658
  }
2635
2659
  switch (action) {
2636
2660
  case 'list':
2637
- result = spaceTools.listFolders(args);
2661
+ result = await spaceTools.listFolders(args);
2638
2662
  break;
2639
2663
  case 'find':
2640
- result = spaceTools.findFolders(args);
2664
+ result = await spaceTools.findFolders(args);
2641
2665
  break;
2642
2666
  case 'resolve':
2643
- result = spaceTools.resolveFolder(args);
2667
+ result = await spaceTools.resolveFolder(args);
2644
2668
  break;
2645
2669
  case 'create':
2646
- result = spaceTools.createFolder(args);
2670
+ result = await spaceTools.createFolder(args);
2647
2671
  break;
2648
2672
  case 'move':
2649
- result = spaceTools.moveFolder(args);
2673
+ result = await spaceTools.moveFolder(args);
2650
2674
  break;
2651
2675
  case 'rename':
2652
- result = spaceTools.renameFolder(args);
2676
+ result = await spaceTools.renameFolder(args);
2653
2677
  break;
2654
2678
  case 'delete':
2655
- result = spaceTools.deleteFolder(args);
2679
+ result = await spaceTools.deleteFolder(args);
2656
2680
  break;
2657
2681
  case 'list_spaces':
2658
- result = spaceTools.listSpaces(args);
2682
+ result = await spaceTools.listSpaces(args);
2659
2683
  break;
2660
2684
  default: throw new Error(`Unknown action: ${action}`);
2661
2685
  }
@@ -2665,22 +2689,22 @@ export function createServer() {
2665
2689
  const action = args?.action;
2666
2690
  switch (action) {
2667
2691
  case 'list':
2668
- result = filterTools.listFilters(args);
2692
+ result = await filterTools.listFilters(args);
2669
2693
  break;
2670
2694
  case 'get':
2671
- result = filterTools.getFilter(args);
2695
+ result = await filterTools.getFilter(args);
2672
2696
  break;
2673
2697
  case 'get_tasks':
2674
- result = filterTools.getFilterTasks(args);
2698
+ result = await filterTools.getFilterTasks(args);
2675
2699
  break;
2676
2700
  case 'list_parameters':
2677
- result = filterTools.listFilterParameters();
2701
+ result = await filterTools.listFilterParameters();
2678
2702
  break;
2679
2703
  case 'save':
2680
- result = filterTools.saveFilter(args);
2704
+ result = await filterTools.saveFilter(args);
2681
2705
  break;
2682
2706
  case 'rename':
2683
- result = filterTools.renameFilter(args);
2707
+ result = await filterTools.renameFilter(args);
2684
2708
  break;
2685
2709
  default: throw new Error(`Unknown action: ${action}`);
2686
2710
  }
@@ -2764,7 +2788,7 @@ export function createServer() {
2764
2788
  result = uiTools.openToday(args);
2765
2789
  break;
2766
2790
  case 'search':
2767
- result = uiTools.searchNotes(args);
2791
+ result = await uiTools.searchNotes(args);
2768
2792
  break;
2769
2793
  case 'run_plugin':
2770
2794
  result = uiTools.runPlugin(args);
@@ -2815,7 +2839,7 @@ export function createServer() {
2815
2839
  result = uiTools.openToday(args);
2816
2840
  break;
2817
2841
  case 'search':
2818
- result = uiTools.searchNotes(args);
2842
+ result = await uiTools.searchNotes(args);
2819
2843
  break;
2820
2844
  case 'run_plugin':
2821
2845
  result = uiTools.runPlugin(args);
@@ -2943,16 +2967,16 @@ export function createServer() {
2943
2967
  const action = args?.action;
2944
2968
  switch (action) {
2945
2969
  case 'add':
2946
- result = attachmentTools.addAttachment(args);
2970
+ result = await attachmentTools.addAttachment(args);
2947
2971
  break;
2948
2972
  case 'list':
2949
- result = attachmentTools.listAttachments(args);
2973
+ result = await attachmentTools.listAttachments(args);
2950
2974
  break;
2951
2975
  case 'get':
2952
- result = attachmentTools.getAttachment(args);
2976
+ result = await attachmentTools.getAttachment(args);
2953
2977
  break;
2954
2978
  case 'move':
2955
- result = attachmentTools.moveAttachment(args);
2979
+ result = await attachmentTools.moveAttachment(args);
2956
2980
  break;
2957
2981
  default: throw new Error(`Unknown action: ${action}. Valid actions: add, list, get, move`);
2958
2982
  }
@@ -2965,15 +2989,16 @@ export function createServer() {
2965
2989
  const resultWithSuggestions = withSuggestedNextTools(enrichedResult, normalizedName, registeredToolNameSet);
2966
2990
  const resultWithMemory = withMemoryHints(resultWithSuggestions, normalizedName);
2967
2991
  const resultWithDuration = withDuration(resultWithMemory, Date.now() - startTime, includeTiming);
2992
+ const finalResult = withBackend(resultWithDuration);
2968
2993
  // Log non-throwing errors (success: false returned without an exception)
2969
- if (resultWithDuration && typeof resultWithDuration === 'object' && resultWithDuration.success === false) {
2994
+ if (finalResult && typeof finalResult === 'object' && finalResult.success === false) {
2970
2995
  const duration = Date.now() - startTime;
2971
- const errMsg = resultWithDuration.error || 'unknown';
2996
+ const errMsg = finalResult.error || 'unknown';
2972
2997
  console.error(`[noteplan-mcp] Tool failed: ${actionLabel} (${duration}ms): ${errMsg}`);
2973
2998
  }
2974
2999
  const hasOutputSchema = Boolean(toolDefinitionByName.get(normalizedName)?.outputSchema);
2975
3000
  // If the result contains image data, return it as an MCP image content block
2976
- const typedResult = resultWithDuration;
3001
+ const typedResult = finalResult;
2977
3002
  if (typedResult._imageData && typedResult._imageMimeType) {
2978
3003
  const imageData = typedResult._imageData;
2979
3004
  const imageMimeType = typedResult._imageMimeType;
@@ -2997,10 +3022,10 @@ export function createServer() {
2997
3022
  content: [
2998
3023
  {
2999
3024
  type: 'text',
3000
- text: JSON.stringify(resultWithDuration),
3025
+ text: JSON.stringify(finalResult),
3001
3026
  },
3002
3027
  ],
3003
- ...(hasOutputSchema ? { structuredContent: resultWithDuration } : {}),
3028
+ ...(hasOutputSchema ? { structuredContent: finalResult } : {}),
3004
3029
  };
3005
3030
  }
3006
3031
  catch (error) {
@@ -3022,26 +3047,82 @@ export function createServer() {
3022
3047
  retryable: meta.retryable,
3023
3048
  };
3024
3049
  const errorWithDuration = withDuration(errorResult, Date.now() - startTime, includeTiming);
3050
+ const finalErrorResult = withBackend(errorWithDuration);
3025
3051
  const hasOutputSchema = Boolean(toolDefinitionByName.get(normalizedName)?.outputSchema);
3026
3052
  return {
3027
3053
  content: [
3028
3054
  {
3029
3055
  type: 'text',
3030
- text: JSON.stringify(errorWithDuration),
3056
+ text: JSON.stringify(finalErrorResult),
3031
3057
  },
3032
3058
  ],
3033
- ...(hasOutputSchema ? { structuredContent: errorWithDuration } : {}),
3059
+ ...(hasOutputSchema ? { structuredContent: finalErrorResult } : {}),
3034
3060
  isError: true,
3035
3061
  };
3036
3062
  }
3037
- });
3063
+ finally {
3064
+ // Per-call backend summary so support reading the log linearly can tell
3065
+ // whether the call used the HTTP bridge or the SQLite/FS fallback. Mixed
3066
+ // calls (multiple bridge ops where one fell back) show "bridge+fallback".
3067
+ const backends = getCurrentBackends();
3068
+ if (backends.length > 0) {
3069
+ console.error(`[noteplan-mcp] ↳ backend: ${backends.join('+')}`);
3070
+ }
3071
+ }
3072
+ }));
3038
3073
  return server;
3039
3074
  }
3075
+ // Discover the bridge and run one cheap search call so NotePlan's
3076
+ // SearchHelper / NoteCache is hot before the first user request lands.
3077
+ // Bounded so a hung bridge can't wedge MCP startup. Errors are logged
3078
+ // but never fatal: if the bridge is unreachable, the cascade falls
3079
+ // through to ripgrep on the first request just like before.
3080
+ const BRIDGE_WARMUP_TIMEOUT_MS = 5_000;
3081
+ async function probeAndWarmBridge() {
3082
+ const deadline = Date.now() + BRIDGE_WARMUP_TIMEOUT_MS;
3083
+ const remaining = () => Math.max(0, deadline - Date.now());
3084
+ const withTimeout = (promise, label) => new Promise((resolve, reject) => {
3085
+ const timer = setTimeout(() => reject(new Error(`${label} timeout`)), remaining());
3086
+ promise.then((value) => {
3087
+ clearTimeout(timer);
3088
+ resolve(value);
3089
+ }, (err) => {
3090
+ clearTimeout(timer);
3091
+ reject(err);
3092
+ });
3093
+ });
3094
+ try {
3095
+ const client = await withTimeout(getBridgeClient(), 'bridge discover');
3096
+ if (!client) {
3097
+ console.error('[noteplan-mcp] Bridge unavailable (NotePlan not running, old build, or Automation denied)');
3098
+ return;
3099
+ }
3100
+ const config = await withTimeout(client.config(), 'bridge config');
3101
+ console.error(`[noteplan-mcp] Bridge OK on 127.0.0.1:${client.port} — storage: ${config.storagePath}`);
3102
+ try {
3103
+ await withTimeout(client.search('__np_mcp_warmup_no_match__', { limit: 1 }), 'bridge warmup');
3104
+ }
3105
+ catch (err) {
3106
+ // Warm-up is best-effort: a failure here means the first user
3107
+ // search may still take the cold path. Don't escalate — the
3108
+ // cascade in `searchWithRipgrep` already handles bridge errors.
3109
+ console.error(`[noteplan-mcp] Bridge warmup skipped: ${err instanceof Error ? err.message : String(err)}`);
3110
+ }
3111
+ }
3112
+ catch (err) {
3113
+ console.error(`[noteplan-mcp] Bridge probe failed: ${err instanceof Error ? err.message : String(err)}`);
3114
+ }
3115
+ }
3040
3116
  // Start the server with stdio transport
3041
3117
  export async function startServer() {
3042
3118
  console.error(`[noteplan-mcp] Starting v${getMcpServerVersion()} (Node ${process.version}, ${process.platform} ${process.arch}, pid ${process.pid})`);
3043
3119
  await initSqlite();
3044
3120
  const server = createServer();
3121
+ // Bridge probe + SearchHelper / NoteCache warm-up. AWAIT this before
3122
+ // accepting requests so the user's first search hits the warm bridge
3123
+ // instead of racing it and falling back to ripgrep. Bounded by a
3124
+ // short overall timeout so a wedged bridge can't block startup.
3125
+ await probeAndWarmBridge();
3045
3126
  const transport = new StdioServerTransport();
3046
3127
  await server.connect(transport);
3047
3128
  console.error('[noteplan-mcp] Server running on stdio');