apple-notes-mcp 2.6.4 → 2.6.5
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 +2 -2
- package/build/index.js +26 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1019,8 +1019,8 @@ All configuration is optional — the server works out of the box. Override beha
|
|
|
1019
1019
|
| `APPLE_NOTES_MCP_MAX_ATTACHMENT_BYTES` | `26214400` (25 MB) | Max size of an attachment that [`fetch-attachment`](#fetch-attachment) will base64-encode inline. Larger attachments are rejected with an error pointing at [`save-attachment`](#save-attachment) (which streams to disk and has no such limit). Raise it to fetch bigger attachments inline; lower it to cap memory. |
|
|
1020
1020
|
| `APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES` | `262144` (256 KB) | Per-image cap on the base64 payload kept inline in a [`get-note-content`](#get-note-content) response. Inline images over the cap are replaced with placeholders (with a warning appended) so an image-heavy note cannot exceed the MCP client's message limit and drop the connection; export the real files with [`save-attachment`](#save-attachment) or [`fetch-attachment`](#fetch-attachment). Raise it to keep bigger images inline. |
|
|
1021
1021
|
| `APPLE_NOTES_MCP_CONFIG_FILE` | `~/Library/Application Support/apple-notes-mcp/config.json` | Path to the JSON config file (see below). |
|
|
1022
|
-
| `APPLE_NOTES_MCP_TIMEOUT_MS` | `30000` (30 s) |
|
|
1023
|
-
| `APPLE_NOTES_MCP_MAX_RETRIES` | `2` |
|
|
1022
|
+
| `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. |
|
|
1023
|
+
| `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. |
|
|
1024
1024
|
| `APPLE_NOTES_MCP_RETRY_DELAY_MS` | `1000` (1 s) | Base delay before the first retry; subsequent retries back off exponentially (1s, 2s, 4s, ...). |
|
|
1025
1025
|
| `DEBUG` / `VERBOSE` | unset | Set either to enable verbose diagnostic logging to stderr. |
|
|
1026
1026
|
|
package/build/index.js
CHANGED
|
@@ -38677,6 +38677,7 @@ function getMaxBuffer() {
|
|
|
38677
38677
|
return envPositiveNumber("APPLE_NOTES_MCP_MAX_BUFFER") ?? DEFAULT_MAX_BUFFER_BYTES;
|
|
38678
38678
|
}
|
|
38679
38679
|
var SCRIPT_TIMEOUT_HEADROOM_MS = 5e3;
|
|
38680
|
+
var MIN_ATTEMPT_BUDGET_MS = 1e3;
|
|
38680
38681
|
function wrapWithTimeout(script, processTimeoutMs) {
|
|
38681
38682
|
const seconds = Math.max(1, Math.ceil((processTimeoutMs - SCRIPT_TIMEOUT_HEADROOM_MS) / 1e3));
|
|
38682
38683
|
return `with timeout of ${seconds} seconds
|
|
@@ -38818,7 +38819,6 @@ function executeAppleScript(script, options = {}) {
|
|
|
38818
38819
|
error: "Cannot execute empty AppleScript"
|
|
38819
38820
|
};
|
|
38820
38821
|
}
|
|
38821
|
-
const preparedScript = wrapWithTimeout(script.trim(), timeoutMs);
|
|
38822
38822
|
debugLog("Executing AppleScript", {
|
|
38823
38823
|
scriptPreview: script.trim().substring(0, 200) + (script.length > 200 ? "..." : ""),
|
|
38824
38824
|
timeout: timeoutMs,
|
|
@@ -38826,13 +38826,16 @@ function executeAppleScript(script, options = {}) {
|
|
|
38826
38826
|
});
|
|
38827
38827
|
let lastError = null;
|
|
38828
38828
|
const startTime = Date.now();
|
|
38829
|
+
const deadline = startTime + timeoutMs;
|
|
38829
38830
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
38831
|
+
const attemptTimeoutMs = Math.max(1, deadline - Date.now());
|
|
38832
|
+
const preparedScript = wrapWithTimeout(script.trim(), attemptTimeoutMs);
|
|
38830
38833
|
const attemptStart = Date.now();
|
|
38831
38834
|
try {
|
|
38832
38835
|
const output = execFileSync("osascript", ["-"], {
|
|
38833
38836
|
input: preparedScript,
|
|
38834
38837
|
encoding: "utf8",
|
|
38835
|
-
timeout:
|
|
38838
|
+
timeout: attemptTimeoutMs,
|
|
38836
38839
|
// SIGKILL (not the default SIGTERM): a wedged osascript blocked on an
|
|
38837
38840
|
// unresponsive Notes.app can ignore SIGTERM and leak, piling up and
|
|
38838
38841
|
// worsening contention. SIGKILL guarantees reaping on timeout. (#17)
|
|
@@ -38885,8 +38888,9 @@ function executeAppleScript(script, options = {}) {
|
|
|
38885
38888
|
};
|
|
38886
38889
|
const canRetry = isTimeout || isRetryableError(errorMessage);
|
|
38887
38890
|
const hasAttemptsLeft = attempt < maxRetries;
|
|
38888
|
-
|
|
38889
|
-
|
|
38891
|
+
const delayMs = retryDelayMs * Math.pow(2, attempt - 1);
|
|
38892
|
+
const hasTimeForRetry = Date.now() + delayMs + MIN_ATTEMPT_BUDGET_MS < deadline;
|
|
38893
|
+
if (canRetry && hasAttemptsLeft && hasTimeForRetry) {
|
|
38890
38894
|
console.error(
|
|
38891
38895
|
`AppleScript retry: Attempt ${attempt}/${maxRetries} failed with "${errorMessage}". Retrying in ${delayMs}ms...`
|
|
38892
38896
|
);
|
|
@@ -39221,6 +39225,9 @@ var FIELD_SEP = "";
|
|
|
39221
39225
|
var RECORD_SEP = "";
|
|
39222
39226
|
var AS_FIELD_SEP = "(ASCII character 31)";
|
|
39223
39227
|
var AS_RECORD_SEP = "(ASCII character 30)";
|
|
39228
|
+
function executeMutationAppleScript(script) {
|
|
39229
|
+
return executeAppleScript(script, { maxRetries: 1 });
|
|
39230
|
+
}
|
|
39224
39231
|
function escapeForAppleScript(text) {
|
|
39225
39232
|
if (!text) {
|
|
39226
39233
|
return "";
|
|
@@ -39481,7 +39488,7 @@ var AppleNotesManager = class {
|
|
|
39481
39488
|
`;
|
|
39482
39489
|
}
|
|
39483
39490
|
const script = buildAccountScopedScript({ account: targetAccount }, createCommand);
|
|
39484
|
-
const result =
|
|
39491
|
+
const result = executeMutationAppleScript(script);
|
|
39485
39492
|
if (!result.success) {
|
|
39486
39493
|
console.error(`Failed to create note "${title}":`, result.error);
|
|
39487
39494
|
return null;
|
|
@@ -39824,7 +39831,7 @@ var AppleNotesManager = class {
|
|
|
39824
39831
|
const safeTitle = escapePlainStringForAppleScript(title);
|
|
39825
39832
|
const deleteCommand = `delete note "${safeTitle}"`;
|
|
39826
39833
|
const script = buildAccountScopedScript({ account: targetAccount }, deleteCommand);
|
|
39827
|
-
const result =
|
|
39834
|
+
const result = executeMutationAppleScript(script);
|
|
39828
39835
|
if (!result.success) {
|
|
39829
39836
|
console.error(`Failed to delete note "${title}":`, result.error);
|
|
39830
39837
|
return false;
|
|
@@ -39844,7 +39851,7 @@ var AppleNotesManager = class {
|
|
|
39844
39851
|
const safeId = sanitizeId(id);
|
|
39845
39852
|
const deleteCommand = `delete note id "${safeId}"`;
|
|
39846
39853
|
const script = buildAppLevelScript(deleteCommand);
|
|
39847
|
-
const result =
|
|
39854
|
+
const result = executeMutationAppleScript(script);
|
|
39848
39855
|
if (!result.success) {
|
|
39849
39856
|
console.error(`Failed to delete note with ID "${id}":`, result.error);
|
|
39850
39857
|
return false;
|
|
@@ -39888,7 +39895,7 @@ var AppleNotesManager = class {
|
|
|
39888
39895
|
}
|
|
39889
39896
|
const updateCommand = `set body of note "${safeCurrentTitle}" to "${fullBody}"`;
|
|
39890
39897
|
const script = buildAccountScopedScript({ account: targetAccount }, updateCommand);
|
|
39891
|
-
const result =
|
|
39898
|
+
const result = executeMutationAppleScript(script);
|
|
39892
39899
|
if (!result.success) {
|
|
39893
39900
|
console.error(`Failed to update note "${title}":`, result.error);
|
|
39894
39901
|
return false;
|
|
@@ -39937,7 +39944,7 @@ var AppleNotesManager = class {
|
|
|
39937
39944
|
const safeId = sanitizeId(id);
|
|
39938
39945
|
const updateCommand = `set body of note id "${safeId}" to "${fullBody}"`;
|
|
39939
39946
|
const script = buildAppLevelScript(updateCommand);
|
|
39940
|
-
const result =
|
|
39947
|
+
const result = executeMutationAppleScript(script);
|
|
39941
39948
|
if (!result.success) {
|
|
39942
39949
|
console.error(`Failed to update note with ID "${id}":`, result.error);
|
|
39943
39950
|
return false;
|
|
@@ -40256,7 +40263,7 @@ var AppleNotesManager = class {
|
|
|
40256
40263
|
createCommand = `make new folder at ${parentRef} with properties {name:"${segmentName}"}`;
|
|
40257
40264
|
}
|
|
40258
40265
|
const script = buildAccountScopedScript({ account: targetAccount }, createCommand);
|
|
40259
|
-
const result =
|
|
40266
|
+
const result = executeMutationAppleScript(script);
|
|
40260
40267
|
if (!result.success) {
|
|
40261
40268
|
console.error(`Failed to create folder "${name}":`, result.error);
|
|
40262
40269
|
return null;
|
|
@@ -40288,7 +40295,7 @@ var AppleNotesManager = class {
|
|
|
40288
40295
|
const targetAccount = this.resolveAccount(account);
|
|
40289
40296
|
const deleteCommand = `delete ${buildFolderReference(name)}`;
|
|
40290
40297
|
const script = buildAccountScopedScript({ account: targetAccount }, deleteCommand);
|
|
40291
|
-
const result =
|
|
40298
|
+
const result = executeMutationAppleScript(script);
|
|
40292
40299
|
if (!result.success) {
|
|
40293
40300
|
console.error(`Failed to delete folder "${name}":`, result.error);
|
|
40294
40301
|
return false;
|
|
@@ -40345,7 +40352,7 @@ var AppleNotesManager = class {
|
|
|
40345
40352
|
move noteRef to destFolder
|
|
40346
40353
|
`;
|
|
40347
40354
|
const script = buildAppLevelScript(moveCommand);
|
|
40348
|
-
const result =
|
|
40355
|
+
const result = executeMutationAppleScript(script);
|
|
40349
40356
|
if (!result.success) {
|
|
40350
40357
|
console.error(
|
|
40351
40358
|
`Cannot move note to "${destinationFolder}" (folder may not exist):`,
|
|
@@ -40500,7 +40507,7 @@ var AppleNotesManager = class {
|
|
|
40500
40507
|
showNoteById(id, separately = false) {
|
|
40501
40508
|
const safeId = sanitizeId(id);
|
|
40502
40509
|
const separatelyClause = separately ? " separately true" : "";
|
|
40503
|
-
const result =
|
|
40510
|
+
const result = executeMutationAppleScript(
|
|
40504
40511
|
buildAppLevelScript(`show note id "${safeId}"${separatelyClause}`)
|
|
40505
40512
|
);
|
|
40506
40513
|
if (!result.success) {
|
|
@@ -40563,7 +40570,7 @@ var AppleNotesManager = class {
|
|
|
40563
40570
|
showFolderById(id, separately = false) {
|
|
40564
40571
|
const safeId = sanitizeId(id);
|
|
40565
40572
|
const separatelyClause = separately ? " separately true" : "";
|
|
40566
|
-
const result =
|
|
40573
|
+
const result = executeMutationAppleScript(
|
|
40567
40574
|
buildAppLevelScript(`show folder id "${safeId}"${separatelyClause}`)
|
|
40568
40575
|
);
|
|
40569
40576
|
if (!result.success) {
|
|
@@ -40585,7 +40592,7 @@ var AppleNotesManager = class {
|
|
|
40585
40592
|
showAccountById(id, separately = false) {
|
|
40586
40593
|
const safeId = sanitizeId(id);
|
|
40587
40594
|
const separatelyClause = separately ? " separately true" : "";
|
|
40588
|
-
const result =
|
|
40595
|
+
const result = executeMutationAppleScript(
|
|
40589
40596
|
buildAppLevelScript(`show account id "${safeId}"${separatelyClause}`)
|
|
40590
40597
|
);
|
|
40591
40598
|
if (!result.success) {
|
|
@@ -40628,7 +40635,7 @@ var AppleNotesManager = class {
|
|
|
40628
40635
|
return "OK"
|
|
40629
40636
|
end tell
|
|
40630
40637
|
`;
|
|
40631
|
-
const result =
|
|
40638
|
+
const result = executeMutationAppleScript(script);
|
|
40632
40639
|
if (!result.success) {
|
|
40633
40640
|
console.error(
|
|
40634
40641
|
`Failed to show attachment "${attachmentId}" on note "${noteId}":`,
|
|
@@ -41030,7 +41037,7 @@ var AppleNotesManager = class {
|
|
|
41030
41037
|
return "OK" & ${AS_FIELD_SEP} & (name of theAttachment) & ${AS_FIELD_SEP} & (content identifier of theAttachment)
|
|
41031
41038
|
end tell
|
|
41032
41039
|
`;
|
|
41033
|
-
const result =
|
|
41040
|
+
const result = executeMutationAppleScript(script);
|
|
41034
41041
|
if (!result.success) {
|
|
41035
41042
|
return { success: false, error: result.error ?? "unknown error" };
|
|
41036
41043
|
}
|
|
@@ -41164,7 +41171,7 @@ var AppleNotesManager = class {
|
|
|
41164
41171
|
end repeat
|
|
41165
41172
|
return out
|
|
41166
41173
|
`);
|
|
41167
|
-
const res =
|
|
41174
|
+
const res = executeMutationAppleScript(script);
|
|
41168
41175
|
if (!res.success) {
|
|
41169
41176
|
for (const r of runnable) {
|
|
41170
41177
|
results[r.index] = this.createBatchResult(
|
|
@@ -41274,7 +41281,7 @@ var AppleNotesManager = class {
|
|
|
41274
41281
|
end repeat
|
|
41275
41282
|
return out
|
|
41276
41283
|
`);
|
|
41277
|
-
const res =
|
|
41284
|
+
const res = executeMutationAppleScript(script);
|
|
41278
41285
|
if (!res.success) {
|
|
41279
41286
|
for (const r of runnable) {
|
|
41280
41287
|
results[r.index] = this.createBatchResult(
|
package/package.json
CHANGED