apple-notes-mcp 2.9.15 → 2.9.17

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
@@ -1710,7 +1710,7 @@ single note.
1710
1710
  | `outputPath` | string | No | Absolute file to create. Create-only: an existing file (or symlink) is refused with `[output_exists]` |
1711
1711
  | `assetsDir` | string | No | Absolute directory for attachment copies. Existing files are never replaced; a taken name gets `-2`, `-3`, ... |
1712
1712
  | `wrap` | number | No | Hard-wrap prose at this many columns. Code, tables and headings are never wrapped |
1713
- | `template` | string | No | Render through a template: `standard-markdown` or `obsidian`. Exclusive with `templateFile` |
1713
+ | `template` | string | No | Render through a template: `standard-markdown`, `obsidian`, or a saved template's name. Exclusive with `templateFile` |
1714
1714
  | `templateFile` | string | No | Render through the JSON template in this `.json` file (same path rules; at most 256 KiB) |
1715
1715
 
1716
1716
  Both paths follow the `save-attachment` rules (absolute, under the home
@@ -1785,6 +1785,88 @@ already written is deleted if a later step fails.
1785
1785
 
1786
1786
  ---
1787
1787
 
1788
+ #### `list-markdown-templates`
1789
+
1790
+ Lists the Markdown export templates: the built-ins (`standard-markdown`,
1791
+ `obsidian`) and every saved template in the library, with its display name,
1792
+ description, size and modification date. Unreadable, invalid or unsafe files
1793
+ in the library are skipped and counted in `skipped`. Takes no parameters.
1794
+
1795
+ **Returns:** `builtins`, `templates`, `skipped`, and `dir` (the library
1796
+ directory).
1797
+
1798
+ ---
1799
+
1800
+ #### `show-markdown-template`
1801
+
1802
+ Returns one template in its portable form: a built-in, or a saved template
1803
+ exactly as stored (overrides only). Use it as the starting point for a new
1804
+ template.
1805
+
1806
+ | Parameter | Type | Required | Description |
1807
+ |-----------|------|----------|-------------|
1808
+ | `name` | string | Yes | A built-in or saved template name |
1809
+ | `expanded` | boolean | No | Also return `expanded`, with every rule filled in from its base |
1810
+
1811
+ **Returns:** `name`, `source` (`builtin` or `saved`), `template`, and
1812
+ optionally `expanded`.
1813
+
1814
+ ---
1815
+
1816
+ #### `validate-markdown-template`
1817
+
1818
+ Checks a template without saving it. Pass exactly one of `name`, `template`
1819
+ or `templateFile`.
1820
+
1821
+ | Parameter | Type | Required | Description |
1822
+ |-----------|------|----------|-------------|
1823
+ | `name` | string | One of three | A saved or built-in template |
1824
+ | `template` | object or string | One of three | The template as a JSON object or JSON text |
1825
+ | `templateFile` | string | One of three | Absolute path of a JSON template file (home, a temp directory, or `/Volumes`; at most 256 KiB) |
1826
+
1827
+ **Returns:** `valid`, and `errors` as `{path, message}` pairs such as
1828
+ `$.rules["inline.bold"].after: is required when mode is "wrap"`. An invalid
1829
+ template is a normal result, not a tool error.
1830
+
1831
+ ---
1832
+
1833
+ #### `save-markdown-template`
1834
+
1835
+ Validates a template and stores it in the library so `export-notes-markdown`
1836
+ can use it by `template` name.
1837
+
1838
+ | Parameter | Type | Required | Description |
1839
+ |-----------|------|----------|-------------|
1840
+ | `name` | string | Yes | Lowercase `a-z`, `0-9`, `-` and `_`, 1-64 characters, starting with a letter or digit. Built-in names are reserved |
1841
+ | `template` | object or string | One of `template`/`templateFile` | The template |
1842
+ | `templateFile` | string | One of `template`/`templateFile` | A JSON template file |
1843
+ | `force` | boolean | No | Replace an existing template of this name (default `false`) |
1844
+
1845
+ Saving is create-only: an existing name is refused with `[template-exists]`
1846
+ unless `force` is true. An invalid template is refused with
1847
+ `[invalid-template]` and one JSON path per problem. The file is written to a
1848
+ temporary name and then moved into place, with mode `0600` in a `0700`
1849
+ directory. The library is `~/Library/Application Support/apple-notes-mcp/templates`,
1850
+ or `APPLE_NOTES_MCP_TEMPLATE_DIR`. A symlinked library or template file is
1851
+ refused.
1852
+
1853
+ **Returns:** `name`, `path`, `bytes`, and `replaced`.
1854
+
1855
+ ---
1856
+
1857
+ #### `delete-markdown-template`
1858
+
1859
+ Removes one saved template file from the library. Built-in templates cannot
1860
+ be deleted.
1861
+
1862
+ | Parameter | Type | Required | Description |
1863
+ |-----------|------|----------|-------------|
1864
+ | `name` | string | Yes | The saved template to delete |
1865
+
1866
+ **Returns:** `name`, `path`, and `deleted: true`.
1867
+
1868
+ ---
1869
+
1788
1870
  #### `get-checklist-state`
1789
1871
 
1790
1872
  Reads checklist done/undone state for a note. This bypasses the AppleScript limitation where `body of note` strips checklist state, by reading directly from the NoteStore SQLite database.
@@ -1990,6 +2072,72 @@ calling this tool again, which would create a second note.
1990
2072
 
1991
2073
  ---
1992
2074
 
2075
+ #### `add-attachment-from-pasteboard`
2076
+
2077
+ Attaches whatever image, PDF, or file is on the pasteboard (a screenshot, "Copy
2078
+ Image", or a file copied in Finder) to an exact note. The pasteboard is read once
2079
+ through AppKit's public `NSPasteboard` API (via JXA, no native build) and its
2080
+ bytes are frozen into a private temporary file before anything touches the note.
2081
+ The pasteboard itself is never written. The note and `expectedContentHash` are
2082
+ checked before the pasteboard is read, so a request with a wrong id, a stale
2083
+ revision, or a malformed `filename` never captures your clipboard. The frozen
2084
+ file then goes through [`add-attachment`](#add-attachment)'s checks.
2085
+
2086
+ | Parameter | Type | Required | Description |
2087
+ |-----------|------|----------|-------------|
2088
+ | `id` | string | Yes | Note ID |
2089
+ | `expectedContentHash` | string | Yes | The note's current `contentHash` (from `get-note-content`) |
2090
+ | `filename` | string | No | Attachment name. Defaults to the copied file's name, or `Pasted image.png` / `Pasted document.pdf` for image or PDF data. Without an extension, the pasted type's extension is added; with one, it must match the pasted type. Otherwise the same rules as `add-attachment` |
2091
+ | `allowPasteAlert` | boolean | No | Default `false`. Read the pasteboard even when macOS will show its paste alert (see **Paste privacy** below). Never overrides a Deny setting |
2092
+
2093
+ **Returns:** the `add-attachment` result plus `source` (`kind`: `file` or
2094
+ `data`, the pasteboard `type`, and the default `filename`). A copied file wins
2095
+ over image data; among data types PNG is preferred, then JPEG, HEIC, GIF, TIFF,
2096
+ and PDF.
2097
+
2098
+ **Paste privacy (macOS 15.4 and later):** macOS can show an alert asking
2099
+ whether to allow a paste when a process reads the general pasteboard without a
2100
+ user paste. Before reading anything, the tool checks AppKit's
2101
+ [`NSPasteboard.accessBehavior`](https://developer.apple.com/documentation/appkit/nspasteboard/accessbehavior-swift.enum).
2102
+ It reads only when the value is `alwaysAllow`, or when you pass
2103
+ `allowPasteAlert: true` and the value is `default` or `ask` (macOS then shows
2104
+ its alert, and the call waits for your answer up to the automation timeout).
2105
+ Otherwise it reads nothing and returns `code: "permission_denied"` with
2106
+ `pasteboardCode: "pasteboard_access_denied"` and `accessBehavior` (`default`,
2107
+ `ask`, `alwaysDeny`, or `unknown`). `alwaysDeny` is always refused; change it in
2108
+ System Settings, where macOS lists an app after its first paste alert. On macOS
2109
+ before 15.4 the property does not exist and the pasteboard is read as before.
2110
+
2111
+ **Errors** carry a `pasteboardCode` next to the shared `code`, and
2112
+ `committed: false` because nothing was written:
2113
+
2114
+ | `pasteboardCode` | `code` | Cause |
2115
+ |------------------|--------|-------|
2116
+ | `pasteboard_access_denied` | `permission_denied` | Reading would show the macOS paste alert, or pasting is denied (see above) |
2117
+ | `pasteboard_empty` | `validation_error` | Nothing is on the pasteboard |
2118
+ | `unsupported_content` | `validation_error` | No PNG, JPEG, HEIC, GIF, TIFF, PDF, or copied file (text belongs in `append-to-note`); the message and `types` list the pasteboard types found |
2119
+ | `multiple_files` | `validation_error` | More than one copied file; `count` says how many. Copy one file, or use `add-attachment` per file |
2120
+ | `too_large` | `validation_error` | More than 64 MiB |
2121
+ | `file_unreadable` | `validation_error` | The copied file is a symlink, empty, over 64 MiB, or unreadable |
2122
+ | `pasteboard_changed` | `operation_failed` | The pasteboard changed while it was being read; try again |
2123
+ | `pasteboard_timeout` | `operation_failed` | The read timed out (for example an unanswered paste alert) |
2124
+ | `pasteboard_unavailable` | `operation_failed` | The MCP host is not running in your logged-in GUI session (for example over SSH) |
2125
+ | `write_failed` | `operation_failed` | The temporary copy could not be written |
2126
+
2127
+ The pasteboard read uses the same timeout as other automation steps
2128
+ (`APPLE_NOTES_MCP_TIMEOUT_MS`, 30 seconds by default).
2129
+
2130
+ **Known limitation ([#236](https://github.com/sweetrb/apple-notes-mcp/issues/236)):** on macOS 27, Notes' AppleScript dictionary does not list
2131
+ PDF attachments, so a PDF (pasted or from `add-attachment`) is inserted but
2132
+ cannot be verified and the call reports "insertion outcome uncertain". Read the
2133
+ note before retrying.
2134
+
2135
+ For testing, `APPLE_NOTES_MCP_PASTEBOARD_NAME` makes the tool read a private
2136
+ named pasteboard instead of the general one, so a test never touches your
2137
+ clipboard.
2138
+
2139
+ ---
2140
+
1993
2141
  #### `list-attachments`
1994
2142
 
1995
2143
  Lists attachments in a note.
@@ -2508,6 +2656,7 @@ All configuration is optional — the server works out of the box. Override beha
2508
2656
  | `APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES` | `262144` (256 KB) | Per-image cap on the base64 payload kept inline in a [`get-note-content`](#get-note-content) response. Inline images over the cap are replaced with placeholders (with a warning appended) so an image-heavy note cannot exceed the MCP client's message limit and drop the connection; export the real files with [`save-attachment`](#save-attachment) or [`fetch-attachment`](#fetch-attachment). Raise it to keep bigger images inline. |
2509
2657
  | `APPLE_NOTES_MCP_CONFIG_FILE` | `~/Library/Application Support/apple-notes-mcp/config.json` | Path to the JSON config file (see below). |
2510
2658
  | `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. |
2659
+ | `APPLE_NOTES_MCP_TEMPLATE_DIR` | `~/Library/Application Support/apple-notes-mcp/templates` | Absolute directory of the saved Markdown template library (`save-markdown-template` and friends). |
2511
2660
  | `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. |
2512
2661
  | `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. |
2513
2662
  | `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. |