apple-notes-mcp 2.9.8 → 2.9.10
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 +12 -1
- package/build/index.js +147 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -261,7 +261,7 @@ Creates a new note in Apple Notes.
|
|
|
261
261
|
| `account` | string | No | Account name (defaults to Notes.app's default account; matched exactly or by a *unique* prefix — an ambiguous prefix is refused). Must be an account Notes.app already has configured — see [`list-accounts`](#list-accounts) |
|
|
262
262
|
| `format` | string | No | Content format: `"plaintext"` (default), `"html"`, or `"markdown"`. In all formats, the title is automatically prepended as the note's title line. In plaintext mode, newlines become `<br>`, tabs become `<br>`, and backslashes are preserved as HTML entities. `"markdown"` produces real Title/Heading/Subheading styles through a Shortcut; see [Markdown notes](#markdown-notes) |
|
|
263
263
|
| `markdownRoute` | string | No | With `format: "markdown"` only: `"shortcut"` (default) or `"html"`. See [Markdown through HTML](#markdown-through-html) |
|
|
264
|
-
| `timeoutSeconds` | number | No | Whole seconds, 1–120, for each Notes.app automation step this call runs; overrides `APPLE_NOTES_MCP_TIMEOUT_MS` for this call only. A timed-out write is uncertain, not failed: read the note by id before any retry. Also accepted by `update-note`, `append-to-note`, `delete-note` and `move-note` |
|
|
264
|
+
| `timeoutSeconds` | number | No | Whole seconds, 1–120, for each Notes.app automation step this call runs; overrides `APPLE_NOTES_MCP_TIMEOUT_MS` for this call only. A timed-out write is uncertain, not failed: read the note by id before any retry. Also accepted by `get-note-content`, `update-note`, `append-to-note`, `delete-note` and `move-note` |
|
|
265
265
|
|
|
266
266
|
**Example (tagged with inline hashtags):**
|
|
267
267
|
```json
|
|
@@ -531,9 +531,12 @@ Retrieves the full content of a specific note.
|
|
|
531
531
|
| `id` | string | No | Note ID (preferred - more reliable than title) |
|
|
532
532
|
| `title` | string | No | Note title (use `id` instead when available) |
|
|
533
533
|
| `account` | string | No | Account containing the note (defaults to Notes.app's default account; exact or unique-prefix match, ignored if `id` is provided) |
|
|
534
|
+
| `timeoutSeconds` | number | No | Whole seconds, 1–120, for the body read; overrides `APPLE_NOTES_MCP_TIMEOUT_MS` for this call only |
|
|
534
535
|
|
|
535
536
|
**Note:** Either `id` or `title` must be provided. Using `id` is recommended as it's unique and avoids issues with duplicate titles.
|
|
536
537
|
|
|
538
|
+
**Large images:** Notes.app returns images inside the body as base64, so a note holding a very large image (tens of MB) can take longer to read than the timeout allows. When the read times out or overflows the output buffer, the error says so and, with Full Disk Access, names the attachments of 5 MB or more. Retry with a larger `timeoutSeconds`; `delete-note` accepts the same argument, and it needs a successful read to verify the note before deleting it.
|
|
539
|
+
|
|
537
540
|
**Example - Using ID (recommended):**
|
|
538
541
|
```json
|
|
539
542
|
{
|
|
@@ -1873,6 +1876,14 @@ fetched attachment bytes with the source before reporting success.
|
|
|
1873
1876
|
that exact name. A mismatch is a warning (`filenameWarning`), not a failure,
|
|
1874
1877
|
because the attachment and its bytes are already verified.
|
|
1875
1878
|
|
|
1879
|
+
On macOS 27, Notes' AppleScript does not list PDF attachments, so it cannot see
|
|
1880
|
+
a PDF this tool just added. When that happens and the server has Full Disk
|
|
1881
|
+
Access, it verifies through the read-only NoteStore database instead: success
|
|
1882
|
+
requires exactly one new attachment row on the note whose media file matches
|
|
1883
|
+
the source bytes, and the result carries `verifiedBy: "database"`. Without Full
|
|
1884
|
+
Disk Access a PDF attach reports "insertion outcome uncertain"; read the note
|
|
1885
|
+
before retrying, because the attachment was probably created.
|
|
1886
|
+
|
|
1876
1887
|
---
|
|
1877
1888
|
|
|
1878
1889
|
#### `create-note-with-attachment`
|
package/build/index.js
CHANGED
|
@@ -42825,15 +42825,27 @@ var AppleNotesManager = class {
|
|
|
42825
42825
|
* @returns HTML content of the note, or empty string if not found
|
|
42826
42826
|
*/
|
|
42827
42827
|
getNoteContentById(id2) {
|
|
42828
|
+
return this.readNoteBodyById(id2).body;
|
|
42829
|
+
}
|
|
42830
|
+
/**
|
|
42831
|
+
* Like {@link getNoteContentById}, but keeps the automation error so a
|
|
42832
|
+
* caller can explain a failed read (for example a timeout on a note whose
|
|
42833
|
+
* body carries a very large inline image, #237) instead of reporting a bare
|
|
42834
|
+
* failure.
|
|
42835
|
+
*
|
|
42836
|
+
* @param id - CoreData URL identifier for the note
|
|
42837
|
+
* @returns The HTML body, or an empty body plus the error that stopped the read
|
|
42838
|
+
*/
|
|
42839
|
+
readNoteBodyById(id2) {
|
|
42828
42840
|
const safeId = sanitizeId(id2);
|
|
42829
42841
|
const getCommand = `get body of note id "${safeId}"`;
|
|
42830
42842
|
const script = buildAppLevelScript(getCommand);
|
|
42831
42843
|
const result = executeAppleScript(script);
|
|
42832
42844
|
if (!result.success) {
|
|
42833
42845
|
console.error(`Failed to get content of note with ID "${id2}":`, result.error);
|
|
42834
|
-
return "";
|
|
42846
|
+
return { body: "", error: result.error };
|
|
42835
42847
|
}
|
|
42836
|
-
return result.output;
|
|
42848
|
+
return { body: result.output };
|
|
42837
42849
|
}
|
|
42838
42850
|
/**
|
|
42839
42851
|
* Retrieves the plain-text content of a note by its exact title.
|
|
@@ -49593,6 +49605,37 @@ function readNoteStructure(id2, { dbPath: dbPath2 = NOTES_DB_PATH7, includeText
|
|
|
49593
49605
|
};
|
|
49594
49606
|
}
|
|
49595
49607
|
|
|
49608
|
+
// src/utils/bodyReadFailure.ts
|
|
49609
|
+
var LARGE_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
49610
|
+
function classifyBodyReadError(error2) {
|
|
49611
|
+
if (!error2) return "other";
|
|
49612
|
+
if (/ENOBUFS|maxBuffer/i.test(error2)) return "buffer";
|
|
49613
|
+
if (/timed out|timeout|-1712/i.test(error2)) return "timeout";
|
|
49614
|
+
return "other";
|
|
49615
|
+
}
|
|
49616
|
+
function formatBytes2(bytes) {
|
|
49617
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
49618
|
+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
49619
|
+
return `${bytes} bytes`;
|
|
49620
|
+
}
|
|
49621
|
+
function largeAttachments(attachments, threshold = LARGE_ATTACHMENT_BYTES) {
|
|
49622
|
+
const flat = attachments.flatMap((a) => [a, ...a.children ?? []]);
|
|
49623
|
+
return flat.filter((a) => typeof a.fileSize === "number" && a.fileSize >= threshold).map((a) => ({ name: a.filename || a.title || "attachment", bytes: a.fileSize })).sort((a, b) => b.bytes - a.bytes);
|
|
49624
|
+
}
|
|
49625
|
+
function describeBodyReadFailure(title, error2, attachments) {
|
|
49626
|
+
const base = `Failed to read content of note "${title}"${error2 ? `: ${error2}` : ""}`;
|
|
49627
|
+
const kind = classifyBodyReadError(error2);
|
|
49628
|
+
if (kind === "other") return base;
|
|
49629
|
+
const large = attachments ? largeAttachments(attachments) : [];
|
|
49630
|
+
const cause = large.length > 0 ? `The note holds ${large.length === 1 ? "a large attachment" : "large attachments"} (${large.map((a) => `${a.name}, ${formatBytes2(a.bytes)}`).join(
|
|
49631
|
+
"; "
|
|
49632
|
+
)}). Notes.app returns images inside the note body as base64, so a large image makes the body too big to read in time.` : "Notes.app returns images inside the note body as base64, so a note with a very large image can take longer to read than the timeout allows.";
|
|
49633
|
+
const remedy = kind === "buffer" ? "Raise APPLE_NOTES_MCP_MAX_BUFFER (bytes) and retry, or remove the attachment in Notes.app." : "Retry with a longer timeoutSeconds (up to 120) or raise APPLE_NOTES_MCP_TIMEOUT_MS. If it still fails, delete or shrink the attachment in Notes.app; delete-note needs a successful read to verify the note first.";
|
|
49634
|
+
return `${base}
|
|
49635
|
+
|
|
49636
|
+
${cause} ${remedy}`;
|
|
49637
|
+
}
|
|
49638
|
+
|
|
49596
49639
|
// src/utils/noteLinkInventory.ts
|
|
49597
49640
|
import { dirname as dirname5 } from "node:path";
|
|
49598
49641
|
var BODY_BATCH = 100;
|
|
@@ -51463,7 +51506,7 @@ function registerDirectOperations(server2, manager) {
|
|
|
51463
51506
|
};
|
|
51464
51507
|
tool(
|
|
51465
51508
|
"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.",
|
|
51509
|
+
"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
51510
|
{ id: noteId, expectedContentHash: revision, ...attachmentInput },
|
|
51468
51511
|
(args) => attachFile(manager, args)
|
|
51469
51512
|
);
|
|
@@ -51532,6 +51575,56 @@ function attachmentName(path10, filename) {
|
|
|
51532
51575
|
);
|
|
51533
51576
|
return filename;
|
|
51534
51577
|
}
|
|
51578
|
+
var UNCERTAIN = "Attachment insertion outcome uncertain; read the exact note before retrying";
|
|
51579
|
+
var pause = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
51580
|
+
var sha256 = (data) => createHash2("sha256").update(data).digest("hex");
|
|
51581
|
+
function storedAttachmentIds(manager, id2) {
|
|
51582
|
+
try {
|
|
51583
|
+
return new Set(
|
|
51584
|
+
manager.getAttachmentAssetsById(id2).attachments.filter((item) => item.parentIdentifier === null).map((item) => item.identifier.toLowerCase())
|
|
51585
|
+
);
|
|
51586
|
+
} catch {
|
|
51587
|
+
return null;
|
|
51588
|
+
}
|
|
51589
|
+
}
|
|
51590
|
+
function storedInsertion(manager, id2, before, bytes, returnedId) {
|
|
51591
|
+
let added = [];
|
|
51592
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
51593
|
+
if (attempt > 0) pause(250);
|
|
51594
|
+
try {
|
|
51595
|
+
added = manager.getAttachmentAssetsById(id2).attachments.filter(
|
|
51596
|
+
(item) => item.parentIdentifier === null && !before.has(item.identifier.toLowerCase())
|
|
51597
|
+
);
|
|
51598
|
+
} catch {
|
|
51599
|
+
added = [];
|
|
51600
|
+
}
|
|
51601
|
+
if (added.length === 1 && added[0].assetPaths.length > 0) break;
|
|
51602
|
+
}
|
|
51603
|
+
if (added.length === 0) return null;
|
|
51604
|
+
if (added.length > 1) throw new Error(UNCERTAIN);
|
|
51605
|
+
const row = added[0];
|
|
51606
|
+
const attachmentId = attachmentCoreDataId(id2, row.pk);
|
|
51607
|
+
if (returnedId && /\/ICAttachment\/p\d+$/.test(returnedId) && returnedId !== attachmentId)
|
|
51608
|
+
throw new Error(UNCERTAIN);
|
|
51609
|
+
const expected = sha256(bytes);
|
|
51610
|
+
const matches = row.assetPaths.some((path10) => {
|
|
51611
|
+
let descriptor;
|
|
51612
|
+
try {
|
|
51613
|
+
descriptor = openSync5(path10, constants5.O_RDONLY | constants5.O_NOFOLLOW);
|
|
51614
|
+
const stat = fstatSync5(descriptor);
|
|
51615
|
+
return stat.isFile() && stat.size === bytes.length && sha256(readFileSync3(descriptor)) === expected;
|
|
51616
|
+
} catch {
|
|
51617
|
+
return false;
|
|
51618
|
+
} finally {
|
|
51619
|
+
if (descriptor !== void 0) closeSync5(descriptor);
|
|
51620
|
+
}
|
|
51621
|
+
});
|
|
51622
|
+
if (!matches)
|
|
51623
|
+
throw new Error(
|
|
51624
|
+
`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`
|
|
51625
|
+
);
|
|
51626
|
+
return { attachmentId, name: row.filename };
|
|
51627
|
+
}
|
|
51535
51628
|
function attachFile(manager, args) {
|
|
51536
51629
|
const { id: id2, expectedContentHash, path: path10 } = args;
|
|
51537
51630
|
const name = attachmentName(path10, args.filename);
|
|
@@ -51539,6 +51632,7 @@ function attachFile(manager, args) {
|
|
|
51539
51632
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
51540
51633
|
const bytes = localAttachment(path10);
|
|
51541
51634
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
51635
|
+
const beforeStored = storedAttachmentIds(manager, id2);
|
|
51542
51636
|
const directory = mkdtempSync4(join22(tmpdir4(), "notes-attachment-add-"));
|
|
51543
51637
|
const temporaryFile = join22(directory, name);
|
|
51544
51638
|
try {
|
|
@@ -51558,27 +51652,39 @@ function attachFile(manager, args) {
|
|
|
51558
51652
|
);
|
|
51559
51653
|
let inserted = readInserted();
|
|
51560
51654
|
for (let attempt = 0; attempt < 4 && (inserted.length !== 1 || returnedId && returnedId !== inserted[0].id); attempt++) {
|
|
51561
|
-
|
|
51655
|
+
pause(250);
|
|
51562
51656
|
inserted = readInserted();
|
|
51563
51657
|
}
|
|
51564
51658
|
const persistentReturnedId = returnedId && /\/ICAttachment\/p\d+$/.test(returnedId);
|
|
51565
|
-
|
|
51566
|
-
|
|
51567
|
-
|
|
51568
|
-
|
|
51569
|
-
|
|
51570
|
-
|
|
51571
|
-
|
|
51572
|
-
|
|
51573
|
-
|
|
51574
|
-
|
|
51659
|
+
let attachmentId;
|
|
51660
|
+
let reportedName;
|
|
51661
|
+
let verifiedBy = "applescript";
|
|
51662
|
+
if (inserted.length === 1 && !(persistentReturnedId && returnedId !== inserted[0].id)) {
|
|
51663
|
+
attachmentId = inserted[0].id;
|
|
51664
|
+
reportedName = inserted[0].name;
|
|
51665
|
+
const fetched = manager.getAttachmentBase64ById(id2, attachmentId);
|
|
51666
|
+
const actual = typeof fetched.base64 === "string" ? Buffer.from(fetched.base64, "base64") : null;
|
|
51667
|
+
if (!actual || sha256(actual) !== sha256(bytes))
|
|
51668
|
+
throw new Error("Attachment bytes were not verified; read the exact note before retrying");
|
|
51669
|
+
} else {
|
|
51670
|
+
const stored = inserted.length === 0 && beforeStored ? storedInsertion(manager, id2, beforeStored, bytes, returnedId) : null;
|
|
51671
|
+
if (!stored)
|
|
51672
|
+
throw new Error(
|
|
51673
|
+
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
|
|
51674
|
+
);
|
|
51675
|
+
attachmentId = stored.attachmentId;
|
|
51676
|
+
reportedName = stored.name;
|
|
51677
|
+
verifiedBy = "database";
|
|
51678
|
+
}
|
|
51679
|
+
const nameVerified = reportedName === name;
|
|
51575
51680
|
return {
|
|
51576
51681
|
ok: true,
|
|
51577
51682
|
id: id2,
|
|
51578
51683
|
attachmentId,
|
|
51579
51684
|
contentHash: after.hash,
|
|
51580
51685
|
bytes: bytes.length,
|
|
51581
|
-
name:
|
|
51686
|
+
name: reportedName,
|
|
51687
|
+
...verifiedBy === "database" ? { verifiedBy } : {},
|
|
51582
51688
|
...args.filename === void 0 ? {} : {
|
|
51583
51689
|
filenameVerified: nameVerified,
|
|
51584
51690
|
...nameVerified ? {} : {
|
|
@@ -53000,7 +53106,7 @@ function canonicalJson(value) {
|
|
|
53000
53106
|
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(",")}}`;
|
|
53001
53107
|
return JSON.stringify(value);
|
|
53002
53108
|
}
|
|
53003
|
-
function
|
|
53109
|
+
function sha2562(text2) {
|
|
53004
53110
|
return createHash4("sha256").update(text2).digest("hex");
|
|
53005
53111
|
}
|
|
53006
53112
|
var round = (v, digits) => {
|
|
@@ -53840,7 +53946,7 @@ function analysisFor(source, analyzer, viewport) {
|
|
|
53840
53946
|
const classification = !importable || requiredLosses.includes("drop-content") ? "unsupported" : requiredLosses.length ? "lossy" : "safe";
|
|
53841
53947
|
const analysis = {
|
|
53842
53948
|
analyzer: SVG_ANALYZER_VERSION,
|
|
53843
|
-
source: { sha256:
|
|
53949
|
+
source: { sha256: sha2562(source), bytes: source.length },
|
|
53844
53950
|
classification,
|
|
53845
53951
|
importable,
|
|
53846
53952
|
defaultWriteAllowed: importable && classification === "safe",
|
|
@@ -53861,7 +53967,7 @@ function analysisFor(source, analyzer, viewport) {
|
|
|
53861
53967
|
issues: analyzer.issues,
|
|
53862
53968
|
issuesTruncated: analyzer.issuesTruncated
|
|
53863
53969
|
};
|
|
53864
|
-
const analysisDigest = "sha256:" +
|
|
53970
|
+
const analysisDigest = "sha256:" + sha2562(canonicalJson({ analysis, drawing }));
|
|
53865
53971
|
return { analysis: { ...analysis, analysisDigest }, drawing };
|
|
53866
53972
|
}
|
|
53867
53973
|
function analyzeSvgBuffer(source) {
|
|
@@ -55947,11 +56053,25 @@ function readExactNoteSnapshot(id2) {
|
|
|
55947
56053
|
error: `Note "${note.title}" is password-protected and cannot be changed. Unlock it in Notes.app first.`
|
|
55948
56054
|
};
|
|
55949
56055
|
}
|
|
55950
|
-
const body = notesManager.
|
|
55951
|
-
if (!body)
|
|
56056
|
+
const { body, error: error2 } = notesManager.readNoteBodyById(id2);
|
|
56057
|
+
if (!body) {
|
|
56058
|
+
const reason = bodyReadFailureMessage(id2, note.title, error2);
|
|
56059
|
+
return { error: `${reason}${error2 ? "\n\nNothing was changed." : ""}` };
|
|
56060
|
+
}
|
|
55952
56061
|
const rich = enrichNoteRead(id2, body);
|
|
55953
56062
|
return { note, body, rich, contentHash: richContentHash(body, rich) };
|
|
55954
56063
|
}
|
|
56064
|
+
function bodyReadFailureMessage(id2, title, error2) {
|
|
56065
|
+
let attachments;
|
|
56066
|
+
if (classifyBodyReadError(error2) !== "other") {
|
|
56067
|
+
try {
|
|
56068
|
+
attachments = readNoteStructure(id2, { includeText: false }).attachments;
|
|
56069
|
+
} catch {
|
|
56070
|
+
attachments = void 0;
|
|
56071
|
+
}
|
|
56072
|
+
}
|
|
56073
|
+
return describeBodyReadFailure(title, error2, attachments);
|
|
56074
|
+
}
|
|
55955
56075
|
var NOT_DELETED_MESSAGE = "Notes.app accepted the delete, but the note is still in its original folder, so it was not moved to Recently Deleted. Nothing was deleted; read the note again before retrying.";
|
|
55956
56076
|
function containerUnknownMessage(title) {
|
|
55957
56077
|
return `Could not read which folder note "${title}" is in, so it may already be in Recently Deleted, where deleting it would remove it permanently. Nothing was deleted. Retry in a moment; if it keeps failing, quit and reopen Notes.app, then retry.`;
|
|
@@ -56365,13 +56485,14 @@ ${lines}${footer}`,
|
|
|
56365
56485
|
registerTool(
|
|
56366
56486
|
"get-note-content",
|
|
56367
56487
|
{
|
|
56368
|
-
description: "Use when: reading the full body text of one known note, by id (preferred) or title.\nReturns: the exact note id, content, contentHash revision token, parsed hashtags, nativeTags, restored links, richContentComplete/writable, and strippedImages/truncated when the body was capped. Read the warning when writable is false.\nDo not use when: you only need metadata (get-note-details) or Markdown with checklist state (get-note-markdown).\nNote: password-protected notes must be unlocked in Notes.app first.\nSafety: inline images larger than APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES (default 256 KB) are replaced with '[inline image omitted: ...]' text placeholders, so the returned body is lossy whenever truncated is true. Mutations refuse attachment-bearing notes; edit those in Notes.app.",
|
|
56488
|
+
description: "Use when: reading the full body text of one known note, by id (preferred) or title.\nReturns: the exact note id, content, contentHash revision token, parsed hashtags, nativeTags, restored links, richContentComplete/writable, and strippedImages/truncated when the body was capped. Read the warning when writable is false.\nDo not use when: you only need metadata (get-note-details) or Markdown with checklist state (get-note-markdown).\nNote: password-protected notes must be unlocked in Notes.app first.\nSafety: inline images larger than APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES (default 256 KB) are replaced with '[inline image omitted: ...]' text placeholders, so the returned body is lossy whenever truncated is true. Mutations refuse attachment-bearing notes; edit those in Notes.app. Notes.app returns images inside the body as base64, so a note with a very large image can time out; the error then names the cause, and a larger timeoutSeconds gives the read more time.",
|
|
56369
56489
|
inputSchema: {
|
|
56370
56490
|
id: looseNoteId(external_exports.string()).optional().describe(`Note ID (preferred - more reliable than title): ${NOTE_ID_FORMS}`),
|
|
56371
56491
|
title: external_exports.string().max(MAX.TITLE).optional().describe("Note title (use id instead when available)"),
|
|
56372
56492
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe(
|
|
56373
56493
|
"Account name (defaults to Notes.app's default account; exact or unique-prefix match, ignored if id is provided)"
|
|
56374
|
-
)
|
|
56494
|
+
),
|
|
56495
|
+
timeoutSeconds: timeoutSecondsInput
|
|
56375
56496
|
},
|
|
56376
56497
|
outputSchema: {
|
|
56377
56498
|
id: external_exports.string().optional(),
|
|
@@ -56404,9 +56525,9 @@ registerTool(
|
|
|
56404
56525
|
`Note "${note2.title}" is password-protected and cannot be read. Unlock it in Notes.app first.`
|
|
56405
56526
|
);
|
|
56406
56527
|
}
|
|
56407
|
-
const rawContent2 = notesManager.
|
|
56528
|
+
const { body: rawContent2, error: readError2 } = notesManager.readNoteBodyById(id2);
|
|
56408
56529
|
if (!rawContent2) {
|
|
56409
|
-
return errorResponse(
|
|
56530
|
+
return errorResponse(bodyReadFailureMessage(id2, note2.title, readError2));
|
|
56410
56531
|
}
|
|
56411
56532
|
const rich2 = enrichNoteRead(id2, rawContent2);
|
|
56412
56533
|
const stripped2 = stripLargeInlineImages(rich2.content);
|
|
@@ -56446,9 +56567,9 @@ registerTool(
|
|
|
56446
56567
|
`Note "${title}" is password-protected and cannot be read. Unlock it in Notes.app first.`
|
|
56447
56568
|
);
|
|
56448
56569
|
}
|
|
56449
|
-
const rawContent = notesManager.
|
|
56570
|
+
const { body: rawContent, error: readError } = notesManager.readNoteBodyById(note.id);
|
|
56450
56571
|
if (!rawContent) {
|
|
56451
|
-
return errorResponse(
|
|
56572
|
+
return errorResponse(bodyReadFailureMessage(note.id, title, readError));
|
|
56452
56573
|
}
|
|
56453
56574
|
const rich = enrichNoteRead(note.id, rawContent);
|
|
56454
56575
|
const stripped = stripLargeInlineImages(rich.content);
|
package/package.json
CHANGED