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.
package/README.md CHANGED
@@ -21,7 +21,7 @@ Beyond creating and editing notes, it manages folders and accounts, native tags,
21
21
 
22
22
  - [What is This?](#what-is-this) · [Quick Start](#quick-start) · [Requirements](#requirements) · [Features](#features)
23
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)
24
+ - [Usage Patterns](#usage-patterns) · [Installation Options](#installation-options) · [Configuration](#configuration) · [Full Disk Access](#full-disk-access) · [Public native helper](#public-native-helper) · [Permission broker](#permission-broker-opt-in)
25
25
  - [Security and Privacy](#security-and-privacy) · [Known Limitations](#known-limitations) · [Troubleshooting](#troubleshooting) · [Recurring macOS permission prompts](#recurring-macos-permission-prompts) · [Development](#development)
26
26
 
27
27
  ## What is This?
@@ -1331,9 +1331,9 @@ fixed HTML subset rather than anything Notes.app can render:
1331
1331
 
1332
1332
  | | Native append |
1333
1333
  |---|---|
1334
- | Elements | `<a>` `<b>` `<br>` `<code>` `<del>` `<div>` `<em>` `<h1>` `<h2>` `<h3>` `<i>` `<li>` `<ol>` `<p>` `<s>` `<span>` `<strong>` `<table>` `<tbody>` `<td>` `<th>` `<thead>` `<tr>` `<tt>` `<u>` `<ul>` |
1334
+ | Elements | `<a>` `<b>` `<br>` `<code>` `<del>` `<div>` `<em>` `<h1>` `<h2>` `<h3>` `<i>` `<li>` `<ol>` `<p>` `<s>` `<span>` `<strong>` `<tt>` `<u>` `<ul>` |
1335
1335
  | Attributes | `href` on `<a>` (`https:`, `http:`, `notes:`, `applenotes:`, `mailto:` only) and a `font-size` style on `<span>`, e.g. `<span style="font-size: 18px">` — the form Notes itself stores a heading as |
1336
- | Refused | every other element and attribute, by name, naming the accepted subset; `<table>` here (use [`create-table`](#create-table)); comments, doctype and processing instructions |
1336
+ | Refused | every other element and attribute, by name, naming the accepted subset; a table, in whole or in part (`<table>` `<thead>` `<tbody>` `<tr>` `<td>` `<th>`) — use [`create-table`](#create-table) instead; comments, doctype and processing instructions |
1337
1337
 
1338
1338
  Ordinary notes take the guarded HTML path and are not restricted to that subset.
1339
1339
 
@@ -2771,6 +2771,10 @@ Private API can break on any macOS update; see
2771
2771
  [TECHNICAL_NOTES.md](TECHNICAL_NOTES.md#private-helper-notesshared) for the
2772
2772
  API surface, risks, and safety contract.
2773
2773
 
2774
+ If you installed the [permission broker](#permission-broker-opt-in), set
2775
+ `APPLE_NOTES_MCP_BROKER=off` for this client first. Broker sessions do not load
2776
+ external native helpers.
2777
+
2774
2778
  To use it:
2775
2779
 
2776
2780
  1. Build it on your Mac from the packaged source (needs the Command Line
@@ -2934,7 +2938,7 @@ The entrypoint is written as (an excerpt of that file, not a whole config):
2934
2938
 
2935
2939
  ### Environment variables
2936
2940
 
2937
- All configuration is optional — the server works out of the box. Override behavior with these variables (set them in your MCP client's `env` block, or via the [config file](#configuration-file-when-the-host-strips-env) below):
2941
+ All configuration is optional — the server works out of the box. Override behavior with these variables (set them in your MCP client's `env` block, or via the [config file](#configuration-file-when-the-host-strips-env) below). The opt-in [permission broker](#permission-broker-opt-in) accepts only a short, explicit allowlist of numeric limits and timeouts; code paths, helper paths, Shortcut overrides, and settings that relax safety checks do not carry over:
2938
2942
 
2939
2943
  | Variable | Default | Description |
2940
2944
  |----------|---------|-------------|
@@ -2983,6 +2987,11 @@ overriding** anything already set there (so an explicit `env` still wins). This
2983
2987
  is the recommended way to configure the server under Claude Desktop. Apple Notes
2984
2988
  MCP stores no secrets, but as a general rule keep only non-secret config here.
2985
2989
 
2990
+ For a brokered session, the client forwards only the broker's allowed numeric
2991
+ settings from this file. The brokered server skips JSON configuration files
2992
+ entirely; editing this file cannot enable helper paths or safety overrides in
2993
+ that server.
2994
+
2986
2995
  ---
2987
2996
 
2988
2997
  ## Full Disk Access
@@ -2997,7 +3006,8 @@ Several tools read directly from the Apple Notes SQLite database, which lives in
2997
3006
  2. Go to **Privacy & Security > Full Disk Access**
2998
3007
  3. Click the **+** button
2999
3008
  4. Add the entry that matches how the server runs:
3000
- - **Claude Desktop**: add the **Node binary** that runs the server (the `doctor` tool prints its path, e.g. `~/.nvm/versions/node/v24.11.1/bin/node`; press ⌘⇧G in the file picker to paste it). Claude Desktop launches MCP servers as their own responsible process, so a grant on `/Applications/Claude.app` alone does not reach them ([#220](https://github.com/sweetrb/apple-notes-mcp/issues/220)).
3009
+ - **Permission broker**: if you opted in with `setup --broker`, add `~/Applications/Apple Notes MCP Broker.app`. Read the [broker's trust trade-off](#permission-broker-opt-in) before enabling it.
3010
+ - **Claude Desktop without the broker**: add the **Node binary** that runs the server (the `doctor` tool prints its path, e.g. `~/.nvm/versions/node/v24.11.1/bin/node`; press ⌘⇧G in the file picker to paste it). Claude Desktop launches MCP servers as their own responsible process, so a grant on `/Applications/Claude.app` alone does not reach them ([#220](https://github.com/sweetrb/apple-notes-mcp/issues/220)).
3001
3011
  - **Terminal**: Add `/Applications/Utilities/Terminal.app`
3002
3012
  - **VS Code**: Add `/Applications/Visual Studio Code.app`
3003
3013
  - **iTerm**: Add `/Applications/iTerm.app`
@@ -3041,7 +3051,7 @@ For each missing grant the report names the System Settings pane and its URL, fo
3041
3051
 
3042
3052
  Automation is the one item that cannot be read without talking to Notes.app, so it is opt-in. By default the command sends no Apple event, and the Automation line reads `unknown` (not probed) with the command that checks it. Add `--probe-automation` to send one read-only event; the first time, macOS may ask whether the app may control Notes, and **Allow** is the only way to add an Automation grant. The probe never runs under `--check` (which reports state only) or when `SSH_CONNECTION` is set, because nobody at the remote shell can answer the prompt. In those cases the line reads `unknown` even if `--probe-automation` was passed. An unprobed Automation line does not change the exit code and the report says Automation was not checked, so run the probe from a terminal on the Mac itself when you need to know. A probe that runs and fails (denied, or no answer) does exit 1.
3043
3053
 
3044
- macOS attributes these grants to the app that launched the process, so the report names it (for example `/Applications/iTerm.app`). Run the check from the app you use as the MCP host when you can. Claude Desktop launches servers as their own responsible process, so for it use the `doctor` tool inside Claude Desktop, and add the Node binary it names to Full Disk Access. The Speech item reads `unknown` until the [public native helper](#public-native-helper) is built.
3054
+ macOS attributes these grants to the app that launched the process, so the report names it (for example `/Applications/iTerm.app`). Run the check from the app you use as the MCP host when you can. Under Claude Desktop, use the `doctor` tool inside the client: for a brokered session, grant Full Disk Access to `~/Applications/Apple Notes MCP Broker.app`; otherwise add the Node binary it names. The Speech item reads `unknown` until the [public native helper](#public-native-helper) is built.
3045
3055
 
3046
3056
  ### Optional checklist window
3047
3057
 
@@ -3060,7 +3070,11 @@ Setup compiles `native/permissions-window/apple-notes-permissions-window.swift`,
3060
3070
 
3061
3071
  ## Public native helper
3062
3072
 
3063
- `get-note-drawings` needs Apple's PencilKit framework and `transcribe-note-audio` needs the Speech framework; neither has an AppleScript or command-line interface. For them, the server uses a small Swift helper that links public Apple frameworks only (AppKit, PencilKit, AVFoundation, and Speech). No prebuilt binary ships with the package. Build it once on your Mac:
3073
+ `get-note-drawings` needs Apple's PencilKit framework and `transcribe-note-audio` needs the Speech framework; neither has an AppleScript or command-line interface. For them, the server uses a small Swift helper that links public Apple frameworks only (AppKit, PencilKit, AVFoundation, and Speech). No prebuilt binary ships with the package.
3074
+
3075
+ If you installed the [permission broker](#permission-broker-opt-in), set `APPLE_NOTES_MCP_BROKER=off` for this client to use these tools. Broker sessions do not load external native helpers.
3076
+
3077
+ Build it once on your Mac:
3064
3078
 
3065
3079
  ```bash
3066
3080
  apple-notes-mcp setup --public-helper # compile, sign, verify, install
@@ -3073,6 +3087,47 @@ The helper never opens the Notes database and never writes under the Notes group
3073
3087
 
3074
3088
  ---
3075
3089
 
3090
+ ## Permission broker (opt-in)
3091
+
3092
+ Under Claude Desktop, macOS holds the Node binary responsible for Full Disk Access and Automation, so moving or replacing Node can require new grants ([#220](https://github.com/sweetrb/apple-notes-mcp/issues/220)). The permission broker moves both grants to one signed app at a fixed path.
3093
+
3094
+ **Trust trade-off:** while the broker is running, every process running as your macOS user can connect and use the server's Notes capabilities under the broker's Full Disk Access and Notes Automation grants. The socket's ownership, file permissions, and `getpeereid` check exclude other users; they do **not** authenticate or distinguish apps running as you. Any such app can request the same Notes reads and writes as your MCP client. This is comparable trust exposure to granting Full Disk Access to a general-purpose Node binary. The broker consolidates permission management; it does not isolate Notes access between same-user apps. Enable it only if you accept that boundary.
3095
+
3096
+ Run setup with a standalone Node runtime whose dynamic library dependencies are all under `/usr/lib/` or `/System/Library/`. Setup rejects runtimes linked to other locations, including common Homebrew Node builds, because pinning Node alone would not protect those libraries. The [dedicated Node instructions](docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md) show how to install an official runtime at a fixed path. To select it for setup, run `/path/to/node /path/to/apple-notes-mcp/build/index.js setup --broker`.
3097
+
3098
+ ```bash
3099
+ apple-notes-mcp setup --broker # build, sign, install, and start the broker
3100
+ apple-notes-mcp setup --broker --check # report whether it is installed and answering
3101
+ apple-notes-mcp setup --broker --uninstall # stop and remove it
3102
+ apple-notes-mcp setup --broker --sign-identity "Developer ID Application: You (TEAMID)"
3103
+ ```
3104
+
3105
+ Setup compiles `native/broker/apple-notes-mcp-broker.swift` into `~/Applications/Apple Notes MCP Broker.app`, copies the server into the bundle before signing with the hardened runtime, runs its `hello` handshake, and starts it as the per-user LaunchAgent `apple-notes-mcp.broker` (`~/Library/LaunchAgents/apple-notes-mcp.broker.plist`). Then add the app under **Privacy & Security > Full Disk Access**, and click **Allow** when macOS first asks whether "Apple Notes MCP Broker" may control Notes. Restart your MCP client afterwards and check `doctor`.
3106
+
3107
+ How it works: the broker listens on a Unix socket in `~/Library/Application Support/apple-notes-mcp/broker/`, created `0600` inside a `0700` folder. For each client it starts the bundled server as its child, so macOS attributes the server's database reads and Apple events to the broker app. The `apple-notes-mcp` process your MCP client launches relays its stdin to that server and returns stdout and stderr to the corresponding client streams. Diagnostics stay separate from MCP responses. The broker never reads Notes itself.
3108
+
3109
+ Child stderr is delivered only to its client and is not copied into a shared broker log. The broker's own lifecycle and connection diagnostics use macOS unified logging, whose storage and retention are managed by the operating system. To inspect them, run `log show --last 1h --predicate 'subsystem == "apple-notes-mcp.broker"'`; use `log stream` with the same predicate for live diagnostics. Direct command-line runs also report broker diagnostics on stderr.
3110
+
3111
+ The broker verifies its app signature and sealed resources at startup and before spawning a server. Its signed configuration pins the Node executable's canonical path and SHA-256 digest, and the server entry point has a fixed location inside the signed bundle. Neither the LaunchAgent's arguments nor a socket request can select a Node binary or server entry point. Changes detected at startup or before spawn are refused until you run setup again. These are pre-spawn tamper checks: they do not make later filesystem reads atomic or provide OS isolation against a hostile, unsandboxed same-user process that can modify the user-writable bundle or runtime after validation.
3112
+
3113
+ Only an explicit allowlist of harmless numeric limits and timeouts travels from a client to the broker. Code and helper paths, `NODE_OPTIONS`, Shortcut overrides, `APPLE_NOTES_MCP_ENABLE_PRIVATE`, `APPLE_NOTES_MCP_ALLOW_UNVERIFIED`, and `APPLE_NOTES_MCP_ALLOW_PRIVATE_CONTENT_PATHS` are not accepted over the socket. External public and private native helpers are unavailable in broker sessions; use an in-process server for tools that require them. These checks constrain the code and settings the broker executes, but do not authenticate same-user clients.
3114
+
3115
+ Installed Shortcuts remain user-managed trusted integrations. The broker does not authenticate their contents; preventing a client from overriding a Shortcut's name does not verify the workflow installed under that name.
3116
+
3117
+ The allowed settings are `APPLE_NOTES_MCP_BLOCKS_MAX_BYTES`, `APPLE_NOTES_MCP_EXPORT_MAX_BYTES`, `APPLE_NOTES_MCP_MAX_ATTACHMENT_BYTES`, `APPLE_NOTES_MCP_MAX_BUFFER`, `APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES`, `APPLE_NOTES_MCP_MAX_RETRIES`, `APPLE_NOTES_MCP_PRIVATE_HELPER_TIMEOUT_MS`, `APPLE_NOTES_MCP_PUBLIC_HELPER_TIMEOUT_MS`, `APPLE_NOTES_MCP_RETRY_DELAY_MS`, and `APPLE_NOTES_MCP_TIMEOUT_MS`. Values must be decimal integers from 1 to 2,147,483,647; other keys or values are ignored. Helper timeouts have no effect while external helpers are disabled.
3118
+
3119
+ Signing affects whether the grants survive a rebuild. Setup automatically selects an identity only when exactly one **Developer ID Application** or **Apple Development** identity qualifies. If more than one qualifies across those types, specify `--sign-identity`. Permission continuity depends on the app's designated code requirement and macOS permission state, not just its bundle identifier and team. In particular, do not assume an Apple Development grant will survive certificate renewal or a signing identity change. Without a suitable identity, setup signs ad hoc and warns that rebuilding may require you to grant permissions again. See [Node runtime and TCC permissions](docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md) for details.
3120
+
3121
+ Nothing changes unless you run `setup --broker`. When the broker is absent, stale, or unavailable, the server runs in-process using that process's own permissions. `doctor` and `get-capabilities` report whether the session is brokered and, if not, why. After updating the package or replacing its Node runtime, run `setup --broker` again to install the matching server and runtime pin, then restart your MCP clients.
3122
+
3123
+ Set `APPLE_NOTES_MCP_BROKER=off` in a client's environment to keep **that client** in-process. This does not stop the broker or remove other same-user processes' access to its socket. To remove that access, run `apple-notes-mcp setup --broker --uninstall` to stop and remove the broker; you can also revoke its Full Disk Access and Notes Automation grants in System Settings. Restart your MCP clients afterwards.
3124
+
3125
+ Reinstall and uninstall wait for launchd to confirm that the old service is gone. If stopping it fails or its state cannot be confirmed, setup reports the failure and preserves the installed artifacts. Successful uninstall removes the app, LaunchAgent, manifest, socket and legacy broker log; it removes the state directory only when empty, preserving unrelated files. Removing the broker does not reset macOS permission grants.
3126
+
3127
+ Install the package globally (`npm i -g apple-notes-mcp`) before running setup. `APPLE_NOTES_MCP_BROKER_DIR`, `APPLE_NOTES_MCP_BROKER_APP_DIR`, and `APPLE_NOTES_MCP_BROKER_AGENT_DIR` override the socket, app, and LaunchAgent folders, and `APPLE_NOTES_MCP_BROKER_SIGN_IDENTITY` the signing identity.
3128
+
3129
+ ---
3130
+
3076
3131
  ## Paragraph anchor resolver (opt-in)
3077
3132
 
3078
3133
  An `applenotes://` paragraph link only opens on an Apple device, and it breaks
@@ -3119,6 +3174,8 @@ paragraph or note.
3119
3174
 
3120
3175
  - **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.
3121
3176
  - **Permission required** - macOS will prompt for automation permission on first use.
3177
+ - **Optional broker trusts all same-user processes** - With the [permission broker](#permission-broker-opt-in) running, every process under your macOS user can use its Notes capabilities with the broker's Full Disk Access and Notes Automation grants. The socket checks exclude other users, but do not authenticate same-user apps. This is comparable trust exposure to granting Full Disk Access to Node. `APPLE_NOTES_MCP_BROKER=off` bypasses it for one client; `apple-notes-mcp setup --broker --uninstall` stops and removes the shared service.
3178
+ - **Broker checks have limits** - Signature and hash checks detect tampering present before spawn; they do not isolate the user-writable bundle and runtime from a hostile, unsandboxed same-user process modifying them after validation. Installed Shortcuts are user-managed trusted integrations whose contents the broker does not authenticate.
3122
3179
  - **Password-protected notes** - Notes with passwords cannot be read or modified via this server.
3123
3180
  - **No credential storage** - The server doesn't store any passwords or authentication tokens.
3124
3181
 
@@ -3310,4 +3367,6 @@ Part of a family of macOS MCP servers:
3310
3367
 
3311
3368
  ## Recurring macOS permission prompts
3312
3369
 
3313
- If macOS keeps re-prompting for Full Disk Access or Automation for `node` (often after a `brew upgrade`), the cause is almost always an **ad-hoc-signed Node** (typically Homebrew's): its code signature (cdhash) changes on every update, so macOS TCC treats each new build as a brand-new binary and silently drops the grants you already made. The fix is to run this server under an official, **Developer-ID-signed Node at a stable path** — its signing identity stays the same across updates, so you grant the permission once and it persists. The `doctor` tool detects the ad-hoc-signature case and the full walkthrough is in [docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md).
3370
+ If macOS keeps re-prompting for Full Disk Access or Automation for `node` after a Node update, check its path and code signature with `doctor`. An **ad-hoc-signed Node** has no stable certificate identity, and version managers may also move the executable when they update it.
3371
+
3372
+ You can opt in to the [permission broker](#permission-broker-opt-in) to put the grants on a signed app at a fixed path; review its same-user trust trade-off first. After a package or runtime update, run `setup --broker` again. Alternatively, keep the server in-process and use an official **Developer-ID-signed Node at a stable path**. Neither approach guarantees that grants survive a change in signing identity or macOS policy. The walkthrough and signing caveats are in [docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md](docs/NODE-RUNTIME-AND-TCC-PERMISSIONS.md).