apple-notes-mcp 2.14.2 → 2.15.0

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.
@@ -1,8 +1,8 @@
1
1
  # Full Disk Access
2
2
 
3
3
  Apple Notes MCP works almost entirely without any special disk permission. The
4
- tools that need **Full Disk Access (FDA)** for the process that runs the MCP
5
- server are the ones that read Notes' own SQLite store:
4
+ tools that need **Full Disk Access (FDA)** for the process responsible for the MCP
5
+ server are the ones that read Notes' own SQLite store. Examples include:
6
6
 
7
7
  - **`get-checklist-state`** — reads a note's checklist done/undone state.
8
8
  - **Checklist annotations in `get-note-markdown`** — the `[x]` / `[ ]` prefixes on
@@ -17,9 +17,9 @@ server are the ones that read Notes' own SQLite store:
17
17
  - **`get-sync-status`** — degrades rather than fails: without database access it
18
18
  cannot see pending uploads or recent write activity.
19
19
 
20
- Everything else (creating, reading, searching, updating, moving, deleting notes;
21
- folders, accounts, attachments, stats, export, etc.) works **without** Full Disk
22
- Access.
20
+ The AppleScript tools (creating, reading, searching, updating, moving, and
21
+ deleting notes, for example) work **without** Full Disk Access. See the
22
+ [full tool list](../README.md#full-disk-access) for all database-backed features.
23
23
 
24
24
  ## Why it's needed
25
25
 
@@ -46,7 +46,10 @@ ever **reads** this database; it never writes to it.)
46
46
  2. Go to **Privacy & Security → Full Disk Access**.
47
47
  3. Click the **+** button (you may need to unlock with Touch ID / your password
48
48
  first) and add the right entry for how you run the server:
49
- - **Claude Desktop** → the **Node binary** that runs the server, e.g.
49
+ - **Permission broker** → `~/Applications/Apple Notes MCP Broker.app`, if you
50
+ opted in with `apple-notes-mcp setup --broker`. Read the
51
+ [trust trade-off below](#optional-permission-broker) before enabling it.
52
+ - **Claude Desktop without the broker** → the **Node binary** that runs the server, e.g.
50
53
  `/usr/local/bin/node` or `~/.nvm/versions/node/v24.11.1/bin/node`. The
51
54
  `doctor` tool prints the exact path. In the file picker, press **⌘⇧G** and
52
55
  paste it (use **⌘⇧.** to show hidden folders such as `~/.nvm`). Adding
@@ -61,7 +64,7 @@ ever **reads** this database; it never writes to it.)
61
64
  enough; quit the host application itself (⌘Q) and relaunch it. If `doctor`
62
65
  still reports Full Disk Access as not granted after that, restart the Mac.
63
66
 
64
- > **Why Claude Desktop needs the Node binary itself.** macOS checks Full Disk
67
+ > **Why Claude Desktop needs the Node binary without the broker.** macOS checks Full Disk
65
68
  > Access against the *responsible process*, the app it holds accountable for a
66
69
  > request. A terminal passes that role on to everything it launches, so a grant
67
70
  > on Terminal or iTerm covers the server. Claude Desktop does not: it starts each
@@ -83,15 +86,61 @@ ever **reads** this database; it never writes to it.)
83
86
  > }
