apple-notes-mcp 2.8.43 → 2.8.45

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 (3) hide show
  1. package/README.md +87 -5
  2. package/build/index.js +754 -140
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -763,6 +763,7 @@ Updates an existing note's content and/or title.
763
763
  | `newContent` | string | Yes | New content for the note body |
764
764
  | `format` | string | No | Content format: `"plaintext"` (default) or `"html"`. When `"html"`, content replaces the entire note body as raw HTML and `newTitle` is ignored (the first HTML element serves as the title) |
765
765
  | `allowLinkChanges` | boolean | No | Set to `true` only when intentionally changing or removing existing links |
766
+ | `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
766
767
 
767
768
  Title-only updates are rejected because Apple Notes titles are not unique.
768
769
 
@@ -807,6 +808,7 @@ Deletes a note (moves to Recently Deleted in Notes.app).
807
808
  |-----------|------|----------|-------------|
808
809
  | `id` | string | Yes | Exact CoreData note ID returned by a read or search |
809
810
  | `expectedContentHash` | string | Yes | `contentHash` from the exact note version being deleted |
811
+ | `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
810
812
 
811
813
  Title-only deletion is rejected. If the note changed after the supplied hash
812
814
  was read, deletion is also rejected.
@@ -842,6 +844,7 @@ Moves a note to a different folder. The note is relocated in place via Notes.app
842
844
  |-----------|------|----------|-------------|
843
845
  | `id` | string | Yes | Exact CoreData note ID returned by a read or search |
844
846
  | `folder` | string | Yes | Destination folder name or nested path (e.g., `"Work/Clients"`) |
847
+ | `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
845
848
 
846
849
  Title-only moves are rejected.
847
850
 
@@ -858,6 +861,35 @@ actual destination folder ID matches the requested folder.
858
861
 
859
862
  ---
860
863
 
864
+ #### Folder scope guards
865
+
866
+ `update-note`, `append-to-note`, `delete-note`, and `move-note` accept three
867
+ optional folder preconditions. Use exact folder ids from `list-folders`.
868
+
869
+ - `ifFolderId`: the note must currently be in exactly this folder.
870
+ - `ifAncestorFolderId`: the note must be inside this folder or any of its
871
+ subfolders.
872
+ - `forbiddenAncestorFolderIds` (up to 50): the note must not be inside any of
873
+ these folders or their subfolders. For `move-note`, the destination must not
874
+ be either.
875
+
876
+ The checks read Notes.app's live folders inside the same AppleScript as the
877
+ write, immediately before it, so a note moved after you reviewed it is left
878
+ alone and the call fails with `Scope guard failed: …`. The one exception is a
879
+ native append to a protected note (it runs through Shortcuts): there the check
880
+ is a separate read just before the append, so it is not atomic.
881
+
882
+ **Example - retire a note only while it is still in the inbox:**
883
+ ```json
884
+ {
885
+ "id": "x-coredata://ABC123/ICNote/p456",
886
+ "expectedContentHash": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
887
+ "ifFolderId": "x-coredata://ABC123/ICFolder/p12"
888
+ }
889
+ ```
890
+
891
+ ---
892
+
861
893
  #### `append-to-note`
862
894
 
863
895
  Appends or prepends content to an existing note without replacing it. Always reads and writes as HTML, preserving all existing rich formatting.
@@ -870,6 +902,7 @@ Appends or prepends content to an existing note without replacing it. Always rea
870
902
  | `position` | string | No | `"after"` (default) appends to the end; `"before"` inserts directly below the note's title line, so the title stays first |
871
903
  | `separator` | string | No | String placed between existing content and new content (default: two newlines → `<div><br></div>` in HTML) |
872
904
  | `format` | string | No | Format of the content being appended: `"plaintext"` (default) or `"html"` |
905
+ | `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
873
906
 
874
907
  Title-only appends are rejected.
875
908
 
@@ -1145,11 +1178,12 @@ Creates a new folder, including a whole nested hierarchy in one call.
1145
1178
 
1146
1179
  #### `get-folder-by-id`
1147
1180
 
