apple-notes-mcp 2.9.4 → 2.9.6
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 +85 -2
- package/build/index.js +964 -71
- package/native/public-helper/apple-notes-public-helper.swift +257 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -746,6 +746,57 @@ HTML.
|
|
|
746
746
|
|
|
747
747
|
---
|
|
748
748
|
|
|
749
|
+
#### `list-note-links`
|
|
750
|
+
|
|
751
|
+
Lists links, read-only from the NoteStore database, in one note (`id`) or
|
|
752
|
+
across a `folder`, an `account`, or the whole library (no selector). Each link
|
|
753
|
+
has a `kind`:
|
|
754
|
+
|
|
755
|
+
| Kind | What it is | Where Notes stores it |
|
|
756
|
+
|------|------------|-----------------------|
|
|
757
|
+
| `inline` | A hyperlink on text | Inside the note body |
|
|
758
|
+
| `card` | A rich link preview | An attachment row with the URL and title |
|
|
759
|
+
| `note` | A native link chip to another note | An inline-attachment row with a Notes deep link |
|
|
760
|
+
| `section` | A native link chip to a heading or paragraph | Same, with `paragraphID` in the deep link |
|
|
761
|
+
|
|
762
|
+
Each row has `url`, `text` (label), `linkSafe`, `targetNote` and `paragraphId`
|
|
763
|
+
for Notes deep links, `section` for section chips, and for cards
|
|
764
|
+
`attachmentId` and `previewPath` (Notes' largest cached preview image, found
|
|
765
|
+
the same way [`list-attachments`](#list-attachments) finds it, or null when
|
|
766
|
+
Notes has none). Each row also names its source: `noteId`, `noteIdentifier`,
|
|
767
|
+
`noteTitle`, `noteModified`, `folder`, `folderPath` (as `list-folders` prints
|
|
768
|
+
it), `account`, and `accountIdentifier`. When bodies were decoded, `start` and
|
|
769
|
+
`blockIndex` give the link's position, and `inBody: false` marks a card or chip
|
|
770
|
+
row with no marker left in the body.
|
|
771
|
+
|
|
772
|
+
Inline links need every body in scope decompressed and decoded, so a folder,
|
|
773
|
+
account, or library scan includes them only with `includeInline: true`
|
|
774
|
+
(slower). A single note always includes them. A `folder` scope includes its
|
|
775
|
+
subfolders unless `includeSubfolders` is false. Scans skip Recently Deleted
|
|
776
|
+
and folderless notes; a note requested by `id` is read wherever it is. Links
|
|
777
|
+
come newest-modified note first, in body order within a note.
|
|
778
|
+
|
|
779
|
+
**Requires:** Full Disk Access.
|
|
780
|
+
|
|
781
|
+
| Parameter | Type | Required | Description |
|
|
782
|
+
|-----------|------|----------|-------------|
|
|
783
|
+
| `id` | string | No | One exact note ID (x-coredata id, Notes UUID, or numeric key). Do not combine with `account` or `folder` |
|
|
784
|
+
| `account` | string | No | Account name (exact or unique-prefix match, as in the other tools) |
|
|
785
|
+
| `folder` | string | No | Folder name (any depth, must be unique) or path from the top level as `list-folders` prints it, such as `Work/Clients`; escape a literal slash as `\/` |
|
|
786
|
+
| `includeSubfolders` | boolean | No | With `folder`, also list notes in its subfolders (default true) |
|
|
787
|
+
| `includeInline` | boolean | No | Decode bodies for inline links (default true for `id`, false otherwise) |
|
|
788
|
+
| `kinds` | string[] | No | Only these kinds: `inline`, `card`, `note`, `section` |
|
|
789
|
+
| `offset` | number | No | First link to return (default 0). Use `page.nextOffset` |
|
|
790
|
+
| `limit` | number | No | Maximum links per page (default 200, max 2000) |
|
|
791
|
+
|
|
792
|
+
The response also reports `scope`, `counts` per kind, `notesInScope`,
|
|
793
|
+
`notesWithoutBody` (locked, empty or undecodable bodies when inline links were
|
|
794
|
+
requested), and `page`. A page stops early to stay under
|
|
795
|
+
`APPLE_NOTES_MCP_BLOCKS_MAX_BYTES`. An ambiguous folder name is refused with a
|
|
796
|
+
message that lists the matching paths.
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
749
800
|
#### `list-native-tags`
|
|
750
801
|
|
|
751
802
|
Lists actual native Notes tags. This differs from textual hashtag search. It is
|
|
@@ -1783,6 +1834,22 @@ A recording extended with more takes has several fragments. Their transcripts ar
|
|
|
1783
1834
|
|
|
1784
1835
|
---
|
|
1785
1836
|
|
|
1837
|
+
#### `get-note-drawings`
|
|
1838
|
+
|
|
1839
|
+
Decodes a note's classic PencilKit drawings (`com.apple.drawing.2` and the older `com.apple.drawing` attachments) into strokes and SVG. The PencilKit bytes are read read-only from the NoteStore database and decoded by Apple's public `PKDrawing(data:)` in the [public native helper](#public-native-helper). Modern Paper sketches (`com.apple.paper`) are a different format and are not decoded here.
|
|
1840
|
+
|
|
1841
|
+
**Requires:** Full Disk Access for the MCP host process, and the public native helper built once with `apple-notes-mcp setup --public-helper`.
|
|
1842
|
+
|
|
1843
|
+
| Parameter | Type | Required | Description |
|
|
1844
|
+
|-----------|------|----------|-------------|
|
|
1845
|
+
| `id` | string | Yes | Note ID (use `search-notes` to find it first) |
|
|
1846
|
+
| `format` | string | No | `"json"` (default) returns strokes, `"svg"` returns a standalone SVG document per drawing, `"both"` returns both |
|
|
1847
|
+
| `includePoints` | boolean | No | Include per-point `x`, `y`, `width`, `opacity`, and `force` in JSON strokes (default `true`) |
|
|
1848
|
+
|
|
1849
|
+
**Returns:** `status` (`ok`, `partial`, `error`, or `none` when the note has no classic drawing), `drawingCount`, and one entry per drawing with `attachmentId`, `identifier`, `typeUti`, `status` (`ok`, or `error` with a `code` such as `no_data`, `undecodable`, or `timeout`), `strokeCount`, `bounds`, `truncated`, `strokes` (each with `inkType`, sRGB `color` with alpha, mean `width`, `pointCount`, `bounds`, and `points`), and `svg`. Points are already in drawing coordinates. When a response would exceed `APPLE_NOTES_MCP_EXPORT_MAX_BYTES`, points are dropped and `pointsOmitted` is set. The SVG draws one path per stroke; it is a faithful outline, not a pixel-exact copy of PencilKit's ink textures.
|
|
1850
|
+
|
|
1851
|
+
---
|
|
1852
|
+
|
|
1786
1853
|
#### `add-attachment`
|
|
1787
1854
|
|
|
1788
1855
|
Adds one nonempty local file of at most 64 MiB to an exact note using `id`, the
|
|
@@ -2326,7 +2393,7 @@ All configuration is optional — the server works out of the box. Override beha
|
|
|
2326
2393
|
| `APPLE_NOTES_MCP_CONFIG_FILE` | `~/Library/Application Support/apple-notes-mcp/config.json` | Path to the JSON config file (see below). |
|
|
2327
2394
|
| `APPLE_NOTES_MCP_TIMEOUT_MS` | `30000` (30 s) | Total AppleScript operation timeout, including retry attempts and delays. Raise it if full-library operations (large searches, exports) time out on a big Notes library. Per-call `timeoutMs` options still win, and a write tool's `timeoutSeconds` argument overrides it for that call. |
|
|
2328
2395
|
| `APPLE_NOTES_MCP_EXPORT_MAX_BYTES` | `8388608` (8 MB) | Largest response `export-notes-json` sends; a page closes early to stay under it. `export-notes-markdown` returns inline Markdown up to half of it. The default sits below the 10 MB per-message limit of MCP SDK stdio clients, which drop the connection on anything larger. Raise it only if your MCP client accepts bigger messages. |
|
|
2329
|
-
| `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`. [`get-note-structure`](#get-note-structure) also omits note text larger than this. A [`list-note-paragraphs`](#list-note-paragraphs) page also stops early to stay under it. |
|
|
2396
|
+
| `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`. [`get-note-structure`](#get-note-structure) also omits note text larger than this. A [`list-note-paragraphs`](#list-note-paragraphs) or [`list-note-links`](#list-note-links) page also stops early to stay under it. |
|
|
2330
2397
|
| `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. |
|
|
2331
2398
|
| `APPLE_NOTES_MCP_RETRY_DELAY_MS` | `1000` (1 s) | Base delay before the first retry; subsequent retries back off exponentially (1s, 2s, 4s, ...). |
|
|
2332
2399
|
| `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. |
|
|
@@ -2359,7 +2426,7 @@ MCP stores no secrets, but as a general rule keep only non-secret config here.
|
|
|
2359
2426
|
|
|
2360
2427
|
## Full Disk Access
|
|
2361
2428
|
|
|
2362
|
-
Several tools read directly from the Apple Notes SQLite database, which lives in a macOS-protected directory. Those tools require **Full Disk Access** for the process running the MCP server: `get-checklist-state`, `get-note-metadata`, `get-note-blocks`, `list-note-paragraphs`, `get-paragraph-link`, `get-note-structure`, `export-notes-markdown`, `export-notes-html`, `get-audio-transcripts`, `list-special-notes`, `list-native-tags`, `list-recent-notes`, `list-folder-tree`, `get-note-link`, the checklist annotations in `get-note-markdown`, `list-attachments` with `includePaths` or `firstImage`, `export-attachments`, `list-paper-attachments`, `export-paper-image`, and the database half of `get-sync-status`.
|
|
2429
|
+
Several tools read directly from the Apple Notes SQLite database, which lives in a macOS-protected directory. Those tools require **Full Disk Access** for the process running the MCP server: `get-checklist-state`, `get-note-metadata`, `get-note-blocks`, `list-note-paragraphs`, `get-paragraph-link`, `get-note-structure`, `list-note-links`, `export-notes-markdown`, `export-notes-html`, `get-audio-transcripts`, `list-special-notes`, `list-native-tags`, `list-recent-notes`, `list-folder-tree`, `get-note-link`, the checklist annotations in `get-note-markdown`, `list-attachments` with `includePaths` or `firstImage`, `export-attachments`, `list-paper-attachments`, `export-paper-image`, and the database half of `get-sync-status`.
|
|
2363
2430
|
|
|
2364
2431
|
> 📘 **For the full why-and-how walkthrough (which app to grant, verifying with `doctor`, graceful degradation), see the [Full Disk Access Setup Guide](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/FULL-DISK-ACCESS.md).** The summary below is the quick version.
|
|
2365
2432
|
|
|
@@ -2385,6 +2452,22 @@ Every tool that does not read the Notes database works normally without Full Dis
|
|
|
2385
2452
|
- `get-note-link` returns an error on macOS 26+; on macOS 12–15 it still works via the AppleScript `note link` fallback
|
|
2386
2453
|
- `get-note-markdown` returns plain list items without `[x]`/`[ ]` annotations (graceful fallback)
|
|
2387
2454
|
- `get-sync-status` still answers, but reports no pending uploads and no active sync — treat that as "unknown", not "idle"
|
|
2455
|
+
- `get-note-drawings` returns the same Full Disk Access error
|
|
2456
|
+
|
|
2457
|
+
---
|
|
2458
|
+
|
|
2459
|
+
## Public native helper
|
|
2460
|
+
|
|
2461
|
+
`get-note-drawings` needs Apple's PencilKit framework, which has no AppleScript or command-line interface. For it, the server uses a small Swift helper that links public Apple frameworks only (AppKit and PencilKit). No prebuilt binary ships with the package. Build it once on your Mac:
|
|
2462
|
+
|
|
2463
|
+
```bash
|
|
2464
|
+
apple-notes-mcp setup --public-helper # compile, sign, verify, install
|
|
2465
|
+
apple-notes-mcp setup --public-helper --check # report the installed state only
|
|
2466
|
+
```
|
|
2467
|
+
|
|
2468
|
+
Setup compiles `native/public-helper/apple-notes-public-helper.swift` with `xcrun swiftc` (install the Command Line Tools with `xcode-select --install` if it is missing), signs it ad hoc, runs its `hello` handshake, and installs it in `~/Library/Application Support/apple-notes-mcp/public-helper/` next to a manifest recording the SHA-256 of the source and the binary. Before every use the server re-checks both digests: after an upgrade that changes the helper source, or if the binary is replaced, the helper is refused until you run setup again. `APPLE_NOTES_MCP_PUBLIC_HELPER_DIR` overrides the install folder and `APPLE_NOTES_MCP_PUBLIC_HELPER_TIMEOUT_MS` the per-call timeout.
|
|
2469
|
+
|
|
2470
|
+
The helper never opens the Notes database and never writes under the Notes group container. The server reads the bytes it needs (read-only) and passes them to the helper on stdin; the helper answers with one JSON object on stdout.
|
|
2388
2471
|
|
|
2389
2472
|
---
|
|
2390
2473
|
|