84
87
  > ```
85
88
  >
86
- > A Developer-ID-signed Node keeps its grant across updates at the same path;
87
- > an ad-hoc-signed one (typically Homebrew's) does not. See
89
+ > A Developer-ID-signed Node at a stable path avoids the identity changes of
90
+ > ad-hoc rebuilds. Permission continuity still depends on its designated code
91
+ > requirement and macOS permission state. See
88
92
  > [Node runtime and TCC permissions](NODE-RUNTIME-AND-TCC-PERMISSIONS.md).
89
93
 
94
+ ## Optional permission broker
95
+
96
+ `apple-notes-mcp setup --broker` installs a signed app at
97
+ `~/Applications/Apple Notes MCP Broker.app`. Grant Full Disk Access to that app
98
+ and allow it to control Notes when macOS asks. Brokered sessions use its grants
99
+ instead of those of the Node binary or MCP host that launched the client.
100
+ Setup requires a standalone Node runtime linked only to system libraries; see
101
+ the [runtime setup guide](NODE-RUNTIME-AND-TCC-PERMISSIONS.md).
102
+
103
+ **This trusts every process running as your macOS user.** While the broker is
104
+ running, any such process can connect and use the server's Notes capabilities
105
+ under the broker's Full Disk Access and Notes Automation grants. The socket's
106
+ permissions and same-UID check exclude other users; they do not authenticate
107
+ apps running as you. That is comparable trust exposure to granting Full Disk
108
+ Access to Node. Use the broker only if you accept this trade-off.
109
+
110
+ The broker runs a server sealed inside its signed app, verifies the bundle and
111
+ the pinned Node executable before spawning, and accepts only a short allowlist
112
+ of numeric limits and timeouts from clients. Code paths, helper paths, and
113
+ safety overrides cannot be selected through a socket request, and brokered
114
+ servers skip local JSON configuration files. External native
115
+ helpers are unavailable in broker sessions; tools that require them need an
116
+ in-process server. See [Permission broker](../README.md#permission-broker-opt-in)
117
+ for the security design and signing caveats.
118
+
119
+ The signature and hash checks reject changes detected at startup or before
120
+ spawn. They do not provide OS isolation against a hostile, unsandboxed
121
+ same-user process modifying the user-writable bundle or runtime after
122
+ validation. Installed Shortcuts are user-managed trusted integrations; the
123
+ broker does not authenticate their contents.
124
+
125
+ After updating the package or replacing the broker's Node runtime, run
126
+ `apple-notes-mcp setup --broker` again, restart your MCP clients, and check
127
+ `doctor`. Rebuilding with an ad-hoc signature or changing the signing identity
128
+ may require new macOS grants.
129
+
130
+ To bypass the broker for one client, set `APPLE_NOTES_MCP_BROKER=off` in that
131
+ client's environment. This does **not** stop the broker or prevent other
132
+ same-user processes from using it. To remove that access, run
133
+ `apple-notes-mcp setup --broker --uninstall`, then restart your clients. You can
134
+ also revoke the broker's Full Disk Access and Notes Automation grants in System
135
+ Settings.
136
+
90
137
  ## Verifying it worked
91
138
 
92
139
  Run the **`doctor`** tool. It reports a dedicated **Full Disk Access** check as
93
140
  `ok` / `warn` / `fail` with the reason, so you can confirm the grant took effect
94
- without guessing. You can also just call `get-checklist-state` on a note that has
141
+ without guessing. Its `broker` field identifies whether the session uses the
142
+ broker or has fallen back to an in-process server with its own permissions.
143
+ You can also just call `get-checklist-state` on a note that has
95
144
  a checklist — if it returns items with `[x]`/`[ ]` state, FDA is working.
96
145
 
97
146
  ## Without Full Disk Access
@@ -108,8 +157,9 @@ The server degrades gracefully — nothing crashes:
108
157
  appear as plain list items without the `[x]`/`[ ]` annotations.
109
158
  - `get-sync-status` still answers, but with no database visibility it reports no
110
159
  pending uploads and no active sync — treat that as "unknown", not "idle".
111
- - **Every other tool works normally**, since the rest of the server is pure
112
- AppleScript.
160
+ - **AppleScript-only tools work normally.** See the
161
+ [full tool list](../README.md#full-disk-access) for other database-backed tools
162
+ that require FDA.
113
163
 
114
164
  See also: [Known Limitations](../README.md#known-limitations) and
115
165
  [Creating Checklists](../README.md#creating-checklists) in the README.
@@ -1,10 +1,10 @@
1
1
  # Node runtime & TCC permission stability
2
2
 
3
3
  macOS gates this MCP server's access to your data behind **TCC** permissions —
4
- **Full Disk Access** (to read app data such as Mail, Notes, or Photos) and
5
- **Automation / Apple Events** (to drive an app like Mail.app or Notes.app via
6
- AppleScript). See this repo's Full Disk Access / Automation notes for *which*
7
- operations need which permission.
4
+ **Full Disk Access** (to read the Notes database) and **Automation / Apple
5
+ Events** (to drive Notes.app via AppleScript). See the
6
+ [Full Disk Access guide](FULL-DISK-ACCESS.md) for which operations need which
7
+ permission.
8
8
 
9
9
  This page is about a **separate, recurring annoyance**: being asked to approve
10
10
  those permissions **over and over**, often right after a routine `brew upgrade`.
@@ -13,19 +13,24 @@ those permissions **over and over**, often right after a routine `brew upgrade`.
13
13
 
14
14
  - You granted Full Disk Access (and/or Automation) to "node", but days later
15
15
  macOS prompts again — `"node" wants access to ...` or `"node" wants to control
16
- "Mail"`.
16
+ "Notes"`.
17
17
  - System Settings → Privacy & Security → Full Disk Access shows **several
18
18
  identical "node" rows**, usually only one enabled.
19
19
  - It tends to happen immediately after you update Node.
20
20
 
21
21
  ## Cause
22
22
 
