apple-notes-mcp 2.8.49 → 2.9.1

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
@@ -809,10 +809,26 @@ Deletes a note (moves to Recently Deleted in Notes.app).
809
809
  | `id` | string | Yes | Exact CoreData note ID returned by a read or search |
810
810
  | `expectedContentHash` | string | Yes | `contentHash` from the exact note version being deleted |
811
811
  | `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
812
+ | `guardNoteId` | string | No | A second note (usually a verified copy) that must still be intact; needs Full Disk Access |
813
+ | `expectedGuardContentHash` | string | With `guardNoteId` | `contentHash` of the guard note from `get-note-content` |
814
+ | `requireActiveNoteId` | string | No | A second note that must still exist, be unlocked, stay outside Recently Deleted, and not be a Quick Note; its content is not fingerprinted. Needs Full Disk Access |
812
815
 
813
816
  Title-only deletion is rejected. If the note changed after the supplied hash
814
817
  was read, deletion is also rejected.
815
818
 
819
+ **Copy-then-retire.** To delete an original only while its copy is still good,
820
+ read both notes, verify the copy, and pass the copy as `guardNoteId` with its
821
+ `contentHash` as `expectedGuardContentHash`. The copy's revision is re-read just
822
+ before the delete, and its body, lock state, and folder are checked again
823
+ inside the delete AppleScript, with the same fail-closed Recently Deleted test
824
+ as the note being deleted. The guard note must not be a Quick Note, a flag only
825
+ the database holds, so the guard needs Full Disk Access. A copy the database
826
+ has not saved yet passes on the live checks alone. The pair is still not one
827
+ transaction: the rich revision (which also covers checklists and attachments)
828
+ is a pre-check, and the in-script check covers the body and state Notes.app
829
+ exposes. `requireActiveNoteId` is the narrower form for a destination you
830
+ wrote yourself rather than copied.
831
+
816
832
  **Example - Using ID (recommended):**
817
833
  ```json
