apple-notes-mcp 2.8.27 → 2.8.28

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.
Files changed (3) hide show
  1. package/README.md +32 -1
  2. package/build/index.js +559 -65
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -513,6 +513,36 @@ Disk Access.
513
513
 
514
514
  ---
515
515
 
516
+ #### `get-note-blocks`
517
+
518
+ Decodes one note's body into typed blocks, read-only, from the NoteStore
519
+ database. Each block is one paragraph with its `style` (`title`, `heading`,
520
+ `subheading`, `body`, `monospaced`, `bulleted`, `dashed`, `numbered`,
521
+ `checklist`, or `unknown` with the raw `styleType`), `indent`, `alignment`,
522
+ `blockQuote`, checklist `id`/`done`, and `paragraphUuid` when stored. Each
523
+ block lists its inline `runs` with `bold`, `italic`, `underline`,
524
+ `strikethrough`, `superscript`, `subscript`, `color`, `highlight`, `link`
525
+ (plus `linkSafe`), `font`, and `attachment`, and its attachment markers in body
526
+ order. Offsets and lengths count UTF-16 code units. `summary` counts styles and
527
+ inline attributes for the whole note. `undecodedFields` lists stored field
528
+ numbers the decoder deliberately does not interpret.
529
+
530
+ **Requires:** Full Disk Access. Password-protected notes are refused.
531
+
532
+ | Parameter | Type | Required | Description |
533
+ |-----------|------|----------|-------------|
534
+ | `id` | string | Yes | Exact note ID |
535
+ | `offset` | number | No | First block to return (default 0). Use `page.nextOffset` |
536
+ | `limit` | number | No | Maximum blocks per page (default 500, max 5000) |
537
+
538
+ A page also stops early to stay under `APPLE_NOTES_MCP_BLOCKS_MAX_BYTES`
539
+ (default 4 MB). `paragraphUuid` is not unique: Notes copies it when a paragraph
540
+ is split. Link URLs are returned as stored, and `linkSafe` is false for schemes
541
+ other than http(s), `notes:`, `applenotes:` and `mailto:`. Errors carry a
542
+ stable code in brackets, such as `[encrypted]` or `[no-full-disk-access]`.
543
+
544
+ ---
545
+
516
546
  #### `list-native-tags`
517
547
 
518
548
  Lists actual native Notes tags used within one explicit `account` and `folder`,
@@ -1610,6 +1640,7 @@ All configuration is optional — the server works out of the box. Override beha
1610
1640
  | `APPLE_NOTES_MCP_CONFIG_FILE` | `~/Library/Application Support/apple-notes-mcp/config.json` | Path to the JSON config file (see below). |
1611
1641
  | `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. |
1612
1642
  | `APPLE_NOTES_MCP_EXPORT_MAX_BYTES` | `8388608` (8 MB) | Largest response `export-notes-json` sends; a page closes early to stay under 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. |
1643
+ | `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`. |
1613
1644
  | `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. |
1614
1645
  | `APPLE_NOTES_MCP_RETRY_DELAY_MS` | `1000` (1 s) | Base delay before the first retry; subsequent retries back off exponentially (1s, 2s, 4s, ...). |
1615
1646
  | `DEBUG` / `VERBOSE` | unset | Set either to enable verbose diagnostic logging to stderr. |
@@ -1638,7 +1669,7 @@ MCP stores no secrets, but as a general rule keep only non-secret config here.
1638
1669
 
1639
1670
  ## Full Disk Access
1640
1671
 
1641
- 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-link`, the checklist annotations in `get-note-markdown`, and the database half of `get-sync-status`.
1672
+ 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`, `get-note-link`, the checklist annotations in `get-note-markdown`, and the database half of `get-sync-status`.
1642
1673
 
1643
1674
  > 📘 **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.
1644
1675
 
