apple-notes-mcp 2.8.41 → 2.8.42

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
@@ -1602,7 +1602,7 @@ Saves a note attachment to disk.
1602
1602
  |-----------|------|----------|-------------|
1603
1603
  | `noteId` | string | Yes | CoreData note ID (from `search-notes`/`list-notes`) |
1604
1604
  | `attachmentId` | string | Yes | Attachment ID (from `list-attachments`) |
1605
- | `savePath` | string | Yes | Absolute destination file path. Must be under your home directory, a temp directory, or `/Volumes` |
1605
+ | `savePath` | string | Yes | Absolute destination file path. Must be under your home directory, a temp directory, or `/Volumes`, and never inside the Notes data folder (`~/Library/Group Containers/group.com.apple.notes`), however it is reached (symlink or different letter case) |
1606
1606
 
1607
1607
  **Returns:** Confirmation with the saved path, name, and content type (also in `structuredContent`).
1608
1608
 
package/build/index.js CHANGED
@@ -40961,6 +40961,9 @@ function allowedSaveRoots() {
40961
40961
  "/private/tmp"
40962
40962
  ];
40963
40963
  }
40964
+ function deniedSaveRoots() {
40965
+ return [join6(homedir6(), "Library/Group Containers/group.com.apple.notes")];
40966
+ }
40964
40967
  function canonicalize(path9) {
40965
40968
  return realpathSync.native(path9);
40966
40969
  }
@@ -41002,11 +41005,11 @@ function deepestExistingAncestor(abs) {
41002
41005
  current = parent;
41003
41006
  }
41004
41007
  }
41005
- function ensureParentDir(abs, roots = allowedSaveRoots()) {
41006
- assertSafeSavePath(abs, roots);
41008
+ function ensureParentDir(abs, roots = allowedSaveRoots(), denied = deniedSaveRoots()) {
41009
+ assertSafeSavePath(abs, roots, denied);
41007
41010
  mkdirSync(dirname(abs), { recursive: true });
41008
41011
  }
41009
- function assertSafeSavePath(p, roots = allowedSaveRoots()) {
41012
+ function assertSafeSavePath(p, roots = allowedSaveRoots(), denied = deniedSaveRoots()) {
41010
41013
  if (!p || !p.trim()) throw new Error("A destination path is required.");
41011
41014
  if (!isAbsolute(p)) throw new Error(`Destination path must be absolute: "${p}"`);
41012
41015
  const abs = resolve(p);
@@ -41034,6 +41037,12 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
41034
41037
  `Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}" resolves to "${canonicalDest}" through a symbolic link.`
41035
41038
  );
41036
41039
  }
41040
+ const deniedForms = [
41041
+ .../* @__PURE__ */ new Set([...denied.map((d) => resolve(d)), ...canonicalRoots(denied)])
41042
+ ].map((d) => d.toLowerCase());
41043
+ if ([abs, canonicalDest].some((form) => isWithinRoots(form.toLowerCase(), deniedForms))) {
41044
+ throw new Error(`Refusing to write inside the Notes library container: "${abs}"`);
41045
+ }
41037
41046
  return abs;
41038
41047
  }
41039
41048
  var DEFAULT_MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
@@ -52764,13 +52773,15 @@ registerTool(
52764
52773
  registerTool(
52765
52774
  "save-attachment",
52766
52775
  {
52767
- description: "Use when: writing one note attachment to a file on disk.\nReturns: the saved path.\nDo not use when: you want the bytes in-memory as base64 (fetch-attachment).\nSafety: writes a file; savePath must be absolute and under the home directory, a temp dir, or /Volumes. Get the ids from list-attachments first.",
52776
+ description: "Use when: writing one note attachment to a file on disk.\nReturns: the saved path.\nDo not use when: you want the bytes in-memory as base64 (fetch-attachment).\nSafety: writes a file; savePath must be absolute and under the home directory, a temp dir, or /Volumes, and not inside the Notes data folder. Get the ids from list-attachments first.",
52768
52777
  inputSchema: {
52769
52778
  noteId: looseNoteId(external_exports.string().min(1, "noteId is required")).describe(
52770
52779
  `Note id (from search/list): ${NOTE_ID_FORMS}`
52771
52780
  ),
52772
52781
  attachmentId: external_exports.string().min(1, "attachmentId is required").max(MAX.ATTACHMENT_ID).describe("Attachment id (from list-attachments)"),
52773
- savePath: external_exports.string().min(1, "savePath is required").max(MAX.SAVE_PATH).describe("Absolute destination file path (must be under home, temp, or /Volumes)")
52782
+ savePath: external_exports.string().min(1, "savePath is required").max(MAX.SAVE_PATH).describe(
52783
+ "Absolute destination file path (must be under home, temp, or /Volumes; never inside the Notes data folder)"
52784
+ )
52774
52785
  },
52775
52786
  outputSchema: {
52776
52787
  savedPath: external_exports.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.8.41",
3
+ "version": "2.8.42",
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",