1148
- Reads one exact folder's current name and parent ID. Use these values with
1149
- `rename-folder`; this avoids relying on ambiguous folder names or paths. The
1150
- `id` may also be the folder's Notes UUID or numeric key, and the result adds
1151
- `identifier`, `parentIdentifier`, and `accountIdentifier` when Full Disk Access
1152
- is granted.
1181
+ Reads one exact folder's current name, parent ID, and account ID (`accountId`,
1182
+ plus `isRoot`, true when the folder sits at the account root). Use these values
1183
+ with `rename-folder` or `delete-folder-by-id`; this avoids relying on ambiguous
1184
+ folder names or paths. The `id` may also be the folder's Notes UUID or numeric
1185
+ key, and the result adds `identifier`, `parentIdentifier`, and
1186
+ `accountIdentifier` when Full Disk Access is granted.
1153
1187
 
1154
1188
  ---
1155
1189
 
@@ -1161,6 +1195,54 @@ and descendants. It refuses stale metadata and a conflicting sibling name.
1161
1195
 
1162
1196
  ---
1163
1197
 
1198
+ #### `delete-folder-by-id`
1199
+
1200
+ Deletes one exact, empty, ordinary folder through a plan-then-apply handshake.
1201
+ Read the folder first with `get-folder-by-id`, call once with `dryRun: true`,
1202
+ then repeat the same guards with `dryRun: false` and the returned `revision`.
1203
+
1204
+ | Parameter | Type | Required | Description |
1205
+ |-----------|------|----------|-------------|
1206
+ | `id` | string | Yes | Exact folder id (`x-coredata://…/ICFolder/pN`), or the folder's Notes UUID or numeric key |
1207
+ | `expectedName` | string | Yes | Current folder name (not a path), matched case-sensitively |
1208
+ | `expectedAccountId` | string | Yes | Owning account id (`x-coredata://…/ICAccount/pN`) |
1209
+ | `expectedParentId` | string | One of these two | Current parent folder id (same forms as `id`) |
1210
+ | `expectedRoot` | `true` | One of these two | The folder sits at the account root |
1211
+ | `dryRun` | boolean | Yes | `true` plans; `false` applies |
1212
+ | `expectedRevision` | string | On apply | The `revision` from the dry run |
1213
+
1214
+ **Example (plan, then apply with the returned revision):**
1215
+ ```json
1216
+ {
1217
+ "id": "x-coredata://ABC/ICFolder/p42",
1218
+ "expectedName": "Old Projects",
1219
+ "expectedAccountId": "x-coredata://ABC/ICAccount/p3",
1220
+ "expectedRoot": true,
1221
+ "dryRun": true
1222
+ }
1223
+ ```
1224
+
1225
+ **Returns:** `status: "planned"` with `wouldDelete`, `identifier`, `name`,
1226
+ `accountId`, `parentId`, `folderType: 0`, zero `childFolderCount` and
1227
+ `noteCount`, and `revision`; on apply, `status: "deleted"`, `committed: true`,
1228
+ `verified: true` (Notes.app no longer resolves the id), and `storeTombstoned`.
1229
+
1230
+ **Safety:** it always refuses Recently Deleted, smart folders, the account's
1231
+ default and other system folders, shared folders (or folders inside a shared
1232
+ folder), and any folder that still holds notes or subfolders. There is no
1233
+ override. It is **not atomic**: the guard is a pre-check followed by an
1234
+ AppleScript delete. The name, parent, account, sharing, and emptiness checks
1235
+ repeat inside the delete script, but the folder type and stable identifier come
1236
+ from the local Notes database just before it. It needs Full Disk Access and
1237
+ fails closed without it.
1238
+
1239
+ **Errors** carry the standard `code`: `revision_conflict` (a `Conflict:` message;
1240
+ read and plan again), `unsupported` (a `Refused:` message), `verification_failed`
1241
+ with `indeterminate: true` (read the folder before any retry), and
1242
+ `full_disk_access_missing`.
1243
+
1244
+ ---
1245
+
1164
1246
  #### `delete-folder`
1165
1247
 
1166
1248
  Deletes a folder.