package/build/index.js CHANGED
@@ -3358,9 +3358,9 @@ var require_utils = __commonJS({
3358
3358
  let output = "";
3359
3359
  for (let i = 0; i < input.length; i++) {
3360
3360
  if (input[i] === "%" && i + 2 < input.length) {
3361
- const hex2 = input.slice(i + 1, i + 3);
3362
- if (isHexPair(hex2)) {
3363
- const normalizedHex = hex2.toUpperCase();
3361
+ const hex3 = input.slice(i + 1, i + 3);
3362
+ if (isHexPair(hex3)) {
3363
+ const normalizedHex = hex3.toUpperCase();
3364
3364
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3365
3365
  if (decodeUnreserved && isUnreserved(decoded)) {
3366
3366
  output += decoded;
@@ -3380,9 +3380,9 @@ var require_utils = __commonJS({
3380
3380
  for (let i = 0; i < input.length; i++) {
3381
3381
  const ch = input[i];
3382
3382
  if (ch === "%" && i + 2 < input.length) {
3383
- const hex2 = input.slice(i + 1, i + 3);
3384
- if (isHexPair(hex2)) {
3385
- const normalizedHex = hex2.toUpperCase();
3383
+ const hex3 = input.slice(i + 1, i + 3);
3384
+ if (isHexPair(hex3)) {
3385
+ const normalizedHex = hex3.toUpperCase();
3386
3386
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3387
3387
  if (decoded !== "." && isUnreserved(decoded)) {
3388
3388
  output += decoded;
@@ -3422,9 +3422,9 @@ var require_utils = __commonJS({
3422
3422
  for (let i = 0; i < input.length; i++) {
3423
3423
  const ch = input[i];
3424
3424
  if (ch === "%" && i + 2 < input.length) {
3425
- const hex2 = input.slice(i + 1, i + 3);
3426
- if (isHexPair(hex2)) {
3427
- output += "%" + hex2.toUpperCase();
3425
+ const hex3 = input.slice(i + 1, i + 3);
3426
+ if (isHexPair(hex3)) {
3427
+ output += "%" + hex3.toUpperCase();
3428
3428
  i += 2;
3429
3429
  continue;
3430
3430
  }
@@ -3460,9 +3460,9 @@ var require_utils = __commonJS({
3460
3460
  for (let i = 0; i < input.length; i++) {
3461
3461
  const ch = input[i];
3462
3462
  if (ch === "%" && i + 2 < input.length) {
3463
- const hex2 = input.slice(i + 1, i + 3);
3464
- if (isHexPair(hex2)) {
3465
- output += "%" + hex2.toUpperCase();
3463
+ const hex3 = input.slice(i + 1, i + 3);
3464
+ if (isHexPair(hex3)) {
3465
+ output += "%" + hex3.toUpperCase();
3466
3466
  i += 2;
3467
3467
  continue;
3468
3468
  }
@@ -3507,9 +3507,9 @@ var require_utils = __commonJS({
3507
3507
  for (let i = 0; i < input.length; i++) {
3508
3508
  const ch = input[i];
3509
3509
  if (ch === "%" && i + 2 < input.length) {
3510
- const hex2 = input.slice(i + 1, i + 3);
3511
- if (isHexPair(hex2)) {
3512
- const normalizedHex = hex2.toUpperCase();
3510
+ const hex3 = input.slice(i + 1, i + 3);
3511
+ if (isHexPair(hex3)) {
3512
+ const normalizedHex = hex3.toUpperCase();
3513
3513
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3514
3514
  if (isUnreserved(decoded)) {
3515
3515
  output += decoded;
@@ -3547,9 +3547,9 @@ var require_utils = __commonJS({
3547
3547
  let output = "";
3548
3548
  for (let i = 0; i < input.length; i++) {
3549
3549
  if (input[i] === "%" && i + 2 < input.length) {
3550
- const hex2 = input.slice(i + 1, i + 3);
3551
- if (isHexPair(hex2)) {
3552
- output += "%" + hex2.toUpperCase();
3550
+ const hex3 = input.slice(i + 1, i + 3);
3551
+ if (isHexPair(hex3)) {
3552
+ output += "%" + hex3.toUpperCase();
3553
3553
  i += 2;
3554
3554
  continue;
3555
3555
  }
@@ -8768,25 +8768,25 @@ var require_ContainerNode = __commonJS({
8768
8768
  return this._firstChild;
8769
8769
  } },
8770
8770
  lastChild: { get: function() {
8771
- var kids = this._childNodes, first;
8771
+ var kids = this._childNodes, first2;
8772
8772
  if (kids) {
8773
8773
  return kids.length === 0 ? null : kids[kids.length - 1];
8774
8774
  }
8775
- first = this._firstChild;
8776
- if (first === null) {
8775
+ first2 = this._firstChild;
8776
+ if (first2 === null) {
8777
8777
  return null;
8778
8778
  }
8779
- return first._previousSibling;
8779
+ return first2._previousSibling;
8780
8780
  } },
8781
8781
  _ensureChildNodes: { value: function() {
8782
8782
  if (this._childNodes) {
8783
8783
  return;
8784
8784
  }
8785
- var first = this._firstChild, kid = first, childNodes = this._childNodes = new NodeList();
8786
- if (first) do {
8785
+ var first2 = this._firstChild, kid = first2, childNodes = this._childNodes = new NodeList();
8786
+ if (first2) do {
8787
8787
  childNodes.push(kid);
8788
8788
  kid = kid._nextSibling;
8789
- } while (kid !== first);
8789
+ } while (kid !== first2);
8790
8790
  this._firstChild = null;
8791
8791
  } },
8792
8792
  // Remove all of this node's children. This is a minor
@@ -10257,22 +10257,22 @@ var require_Element = __commonJS({
10257
10257
  }
10258
10258
  },
10259
10259
  _insertAdjacent: { value: function _insertAdjacent(position, node) {
10260
- var first = false;
10260
+ var first2 = false;
10261
10261
  switch (position) {
10262
10262
  case "beforebegin":
10263
- first = true;
10263
+ first2 = true;
10264
10264
  /* falls through */
10265
10265
  case "afterend":
10266
10266
  var parent = this.parentNode;
10267
10267
  if (parent === null) {
10268
10268
  return null;
10269
10269
  }
10270
- return parent.insertBefore(node, first ? this : this.nextSibling);
10270
+ return parent.insertBefore(node, first2 ? this : this.nextSibling);
10271
10271
  case "afterbegin":
10272
- first = true;
10272
+ first2 = true;
10273
10273
  /* falls through */
10274
10274
  case "beforeend":
10275
- return this.insertBefore(node, first ? this.firstChild : null);
10275
+ return this.insertBefore(node, first2 ? this.firstChild : null);
10276
10276
  default:
10277
10277
  return utils.SyntaxError();
10278
10278
  }
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
24465
24465
  } else if (node.nodeType === 1) {
24466
24466
  replacement = replacementForNode.call(self, node);
24467
24467
  }
24468
- return join14(output, replacement);
24468
+ return join15(output, replacement);
24469
24469
  }, "");
24470
24470
  }
24471
24471
  function postProcess(output) {
24472
24472
  var self = this;
24473
24473
  this.rules.forEach(function(rule) {
24474
24474
  if (typeof rule.append === "function") {
24475
- output = join14(output, rule.append(self.options));
24475
+ output = join15(output, rule.append(self.options));
24476
24476
  }
24477
24477
  });
24478
24478
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
24484
24484
  if (whitespace.leading || whitespace.trailing) content = content.trim();
24485
24485
  return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
24486
24486
  }
24487
- function join14(output, replacement) {
24487
+ function join15(output, replacement) {
24488
24488
  var s1 = trimTrailingNewlines(output);
24489
24489
  var s2 = trimLeadingNewlines(replacement);
24490
24490
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -24675,9 +24675,9 @@ var util;
24675
24675
  })(util || (util = {}));
24676
24676
  var objectUtil;
24677
24677
  (function(objectUtil2) {
24678
- objectUtil2.mergeShapes = (first, second) => {
24678
+ objectUtil2.mergeShapes = (first2, second) => {
24679
24679
  return {
24680
- ...first,
24680
+ ...first2,
24681
24681
  ...second
24682
24682
  // second overwrites first
24683
24683
  };
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
24777
24777
  constructor(issues) {
24778
24778
  super();
24779
24779
  this.issues = [];
24780
- this.addIssue = (sub2) => {
24781
- this.issues = [...this.issues, sub2];
24780
+ this.addIssue = (sub3) => {
24781
+ this.issues = [...this.issues, sub3];
24782
24782
  };
24783
24783
  this.addIssues = (subs = []) => {
24784
24784
  this.issues = [...this.issues, ...subs];
@@ -24845,13 +24845,13 @@ var ZodError = class _ZodError extends Error {
24845
24845
  flatten(mapper = (issue2) => issue2.message) {
24846
24846
  const fieldErrors = {};
24847
24847
  const formErrors = [];
24848
- for (const sub2 of this.issues) {
24849
- if (sub2.path.length > 0) {
24850
- const firstEl = sub2.path[0];
24848
+ for (const sub3 of this.issues) {
24849
+ if (sub3.path.length > 0) {
24850
+ const firstEl = sub3.path[0];
24851
24851
  fieldErrors[firstEl] = fieldErrors[firstEl] || [];
24852
- fieldErrors[firstEl].push(mapper(sub2));
24852
+ fieldErrors[firstEl].push(mapper(sub3));
24853
24853
  } else {
24854
- formErrors.push(mapper(sub2));
24854
+ formErrors.push(mapper(sub3));
24855
24855
  }
24856
24856
  }
24857
24857
  return { formErrors, fieldErrors };
@@ -27599,10 +27599,10 @@ var ZodRecord = class _ZodRecord extends ZodType {
27599
27599
  get element() {
27600
27600
  return this._def.valueType;
27601
27601
  }
27602
- static create(first, second, third) {
27602
+ static create(first2, second, third) {
27603
27603
  if (second instanceof ZodType) {
27604
27604
  return new _ZodRecord({
27605
- keyType: first,
27605
+ keyType: first2,
27606
27606
  valueType: second,
27607
27607
  typeName: ZodFirstPartyTypeKind.ZodRecord,
27608
27608
  ...processCreateParams(third)
@@ -27610,7 +27610,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
27610
27610
  }
27611
27611
  return new _ZodRecord({
27612
27612
  keyType: ZodString.create(),
27613
- valueType: first,
27613
+ valueType: first2,
27614
27614
  typeName: ZodFirstPartyTypeKind.ZodRecord,
27615
27615
  ...processCreateParams(second)
27616
27616
  });
@@ -29150,12 +29150,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
29150
29150
  function flattenError(error2, mapper = (issue2) => issue2.message) {
29151
29151
  const fieldErrors = {};
29152
29152
  const formErrors = [];
29153
- for (const sub2 of error2.issues) {
29154
- if (sub2.path.length > 0) {
29155
- fieldErrors[sub2.path[0]] = fieldErrors[sub2.path[0]] || [];
29156
- fieldErrors[sub2.path[0]].push(mapper(sub2));
29153
+ for (const sub3 of error2.issues) {
29154
+ if (sub3.path.length > 0) {
29155
+ fieldErrors[sub3.path[0]] = fieldErrors[sub3.path[0]] || [];
29156
+ fieldErrors[sub3.path[0]].push(mapper(sub3));
29157
29157
  } else {
29158
- formErrors.push(mapper(sub2));
29158
+ formErrors.push(mapper(sub3));
29159
29159
  }
29160
29160
  }
29161
29161
  return { formErrors, fieldErrors };
@@ -39330,6 +39330,68 @@ function embeddedMessage(field) {
39330
39330
  if (!bytes) return void 0;
39331
39331
  return decodeMessage(bytes);
39332
39332
  }
39333
+ var ProtobufDecodeError = class extends Error {
39334
+ constructor(message) {
39335
+ super(message);
39336
+ this.name = "ProtobufDecodeError";
39337
+ }
39338
+ };
39339
+ function decodeVarint64(buf, offset) {
39340
+ let result = 0n;
39341
+ let shift = 0n;
39342
+ let pos = offset;
39343
+ while (pos < buf.length) {
39344
+ const byte = buf[pos++];
39345
+ result |= BigInt(byte & 127) << shift;
39346
+ if ((byte & 128) === 0) return [BigInt.asUintN(64, result), pos];
39347
+ shift += 7n;
39348
+ if (shift >= 70n) throw new ProtobufDecodeError(`Varint too long at offset ${offset}`);
39349
+ }
39350
+ throw new ProtobufDecodeError(`Unexpected end of buffer reading varint at offset ${offset}`);
39351
+ }
39352
+ function decodeWireFields(buf) {
39353
+ const fields = [];
39354
+ let offset = 0;
39355
+ while (offset < buf.length) {
39356
+ const [tag, afterTag] = decodeVarint64(buf, offset);
39357
+ offset = afterTag;
39358
+ const fieldNumber = Number(tag >> 3n);
39359
+ const wireType = Number(tag & 7n);
39360
+ if (fieldNumber === 0 || fieldNumber > 536870911)
39361
+ throw new ProtobufDecodeError("Invalid field number");
39362
+ if (wireType === 0) {
39363
+ const [value, next] = decodeVarint64(buf, offset);
39364
+ offset = next;
39365
+ fields.push({ fieldNumber, wireType, varint: value });
39366
+ continue;
39367
+ }
39368
+ let length;
39369
+ if (wireType === 1) length = 8;
39370
+ else if (wireType === 5) length = 4;
39371
+ else if (wireType === 2) {
39372
+ const [value, next] = decodeVarint64(buf, offset);
39373
+ if (value > BigInt(buf.length)) throw new ProtobufDecodeError("Truncated field");
39374
+ length = Number(value);
39375
+ offset = next;
39376
+ } else throw new ProtobufDecodeError(`Unsupported wire type ${wireType}`);
39377
+ if (offset + length > buf.length) throw new ProtobufDecodeError("Truncated field");
39378
+ fields.push({
39379
+ fieldNumber,
39380
+ wireType,
39381
+ bytes: buf.subarray(offset, offset + length)
39382
+ });
39383
+ offset += length;
39384
+ }
39385
+ return fields;
39386
+ }
39387
+ function signedVarint(value) {
39388
+ return Number(BigInt.asIntN(64, value));
39389
+ }
39390
+ function fixed32Float(field) {
39391
+ if (!field || field.wireType !== 5 || !field.bytes || field.bytes.length !== 4) return void 0;
39392
+ const bytes = field.bytes;
39393
+ return new DataView(bytes.buffer, bytes.byteOffset, 4).getFloat32(0, true);
39394
+ }
39333
39395
 
39334
39396
  // src/utils/checklistRuns.ts
39335
39397
  function checklistRunLineStart(text, position, length) {
@@ -39373,9 +39435,9 @@ function queryNoteData(noteId3) {
39373
39435
  timeout: 5e3,
39374
39436
  stdio: ["pipe", "pipe", "pipe"]
39375
39437
  });
39376
- const hex2 = result.trim();
39377
- if (!hex2) return { hex: null };
39378
- return { hex: hex2 };
39438
+ const hex3 = result.trim();
39439
+ if (!hex3) return { hex: null };
39440
+ return { hex: hex3 };
39379
39441
  } catch (error2) {
39380
39442
  const message = error2 instanceof Error ? error2.message : String(error2);
39381
39443
  console.error(`Failed to query NoteStore database: ${message}`);
@@ -39385,10 +39447,10 @@ function queryNoteData(noteId3) {
39385
39447
  return { hex: null };
39386
39448
  }
39387
39449
  }
39388
- function hexToBytes(hex2) {
39389
- const bytes = new Uint8Array(hex2.length / 2);
39390
- for (let i = 0; i < hex2.length; i += 2) {
39391
- bytes[i / 2] = parseInt(hex2.substring(i, i + 2), 16);
39450
+ function hexToBytes(hex3) {
39451
+ const bytes = new Uint8Array(hex3.length / 2);
39452
+ for (let i = 0; i < hex3.length; i += 2) {
39453
+ bytes[i / 2] = parseInt(hex3.substring(i, i + 2), 16);
39392
39454
  }
39393
39455
  return bytes;
39394
39456
  }
@@ -43361,7 +43423,7 @@ function decodeHtmlEntities(text) {
43361
43423
  }
43362
43424
  return String.fromCodePoint(codePoint);
43363
43425
  };
43364
- return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex2) => decodeCodePoint(match, hex2, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/&nbsp(?:;|(?![0-9a-z]))/gi, " ").replace(/&quot(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/&lt(?:;|(?![0-9a-z]))/gi, "<").replace(/&gt(?:;|(?![0-9a-z]))/gi, ">").replace(/&amp(?:;|(?![0-9a-z]))/gi, "&");
43426
+ return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex3) => decodeCodePoint(match, hex3, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/&nbsp(?:;|(?![0-9a-z]))/gi, " ").replace(/&quot(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/&lt(?:;|(?![0-9a-z]))/gi, "<").replace(/&gt(?:;|(?![0-9a-z]))/gi, ">").replace(/&amp(?:;|(?![0-9a-z]))/gi, "&");
43365
43427
  }
43366
43428
  function firstVisibleHtmlLine(html) {
43367
43429
  let text = html;
@@ -44389,10 +44451,10 @@ var LEGACY_ENTITIES = {
44389
44451
  function comparableVisibleText(html) {
44390
44452
  return html.replace(/<br\s*\/?\s*>/gi, " ").replace(/<[^>]*>/g, (tag) => INLINE_TAG.test(tag) ? "" : " ").replace(
44391
44453
  /&(?:(nbsp|quot|lt|gt|amp);?|apos;|#(\d+);|#x([0-9a-f]+);)/gi,
44392
- (_match, legacy, dec, hex2) => {
44454
+ (_match, legacy, dec, hex3) => {
44393
44455
  if (legacy) return LEGACY_ENTITIES[legacy.toLowerCase()];
44394
44456
  if (dec) return String.fromCodePoint(Number(dec));
44395
- if (hex2) return String.fromCodePoint(Number.parseInt(hex2, 16));
44457
+ if (hex3) return String.fromCodePoint(Number.parseInt(hex3, 16));
44396
44458
  return "'";
44397
44459
  }
44398
44460
  ).replace(/\s+/g, " ").trim();
@@ -45592,6 +45654,402 @@ function parseNoteTable(compressed) {
45592
45654
  return { rows: values, rowIds: rows.ids, columnIds: columns.ids };
45593
45655
  }
45594
45656
 
45657
+ // src/utils/noteBlocks.ts
45658
+ import { execFileSync as execFileSync12 } from "node:child_process";
45659
+ import { existsSync as existsSync10 } from "node:fs";
45660
+ import { homedir as homedir11 } from "node:os";
45661
+ import { join as join13 } from "node:path";
45662
+ import { gunzipSync as gunzipSync5 } from "node:zlib";
45663
+ var NoteBlocksError = class extends Error {
45664
+ code;
45665
+ constructor(code, message) {
45666
+ super(message);
45667
+ this.name = "NoteBlocksError";
45668
+ this.code = code;
45669
+ }
45670
+ };
45671
+ var STYLE_NAMES = {
45672
+ 0: "title",
45673
+ 1: "heading",
45674
+ 2: "subheading",
45675
+ 4: "monospaced",
45676
+ 100: "bulleted",
45677
+ 101: "dashed",
45678
+ 102: "numbered",
45679
+ 103: "checklist"
45680
+ };
45681
+ var ALIGNMENTS = {
45682
+ 0: "left",
45683
+ 1: "center",
45684
+ 2: "right",
45685
+ 3: "justify"
45686
+ };
45687
+ var HIGHLIGHTS = {
45688
+ 1: "purple",
45689
+ 2: "pink",
45690
+ 3: "orange",
45691
+ 4: "mint",
45692
+ 5: "blue"
45693
+ };
45694
+ var KNOWN_RUN_FIELDS = /* @__PURE__ */ new Set([1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14]);
45695
+ var KNOWN_PARAGRAPH_FIELDS = /* @__PURE__ */ new Set([1, 2, 4, 5, 8, 9]);
45696
+ var isSafeLink = (url) => /^(?:https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) && !Array.from(url).some((char) => char.charCodeAt(0) < 32);
45697
+ var utf8 = new TextDecoder();
45698
+ var first = (fields, n) => fields.find((f) => f.fieldNumber === n);
45699
+ var varintOf = (fields, n) => {
45700
+ const f = first(fields, n);
45701
+ return f?.wireType === 0 && f.varint !== void 0 ? signedVarint(f.varint) : void 0;
45702
+ };
45703
+ var bytesOf = (fields, n) => {
45704
+ const f = first(fields, n);
45705
+ return f?.wireType === 2 ? f.bytes : void 0;
45706
+ };
45707
+ var stringOf = (fields, n) => {
45708
+ const b = bytesOf(fields, n);
45709
+ return b ? utf8.decode(b) : void 0;
45710
+ };
45711
+ var sub2 = (fields, n) => {
45712
+ const b = bytesOf(fields, n);
45713
+ return b ? decodeWireFields(b) : void 0;
45714
+ };
45715
+ var hex2 = (bytes) => Buffer.from(bytes).toString("hex");
45716
+ var uuidString = (bytes) => {
45717
+ const h = hex2(bytes).toUpperCase();
45718
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
45719
+ };
45720
+ var channel = (value) => Math.max(0, Math.min(255, Math.round((Number.isFinite(value) ? value : 0) * 255))).toString(16).toUpperCase().padStart(2, "0");
45721
+ function colorOf(fields) {
45722
+ const [r, g, b, a] = [1, 2, 3, 4].map((n) => fixed32Float(first(fields, n)));
45723
+ if (r === void 0 && g === void 0 && b === void 0) return void 0;
45724
+ const rgb = `#${channel(r)}${channel(g)}${channel(b)}`;
45725
+ return a === void 0 || a >= 0.999 ? rgb : `${rgb}${channel(a)}`;
45726
+ }
45727
+ var DEFAULT_PARAGRAPH = {
45728
+ styleType: null,
45729
+ alignmentValue: 0,
45730
+ indent: 0,
45731
+ blockQuote: false
45732
+ };
45733
+ function decodeParagraph(fields, tally) {
45734
+ if (!fields) return DEFAULT_PARAGRAPH;
45735
+ for (const n of new Set(fields.map((f) => f.fieldNumber)))
45736
+ if (!KNOWN_PARAGRAPH_FIELDS.has(n)) tally.set(n, (tally.get(n) || 0) + 1);
45737
+ const attrs = {
45738
+ styleType: varintOf(fields, 1) ?? null,
45739
+ alignmentValue: varintOf(fields, 2) ?? 0,
45740
+ indent: Math.max(0, varintOf(fields, 4) ?? 0),
45741
+ blockQuote: (varintOf(fields, 8) ?? 0) !== 0
45742
+ };
45743
+ if (attrs.styleType === -1) attrs.styleType = null;
45744
+ const checklist = sub2(fields, 5);
45745
+ const checklistId = checklist && bytesOf(checklist, 1);
45746
+ if (attrs.styleType === 103 && checklistId)
45747
+ attrs.checklist = { id: hex2(checklistId), done: varintOf(checklist, 2) === 1 };
45748
+ const uuid2 = bytesOf(fields, 9);
45749
+ if (uuid2?.length === 16) attrs.paragraphUuid = uuidString(uuid2);
45750
+ return attrs;
45751
+ }
45752
+ function decodeInline(fields) {
45753
+ const inline = {};
45754
+ const font = sub2(fields, 3);
45755
+ const hints = font ? varintOf(font, 3) ?? 0 : 0;
45756
+ const weight = varintOf(fields, 5) ?? 0;
45757
+ if (weight === 1 || weight === 3 || hints & 1) inline.bold = true;
45758
+ if (weight === 2 || weight === 3 || hints & 2) inline.italic = true;
45759
+ if (varintOf(fields, 6)) inline.underline = true;
45760
+ if (varintOf(fields, 7)) inline.strikethrough = true;
45761
+ const baseline = varintOf(fields, 8) ?? 0;
45762
+ if (baseline > 0) inline.superscript = true;
45763
+ if (baseline < 0) inline.subscript = true;
45764
+ const color = sub2(fields, 10);
45765
+ const colorValue = color && colorOf(color);
45766
+ if (colorValue) inline.color = colorValue;
45767
+ const emphasis = varintOf(fields, 14);
45768
+ if (emphasis !== void 0 && emphasis !== 0) {
45769
+ inline.highlight = HIGHLIGHTS[emphasis] ?? "unknown";
45770
+ if (inline.highlight === "unknown") inline.highlightValue = emphasis;
45771
+ }
45772
+ const link = stringOf(fields, 9);
45773
+ if (link) {
45774
+ inline.link = link;
45775
+ inline.linkSafe = isSafeLink(link);
45776
+ }
45777
+ if (font) {
45778
+ const name = stringOf(font, 1);
45779
+ const size = fixed32Float(first(font, 2));
45780
+ if (name || size !== void 0)
45781
+ inline.font = {
45782
+ ...name ? { name } : {},
45783
+ ...size !== void 0 && Number.isFinite(size) ? { size } : {}
45784
+ };
45785
+ }
45786
+ const attachment = sub2(fields, 12);
45787
+ const attachmentId = attachment && stringOf(attachment, 1);
45788
+ if (attachmentId)
45789
+ inline.attachment = { id: attachmentId, uti: stringOf(attachment, 2) || "unknown" };
45790
+ return inline;
45791
+ }
45792
+ function wrap(error2) {
45793
+ if (error2 instanceof NoteBlocksError) throw error2;
45794
+ if (error2 instanceof ProtobufDecodeError)
45795
+ throw new NoteBlocksError("malformed-protobuf", error2.message);
45796
+ throw error2;
45797
+ }
45798
+ function decodeNoteBlocks(data) {
45799
+ let note;
45800
+ try {
45801
+ const document = sub2(decodeWireFields(data), 2);
45802
+ note = document && sub2(document, 3);
45803
+ } catch (error2) {
45804
+ wrap(error2);
45805
+ }
45806
+ const textBytes = note && bytesOf(note, 2);
45807
+ if (!note || !textBytes)
45808
+ throw new NoteBlocksError("unsupported-structure", "Unsupported Notes document structure");
45809
+ const text = utf8.decode(textBytes);
45810
+ const runTally = /* @__PURE__ */ new Map();
45811
+ const paragraphTally = /* @__PURE__ */ new Map();
45812
+ const runs = [];
45813
+ let position = 0;
45814
+ try {
45815
+ for (const field of note.filter((f) => f.fieldNumber === 5)) {
45816
+ if (field.wireType !== 2 || !field.bytes)
45817
+ throw new NoteBlocksError("invalid-runs", "Invalid Notes attribute run");
45818
+ const fields = decodeWireFields(field.bytes);
45819
+ const length = varintOf(fields, 1);
45820
+ if (length === void 0 || length < 0 || position + length > text.length)
45821
+ throw new NoteBlocksError("invalid-runs", "Invalid Notes run length");
45822
+ for (const n of new Set(fields.map((f) => f.fieldNumber)))
45823
+ if (!KNOWN_RUN_FIELDS.has(n)) runTally.set(n, (runTally.get(n) || 0) + 1);
45824
+ runs.push({
45825
+ start: position,
45826
+ length,
45827
+ paragraph: decodeParagraph(sub2(fields, 2), paragraphTally),
45828
+ inline: decodeInline(fields)
45829
+ });
45830
+ position += length;
45831
+ }
45832
+ } catch (error2) {
45833
+ wrap(error2);
45834
+ }
45835
+ if (position !== text.length)
45836
+ throw new NoteBlocksError("invalid-runs", "Incomplete Notes attribute runs");
45837
+ const blocks = [];
45838
+ const attachments = [];
45839
+ let runIndex = 0;
45840
+ let paragraphStart = 0;
45841
+ while (paragraphStart < text.length) {
45842
+ const newline = text.indexOf("\n", paragraphStart);
45843
+ const end = newline === -1 ? text.length : newline;
45844
+ while (runIndex < runs.length - 1 && runs[runIndex].start + runs[runIndex].length <= paragraphStart)
45845
+ runIndex++;
45846
+ const attrs = runs[runIndex]?.paragraph ?? DEFAULT_PARAGRAPH;
45847
+ const style = attrs.styleType === null ? "body" : STYLE_NAMES[attrs.styleType] ?? "unknown";
45848
+ const alignment = ALIGNMENTS[attrs.alignmentValue] ?? "unknown";
45849
+ const block = {
45850
+ index: blocks.length,
45851
+ start: paragraphStart,
45852
+ length: end - paragraphStart,
45853
+ text: text.slice(paragraphStart, end),
45854
+ style,
45855
+ styleType: attrs.styleType,
45856
+ indent: attrs.indent,
45857
+ alignment,
45858
+ ...alignment === "unknown" ? { alignmentValue: attrs.alignmentValue } : {},
45859
+ blockQuote: attrs.blockQuote,
45860
+ ...attrs.checklist ? { checklist: attrs.checklist } : {},
45861
+ ...attrs.paragraphUuid ? { paragraphUuid: attrs.paragraphUuid } : {},
45862
+ runs: [],
45863
+ attachments: []
45864
+ };
45865
+ for (let i = runIndex; i < runs.length && runs[i].start < end; i++) {
45866
+ const run = runs[i];
45867
+ const start = Math.max(run.start, paragraphStart);
45868
+ const stop = Math.min(run.start + run.length, end);
45869
+ if (stop <= start) continue;
45870
+ block.runs.push({
45871
+ start,
45872
+ length: stop - start,
45873
+ text: text.slice(start, stop),
45874
+ ...run.inline
45875
+ });
45876
+ if (run.inline.attachment)
45877
+ for (let at = text.indexOf("\uFFFC", start); at !== -1 && at < stop; at = text.indexOf("\uFFFC", at + 1)) {
45878
+ const marker = { ...run.inline.attachment, start: at, blockIndex: block.index };
45879
+ block.attachments.push(marker);
45880
+ attachments.push(marker);
45881
+ }
45882
+ }
45883
+ blocks.push(block);
45884
+ paragraphStart = end + 1;
45885
+ }
45886
+ return {
45887
+ text,
45888
+ textLength: text.length,
45889
+ blocks,
45890
+ attachments,
45891
+ undecodedFields: {
45892
+ attributeRun: Object.fromEntries([...runTally].sort((a, b) => a[0] - b[0])),
45893
+ paragraphStyle: Object.fromEntries([...paragraphTally].sort((a, b) => a[0] - b[0]))
45894
+ },
45895
+ summary: summarize(blocks, attachments)
45896
+ };
45897
+ }
45898
+ function summarize(blocks, attachments) {
45899
+ const summary = {
45900
+ blocks: blocks.length,
45901
+ styles: {},
45902
+ alignments: {},
45903
+ blockQuotes: 0,
45904
+ indented: 0,
45905
+ checklist: { total: 0, done: 0 },
45906
+ inline: {
45907
+ bold: 0,
45908
+ italic: 0,
45909
+ underline: 0,
45910
+ strikethrough: 0,
45911
+ superscript: 0,
45912
+ subscript: 0,
45913
+ color: 0,
45914
+ highlight: 0,
45915
+ link: 0,
45916
+ unsafeLink: 0
45917
+ },
45918
+ attachments: attachments.length
45919
+ };
45920
+ for (const block of blocks) {
45921
+ summary.styles[block.style] = (summary.styles[block.style] || 0) + 1;
45922
+ summary.alignments[block.alignment] = (summary.alignments[block.alignment] || 0) + 1;
45923
+ if (block.blockQuote) summary.blockQuotes++;
45924
+ if (block.indent > 0) summary.indented++;
45925
+ if (block.checklist) {
45926
+ summary.checklist.total++;
45927
+ if (block.checklist.done) summary.checklist.done++;
45928
+ }
45929
+ for (const run of block.runs) {
45930
+ for (const key of [
45931
+ "bold",
45932
+ "italic",
45933
+ "underline",
45934
+ "strikethrough",
45935
+ "superscript",
45936
+ "subscript",
45937
+ "color",
45938
+ "highlight",
45939
+ "link"
45940
+ ])
45941
+ if (run[key]) summary.inline[key]++;
45942
+ if (run.link && !run.linkSafe) summary.inline.unsafeLink++;
45943
+ }
45944
+ }
45945
+ return summary;
45946
+ }
45947
+ function decodeCompressedNoteBlocks(compressed) {
45948
+ let data;
45949
+ try {
45950
+ data = gunzipSync5(compressed, { maxOutputLength: 32 * 1024 * 1024 });
45951
+ } catch (error2) {
45952
+ throw new NoteBlocksError(
45953
+ "decompress-failed",
45954
+ `Failed to decompress note data: ${error2 instanceof Error ? error2.message : String(error2)}`
45955
+ );
45956
+ }
45957
+ return decodeNoteBlocks(data);
45958
+ }
45959
+ var DEFAULT_BLOCKS_MAX_BYTES = 4 * 1024 * 1024;
45960
+ function blocksMaxResponseBytes(env = process.env) {
45961
+ const raw = env.APPLE_NOTES_MCP_BLOCKS_MAX_BYTES;
45962
+ if (raw !== void 0) {
45963
+ const n = Number(raw);
45964
+ if (Number.isFinite(n) && n > 0) return Math.floor(n);
45965
+ }
45966
+ return DEFAULT_BLOCKS_MAX_BYTES;
45967
+ }
45968
+ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResponseBytes() } = {}) {
45969
+ const total = doc.blocks.length;
45970
+ const start = Math.min(Math.max(0, offset), total);
45971
+ const blocks = [];
45972
+ let bytes = 0;
45973
+ for (let i = start; i < total && blocks.length < limit; i++) {
45974
+ let block = doc.blocks[i];
45975
+ let size = Buffer.byteLength(JSON.stringify(block));
45976
+ if (bytes + size > maxBytes) {
45977
+ if (blocks.length) break;
45978
+ block = {
45979
+ ...block,
45980
+ text: "",
45981
+ textOmitted: true,
45982
+ runs: block.runs.map((run) => ({ ...run, text: "" }))
45983
+ };
45984
+ size = Buffer.byteLength(JSON.stringify(block));
45985
+ }
45986
+ blocks.push(block);
45987
+ bytes += size;
45988
+ }
45989
+ const next = start + blocks.length;
45990
+ return {
45991
+ textLength: doc.textLength,
45992
+ blocks,
45993
+ page: {
45994
+ offset: start,
45995
+ returned: blocks.length,
45996
+ total,
45997
+ hasMore: next < total,
45998
+ ...next < total ? { nextOffset: next } : {}
45999
+ },
46000
+ summary: doc.summary,
46001
+ undecodedFields: doc.undecodedFields
46002
+ };
46003
+ }
46004
+ var NOTES_DB_PATH7 = join13(
46005
+ homedir11(),
46006
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
46007
+ );
46008
+ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
46009
+ const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
46010
+ if (!pk)
46011
+ throw new NoteBlocksError(
46012
+ "invalid-id",
46013
+ `Invalid note ID: expected x-coredata://<store>/ICNote/p<number>`
46014
+ );
46015
+ if (!existsSync10(dbPath2))
46016
+ throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
46017
+ const sql = "SELECT json_object('exists', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = @pk AND Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICNote')), 'data', (SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = @pk), 'encrypted', (SELECT ZCRYPTOINITIALIZATIONVECTOR IS NOT NULL FROM ZICNOTEDATA WHERE ZNOTE = @pk));";
46018
+ let output;
46019
+ try {
46020
+ output = execFileSync12(
46021
+ "/usr/bin/sqlite3",
46022
+ ["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
46023
+ {
46024
+ encoding: "utf8",
46025
+ timeout: 5e3,
46026
+ maxBuffer: 64 * 1024 * 1024,
46027
+ stdio: ["pipe", "pipe", "pipe"]
46028
+ }
46029
+ ).trim();
46030
+ } catch (error2) {
46031
+ const message = error2 instanceof Error ? error2.message : String(error2);
46032
+ if (/authorization denied|unable to open database/i.test(message))
46033
+ throw new NoteBlocksError("no-full-disk-access", "The Notes database is not readable");
46034
+ throw new NoteBlocksError("query-failed", "Failed to query the Notes database");
46035
+ }
46036
+ let row;
46037
+ try {
46038
+ row = JSON.parse(output);
46039
+ } catch {
46040
+ throw new NoteBlocksError("query-failed", "Unexpected Notes database response");
46041
+ }
46042
+ if (!row.exists) throw new NoteBlocksError("not-found", `No note found for ID "${id2}"`);
46043
+ if (row.encrypted)
46044
+ throw new NoteBlocksError(
46045
+ "encrypted",
46046
+ "This note is password-protected; its body is encrypted and cannot be decoded"
46047
+ );
46048
+ if (!row.data || !/^[0-9a-f]+$/i.test(row.data))
46049
+ throw new NoteBlocksError("no-body", "No body data is stored for this note");
46050
+ return decodeCompressedNoteBlocks(Buffer.from(row.data, "hex"));
46051
+ }
46052
+
45595
46053
  // src/utils/linkInsert.ts
45596
46054
  var MAX_LINK_URL_LENGTH = 4096;
45597
46055
  var MAX_LINK_LABEL_LENGTH = 2e3;
@@ -45720,7 +46178,7 @@ import {
45720
46178
  writeFileSync as writeFileSync3
45721
46179
  } from "node:fs";
45722
46180
  import { tmpdir as tmpdir4 } from "node:os";
45723
- import { basename, isAbsolute as isAbsolute2, join as join13 } from "node:path";
46181
+ import { basename, isAbsolute as isAbsolute2, join as join14 } from "node:path";
45724
46182
  var noteId = exactIdInput(
45725
46183
  "ICNote",
45726
46184
  /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
@@ -45834,8 +46292,8 @@ function registerDirectOperations(server2, manager) {
45834
46292
  if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
45835
46293
  const bytes = localAttachment(path7);
45836
46294
  const beforeAttachments = manager.listAttachmentsById(id2);
45837
- const directory = mkdtempSync4(join13(tmpdir4(), "notes-attachment-add-"));
45838
- const temporaryFile = join13(directory, basename(path7));
46295
+ const directory = mkdtempSync4(join14(tmpdir4(), "notes-attachment-add-"));
46296
+ const temporaryFile = join14(directory, basename(path7));
45839
46297
  try {
45840
46298
  writeFileSync3(temporaryFile, bytes, { mode: 384 });
45841
46299
  if (readSnapshot(manager, id2).hash !== before.hash)
@@ -46381,7 +46839,7 @@ function registerNativeOperations(server2, manager) {
46381
46839
 
46382
46840
  // src/setupShortcuts.ts
46383
46841
  import { spawnSync as spawnSync2 } from "node:child_process";
46384
- import { existsSync as existsSync10 } from "node:fs";
46842
+ import { existsSync as existsSync11 } from "node:fs";
46385
46843
  import { release as release2 } from "node:os";
46386
46844
  import { dirname as dirname2, resolve as resolve2 } from "node:path";
46387
46845
  import { fileURLToPath } from "node:url";
@@ -46401,7 +46859,7 @@ var shortcutFiles = [
46401
46859
  ];
46402
46860
  function setupShortcuts(checkOnly, dependencies = {}) {
46403
46861
  const status = dependencies.status || nativeTagsStatus;
46404
- const exists = dependencies.exists || existsSync10;
46862
+ const exists = dependencies.exists || existsSync11;
46405
46863
  const open = dependencies.open || ((path7) => {
46406
46864
  const result = spawnSync2("/usr/bin/open", [path7], { encoding: "utf8" });
46407
46865
  return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
@@ -47217,6 +47675,42 @@ registerTool(
47217
47675
  });
47218
47676
  }, "Error reading native objects")
47219
47677
  );
47678
+ registerTool(
47679
+ "get-note-blocks",
47680
+ {
47681
+ description: "Use when: you need a note's structure, not just its text: paragraph styles (title, heading, subheading, body, monospaced, bulleted/dashed/numbered list, checklist with done state), indent, alignment, block quote, inline formatting (bold, italic, underline, strikethrough, superscript, subscript, color, highlight, links) and attachment positions, by exact id.\nReturns: one page of blocks in body order (default 500, stopped early under APPLE_NOTES_MCP_BLOCKS_MAX_BYTES, default 4 MB), whole-note summary counts, undecodedFields, and page info; while page.hasMore is true, call again with offset set to page.nextOffset. Offsets and lengths count UTF-16 code units.\nDo not use when: you want the editable HTML body (get-note-content) or Markdown (get-note-markdown).\nSafety: read-only; decodes the NoteStore database directly and requires Full Disk Access. Password-protected notes are refused. Link URLs are returned as stored; linkSafe is false for schemes other than http(s), notes, applenotes and mailto.",
47682
+ inputSchema: {
47683
+ id: noteIdInput,
47684
+ offset: external_exports.number().int().min(0).optional().describe("Index of the first block to return (default 0); use page.nextOffset"),
47685
+ limit: external_exports.number().int().min(1).max(5e3).optional().describe("Maximum blocks to return (default 500, max 5000)")
47686
+ },
47687
+ outputSchema: {
47688
+ id: external_exports.string().optional(),
47689
+ textLength: external_exports.number().optional(),
47690
+ blocks: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
47691
+ page: external_exports.record(external_exports.unknown()).optional(),
47692
+ summary: external_exports.record(external_exports.unknown()).optional(),
47693
+ undecodedFields: external_exports.record(external_exports.unknown()).optional()
47694
+ },
47695
+ annotations: { readOnlyHint: true }
47696
+ },
47697
+ withErrorHandling(({ id: id2, offset, limit }) => {
47698
+ let page;
47699
+ try {
47700
+ page = pageNoteBlocks(readNoteBlocks(id2), { offset, limit });
47701
+ } catch (error2) {
47702
+ if (!(error2 instanceof NoteBlocksError)) throw error2;
47703
+ const hint = error2.code === "no-full-disk-access" ? ` Grant Full Disk Access to the app that launches this server: ${FULL_DISK_ACCESS_GUIDE_URL}` : "";
47704
+ return errorResponse(`Error reading note blocks [${error2.code}]: ${error2.message}${hint}`);
47705
+ }
47706
+ const { summary } = page;
47707
+ const styles = Object.entries(summary.styles).map(([style, count]) => `${style} ${count}`).join(", ");
47708
+ return successResponse(
47709
+ `Decoded ${summary.blocks} blocks (${styles || "none"}); returned ${page.page.returned} from offset ${page.page.offset}` + (page.page.hasMore ? `; more available at offset ${page.page.nextOffset}` : "") + ".",
47710
+ { id: id2, ...page }
47711
+ );
47712
+ }, "Error reading note blocks")
47713
+ );
47220
47714
  registerTool(
47221
47715
  "list-native-tags",
47222
47716
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.8.27",
3
+ "version": "2.8.28",
4
4
  "description": "MCP server for Apple Notes - create, search, update, and manage notes via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",