818
834
  {
@@ -1849,7 +1865,9 @@ It also reports `runtimeOS` (`platform`, `macOSVersion` from `sw_vers`,
1849
1865
  `applescriptCore`, `fullDiskAccessReads`, `shortcutsBridges`,
1850
1866
  `backgroundOperationsBridge`, `nativeTagsBridge`, `markdownNoteBridge`, and the
1851
1867
  placeholders `checklistToggle`, `smartFolders`, `paragraphLinks`, and
1852
- `audioTranscription`, which need a native helper this server does not ship.
1868
+ `audioTranscription`, which need a native *write* helper this server does not
1869
+ ship (requirement `native_write_helper`). The opt-in private helper below is
1870
+ read-only, so enabling it does not change them: they stay `not_implemented`.
1853
1871
  Each entry carries `available`, `osSupported`, `minimumMacOSVersion`,
1854
1872
  `requirements`, `missing`, `unverified`, `tools`, and a machine-readable
1855
1873
  `reason` that is `null` when available and otherwise the first that applies:
@@ -1955,6 +1973,55 @@ not changed.
1955
1973
  Retrieves another note's real deep link and appends it with a static label while
1956
1974
  preserving the target note's native objects.
1957
1975
 
1976
+ ### Private helper (opt-in, unsupported Apple API)
1977
+
1978
+ An optional, **read-only** native helper reads note state through Notes' own
1979
+ data model (Apple's private NotesShared framework) instead of AppleScript or
1980
+ Shortcuts. It is **off by default** and nothing in the rest of the server
1981
+ depends on it. It cannot write: every store it opens is opened read-only, and
1982
+ write support was deliberately deferred by the maintainer until a second
1983
+ writer beside a running Notes.app, CRDT replica identity, and the iCloud
1984
+ upload lag are understood.
1985
+ Private API can break on any macOS update; see
1986
+ [TECHNICAL_NOTES.md](TECHNICAL_NOTES.md#private-helper-notesshared) for the
1987
+ API surface, risks, and safety contract.
1988
+
1989
+ To use it:
1990
+
1991
+ 1. Build it on your Mac from the packaged source (needs the Command Line
1992
+ Tools, `xcode-select --install`). No prebuilt binary ships with the package.
1993
+
1994
+ ```bash
1995
+ apple-notes-mcp setup --native-helper # build, ad-hoc sign, install
1996
+ apple-notes-mcp setup --native-helper --check # report only
1997
+ ```
1998
+
1999
+ 2. Set `APPLE_NOTES_MCP_ENABLE_PRIVATE=1` in the server's environment (or in
2000
+ the [config file](#configuration-file-when-the-host-strips-env)).
2001
+ 3. Give the app that launches the server Full Disk Access, the same grant the
2002
+ database reads already need.
2003
+
2004
+ The server checks the helper's recorded source and binary SHA-256 before every
2005
+ call and refuses a missing, stale, or modified helper with a machine-readable
2006
+ `code`. Rerun setup after upgrading apple-notes-mcp.
2007
+
2008
+ #### `native-helper-status`
2009
+
2010
+ Reports whether the helper is enabled, built, and current, and runs its live
2011
+ probe: macOS and Notes versions, whether NotesShared loads, whether every
2012
+ required class, selector, and model property exists, and whether the Notes
2013
+ store opens. Each feature reports `available` plus a `reason` code
2014
+ (`disabled`, `helper_not_installed`, `helper_stale`, `helper_modified`,
2015
+ `private_api_unavailable`, `store_unavailable`, …). Read-only; the response
2016
+ carries `readOnly: true`.
2017
+
2018
+ #### `native-note-state`
2019
+
2020
+ Reads one note's native state by Notes UUID (`identifier`) or x-coredata `id`:
2021
+ title, modification date, folder identifier, lock/trash/shared/editable
2022
+ flags, iCloud version counters, and a `revision` change token (compare two
2023
+ reads to detect a change). Opens the store with Core Data's read-only option.
2024
+
1958
2025
  ## Usage Patterns
1959
2026
 
1960
2027
  ### Basic Workflow
@@ -2095,6 +2162,10 @@ All configuration is optional — the server works out of the box. Override beha
2095
2162
  | `APPLE_NOTES_MCP_BLOCKS_MAX_BYTES` | `4194304` (4 MB) | Largest block payload one [`get-note-blocks`](#get-note-blocks) page returns; the page closes early to stay under it, and a single oversized paragraph comes back with `textOmitted: true`. |
2096
2163
  | `APPLE_NOTES_MCP_MAX_RETRIES` | `2` | Maximum attempts for a read-only AppleScript call that fails with a **transient** error (Notes.app busy / not responding / lost connection). `2` means one retry; set `1` to fail fast with no retries. Retries share the single `APPLE_NOTES_MCP_TIMEOUT_MS` budget rather than each getting a fresh one, and a retry is skipped when under a second of that budget remains — so this is a ceiling, not a guarantee. In particular a call that exhausts the budget with a **timeout** has no time left to retry by construction. Mutating operations run once because a timeout can occur after Notes.app applied the change. Non-transient errors (e.g. "note not found") never retry. |
2097
2164
  | `APPLE_NOTES_MCP_RETRY_DELAY_MS` | `1000` (1 s) | Base delay before the first retry; subsequent retries back off exponentially (1s, 2s, 4s, ...). |
2165
+ | `APPLE_NOTES_MCP_ENABLE_PRIVATE` | unset | Set to `1` to allow the opt-in [private helper](#private-helper-opt-in-unsupported-apple-api). Any other value keeps it off. |
2166
+ | `APPLE_NOTES_MCP_PRIVATE_HELPER_DIR` | `~/Library/Application Support/apple-notes-mcp/private-helper` | Where `setup --native-helper` installs the helper and its checksum manifest. |
2167
+ | `APPLE_NOTES_MCP_PRIVATE_HELPER_TIMEOUT_MS` | `20000` (20 s) | Per-call helper timeout. A timed-out write is indeterminate. |
2168
+ | `APPLE_NOTES_MCP_PRIVATE_STORE` | unset | Testing only: points the helper at a **copy** of `NoteStore.sqlite`. The helper refuses a path that resolves to the live store. |
2098
2169
  | `DEBUG` / `VERBOSE` | unset | Set either to enable verbose diagnostic logging to stderr. |
2099
2170
 
2100
2171
  ### Configuration file (when the host strips `env`)