apple-notes-mcp 2.9.8 → 2.9.9
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 +8 -0
- package/build/index.js +79 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1873,6 +1873,14 @@ fetched attachment bytes with the source before reporting success.
|
|
|
1873
1873
|
that exact name. A mismatch is a warning (`filenameWarning`), not a failure,
|
|
1874
1874
|
because the attachment and its bytes are already verified.
|
|
1875
1875
|
|
|
1876
|
+
On macOS 27, Notes' AppleScript does not list PDF attachments, so it cannot see
|
|
1877
|
+
a PDF this tool just added. When that happens and the server has Full Disk
|
|
1878
|
+
Access, it verifies through the read-only NoteStore database instead: success
|
|
1879
|
+
requires exactly one new attachment row on the note whose media file matches
|
|
1880
|
+
the source bytes, and the result carries `verifiedBy: "database"`. Without Full
|
|
1881
|
+
Disk Access a PDF attach reports "insertion outcome uncertain"; read the note
|
|
1882
|
+
before retrying, because the attachment was probably created.
|
|
1883
|
+
|
|
1876
1884
|
---
|
|
1877
1885
|
|
|
1878
1886
|
#### `create-note-with-attachment`
|
package/build/index.js
CHANGED
|
@@ -51463,7 +51463,7 @@ function registerDirectOperations(server2, manager) {
|
|
|
51463
51463
|
};
|
|
51464
51464
|
tool(
|
|
51465
51465
|
"add-attachment",
|
|
51466
|
-
"Use when: adding one local file to an exact note without replacing its body.\nReturns: the new attachment id, byte count, attachment name, and post-write content hash after exact byte verification.\nDo not use when: reading or exporting an existing attachment.\nSafety: requires a fresh rich revision, copies at most 64 MiB through a private temporary file, never retries insertion, and verifies existing content plus fetched bytes.",
|
|
51466
|
+
"Use when: adding one local file to an exact note without replacing its body.\nReturns: the new attachment id, byte count, attachment name, and post-write content hash after exact byte verification.\nDo not use when: reading or exporting an existing attachment.\nSafety: requires a fresh rich revision, copies at most 64 MiB through a private temporary file, never retries insertion, and verifies existing content plus fetched bytes. On macOS 27 Notes' AppleScript does not list PDF attachments; with Full Disk Access the new attachment is verified through the read-only NoteStore database instead (verifiedBy: database), otherwise the outcome is reported as uncertain.",
|
|
51467
51467
|
{ id: noteId, expectedContentHash: revision, ...attachmentInput },
|
|
51468
51468
|
(args) => attachFile(manager, args)
|
|
51469
51469
|
);
|
|
@@ -51532,6 +51532,56 @@ function attachmentName(path10, filename) {
|
|
|
51532
51532
|
);
|
|
51533
51533
|
return filename;
|
|
51534
51534
|
}
|
|
51535
|
+
var UNCERTAIN = "Attachment insertion outcome uncertain; read the exact note before retrying";
|
|
51536
|
+
var pause = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
51537
|
+
var sha256 = (data) => createHash2("sha256").update(data).digest("hex");
|
|
51538
|
+
function storedAttachmentIds(manager, id2) {
|
|
51539
|
+
try {
|
|
51540
|
+
return new Set(
|
|
51541
|
+
manager.getAttachmentAssetsById(id2).attachments.filter((item) => item.parentIdentifier === null).map((item) => item.identifier.toLowerCase())
|
|
51542
|
+
);
|
|
51543
|
+
} catch {
|
|
51544
|
+
return null;
|
|
51545
|
+
}
|
|
51546
|
+
}
|
|
51547
|
+
function storedInsertion(manager, id2, before, bytes, returnedId) {
|
|
51548
|
+
let added = [];
|
|
51549
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
51550
|
+
if (attempt > 0) pause(250);
|
|
51551
|
+
try {
|
|
51552
|
+
added = manager.getAttachmentAssetsById(id2).attachments.filter(
|
|
51553
|
+
(item) => item.parentIdentifier === null && !before.has(item.identifier.toLowerCase())
|
|
51554
|
+
);
|
|
51555
|
+
} catch {
|
|
51556
|
+
added = [];
|
|
51557
|
+
}
|
|
51558
|
+
if (added.length === 1 && added[0].assetPaths.length > 0) break;
|
|
51559
|
+
}
|
|
51560
|
+
if (added.length === 0) return null;
|
|
51561
|
+
if (added.length > 1) throw new Error(UNCERTAIN);
|
|
51562
|
+
const row = added[0];
|
|
51563
|
+
const attachmentId = attachmentCoreDataId(id2, row.pk);
|
|
51564
|
+
if (returnedId && /\/ICAttachment\/p\d+$/.test(returnedId) && returnedId !== attachmentId)
|
|
51565
|
+
throw new Error(UNCERTAIN);
|
|
51566
|
+
const expected = sha256(bytes);
|
|
51567
|
+
const matches = row.assetPaths.some((path10) => {
|
|
51568
|
+
let descriptor;
|
|
51569
|
+
try {
|
|
51570
|
+
descriptor = openSync5(path10, constants5.O_RDONLY | constants5.O_NOFOLLOW);
|
|
51571
|
+
const stat = fstatSync5(descriptor);
|
|
51572
|
+
return stat.isFile() && stat.size === bytes.length && sha256(readFileSync3(descriptor)) === expected;
|
|
51573
|
+
} catch {
|
|
51574
|
+
return false;
|
|
51575
|
+
} finally {
|
|
51576
|
+
if (descriptor !== void 0) closeSync5(descriptor);
|
|
51577
|
+
}
|
|
51578
|
+
});
|
|
51579
|
+
if (!matches)
|
|
51580
|
+
throw new Error(
|
|
51581
|
+
`Notes' database shows new attachment ${attachmentId} on this note, but its file bytes could not be verified; read the exact note and do not attach the file again`
|
|
51582
|
+
);
|
|
51583
|
+
return { attachmentId, name: row.filename };
|
|
51584
|
+
}
|
|
51535
51585
|
function attachFile(manager, args) {
|
|
51536
51586
|
const { id: id2, expectedContentHash, path: path10 } = args;
|
|
51537
51587
|
const name = attachmentName(path10, args.filename);
|
|
@@ -51539,6 +51589,7 @@ function attachFile(manager, args) {
|
|
|
51539
51589
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
51540
51590
|
const bytes = localAttachment(path10);
|
|
51541
51591
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
51592
|
+
const beforeStored = storedAttachmentIds(manager, id2);
|
|
51542
51593
|
const directory = mkdtempSync4(join22(tmpdir4(), "notes-attachment-add-"));
|
|
51543
51594
|
const temporaryFile = join22(directory, name);
|
|
51544
51595
|
try {
|
|
@@ -51558,27 +51609,39 @@ function attachFile(manager, args) {
|
|
|
51558
51609
|
);
|
|
51559
51610
|
let inserted = readInserted();
|
|
51560
51611
|
for (let attempt = 0; attempt < 4 && (inserted.length !== 1 || returnedId && returnedId !== inserted[0].id); attempt++) {
|
|
51561
|
-
|
|
51612
|
+
pause(250);
|
|
51562
51613
|
inserted = readInserted();
|
|
51563
51614
|
}
|
|
51564
51615
|
const persistentReturnedId = returnedId && /\/ICAttachment\/p\d+$/.test(returnedId);
|
|
51565
|
-
|
|
51566
|
-
|
|
51567
|
-
|
|
51568
|
-
|
|
51569
|
-
|
|
51570
|
-
|
|
51571
|
-
|
|
51572
|
-
|
|
51573
|
-
|
|
51574
|
-
|
|
51616
|
+
let attachmentId;
|
|
51617
|
+
let reportedName;
|
|
51618
|
+
let verifiedBy = "applescript";
|
|
51619
|
+
if (inserted.length === 1 && !(persistentReturnedId && returnedId !== inserted[0].id)) {
|
|
51620
|
+
attachmentId = inserted[0].id;
|
|
51621
|
+
reportedName = inserted[0].name;
|
|
51622
|
+
const fetched = manager.getAttachmentBase64ById(id2, attachmentId);
|
|
51623
|
+
const actual = typeof fetched.base64 === "string" ? Buffer.from(fetched.base64, "base64") : null;
|
|
51624
|
+
if (!actual || sha256(actual) !== sha256(bytes))
|
|
51625
|
+
throw new Error("Attachment bytes were not verified; read the exact note before retrying");
|
|
51626
|
+
} else {
|
|
51627
|
+
const stored = inserted.length === 0 && beforeStored ? storedInsertion(manager, id2, beforeStored, bytes, returnedId) : null;
|
|
51628
|
+
if (!stored)
|
|
51629
|
+
throw new Error(
|
|
51630
|
+
inserted.length === 0 && !beforeStored ? `${UNCERTAIN}. Notes' AppleScript does not list some attachments (PDFs on macOS 27); grant Full Disk Access so the server can verify through the Notes database` : UNCERTAIN
|
|
51631
|
+
);
|
|
51632
|
+
attachmentId = stored.attachmentId;
|
|
51633
|
+
reportedName = stored.name;
|
|
51634
|
+
verifiedBy = "database";
|
|
51635
|
+
}
|
|
51636
|
+
const nameVerified = reportedName === name;
|
|
51575
51637
|
return {
|
|
51576
51638
|
ok: true,
|
|
51577
51639
|
id: id2,
|
|
51578
51640
|
attachmentId,
|
|
51579
51641
|
contentHash: after.hash,
|
|
51580
51642
|
bytes: bytes.length,
|
|
51581
|
-
name:
|
|
51643
|
+
name: reportedName,
|
|
51644
|
+
...verifiedBy === "database" ? { verifiedBy } : {},
|
|
51582
51645
|
...args.filename === void 0 ? {} : {
|
|
51583
51646
|
filenameVerified: nameVerified,
|
|
51584
51647
|
...nameVerified ? {} : {
|
|
@@ -53000,7 +53063,7 @@ function canonicalJson(value) {
|
|
|
53000
53063
|
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(",")}}`;
|
|
53001
53064
|
return JSON.stringify(value);
|
|
53002
53065
|
}
|
|
53003
|
-
function
|
|
53066
|
+
function sha2562(text2) {
|
|
53004
53067
|
return createHash4("sha256").update(text2).digest("hex");
|
|
53005
53068
|
}
|
|
53006
53069
|
var round = (v, digits) => {
|
|
@@ -53840,7 +53903,7 @@ function analysisFor(source, analyzer, viewport) {
|
|
|
53840
53903
|
const classification = !importable || requiredLosses.includes("drop-content") ? "unsupported" : requiredLosses.length ? "lossy" : "safe";
|
|
53841
53904
|
const analysis = {
|
|
53842
53905
|
analyzer: SVG_ANALYZER_VERSION,
|
|
53843
|
-
source: { sha256:
|
|
53906
|
+
source: { sha256: sha2562(source), bytes: source.length },
|
|
53844
53907
|
classification,
|
|
53845
53908
|
importable,
|
|
53846
53909
|
defaultWriteAllowed: importable && classification === "safe",
|
|
@@ -53861,7 +53924,7 @@ function analysisFor(source, analyzer, viewport) {
|
|
|
53861
53924
|
issues: analyzer.issues,
|
|
53862
53925
|
issuesTruncated: analyzer.issuesTruncated
|
|
53863
53926
|
};
|
|
53864
|
-
const analysisDigest = "sha256:" +
|
|
53927
|
+
const analysisDigest = "sha256:" + sha2562(canonicalJson({ analysis, drawing }));
|
|
53865
53928
|
return { analysis: { ...analysis, analysisDigest }, drawing };
|
|
53866
53929
|
}
|
|
53867
53930
|
function analyzeSvgBuffer(source) {
|
package/package.json
CHANGED