apple-notes-mcp 2.6.2 → 2.6.4

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.
package/README.md CHANGED
@@ -362,6 +362,8 @@ Updates an existing note's content and/or title.
362
362
 
363
363
  **Note:** Either `id` or `title` must be provided. Using `id` is recommended.
364
364
 
365
+ **Returns:** Confirmation with the note's visible title and, for ID-based updates, its ID. For HTML updates, the title comes from the first rendered line of `newContent`, matching Notes.app. The response also warns if the note is shared.
366
+
365
367
  **Example - Using ID (recommended):**
366
368
  ```json
367
369
  {
package/build/index.js CHANGED
@@ -39565,12 +39565,24 @@ var AppleNotesManager = class {
39565
39565
  set noteId to id of n
39566
39566
  if seenIds does not contain noteId then
39567
39567
  set end of seenIds to noteId
39568
+ try
39569
+ set noteCreated to creation date of n
39570
+ set createdParts to ${asDatePartsExpr("noteCreated")}
39571
+ on error
39572
+ set createdParts to ""
39573
+ end try
39574
+ try
39575
+ set noteModified to modification date of n
39576
+ set modifiedParts to ${asDatePartsExpr("noteModified")}
39577
+ on error
39578
+ set modifiedParts to ""
39579
+ end try
39568
39580
  try
39569
39581
  set noteFolder to name of container of n
39570
39582
  on error
39571
39583
  set noteFolder to "Notes"
39572
39584
  end try
39573
- set end of resultList to noteName & ${AS_FIELD_SEP} & noteId & ${AS_FIELD_SEP} & noteFolder${limitCheck}
39585
+ set end of resultList to noteName & ${AS_FIELD_SEP} & noteId & ${AS_FIELD_SEP} & noteFolder & ${AS_FIELD_SEP} & createdParts & ${AS_FIELD_SEP} & modifiedParts${limitCheck}
39574
39586
  end if
39575
39587
  end try
39576
39588
  end repeat
@@ -39589,7 +39601,7 @@ var AppleNotesManager = class {
39589
39601
  const notes = [];
39590
39602
  const seenIds = /* @__PURE__ */ new Set();
39591
39603
  for (const item of items) {
39592
- const [title, id, folder2] = item.split(FIELD_SEP);
39604
+ const [title, id, folder2, created, modified] = item.split(FIELD_SEP);
39593
39605
  if (!title?.trim()) continue;
39594
39606
  const noteId = id?.trim() || generateFallbackId();
39595
39607
  if (seenIds.has(noteId)) continue;
@@ -39600,8 +39612,8 @@ var AppleNotesManager = class {
39600
39612
  content: "",
39601
39613
  // Not fetched in search
39602
39614
  tags: [],
39603
- created: /* @__PURE__ */ new Date(),
39604
- modified: /* @__PURE__ */ new Date(),
39615
+ created: parseAppleScriptDate(created ?? ""),
39616
+ modified: parseAppleScriptDate(modified ?? ""),
39605
39617
  folder: folder2?.trim(),
39606
39618
  account: targetAccount
39607
39619
  });
@@ -41754,6 +41766,40 @@ function strippedImagesWarning(stripped) {
41754
41766
  )} decoded) exceeded the per-image inline cap and ${stripped.strippedCount === 1 ? "was" : "were"} replaced with placeholders so the response stays within MCP message limits. The images are still in the note: use list-attachments with save-attachment or fetch-attachment to export them, or raise APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES.`;
41755
41767
  }
41756
41768
 
