apple-notes-mcp 2.9.21 → 2.9.22
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 +112 -23
- package/docs/APPLESCRIPT-LIMITATIONS.md +19 -12
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Apple Notes MCP Server
|
|
2
2
|
|
|
3
|
-
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that
|
|
3
|
+
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that lets Claude (Claude Code and Claude Desktop), Codex, and other MCP clients read, search, create, edit, organize, and export notes in Apple Notes on macOS. It runs locally on your Mac and talks to Notes through AppleScript, Apple Shortcuts, and read-only access to the Notes database.
|
|
4
|
+
|
|
5
|
+
Beyond creating and editing notes, it manages folders and accounts, native tags, checklists, tables, pinned notes, links, and attachments. It can also export notes as Markdown, HTML, or JSON, read audio transcripts and drawings, and run a query language across your whole library.
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/apple-notes-mcp)
|
|
6
8
|
[](https://www.npmjs.com/package/apple-notes-mcp)
|
|
@@ -15,6 +17,13 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that e
|
|
|
15
17
|
<img src="https://raw.githubusercontent.com/sweetrb/apple-notes-mcp/main/codex/assets/screenshot.png" alt="Apple Notes MCP — create, search, and organize Apple Notes from Codex, Claude, and other AI assistants" width="680">
|
|
16
18
|
</p>
|
|
17
19
|
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [What is This?](#what-is-this) · [Quick Start](#quick-start) · [Requirements](#requirements) · [Features](#features)
|
|
23
|
+
- [Tool Reference](#tool-reference): [notes](#note-operations), [folders](#folder-operations), [accounts](#account-operations), [batch](#batch-operations), [export, attachments, and media](#export-operations), [diagnostics](#diagnostics), [native background operations](#native-background-operations), [private helper](#private-helper-opt-in-unsupported-apple-api)
|
|
24
|
+
- [Usage Patterns](#usage-patterns) · [Installation Options](#installation-options) · [Configuration](#configuration) · [Full Disk Access](#full-disk-access) · [Public native helper](#public-native-helper)
|
|
25
|
+
- [Security and Privacy](#security-and-privacy) · [Known Limitations](#known-limitations) · [Troubleshooting](#troubleshooting) · [Recurring macOS permission prompts](#recurring-macos-permission-prompts) · [Development](#development)
|
|
26
|
+
|
|
18
27
|
## What is This?
|
|
19
28
|
|
|
20
29
|
This server acts as a bridge between AI assistants and Apple Notes. Once configured, you can ask Claude (or any MCP-compatible AI) to:
|
|
@@ -25,7 +34,7 @@ This server acts as a bridge between AI assistants and Apple Notes. Once configu
|
|
|
25
34
|
- "Move my draft notes to the Archive folder"
|
|
26
35
|
- "What notes do I have in my Work folder?"
|
|
27
36
|
|
|
28
|
-
The AI assistant communicates with this server, which then uses AppleScript to interact with the Notes app on your Mac.
|
|
37
|
+
The AI assistant communicates with this server, which then uses AppleScript to interact with the Notes app on your Mac. Some features also use packaged Apple Shortcuts (native tags, checklists, tables, pinning) or read the Notes database read-only (queries, checklist state, transcripts). The server itself makes no network requests; what your MCP client does with the results is up to that client.
|
|
29
38
|
|
|
30
39
|
## Quick Start
|
|
31
40
|
|
|
@@ -56,7 +65,7 @@ Install as a Claude Code plugin for automatic configuration and enhanced AI beha
|
|
|
56
65
|
|
|
57
66
|
This method also installs a **skill** that teaches Claude when and how to use Apple Notes effectively.
|
|
58
67
|
|
|
59
|
-
On the first tool call, macOS shows an Automation permission prompt ("Claude" wants access to control "Notes") — click **OK**. Optionally, grant **Full Disk Access** (under Claude Desktop, to the Node binary that runs the server; from a terminal, to the terminal app) to enable the database-backed tools
|
|
68
|
+
On the first tool call, macOS shows an Automation permission prompt ("Claude" wants access to control "Notes") — click **OK**. Optionally, grant **Full Disk Access** (under Claude Desktop, to the Node binary that runs the server; from a terminal, to the terminal app) to enable the database-backed tools, such as `query-notes`, `get-checklist-state`, `get-note-tables`, `get-audio-transcripts`, `list-native-tags`, and `list-recent-notes` (the full list is under [Full Disk Access](#full-disk-access)); see the [Full Disk Access Setup Guide](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/FULL-DISK-ACCESS.md). The rest of the server is pure AppleScript and works without it.
|
|
60
69
|
|
|
61
70
|
Native tag, checklist, table, pin, and rich append operations use two packaged
|
|
62
71
|
Apple Shortcuts. A third, `Apple Notes MCP - Create Markdown Note`, is optional:
|
|
@@ -148,6 +157,10 @@ npm install -g apple-notes-mcp
|
|
|
148
157
|
|
|
149
158
|
On first use, macOS will ask for permission to automate Notes.app. Click "OK" to allow.
|
|
150
159
|
|
|
160
|
+
### Other MCP clients
|
|
161
|
+
|
|
162
|
+
The server is a standard MCP server over stdio, so any client that can launch a local stdio server can run it with the same command the hosts above use: `npx -y apple-notes-mcp`. Most clients take it in an `mcpServers` entry shaped like the Claude Desktop example. If your client cannot pass environment variables, use the [configuration file](#configuration-file-when-the-host-strips-env).
|
|
163
|
+
|
|
151
164
|
## Requirements
|
|
152
165
|
|
|
153
166
|
- **macOS** - Apple Notes and AppleScript are macOS-only
|
|
@@ -158,24 +171,30 @@ On first use, macOS will ask for permission to automate Notes.app. Click "OK" to
|
|
|
158
171
|
|
|
159
172
|
| Feature | Description |
|
|
160
173
|
|---------|-------------|
|
|
161
|
-
| **Create Notes** | Create notes
|
|
174
|
+
| **Create Notes** | Create notes from plaintext, HTML, or Markdown, with optional folder/account targeting (Markdown creation uses an optional Shortcut on macOS 26 or later) |
|
|
162
175
|
| **Search Notes** | Find notes by title or search within note content |
|
|
163
176
|
| **Query Language** | `query-notes` combines text, folder, account, tag, attachment, checklist, flag, word-count, and date conditions with AND/OR/NOT, read from the Notes database (requires Full Disk Access) |
|
|
164
|
-
| **Read Notes** | Retrieve note content
|
|
165
|
-
| **
|
|
177
|
+
| **Read Notes** | Retrieve note content as HTML, plain text, or Markdown, plus metadata that AppleScript does not expose (pinned, Quick Note, locked, Recently Deleted) |
|
|
178
|
+
| **Note Structure** | Decode a note into typed paragraph blocks, list its paragraphs and links, and get a direct link to one paragraph (requires Full Disk Access) |
|
|
179
|
+
| **Update Notes** | Replace, append to, or prepend to an existing note, guarded by a content hash so a note that changed since it was read is never overwritten |
|
|
166
180
|
| **Delete Notes** | Remove notes (moves to Recently Deleted) |
|
|
167
181
|
| **Move Notes** | Organize notes into folders (supports nested paths) |
|
|
168
|
-
| **Folder Management** | Create, list, and delete folders with full hierarchical path support |
|
|
182
|
+
| **Folder Management** | Create, list, rename, and delete folders with full hierarchical path support; read the folder tree with note counts and Smart Folder rules |
|
|
169
183
|
| **Multi-Account** | Work with iCloud, Gmail, Exchange, or any configured account, including account IDs and default folders |
|
|
170
184
|
| **Batch Operations** | Delete or move multiple notes at once |
|
|
171
|
-
| **
|
|
172
|
-
| **
|
|
173
|
-
| **
|
|
174
|
-
| **
|
|
175
|
-
| **
|
|
185
|
+
| **Native Tags** | List, add, remove, and replace real Notes tags (not just `#hashtag` text) |
|
|
186
|
+
| **Checklists** | Read checklist done/undone state from the Notes database, and append real checklist items through a Shortcut |
|
|
187
|
+
| **Tables** | Read native tables as Markdown and JSON, and append new native tables |
|
|
188
|
+
| **Pinning** | Read pinned state and pin or unpin a note |
|
|
189
|
+
| **Links** | Get a note's `notes://` deep link, insert web, mail, or note-to-note links, and list the links in a note or folder |
|
|
190
|
+
| **Export** | Export notes as paginated JSON, or render a note or folder as Markdown (with reusable templates) or standalone HTML |
|
|
191
|
+
| **Attachments** | Add files to notes (from a path or the pasteboard), list attachments with their on-disk paths, find a note's lead image, save or batch-export them, or fetch their bytes as base64 |
|
|
192
|
+
| **Audio and Drawings** | Read the transcripts Notes stored for recordings, transcribe audio on-device, and decode drawings to strokes and SVG |
|
|
193
|
+
| **Incremental Sync** | `list-recent-notes` pages through notes by modification time with an exact cursor |
|
|
194
|
+
| **Notes.app UI State** | Reveal a note, folder, account, or attachment in Notes.app, or read the current Notes.app selection |
|
|
176
195
|
| **Sync Awareness** | Detect iCloud sync in progress, warn about incomplete results |
|
|
177
196
|
| **Collaboration** | Detect shared notes, warn before modifying |
|
|
178
|
-
| **Diagnostics** | `health-check` plus a richer `doctor` (reachability, automation permission, accounts, Full Disk Access), sync status, and
|
|
197
|
+
| **Diagnostics** | `health-check` plus a richer `doctor` (reachability, automation permission, accounts, Full Disk Access), sync status, statistics, and `get-capabilities` for the Shortcut bridges |
|
|
179
198
|
|
|
180
199
|
Read/list/get tools also return **structured JSON** (`structuredContent`) alongside the text, so agents can consume results without parsing prose.
|
|
181
200
|
|
|
@@ -193,8 +212,8 @@ reading Notes' own database instead; the rest genuinely cannot be supported. See
|
|
|
193
212
|
**[docs/APPLESCRIPT-LIMITATIONS.md](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/APPLESCRIPT-LIMITATIONS.md)**
|
|
194
213
|
for the investigation and verification behind each:
|
|
195
214
|
|
|
196
|
-
- **Pinned notes** — Notes has no scriptable `pinned` property via AppleScript. Pin state
|
|
197
|
-
- **Note-to-note links** — AppleScript exposes no link property or link element
|
|
215
|
+
- **Pinned notes** — Notes has no scriptable `pinned` property via AppleScript. Pin state is **read** from the NoteStore database by the BETA `get-note-metadata` tool and `list-special-notes`, and **set** through the Background Operations Shortcut by [`set-note-pinned`](#set-note-pinned).
|
|
216
|
+
- **Note-to-note links** — AppleScript exposes no link property or link element. Links are instead read from the NoteStore database by [`list-note-links`](#list-note-links), and inserted by [`insert-note-link`](#insert-note-link) (through a Shortcut) or [`insert-link`](#insert-link). A shareable `notes://showNote?identifier=<uuid>` deep link is available via [`get-note-link`](#get-note-link).
|
|
198
217
|
|
|
199
218
|
---
|
|
200
219
|
|
|
@@ -601,6 +620,10 @@ stable row and column identifiers. `tableCellsComplete` is false when Notes
|
|
|
601
620
|
metadata cannot be decoded completely. This tool is read-only and requires Full
|
|
602
621
|
Disk Access.
|
|
603
622
|
|
|
623
|
+
| Parameter | Type | Required | Description |
|
|
624
|
+
|-----------|------|----------|-------------|
|
|
625
|
+
| `id` | string | Yes | Exact note ID, in any [identifier form](#identifier-forms) |
|
|
626
|
+
|
|
604
627
|
---
|
|
605
628
|
|
|
606
629
|
#### `get-note-tables`
|
|
@@ -861,6 +884,13 @@ Shortcut runs. It refuses ambiguous title-and-scope matches and never retries an
|
|
|
861
884
|
uncertain write. Install the signed workflow as described in
|
|
862
885
|
[`shortcuts/README.md`](shortcuts/README.md).
|
|
863
886
|
|
|
887
|
+
| Parameter | Type | Required | Description |
|
|
888
|
+
|-----------|------|----------|-------------|
|
|
889
|
+
| `id` | string | Yes | Exact note ID, in any [identifier form](#identifier-forms) |
|
|
890
|
+
| `expectedContentHash` | string | Yes | `contentHash` from a fresh read of the note |
|
|
891
|
+
| `scopeText` | string | Yes | Distinctive phrase already in the note, 12–500 characters |
|
|
892
|
+
| `tags` | string[] | Yes | 1–100 tag names to add |
|
|
893
|
+
|
|
864
894
|
---
|
|
865
895
|
|
|
866
896
|
#### `get-note-plaintext`
|
|
@@ -948,6 +978,7 @@ Updates an existing note's content and/or title.
|
|
|
948
978
|
| `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) |
|
|
949
979
|
| `allowLinkChanges` | boolean | No | Set to `true` only when intentionally changing or removing existing links |
|
|
950
980
|
| `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
|
|
981
|
+
| `timeoutSeconds` | number | No | Per-call timeout, 1–120 seconds, as for [`create-note`](#create-note). A timed-out write is uncertain: read the note by id before any retry |
|
|
951
982
|
|
|
952
983
|
Title-only updates are rejected because Apple Notes titles are not unique.
|
|
953
984
|
|
|
@@ -993,6 +1024,7 @@ Deletes a note (moves to Recently Deleted in Notes.app).
|
|
|
993
1024
|
| `id` | string | Yes | Exact CoreData note ID returned by a read or search |
|
|
994
1025
|
| `expectedContentHash` | string | Yes | `contentHash` from the exact note version being deleted |
|
|
995
1026
|
| `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
|
|
1027
|
+
| `timeoutSeconds` | number | No | Per-call timeout, 1–120 seconds, as for [`create-note`](#create-note). A timed-out write is uncertain: read the note by id before any retry |
|
|
996
1028
|
| `guardNoteId` | string | No | A second note (usually a verified copy) that must still be intact; needs Full Disk Access |
|
|
997
1029
|
| `expectedGuardContentHash` | string | With `guardNoteId` | `contentHash` of the guard note from `get-note-content` |
|
|
998
1030
|
| `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 |
|
|
@@ -1056,7 +1088,9 @@ A smart folder is never a destination: it only gathers notes by its rules, and N
|
|
|
1056
1088
|
|-----------|------|----------|-------------|
|
|
1057
1089
|
| `id` | string | Yes | Exact CoreData note ID returned by a read or search |
|
|
1058
1090
|
| `folder` | string | Yes | Destination folder name or nested path (e.g., `"Work/Clients"`) |
|
|
1091
|
+
| `account` | string | No | Account whose `folder` is the destination (default: the default account) |
|
|
1059
1092
|
| `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
|
|
1093
|
+
| `timeoutSeconds` | number | No | Per-call timeout, 1–120 seconds, as for [`create-note`](#create-note). A timed-out write is uncertain: read the note by id before any retry |
|
|
1060
1094
|
|
|
1061
1095
|
Title-only moves are rejected.
|
|
1062
1096
|
|
|
@@ -1114,7 +1148,9 @@ Appends or prepends content to an existing note without replacing it. Always rea
|
|
|
1114
1148
|
| `position` | string | No | `"after"` (default) appends to the end; `"before"` inserts directly below the note's title line, so the title stays first |
|
|
1115
1149
|
| `separator` | string | No | String placed between existing content and new content (default: two newlines → `<div><br></div>` in HTML) |
|
|
1116
1150
|
| `format` | string | No | Format of the content being appended: `"plaintext"` (default) or `"html"` |
|
|
1151
|
+
| `scopeText` | string | Native-object notes only | Unique existing phrase, as for [`append-native`](#append-native) |
|
|
1117
1152
|
| `ifFolderId`, `ifAncestorFolderId`, `forbiddenAncestorFolderIds` | string, string, string[] | No | Folder preconditions; see [Folder scope guards](#folder-scope-guards) |
|
|
1153
|
+
| `timeoutSeconds` | number | No | Per-call timeout, 1–120 seconds, as for [`create-note`](#create-note). A timed-out write is uncertain: read the note by id before any retry |
|
|
1118
1154
|
|
|
1119
1155
|
Title-only appends are rejected.
|
|
1120
1156
|
|
|
@@ -2355,6 +2391,15 @@ distinctive existing `scopeText` so the Shortcut and server can independently
|
|
|
2355
2391
|
resolve the same note. Creating a note from Markdown is
|
|
2356
2392
|
[`create-note`](#create-note) with `format: "markdown"`.
|
|
2357
2393
|
|
|
2394
|
+
Every tool below that edits one note takes these parameters, plus the ones
|
|
2395
|
+
listed under the tool itself:
|
|
2396
|
+
|
|
2397
|
+
| Parameter | Type | Required | Description |
|
|
2398
|
+
|-----------|------|----------|-------------|
|
|
2399
|
+
| `id` | string | Yes | Exact note ID: x-coredata id, Notes UUID, or numeric key (see [Identifier forms](#identifier-forms)) |
|
|
2400
|
+
| `expectedContentHash` | string | Yes | `contentHash` (`sha256:…`) from a fresh read of the note |
|
|
2401
|
+
| `scopeText` | string | Yes | Distinctive phrase already in the note, 12–500 characters, that Notes search can find. Prefer plain words without punctuation, hashtags, or paths |
|
|
2402
|
+
|
|
2358
2403
|
When the Shortcut refuses a request (its Find Notes step did not return
|
|
2359
2404
|
exactly one note with that exact title and `scopeText`) or stops with its own
|
|
2360
2405
|
action error, and the note reads back unchanged, the tool reports the
|
|
@@ -2419,11 +2464,20 @@ unapproved bridge is identified rather than guessed at; a timeout also says it
|
|
|
2419
2464
|
may be an unanswered first-run consent prompt and names the Shortcut to run once
|
|
2420
2465
|
in the foreground.
|
|
2421
2466
|
|
|
2467
|
+
| Parameter | Type | Required | Description |
|
|
2468
|
+
|-----------|------|----------|-------------|
|
|
2469
|
+
| `content` | string | Yes | Content to append, up to 1,048,576 characters |
|
|
2470
|
+
| `format` | string | No | `"plaintext"` (default), `"html"`, or `"markdown"` |
|
|
2471
|
+
|
|
2422
2472
|
#### `create-checklist-item`
|
|
2423
2473
|
|
|
2424
2474
|
Appends one real unchecked Notes checklist item and verifies its native identity
|
|
2425
2475
|
and text.
|
|
2426
2476
|
|
|
2477
|
+
| Parameter | Type | Required | Description |
|
|
2478
|
+
|-----------|------|----------|-------------|
|
|
2479
|
+
| `text` | string | Yes | The item's text |
|
|
2480
|
+
|
|
2427
2481
|
#### `create-checklist-items`
|
|
2428
2482
|
|
|
2429
2483
|
Appends several real unchecked checklist items, 1 to 20, in the order given.
|
|
@@ -2463,27 +2517,54 @@ cell. It never substitutes a text table. Omit `rows` for an empty 2 × 2 table,
|
|
|
2463
2517
|
the size Notes itself inserts from Format > Table; its four empty cells are
|
|
2464
2518
|
verified the same way.
|
|
2465
2519
|
|
|
2520
|
+
| Parameter | Type | Required | Description |
|
|
2521
|
+
|-----------|------|----------|-------------|
|
|
2522
|
+
| `rows` | string[][] | No | Cell text, row by row: 1–1,000 rows of 1–100 cells, every row the same length. Omit for an empty 2 × 2 table |
|
|
2523
|
+
|
|
2466
2524
|
#### `set-note-pinned`
|
|
2467
2525
|
|
|
2468
2526
|
Sets an explicit pinned state after checking both the expected current state and
|
|
2469
2527
|
the note revision. It does not rewrite the body.
|
|
2470
2528
|
|
|
2529
|
+
| Parameter | Type | Required | Description |
|
|
2530
|
+
|-----------|------|----------|-------------|
|
|
2531
|
+
| `expectedPinned` | boolean | Yes | The pinned state you last read; the call is refused if it differs |
|
|
2532
|
+
| `pinned` | boolean | Yes | The pinned state to set |
|
|
2533
|
+
|
|
2471
2534
|
#### `remove-native-tags`
|
|
2472
2535
|
|
|
2473
2536
|
Removes specified tags from one exact note while preserving unrelated tags and
|
|
2474
2537
|
native objects. It does not delete global tag definitions.
|
|
2475
2538
|
|
|
2539
|
+
| Parameter | Type | Required | Description |
|
|
2540
|
+
|-----------|------|----------|-------------|
|
|
2541
|
+
| `tags` | string[] | Yes | 1–100 tag names to remove |
|
|
2542
|
+
|
|
2476
2543
|
#### `replace-native-tag`
|
|
2477
2544
|
|
|
2478
2545
|
Adds and verifies the new tag before removing the old tag on an explicit list of
|
|
2479
2546
|
freshly read notes. Stops on the first uncertain result. Smart Folder rules are
|
|
2480
2547
|
not changed.
|
|
2481
2548
|
|
|
2549
|
+
Unlike the other tools in this section, it takes a list of notes instead of one
|
|
2550
|
+
`id`:
|
|
2551
|
+
|
|
2552
|
+
| Parameter | Type | Required | Description |
|
|
2553
|
+
|-----------|------|----------|-------------|
|
|
2554
|
+
| `notes` | object[] | Yes | 1–100 entries, each with the note's `id`, `expectedContentHash`, and `scopeText` |
|
|
2555
|
+
| `oldTag` | string | Yes | Tag to remove after the new one is verified |
|
|
2556
|
+
| `newTag` | string | Yes | Tag to add |
|
|
2557
|
+
|
|
2482
2558
|
#### `insert-note-link`
|
|
2483
2559
|
|
|
2484
2560
|
Retrieves another note's real deep link and appends it with a static label while
|
|
2485
2561
|
preserving the target note's native objects.
|
|
2486
2562
|
|
|
2563
|
+
| Parameter | Type | Required | Description |
|
|
2564
|
+
|-----------|------|----------|-------------|
|
|
2565
|
+
| `linkedNoteId` | string | Yes | ID of the note to link to, in any [identifier form](#identifier-forms) |
|
|
2566
|
+
| `label` | string | No | Visible link text (default: the linked note's title) |
|
|
2567
|
+
|
|
2487
2568
|
### Private helper (opt-in, unsupported Apple API)
|
|
2488
2569
|
|
|
2489
2570
|
An optional, **read-only** native helper reads note state through Notes' own
|
|
@@ -2676,8 +2757,14 @@ All configuration is optional — the server works out of the box. Override beha
|
|
|
2676
2757
|
| `APPLE_NOTES_MCP_RETRY_DELAY_MS` | `1000` (1 s) | Base delay before the first retry; subsequent retries back off exponentially (1s, 2s, 4s, ...). |
|
|
2677
2758
|
| `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. |
|
|
2678
2759
|
| `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. |
|
|
2679
|
-
| `APPLE_NOTES_MCP_PRIVATE_HELPER_TIMEOUT_MS` | `20000` (20 s) | Per-call helper
|
|
2760
|
+
| `APPLE_NOTES_MCP_PRIVATE_HELPER_TIMEOUT_MS` | `20000` (20 s) | Per-call timeout for the private helper. The helper is read-only, so a timeout changes nothing in Notes. |
|
|
2680
2761
|
| `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. |
|
|
2762
|
+
| `APPLE_NOTES_MCP_PUBLIC_HELPER_DIR` | `~/Library/Application Support/apple-notes-mcp/public-helper` | Where `setup --public-helper` installs the [public native helper](#public-native-helper) and its checksum manifest. |
|
|
2763
|
+
| `APPLE_NOTES_MCP_PUBLIC_HELPER_TIMEOUT_MS` | `30000` (30 s) | Per-call timeout for the public native helper (`get-note-drawings`, `transcribe-note-audio`). |
|
|
2764
|
+
| `APPLE_NOTES_MCP_TAGS_SHORTCUT` | `Apple Notes MCP - Native Tags` | Name or UUID of the installed Native Tags bridge to run. `setup` still installs and checks the default name. |
|
|
2765
|
+
| `APPLE_NOTES_MCP_BACKGROUND_SHORTCUT` | `Apple Notes MCP - Background Operations v5` | Name or UUID of the installed Background Operations bridge to run. `setup` still installs and checks the default name. |
|
|
2766
|
+
| `APPLE_NOTES_MCP_MARKDOWN_SHORTCUT` | `Apple Notes MCP - Create Markdown Note` | Name or UUID of the installed Create Markdown Note bridge to run. `setup` still installs and checks the default name. |
|
|
2767
|
+
| `APPLE_NOTES_MCP_PASTEBOARD_NAME` | unset | Testing only: makes [`add-attachment-from-pasteboard`](#add-attachment-from-pasteboard) read a private named pasteboard instead of the general clipboard. |
|
|
2681
2768
|
| `DEBUG` / `VERBOSE` | unset | Set either to enable verbose diagnostic logging to stderr. |
|
|
2682
2769
|
|
|
2683
2770
|
### Configuration file (when the host strips `env`)
|
|
@@ -2704,7 +2791,7 @@ MCP stores no secrets, but as a general rule keep only non-secret config here.
|
|
|
2704
2791
|
|
|
2705
2792
|
## Full Disk Access
|
|
2706
2793
|
|
|
2707
|
-
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`.
|
|
2794
|
+
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: `query-notes`, `get-native-objects`, `get-note-tables`, `list-smart-folders`, `delete-folder-by-id`, `get-note-drawings`, `transcribe-note-audio`, `native-note-state`, `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`.
|
|
2708
2795
|
|
|
2709
2796
|
> 📘 **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.
|
|
2710
2797
|
|
|
@@ -2724,6 +2811,8 @@ Several tools read directly from the Apple Notes SQLite database, which lives in
|
|
|
2724
2811
|
|
|
2725
2812
|
Every tool that does not read the Notes database works normally without Full Disk Access — that is the whole AppleScript surface (create, read, search, update, move, delete, folders, accounts, attachments, stats, export). The database-backed tools degrade like this:
|
|
2726
2813
|
- `get-checklist-state` returns an error explaining that database access is needed
|
|
2814
|
+
- `query-notes` returns the same kind of error; use `search-notes` instead
|
|
2815
|
+
- `delete-folder-by-id` refuses to delete anything (it fails closed)
|
|
2727
2816
|
- `get-note-metadata` returns the same kind of error — it has no non-database path
|
|
2728
2817
|
- `list-special-notes` and the `list-native-tags` inventory return the same kind of error
|
|
2729
2818
|
- `list-recent-notes` and `list-folder-tree` return the same kind of error
|
|
@@ -2751,7 +2840,7 @@ The helper never opens the Notes database and never writes under the Notes group
|
|
|
2751
2840
|
|
|
2752
2841
|
## Security and Privacy
|
|
2753
2842
|
|
|
2754
|
-
- **Local only** - All operations happen locally
|
|
2843
|
+
- **Local only** - All operations happen locally, through AppleScript, the packaged Shortcuts, read-only reads of the Notes database, and helpers you build on your own Mac. The server makes no network requests, and `transcribe-note-audio` uses on-device speech recognition only. Whatever your MCP client does with the results is governed by that client.
|
|
2755
2844
|
- **Permission required** - macOS will prompt for automation permission on first use.
|
|
2756
2845
|
- **Password-protected notes** - Notes with passwords cannot be read or modified via this server.
|
|
2757
2846
|
- **No credential storage** - The server doesn't store any passwords or authentication tokens.
|
|
@@ -2764,15 +2853,15 @@ The helper never opens the Notes database and never writes under the Notes group
|
|
|
2764
2853
|
|------------|--------|
|
|
2765
2854
|
| macOS only | Apple Notes and AppleScript are macOS-specific |
|
|
2766
2855
|
| Batch ops run per-note | `batch-delete-notes` / `batch-move-notes` apply each note individually rather than as one bulk operation — AppleScript has no bulk equivalent to IMAP's `UID STORE`/`MOVE`. This is deliberate: it preserves per-note success/failure reporting. ([#26](https://github.com/sweetrb/apple-notes-mcp/issues/26)) |
|
|
2767
|
-
|
|
|
2856
|
+
| Pinning needs a Shortcut | AppleScript exposes no `pinned` property. Pin state is readable via the BETA `get-note-metadata` tool (NoteStore database, needs Full Disk Access), and [`set-note-pinned`](#set-note-pinned) sets it through the Background Operations Shortcut rather than AppleScript ([#28](https://github.com/sweetrb/apple-notes-mcp/issues/28)) |
|
|
2768
2857
|
| Limited rich formatting | Use `format: "html"` on create/update for headings, lists, bold, code blocks; some complex formatting may not render |
|
|
2769
|
-
|
|
|
2858
|
+
| Exact IDs for writes | Writes take an exact note ID and a fresh `contentHash`, because Notes titles are not unique. Read tools that accept a `title` need an exact, case-sensitive match |
|
|
2770
2859
|
| Checklist state | Requires [Full Disk Access](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/FULL-DISK-ACCESS.md) to read done/undone state from the database |
|
|
2771
|
-
| Checklist **creation** | Not
|
|
2860
|
+
| Checklist **creation** | Not possible through AppleScript: its `body of note` setter strips `<input type="checkbox">` and ignores any checklist-styling CSS class, because Notes stores checklist items as a protobuf paragraph style (`style_type=103`). The server creates real checklist items through Shortcuts instead; see [Creating Checklists](#creating-checklists). Checking or unchecking an existing item is not supported. |
|
|
2772
2861
|
|
|
2773
2862
|
### Creating Checklists
|
|
2774
2863
|
|
|
2775
|
-
**There is no programmatic way to create a true Apple Notes checklist via AppleScript
|
|
2864
|
+
**There is no programmatic way to create a true Apple Notes checklist via AppleScript.** This is an Apple limitation, not a bug. Plaintext and HTML writes therefore cannot produce checklists, so this server creates them through Apple Shortcuts instead (workaround 2 below).
|
|
2776
2865
|
|
|
2777
2866
|
When a note is created or updated via AppleScript:
|
|
2778
2867
|
|
|
@@ -2787,7 +2876,7 @@ Apple Notes stores checklists as a paragraph style (`style_type=103`) inside a g
|
|
|
2787
2876
|
**Workarounds:**
|
|
2788
2877
|
|
|
2789
2878
|
1. **Create the note with bulleted list items, then convert manually in Notes.app.** Select the items and press <kbd>⇧⌘L</kbd> (or **Format → Checklist**). This converts the list in place and the resulting checklist will be readable by `get-checklist-state` and annotated by `get-note-markdown`.
|
|
2790
|
-
2. **Use the Apple Shortcuts app** to script the checklist creation, since Shortcuts can manipulate Notes content at a higher level than AppleScript. This server does that for you in two ways: [`create-checklist-item`](#create-checklist-item)
|
|
2879
|
+
2. **Use the Apple Shortcuts app** to script the checklist creation, since Shortcuts can manipulate Notes content at a higher level than AppleScript. This server does that for you in two ways: [`create-checklist-item`](#create-checklist-item) and [`create-checklist-items`](#create-checklist-items) append unchecked items to an existing note, and [`create-note`](#create-note) with `format: "markdown"` turns `- [ ]` / `- [x]` lines into checklist items with that done state through Notes' own Markdown importer (see [Markdown notes](#markdown-notes)).
|
|
2791
2880
|
3. **Read-only checklist support is fully implemented** — once a checklist exists (created manually or by another app), `get-checklist-state` and `get-note-markdown` will read its done/undone state correctly (with Full Disk Access).
|
|
2792
2881
|
|
|
2793
2882
|
If you need to *track* todos programmatically and don't strictly need them rendered as Apple Notes checklist UI, plain markdown-style `- [ ] item` / `- [x] item` lines in a `plaintext` note are a reasonable alternative — they are searchable, human-readable, and can be parsed by downstream tooling.
|
|
@@ -19,7 +19,7 @@ shared, body, id, name, plaintext
|
|
|
19
19
|
|
|
20
20
|
## Pinned notes (#28)
|
|
21
21
|
|
|
22
|
-
**Status: not feasible via AppleScript;
|
|
22
|
+
**Status: not feasible via AppleScript; read from the NoteStore database and set through a Shortcut.**
|
|
23
23
|
The Notes UI lets you pin a note to the top of a folder, but the `note` class
|
|
24
24
|
has no `pinned` property. Asking for it raises error `-1700`:
|
|
25
25
|
|
|
@@ -33,7 +33,7 @@ end tell
|
|
|
33
33
|
There is no alternative property, element, or command (`pin`, `pinned`,
|
|
34
34
|
`favorite`, …) in the dictionary. Pinned state lives only in Notes' private
|
|
35
35
|
Core Data store (`NoteStore.sqlite`), which is not part of the scriptable
|
|
36
|
-
surface, and
|
|
36
|
+
surface, and AppleScript offers no way to _set_ it.
|
|
37
37
|
|
|
38
38
|
Reading it, however, did turn out to be worth doing. Since 2.5.0 the BETA
|
|
39
39
|
`get-note-metadata` tool queries `ZISPINNED` on `ZICCLOUDSYNCINGOBJECT` in that
|
|
@@ -43,14 +43,17 @@ changes across macOS releases. It requires
|
|
|
43
43
|
[Full Disk Access](./FULL-DISK-ACCESS.md) and is marked BETA precisely because
|
|
44
44
|
the schema is version-dependent.
|
|
45
45
|
|
|
46
|
-
**Conclusion:** pin state is **
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
**Conclusion:** pin state is **not scriptable** through AppleScript in either
|
|
47
|
+
direction. It is **readable** (BETA, from the NoteStore database, Full Disk
|
|
48
|
+
Access required) and, since the native background operations were added,
|
|
49
|
+
**settable** through the packaged Background Operations Shortcut by
|
|
50
|
+
`set-note-pinned`, which checks the expected current state and the note
|
|
51
|
+
revision first. If a future macOS exposes a scriptable property, revisit by
|
|
52
|
+
re-running the probe above.
|
|
50
53
|
|
|
51
54
|
## Note-to-note links (#30)
|
|
52
55
|
|
|
53
|
-
**Status: link
|
|
56
|
+
**Status: link _relationships_ are not exposed to AppleScript; the server reads them from the database and inserts links by other routes.**
|
|
54
57
|
Apple Notes lets you insert a link from one note to another in the UI, but
|
|
55
58
|
AppleScript exposes no property or element for that relationship:
|
|
56
59
|
|
|
@@ -58,7 +61,7 @@ AppleScript exposes no property or element for that relationship:
|
|
|
58
61
|
(undefined). There is no element that enumerates outgoing/incoming links.
|
|
59
62
|
- Nothing in the dictionary inserts a link into a note's body.
|
|
60
63
|
|
|
61
|
-
A shareable deep link to a note
|
|
64
|
+
A shareable deep link to a note _is_ available, and has been since 2.6.0:
|
|
62
65
|
`get-note-link` returns a `notes://showNote?identifier=<uuid>` URL that opens
|
|
63
66
|
the note in Notes.app on macOS and iOS.
|
|
64
67
|
|
|
@@ -84,10 +87,14 @@ It **is** wrapped, as `show-note`, `show-folder`, `show-account`, and
|
|
|
84
87
|
something useful on a machine with an active desktop session; to read a note's
|
|
85
88
|
content, use `get-note-content` / `get-note-markdown` instead.
|
|
86
89
|
|
|
87
|
-
**Conclusion:** link relationships between notes
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
**Conclusion:** AppleScript can neither read link relationships between notes
|
|
91
|
+
nor insert a link into a body. The server does both by other routes:
|
|
92
|
+
`list-note-links` reads a note's or folder's links from the NoteStore database
|
|
93
|
+
(Full Disk Access required), `insert-link` appends a web, mail or Notes link,
|
|
94
|
+
and `insert-note-link` appends another note's real deep link through the
|
|
95
|
+
Background Operations Shortcut. To hand a note to a person or another app, use
|
|
96
|
+
`get-note-link`; to address a note in a follow-up tool call, use the `id`
|
|
97
|
+
returned by every read tool.
|
|
91
98
|
|
|
92
99
|
## Tags / hashtags (#29)
|
|
93
100
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-notes-mcp",
|
|
3
|
-
"version": "2.9.
|
|
4
|
-
"description": "MCP server for Apple Notes -
|
|
3
|
+
"version": "2.9.22",
|
|
4
|
+
"description": "MCP server for Apple Notes - read, search, create, edit, organize, and export notes on macOS via Claude and other AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
@@ -25,7 +25,16 @@
|
|
|
25
25
|
"applescript",
|
|
26
26
|
"macos",
|
|
27
27
|
"notes",
|
|
28
|
-
"model-context-protocol"
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"mcp-server",
|
|
30
|
+
"apple",
|
|
31
|
+
"notes-app",
|
|
32
|
+
"claude-code",
|
|
33
|
+
"claude-desktop",
|
|
34
|
+
"shortcuts",
|
|
35
|
+
"markdown",
|
|
36
|
+
"note-taking",
|
|
37
|
+
"icloud"
|
|
29
38
|
],
|
|
30
39
|
"author": "Rob Sweet <rob@superiortech.io>",
|
|
31
40
|
"license": "MIT",
|