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.
- package/README.md +32 -1
- package/build/index.js +559 -65
- 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
|
|
3362
|
-
if (isHexPair(
|
|
3363
|
-
const normalizedHex =
|
|
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
|
|
3384
|
-
if (isHexPair(
|
|
3385
|
-
const normalizedHex =
|
|
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
|
|
3426
|
-
if (isHexPair(
|
|
3427
|
-
output += "%" +
|
|
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
|
|
3464
|
-
if (isHexPair(
|
|
3465
|
-
output += "%" +
|
|
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
|
|
3511
|
-
if (isHexPair(
|
|
3512
|
-
const normalizedHex =
|
|
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
|
|
3551
|
-
if (isHexPair(
|
|
3552
|
-
output += "%" +
|
|
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,
|
|
8771
|
+
var kids = this._childNodes, first2;
|
|
8772
8772
|
if (kids) {
|
|
8773
8773
|
return kids.length === 0 ? null : kids[kids.length - 1];
|
|
8774
8774
|
}
|
|
8775
|
-
|
|
8776
|
-
if (
|
|
8775
|
+
first2 = this._firstChild;
|
|
8776
|
+
if (first2 === null) {
|
|
8777
8777
|
return null;
|
|
8778
8778
|
}
|
|
8779
|
-
return
|
|
8779
|
+
return first2._previousSibling;
|
|
8780
8780
|
} },
|
|
8781
8781
|
_ensureChildNodes: { value: function() {
|
|
8782
8782
|
if (this._childNodes) {
|
|
8783
8783
|
return;
|
|
8784
8784
|
}
|
|
8785
|
-
var
|
|
8786
|
-
if (
|
|
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 !==
|
|
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
|
|
10260
|
+
var first2 = false;
|
|
10261
10261
|
switch (position) {
|
|
10262
10262
|
case "beforebegin":
|
|
10263
|
-
|
|
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,
|
|
10270
|
+
return parent.insertBefore(node, first2 ? this : this.nextSibling);
|
|
10271
10271
|
case "afterbegin":
|
|
10272
|
-
|
|
10272
|
+
first2 = true;
|
|
10273
10273
|
/* falls through */
|
|
10274
10274
|
case "beforeend":
|
|
10275
|
-
return this.insertBefore(node,
|
|
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
|
|
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 =
|
|
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
|
|
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 = (
|
|
24678
|
+
objectUtil2.mergeShapes = (first2, second) => {
|
|
24679
24679
|
return {
|
|
24680
|
-
...
|
|
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 = (
|
|
24781
|
-
this.issues = [...this.issues,
|
|
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
|
|
24849
|
-
if (
|
|
24850
|
-
const firstEl =
|
|
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(
|
|
24852
|
+
fieldErrors[firstEl].push(mapper(sub3));
|
|
24853
24853
|
} else {
|
|
24854
|
-
formErrors.push(mapper(
|
|
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(
|
|
27602
|
+
static create(first2, second, third) {
|
|
27603
27603
|
if (second instanceof ZodType) {
|
|
27604
27604
|
return new _ZodRecord({
|
|
27605
|
-
keyType:
|
|
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:
|
|
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
|
|
29154
|
-
if (
|
|
29155
|
-
fieldErrors[
|
|
29156
|
-
fieldErrors[
|
|
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(
|
|
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
|
|
39377
|
-
if (!
|
|
39378
|
-
return { hex:
|
|
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(
|
|
39389
|
-
const bytes = new Uint8Array(
|
|
39390
|
-
for (let i = 0; i <
|
|
39391
|
-
bytes[i / 2] = parseInt(
|
|
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,
|
|
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(/ (?:;|(?![0-9a-z]))/gi, " ").replace(/"(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/<(?:;|(?![0-9a-z]))/gi, "<").replace(/>(?:;|(?![0-9a-z]))/gi, ">").replace(/&(?:;|(?![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,
|
|
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 (
|
|
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
|
|
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(
|
|
45838
|
-
const temporaryFile =
|
|
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
|
|
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 ||
|
|
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