23
- TCC binds a permission grant to the **code identity of the binary that performs
24
- the access** — here, the `node` executable that launches the MCP server. For a
25
- binary that is only **ad-hoc signed** (no Developer ID / Team ID), TCC keys the
26
- grant to the binary's **cdhash**, a hash of its contents.
23
+ TCC checks the **responsible process** for an access. Under Claude Desktop
24
+ without the broker, that is the `node` executable running the server; under a
25
+ terminal host it can be the terminal app. With the broker, it is
26
+ `~/Applications/Apple Notes MCP Broker.app`.
27
27
 
28
- Homebrew's `node` formula is ad-hoc signed:
28
+ An **ad-hoc-signed** binary has no certificate-backed signing identity. Its
29
+ **cdhash** changes when its code changes, so a grant to one build may not apply
30
+ after an update. Moving the executable, including changing the resolved path
31
+ behind a version-manager symlink, can also require a new grant.
32
+
33
+ Check the Node binary you actually use. An ad-hoc signature looks like this:
29
34
 
30
35
  ```bash
31
36
  $ codesign -dvvv "$(which node)" 2>&1 | grep -E 'Signature|TeamIdentifier'
@@ -33,21 +38,86 @@ Signature=adhoc
33
38
  TeamIdentifier=not set
34
39
  ```
35
40
 
