apple-tools-mcp 2.0.8 → 2.0.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 +4 -2
- package/lib/mailWrite.js +49 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -466,13 +466,15 @@ Two arguments are available on **every** write tool:
|
|
|
466
466
|
|
|
467
467
|
**Compose is not quoted.** `mail_send` / `mail_draft` (plain and html) call `make new outgoing message` **without** AppleScript `content:` so `newMessage` is a real Mail object, then paste the body into the compose window (System Events). Mail's `mailto` command was shipped in 2.0.4 and **fails on Mini/MacBook Mail**: it does not return an outgoing message (`newMessage` undefined, AppleScript **-2753**). On current Mail (Ventura+, FB11734014) `content` / `html content` store the body as a citation: every plain-text line prefixed with `>`, plus a `multipart/alternative` HTML part wrapped in `<blockquote type="cite">`. Desktop Mail often hides the bar with inline styles; iOS Mail paints the whole body purple with a left quote bar — even when the subject is not `Re:`/`Fwd:` and there is no `In-Reply-To`. Reply and forward still quote the original, which is expected.
|
|
468
468
|
|
|
469
|
-
Paste uses System Events, so **node needs Accessibility** (Privacy & Security → Accessibility) in addition to Automation → Mail. That Accessibility deny is not a Mail Automation deny (`-1743` / `-10004`). Body focus uses `text area` / `scroll area` / `text field` and `UI element whose role is "AXWebArea"` — never the System Events class `web area`, which does not compile on macOS 26.x (**-2741**, “Expected class name but found identifier”).
|
|
469
|
+
Paste uses System Events, so **node needs Accessibility** (Privacy & Security → Accessibility) in addition to Automation → Mail. That Accessibility deny is not a Mail Automation deny (`-1743` / `-10004`). Body focus uses `text area` / `scroll area` / `text field` and `UI element whose role is "AXWebArea"` — never the System Events class `web area`, which does not compile on macOS 26.x (**-2741**, “Expected class name but found identifier”). The pre-paste focus read uses `first UI element whose focused is true` and `value of attribute "AXFocusedUIElement"` — never the compound `focused UI element`, which does not compile on System Events 26.x (**-2741** on MacBook 26.1, **-2740** on Mini 26.6.2).
|
|
470
470
|
|
|
471
471
|
**Compose body focus (2.0.8).** After `make new outgoing message`, To and Subject are AppleScript properties (never paste). Cmd-V runs only after the caret leaves the header: a tall `AXWebArea` (HTML compose body), a tall `text area` (plain compose; short To/Subject fields are skipped), Subject then Tab, or a click in the lower two-thirds of the compose window. If focus is still a header field, the tool raises `BODY_FOCUS_FAILED` and does **not** paste. After paste it checks To/Cc/Bcc counts, the exact subject, and that the native body contains the intended text. A mismatch is `BODY_PASTE_MISDIRECTED`: the outgoing message is deleted and nothing is sent. Quote-prefix Sent prove and Sent/Outbox verify stay gated on this paste-focus contract — a MacBook 2.0.7 live `mail_send` with Accessibility allowed still hit `BODY_PASTE_MISDIRECTED` when Cmd-V landed in To/Subject (`ATP-207-SENT-PROVE-20260921-084048`).
|
|
472
472
|
|
|
473
|
+
**macOS 26 focus compile (2.0.9).** Dual-host Sent prove on `@2.0.8` never reached paste: `atmSafeToPaste` used bare `focused UI element`, which System Events 26.x rejects at compile. The 2.0.8 fail-closed gates stay; only the focus *query* changed to the forms hosts compiled.
|
|
474
|
+
|
|
473
475
|
**mail_send success is Sent/Outbox verify (2.0.7).** AppleScript `send` returning without throw is not enough. After a real send the tool looks in **Sent** and **Outbox** and reports success only if the message is there. The success text names the delivery state (`mailbox: sent` + `delivery: sent`, or `mailbox: outbox` + `delivery: outbox` while still sending). If verify misses, the tool returns a failure (`isError`) — not success. Hang/timeout recover matches **To + subject** (and Message-ID when compose captured one). It never matches subject alone (short subjects like `test` are unsafe). `from` / account selection is out of scope; Mail's default From is used.
|
|
474
476
|
|
|
475
|
-
**Manual prove (2.0.
|
|
477
|
+
**Manual prove (2.0.9 on the Mac host):** AppleScript for safe-to-paste / focus check **compiles** (no `-2741`/`-2740` on `focused UI element`). Then `mail_send` a short plain message and a short `body_format: "html"` message whose body looks like ordinary paragraphs (for example `<p>Quick note</p>`), neither with a `Re:`/`Fwd:` subject. The caret must land in the **body** (not To/Subject): no `BODY_FOCUS_FAILED` / `BODY_PASTE_MISDIRECTED`, and the intended text must be in the native body before send. Inspect each Sent `.emlx`: the text/plain part must not prefix every body line with `>`, and any HTML alternative must not wrap the whole body in `<blockquote type="cite">`. Compose must succeed (no `-2753` / undefined `newMessage`, no **-2741** / **-2740** on body focus). That quote-prefix dual-host Sent prove is a separate bar from the 2.0.7 verify contract.
|
|
476
478
|
|
|
477
479
|
Emails are addressed by their RFC822 **Message-ID**. Pass `message_id`, or pass the `file_path` from `mail_search` / `mail_recent` and the server reads the Message-ID out of the `.emlx` headers for you. `mail_archive` moves the message to its account's Archive (or All Mail) mailbox; `mail_trash` moves it to that account's Trash.
|
|
478
480
|
|
package/lib/mailWrite.js
CHANGED
|
@@ -159,6 +159,37 @@ export const BODY_FOCUS_FAILED_SENTINEL = "BODY_FOCUS_FAILED";
|
|
|
159
159
|
/** Compose body AX height vs To/Subject/Cc fields (those are ~22px). */
|
|
160
160
|
export const MAIL_BODY_MIN_AX_HEIGHT = 50;
|
|
161
161
|
|
|
162
|
+
/**
|
|
163
|
+
* macOS 26.x System Events rejects the compound `focused UI element`
|
|
164
|
+
* (MacBook 26.1 compile -2741; Mini 26.6.2 compile -2740). Hosts compile
|
|
165
|
+
* these instead — same class of dictionary break as bare `web area`.
|
|
166
|
+
*/
|
|
167
|
+
export const ATM_FOCUSED_WHOSE_QUERY = "first UI element whose focused is true";
|
|
168
|
+
export const ATM_FOCUSED_AX_QUERY = 'value of attribute "AXFocusedUIElement"';
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Resolve the focused System Events element without `focused UI element`.
|
|
172
|
+
* AXFocusedUIElement is tried first (nested caret); `whose focused is true`
|
|
173
|
+
* is the compile-proven fallback. A process-level whose match can be the
|
|
174
|
+
* key window — callers must still reject header roles / AXWindow.
|
|
175
|
+
*/
|
|
176
|
+
export function buildAtmFocusedElementHandler() {
|
|
177
|
+
return `on atmFocusedElement()
|
|
178
|
+
tell application "System Events"
|
|
179
|
+
tell process "Mail"
|
|
180
|
+
try
|
|
181
|
+
set fe to ${ATM_FOCUSED_AX_QUERY}
|
|
182
|
+
if fe is not missing value then return fe
|
|
183
|
+
end try
|
|
184
|
+
try
|
|
185
|
+
return ${ATM_FOCUSED_WHOSE_QUERY}
|
|
186
|
+
end try
|
|
187
|
+
end tell
|
|
188
|
+
end tell
|
|
189
|
+
return missing value
|
|
190
|
+
end atmFocusedElement`;
|
|
191
|
+
}
|
|
192
|
+
|
|
162
193
|
/**
|
|
163
194
|
* Body text for native Mail compose (clipboard paste, not AppleScript content).
|
|
164
195
|
*
|
|
@@ -192,10 +223,13 @@ const BODY_MIN_H = asInteger(MAIL_BODY_MIN_AX_HEIGHT, { min: 1, max: 500, field:
|
|
|
192
223
|
* System Events needs Accessibility; Mail make/send still needs Automation.
|
|
193
224
|
*
|
|
194
225
|
* macOS 26.x System Events has no `web area` class (compile -2741,
|
|
195
|
-
* "Expected class name but found identifier")
|
|
196
|
-
* `
|
|
197
|
-
*
|
|
198
|
-
*
|
|
226
|
+
* "Expected class name but found identifier") and no compound
|
|
227
|
+
* `focused UI element` (compile -2741 / -2740). Hosts compile
|
|
228
|
+
* `text area` / `scroll area` / `text field`, `UI element whose role is
|
|
229
|
+
* "AXWebArea"`, `first UI element whose focused is true`, and
|
|
230
|
+
* `value of attribute "AXFocusedUIElement"`. Never focus the first
|
|
231
|
+
* AXTextArea blindly — on MacBook Mail that is a header field, and
|
|
232
|
+
* Cmd-V becomes BODY_PASTE_MISDIRECTED.
|
|
199
233
|
*
|
|
200
234
|
* Strategies, each fail-closed unless the caret left the header:
|
|
201
235
|
* 1. Click a tall AXWebArea (HTML compose body)
|
|
@@ -204,12 +238,16 @@ const BODY_MIN_H = asInteger(MAIL_BODY_MIN_AX_HEIGHT, { min: 1, max: 500, field:
|
|
|
204
238
|
* 4. Click the lower two-thirds of the compose window
|
|
205
239
|
*/
|
|
206
240
|
export function buildMailBodyPasteHandler() {
|
|
207
|
-
return
|
|
241
|
+
return `${buildAtmFocusedElementHandler()}
|
|
242
|
+
|
|
243
|
+
on atmSafeToPaste()
|
|
208
244
|
tell application "System Events"
|
|
209
245
|
tell process "Mail"
|
|
210
246
|
try
|
|
211
|
-
set fe to
|
|
247
|
+
set fe to my atmFocusedElement()
|
|
248
|
+
if fe is missing value then return false
|
|
212
249
|
set r to (role of fe) as string
|
|
250
|
+
if r is "AXWindow" then return false
|
|
213
251
|
if r is "AXTextField" then return false
|
|
214
252
|
if r is "AXComboBox" then return false
|
|
215
253
|
if r is "AXButton" then return false
|
|
@@ -436,12 +474,13 @@ function composeMisdirectChecks({ to, cc = [], bcc = [], subject, body }) {
|
|
|
436
474
|
end if
|
|
437
475
|
if atmGotBody does not contain ${asString(needle)} then
|
|
438
476
|
try
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
477
|
+
set atmFe to my atmFocusedElement()
|
|
478
|
+
if atmFe is not missing value then
|
|
479
|
+
tell application "System Events"
|
|
480
|
+
set atmAxVal to value of atmFe as string
|
|
442
481
|
if atmAxVal contains ${asString(needle)} then set atmGotBody to atmAxVal
|
|
443
482
|
end tell
|
|
444
|
-
end
|
|
483
|
+
end if
|
|
445
484
|
end try
|
|
446
485
|
end if
|
|
447
486
|
if atmGotBody does not contain ${asString(needle)} then error "${BODY_PASTE_MISDIRECTED_SENTINEL}"`
|