apple-tools-mcp 3.0.2 → 3.0.3
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/lib/mailWrite.js +27 -0
- package/package.json +1 -1
package/lib/mailWrite.js
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
isHardTccDenial
|
|
32
32
|
} from "./appleScript.js";
|
|
33
33
|
import { isIndexerMode } from "./processMode.js";
|
|
34
|
+
import { safeSpawn } from "./shell.js";
|
|
34
35
|
import {
|
|
35
36
|
planWrite,
|
|
36
37
|
plannedWriteResult,
|
|
@@ -160,6 +161,21 @@ export const BODY_PASTE_MISDIRECTED_SENTINEL = "BODY_PASTE_MISDIRECTED";
|
|
|
160
161
|
export const BODY_FOCUS_FAILED_SENTINEL = "BODY_FOCUS_FAILED";
|
|
161
162
|
/** Focus request ran, but the re-read AXFocusedUIElement was not the chosen body. */
|
|
162
163
|
export const BODY_FOCUS_UNPROVEN_SENTINEL = "BODY_FOCUS_UNPROVEN";
|
|
164
|
+
/** System Events sees 0 Mail windows (display asleep) while Mail AppleScript still lists the compose. */
|
|
165
|
+
export const DISPLAY_ASLEEP_GUI_UNAVAILABLE_SENTINEL = "DISPLAY_ASLEEP_GUI_UNAVAILABLE";
|
|
166
|
+
export const DISPLAY_WAKE_SECONDS = 15;
|
|
167
|
+
export const DISPLAY_WAKE_POLL_ATTEMPTS = 20;
|
|
168
|
+
|
|
169
|
+
/** Declare user activity so a sleeping display wakes before GUI scripting. Best-effort; never throws. */
|
|
170
|
+
export function wakeDisplay() {
|
|
171
|
+
if (process.platform !== "darwin" || process.env.VITEST) return false;
|
|
172
|
+
try {
|
|
173
|
+
return safeSpawn("/usr/bin/caffeinate", ["-u", "-t", String(DISPLAY_WAKE_SECONDS)], { timeout: 5000 }).status === 0;
|
|
174
|
+
} catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
163
179
|
/** Mail To: recipient chips surface as U+FFFC (object replacement), not body text. */
|
|
164
180
|
export const MAIL_RECIPIENT_TOKEN_CHAR_ID = 65532;
|
|
165
181
|
export const GUI_SCRIPTING_UNAVAILABLE_SENTINEL = "GUI_SCRIPTING_UNAVAILABLE";
|
|
@@ -630,6 +646,11 @@ on atmFillMailBody(bodyText, needle, titleMarker, msg)
|
|
|
630
646
|
tell application "System Events"
|
|
631
647
|
tell process "Mail"
|
|
632
648
|
set frontmost to true
|
|
649
|
+
repeat ${DISPLAY_WAKE_POLL_ATTEMPTS} times
|
|
650
|
+
if (count of (every window)) > 0 then exit repeat
|
|
651
|
+
delay 0.5
|
|
652
|
+
end repeat
|
|
653
|
+
set atmSeWindowCount to count of (every window)
|
|
633
654
|
delay 0.3
|
|
634
655
|
set atmWin to missing value
|
|
635
656
|
repeat with atmCand in windows
|
|
@@ -658,6 +679,7 @@ on atmFillMailBody(bodyText, needle, titleMarker, msg)
|
|
|
658
679
|
end if
|
|
659
680
|
end repeat
|
|
660
681
|
end if
|
|
682
|
+
if atmWin is missing value and atmSeWindowCount is 0 then error "${DISPLAY_ASLEEP_GUI_UNAVAILABLE_SENTINEL}"
|
|
661
683
|
if atmWin is missing value then error "${BODY_FOCUS_FAILED_SENTINEL}"
|
|
662
684
|
try
|
|
663
685
|
perform action "AXRaise" of atmWin
|
|
@@ -744,6 +766,7 @@ on atmFillMailBody(bodyText, needle, titleMarker, msg)
|
|
|
744
766
|
on error errMsg number errNum
|
|
745
767
|
if errMsg contains "assistive access" or errNum is -25211 then error "${ACCESSIBILITY_DENIED_SENTINEL}"
|
|
746
768
|
if errMsg contains "${GUI_SCRIPTING_UNAVAILABLE_SENTINEL}" then error "${GUI_SCRIPTING_UNAVAILABLE_SENTINEL}"
|
|
769
|
+
if errMsg contains "${DISPLAY_ASLEEP_GUI_UNAVAILABLE_SENTINEL}" then error "${DISPLAY_ASLEEP_GUI_UNAVAILABLE_SENTINEL}"
|
|
747
770
|
error errMsg number errNum
|
|
748
771
|
end try
|
|
749
772
|
end atmFillMailBody`;
|
|
@@ -1292,6 +1315,9 @@ function failure(action, summary, result, secrets) {
|
|
|
1292
1315
|
if (err.includes("gui_scripting_unavailable")) {
|
|
1293
1316
|
return `${action} failed — attempted to ${summary}. ${MAIL_GUI_SCRIPTING_GUIDANCE}`;
|
|
1294
1317
|
}
|
|
1318
|
+
if (err.includes("display_asleep_gui_unavailable")) {
|
|
1319
|
+
return `${action} failed — attempted to ${summary}. The display is asleep, so System Events cannot see Mail's compose window (GUI unavailable even after a wake attempt); nothing was sent. Wake the display and retry.`;
|
|
1320
|
+
}
|
|
1295
1321
|
if (err.includes("body_focus_unproven")) {
|
|
1296
1322
|
return `${action} failed — attempted to ${summary}. Keyboard focus was not on the message body (focus proof failed); the draft was discarded and nothing was sent.`;
|
|
1297
1323
|
}
|
|
@@ -1480,6 +1506,7 @@ export function mailCompose(args = {}, { draft = false, indexerMode = isIndexerM
|
|
|
1480
1506
|
});
|
|
1481
1507
|
|
|
1482
1508
|
const deadlineMs = sentVerifyClock.now() + MCP_CALL_DEADLINE_MS;
|
|
1509
|
+
wakeDisplay();
|
|
1483
1510
|
const result = runAppleScript(script, { timeout: MAIL_COMPOSE_TIMEOUT_MS, appName: "Mail" });
|
|
1484
1511
|
if (
|
|
1485
1512
|
!result.ok &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-tools-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "MCP server for semantic search and write actions across Apple Mail, Messages, Calendar, and Contacts. Speaks stdio (local) or bearer-token-authenticated Streamable HTTP (LAN / Tailscale).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|