36
- Every Node update **replaces the binary**, which **changes the cdhash**, so TCC
37
- no longer recognizes it as the thing you approved — and re-prompts. The extra
38
- "node" rows are stale cdhashes from previous versions.
41
+ The `doctor` tool reports the runtime path and warns about ad-hoc signing.
42
+ Duplicate "node" rows in System Settings can be entries for previous binaries.
43
+
44
+ Certificate-signed code has a **designated requirement** that can identify it
45
+ across builds. Apple's documented Apple Development requirement includes the
46
+ leaf certificate's Common Name, while its Developer ID example uses certificate
47
+ type and Team ID constraints. A matching bundle identifier and Team ID alone
48
+ are therefore not a promise of permission continuity. See Apple's
49
+ [TN3127: Inside Code Signing: Requirements](https://developer.apple.com/documentation/technotes/tn3127-inside-code-signing-requirements).
39
50
 
40
- By contrast, properly signed apps (Chrome, Slack, …) keep their permissions
41
- across auto-updates because TCC matches them on a stable **Designated
42
- Requirement** derived from their Developer ID, not on the cdhash.
51
+ ## Option 1: opt in to the permission broker
43
52
 
44
- ## Fix: run the MCP under the official, Developer-ID-signed Node
53
+ The broker puts the grants on a signed app at a fixed path and runs a bundled
54
+ copy of the MCP server for connected clients. Run setup using a standalone Node
55
+ runtime whose dynamic library dependencies are all under `/usr/lib/` or
56
+ `/System/Library/`. Other dependencies, including the non-system libraries used
57
+ by common Homebrew Node builds, are rejected: pinning the Node executable alone
58
+ would not protect those libraries. The download steps under Option 2 below show
59
+ how to install an official Node runtime at a fixed path.
60
+
61
+ ```bash
62
+ apple-notes-mcp setup --broker
63
+ apple-notes-mcp setup --broker --check
64
+ # Or explicitly select your standalone runtime for setup:
65
+ /path/to/node /path/to/apple-notes-mcp/build/index.js setup --broker
66
+ ```
67
+
68
+ Add `~/Applications/Apple Notes MCP Broker.app` in **System Settings → Privacy
69
+ & Security → Full Disk Access**, and allow it to control Notes when prompted.
70
+ Restart your MCP client and run `doctor` to confirm that it is using the broker.
71
+
72
+ **Review the trust trade-off before installing:** every process running as your
73
+ macOS user can use the broker's Notes capabilities under its Full Disk Access
74
+ and Notes Automation grants. The socket's same-UID check excludes other users,
75
+ but does not authenticate same-user apps. This is comparable trust exposure to
76
+ granting Full Disk Access to a general-purpose Node binary. Socket requests
77
+ cannot select executable paths or safety overrides; this does not identify a
78
+ trusted MCP client. Brokered servers skip local JSON configuration files, and
79
+ external native helpers are unavailable in broker sessions.
80
+ See the [broker security design](../README.md#permission-broker-opt-in).
81
+
82
+ The signature and hash checks reject modifications detected at startup or
83
+ before spawn. They do not make later filesystem reads atomic or provide OS
84
+ isolation against a hostile, unsandboxed same-user process modifying the
85
+ user-writable bundle or runtime after validation. Installed Shortcuts remain
86
+ user-managed trusted integrations; the broker does not authenticate their
87
+ contents.
88
+
89
+ Setup automatically selects an identity only when exactly one Developer ID
90
+ Application or Apple Development identity qualifies. If more than one qualifies
91
+ across those types, specify `--sign-identity`. If none qualify, it signs ad hoc
92
+ and warns about re-granting permissions.
93
+ Keeping a compatible designated requirement can help preserve grants across
94
+ rebuilds, but **Apple Development signing is not a guarantee of continuity across
95
+ certificate renewal or signing identity changes**. Ad-hoc rebuilds may require
96
+ new grants. Always check `doctor` after reinstalling.
97
+
98
+ The signed configuration pins the Node executable's canonical path and digest.
99
+ After a package update, or after replacing or moving that runtime, run
100
+ `apple-notes-mcp setup --broker` again to refresh the bundled server and pin,
101
+ then restart your clients. A stale or unavailable broker causes clients to run
102
+ in-process with their own permissions; `doctor` reports that fallback.
103
+
104
+ Set `APPLE_NOTES_MCP_BROKER=off` to bypass it for one client. The broker continues
105
+ running and remains reachable by other same-user processes. To remove that
106
+ access, run `apple-notes-mcp setup --broker --uninstall` and restart your clients;
107
+ you can also revoke the broker's grants in System Settings.
108
+
109
+ ## Option 2: run in-process under Developer-ID-signed Node
45
110
 
46
111
  Node binaries distributed from **nodejs.org** are signed with a real Developer
47
112
  ID (`Node.js Foundation`, Team `HX7739G8FX`), notarized, and self-contained.
48
- Pointing the MCP server at one gives TCC a **stable** identity to match, so a
49
- permission you grant **persists across future Node updates**. It also decouples
50
- the MCP runtime from your Homebrew/dev Node, which can keep updating freely.
113
+ Keeping the executable at a stable path avoids the path changes of version
114
+ managers and the code-identity changes of ad-hoc rebuilds. Permission continuity
115
+ still depends on the designated requirement and macOS permission state. This
116
+ also decouples the MCP runtime from your Homebrew/dev Node.
117
+
118
+ If you installed the broker, set `APPLE_NOTES_MCP_BROKER=off` in this client's
119
+ environment or uninstall it before using this option. Otherwise the client
120
+ will hand off to the broker when it is available.
51
121
 
52
122
  ### Steps (Apple Silicon shown; use `darwin-x64` on Intel)
53
123
 
@@ -92,19 +162,18 @@ the MCP runtime from your Homebrew/dev Node, which can keep updating freely.
92
162
 
93
163
  4. **Restart your MCP client** so the server relaunches under the new Node.
94
164
 
95
- 5. **Grant the permissions once** to the new binary:
165
+ 5. **Grant the permissions** to the new binary:
96
166
  - *Full Disk Access*: System Settings → Privacy & Security → Full Disk Access
97
167
  → **+** → ⌘⇧G → paste `~/mcp-runtime/node-current/bin/node`.
98
- - *Automation*: the first time the server drives an app you'll get a one-time
168
+ - *Automation*: the first time the server drives an app you'll get a
99
169
  `"node" wants to control "<App>"` prompt — click **Allow**.
100
170
 
101
171
  ⚠️ **A TCC grant is keyed to the binary's resolved *path*, not only to its
102
172
  signature.** Because the steps above unpack Node into a fixed directory
103
173
  (`~/mcp-runtime/node-current`) rather than a versioned one, that path never
104
- moves and the grants survive Node updates — see "Updating" below. If you
174
+ moves — see "Updating" below. If you
105
175
  instead point `node-current` at a `node-vX.Y.Z-…` directory, every update
106
- changes the resolved path, presents a brand-new ungranted identity, and you
107
- will be re-prompted for all of it.
176
+ changes the resolved path and may require you to grant permissions again.
108
177
 
109
178
  You can delete any stale "node" rows from the Full Disk Access list.
110
179
 
@@ -123,11 +192,12 @@ rm -rf node-current && mkdir -p node-current
123
192
  tar -xzf node-$VER-$ARCH.tar.gz --strip-components=1 -C node-current
124
193
  ```
125
194
 
126
- The path is unchanged and the official builds are Developer-ID signed with a
127
- requirement that pins identifier + Team ID (no cdhash), so **both** halves of
128
- the grant still match: existing grants carry over with no re-approval.
195
+ The path stays unchanged. Check the replacement binary's signature as above,
196
+ then run `doctor` in your MCP client to verify that macOS retained its grants.
197
+ Do not assume a new signing identity or a changed designated requirement will
198
+ match an earlier grant.
129
199
 
130
200
  ⚠️ **Restart your MCP client afterwards.** Replacing the binary unlinks the one
131
- any already-running server is executing; macOS then cannot validate that path,
132
- so those processes fail with *"Permission denied"* until they are restarted.
133
- Nothing is wrong with your grants — a freshly launched server works fine.
201
+ any already-running server is executing; those processes can fail with
202
+ *"Permission denied"* until restarted. Recheck permissions from a fresh session
203
+ before changing grants.