41769
+ // src/utils/updateResponseTitle.ts
41770
+ var BLOCK_END_RE = /<\/(?:div|h[1-6]|p|li)>/gi;
41771
+ var BREAK_RE = /<br\s*\/?\s*>/gi;
41772
+ var TAG_RE = /<[^>]*>/g;
41773
+ var NON_RENDERED_BLOCK_RE = /<(script|style)\b[^>]*>[\s\S]*?(?:<\/\1>|$)/gi;
41774
+ function decodeHtmlEntities(text) {
41775
+ const decodeCodePoint = (match, value, radix) => {
41776
+ const codePoint = Number.parseInt(value, radix);
41777
+ if (!Number.isInteger(codePoint) || codePoint < 0 || codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343) {
41778
+ return match;
41779
+ }
41780
+ return String.fromCodePoint(codePoint);
41781
+ };
41782
+ return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex) => decodeCodePoint(match, hex, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/&nbsp(?:;|(?![0-9a-z]))/gi, " ").replace(/&quot(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/&lt(?:;|(?![0-9a-z]))/gi, "<").replace(/&gt(?:;|(?![0-9a-z]))/gi, ">").replace(/&amp(?:;|(?![0-9a-z]))/gi, "&");
41783
+ }
41784
+ function firstVisibleHtmlLine(html) {
41785
+ let text = html;
41786
+ let previous;
41787
+ do {
41788
+ previous = text;
41789
+ text = text.replace(NON_RENDERED_BLOCK_RE, "");
41790
+ } while (text !== previous);
41791
+ text = text.replace(BREAK_RE, "\n").replace(BLOCK_END_RE, "\n");
41792
+ do {
41793
+ previous = text;
41794
+ text = text.replace(TAG_RE, "");
41795
+ } while (text !== previous);
41796
+ return decodeHtmlEntities(text).split(/[\r\n\u2028\u2029]+/).map((line) => line.replace(/\s+/g, " ").trim()).find(Boolean);
41797
+ }
41798
+ function resolveUpdateResponseTitle(currentTitle, newTitle, format, newContent) {
41799
+ if (format === "html") return firstVisibleHtmlLine(newContent) ?? currentTitle;
41800
+ return newTitle || currentTitle;
41801
+ }
41802
+
41757
41803
  // src/tools/doctor.ts
41758
41804
  import { spawnSync } from "child_process";
41759
41805
  function runDoctor(manager) {
@@ -42389,7 +42435,9 @@ server.registerTool(
42389
42435
  inputSchema: {
42390
42436
  id: external_exports.string().max(MAX.ID).optional().describe("Note ID (preferred - more reliable than title)"),
42391
42437
  title: external_exports.string().max(MAX.TITLE).optional().describe("Current note title (use id instead when available)"),
42392
- newTitle: external_exports.string().max(MAX.TITLE).optional().describe("New title for the note"),
42438
+ newTitle: external_exports.string().max(MAX.TITLE).optional().describe(
42439
+ "New title for plaintext updates. Ignored when format is 'html'; include the visible title as the first line of newContent instead."
42440
+ ),
42393
42441
  newContent: external_exports.string().min(1, "New content is required").max(MAX.CONTENT).describe(
42394
42442
  "New note body. AppleScript cannot produce true Apple Notes checklists; checkbox inputs and `- [ ]` markdown do not render as checkable items. Use a plain list and convert in Notes.app with \u21E7\u2318L."
42395
42443
  ),
@@ -42418,7 +42466,7 @@ server.registerTool(
42418
42466
  if (!success2) {
42419
42467
  return errorResponse(`Failed to update note "${note2.title}"`);
42420
42468
  }
42421
- const displayTitle = newTitle || note2.title;
42469
+ const displayTitle = resolveUpdateResponseTitle(note2.title, newTitle, format, newContent);
42422
42470
  const sharedWarning2 = note2.shared ? "\n\n\u26A0\uFE0F This note is shared with collaborators. Your changes will be visible to them." : "";
42423
42471
  const checklistWarning2 = detectChecklistAttempt(newContent) ?? "";
42424
42472
  return successResponse(`Note updated: "${displayTitle}"${sharedWarning2}${checklistWarning2}`, {
@@ -42446,7 +42494,7 @@ server.registerTool(
42446
42494
  if (!success) {
42447
42495
  return errorResponse(`Failed to update note "${title}"`);
42448
42496
  }
42449
- const finalTitle = newTitle || title;
42497
+ const finalTitle = resolveUpdateResponseTitle(note.title, newTitle, format, newContent);
42450
42498
  const sharedWarning = note.shared ? "\n\n\u26A0\uFE0F This note is shared with collaborators. Your changes will be visible to them." : "";
42451
42499
  const checklistWarning = detectChecklistAttempt(newContent) ?? "";
42452
42500
  return successResponse(`Note updated: "${finalTitle}"${sharedWarning}${checklistWarning}`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.6.2",
3
+ "version": "2.6.4",
4
4
  "description": "MCP server for Apple Notes - create, search, update, and manage notes via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",