apple-mail-mcp 2.13.2 → 2.14.1
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 +17 -3
- package/build/index.js +108 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -935,9 +935,23 @@ List all mailboxes for an account.
|
|
|
935
935
|
|
|
936
936
|
| Parameter | Type | Required | Description |
|
|
937
937
|
|-----------|------|----------|-------------|
|
|
938
|
-
| `account` | string | No | Account to list from |
|
|
939
|
-
|
|
940
|
-
**Returns:** List of mailbox names with message and unread counts.
|
|
938
|
+
| `account` | string | No | Account to list from, or `"On My Mac"` for the local store |
|
|
939
|
+
|
|
940
|
+
**Returns:** List of mailbox names with message and unread counts. A source that
|
|
941
|
+
could not be read is **named** (`partial: true` + `failedAccounts`) rather than
|
|
942
|
+
dropped, and a listing Mail refused outright returns an error naming the accounts
|
|
943
|
+
that do exist — never an empty list.
|
|
944
|
+
|
|
945
|
+
**Mail's local "On My Mac" mailboxes** are not children of any account — they
|
|
946
|
+
hang off the application — so they are reported under the synthetic account label
|
|
947
|
+
**`On My Mac`**. An unscoped call includes them (listed last); `account="On My
|
|
948
|
+
Mac"` lists only them. `on my computer`, `local` and `local folders` are accepted
|
|
949
|
+
as aliases.
|
|
950
|
+
|
|
951
|
+
They deliberately do **not** appear in `list-accounts`, which reports real
|
|
952
|
+
accounts only: the local store is a store, not an account. Nothing selects it
|
|
953
|
+
implicitly — omitting `account` still resolves to a real account for every other
|
|
954
|
+
tool.
|
|
941
955
|
|
|
942
956
|
---
|
|
943
957
|
|
package/build/index.js
CHANGED
|
@@ -79129,6 +79129,24 @@ function buildAccountScopedScript(account, command) {
|
|
|
79129
79129
|
end tell
|
|
79130
79130
|
`;
|
|
79131
79131
|
}
|
|
79132
|
+
var LOCAL_STORE_LABEL = "On My Mac";
|
|
79133
|
+
var LOCAL_STORE_ALIASES = ["on my mac", "on my computer", "local", "local folders"];
|
|
79134
|
+
function isLocalStoreLabel(name) {
|
|
79135
|
+
return name !== void 0 && LOCAL_STORE_ALIASES.includes(name.trim().toLowerCase());
|
|
79136
|
+
}
|
|
79137
|
+
function localMailboxBindingFragment() {
|
|
79138
|
+
return `
|
|
79139
|
+
set _mbs to {}
|
|
79140
|
+
repeat with _m in mailboxes
|
|
79141
|
+
set _isLoc to false
|
|
79142
|
+
try
|
|
79143
|
+
if (account of _m) is missing value then set _isLoc to true
|
|
79144
|
+
on error
|
|
79145
|
+
set _isLoc to true
|
|
79146
|
+
end try
|
|
79147
|
+
if _isLoc then set end of _mbs to (contents of _m)
|
|
79148
|
+
end repeat`;
|
|
79149
|
+
}
|
|
79132
79150
|
function groupKey(account, mailbox) {
|
|
79133
79151
|
return `${account}\0${mailbox}`;
|
|
79134
79152
|
}
|
|
@@ -79452,16 +79470,61 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79452
79470
|
set _sMiss to ""
|
|
79453
79471
|
set _sPairs to {}
|
|
79454
79472
|
set _sChunk to ${chunk}
|
|
79473
|
+
-- The mailbox's MEASURED length, emitted only when it disagrees with the
|
|
79474
|
+
-- count (#187). -1 = "not measured", which is the normal case: the probe
|
|
79475
|
+
-- only runs a binary search when the count's last position is unreadable.
|
|
79476
|
+
-- Initialised here, not in the else-branch, or the skipped/unavailable
|
|
79477
|
+
-- paths would reference an unbound variable when emitting.
|
|
79478
|
+
set _sTrue to -1
|
|
79455
79479
|
if ${countVar} < 0 then
|
|
79456
79480
|
set _sStatus to "unavailable"
|
|
79457
79481
|
else if ${countVar} > ${max} then
|
|
79458
79482
|
set _sStatus to "skipped"
|
|
79459
79483
|
set _sPayload to "mailbox holds " & (${countVar} as string) & " messages, above ${AUDIT_SNAPSHOT_MAX_ENV}=${max}"
|
|
79460
79484
|
else
|
|
79485
|
+
-- #187: the count can read HIGH, and an out-of-range range RAISES as
|
|
79486
|
+
-- a whole rather than clamping. On a mailbox smaller than one chunk
|
|
79487
|
+
-- there is only ONE slice, so a high count made it fail entirely:
|
|
79488
|
+
-- _sPairs stayed empty, the status collapsed to "unavailable", and
|
|
79489
|
+
-- the record carried no holes and no warning. The collateral
|
|
79490
|
+
-- instrument switched itself off in exactly the stale direction #155
|
|
79491
|
+
-- evidences, silently.
|
|
79492
|
+
--
|
|
79493
|
+
-- So establish a bound that actually EXISTS before slicing. If the
|
|
79494
|
+
-- last position the count claims is readable, the count is not high
|
|
79495
|
+
-- and this costs one probe. Otherwise binary-search the true end,
|
|
79496
|
+
-- which is O(log n) probes and also MEASURES how stale the count is.
|
|
79497
|
+
set _sBound to ${countVar}
|
|
79498
|
+
if _sBound > 0 then
|
|
79499
|
+
set _sEndOk to false
|
|
79500
|
+
try
|
|
79501
|
+
get id of message _sBound of ${mbVar}
|
|
79502
|
+
set _sEndOk to true
|
|
79503
|
+
end try
|
|
79504
|
+
if not _sEndOk then
|
|
79505
|
+
set _sLoB to 0
|
|
79506
|
+
set _sHiB to _sBound
|
|
79507
|
+
repeat while (_sHiB - _sLoB) > 1
|
|
79508
|
+
set _sMid to (_sLoB + _sHiB) div 2
|
|
79509
|
+
set _sMidOk to false
|
|
79510
|
+
try
|
|
79511
|
+
get id of message _sMid of ${mbVar}
|
|
79512
|
+
set _sMidOk to true
|
|
79513
|
+
end try
|
|
79514
|
+
if _sMidOk then
|
|
79515
|
+
set _sLoB to _sMid
|
|
79516
|
+
else
|
|
79517
|
+
set _sHiB to _sMid
|
|
79518
|
+
end if
|
|
79519
|
+
end repeat
|
|
79520
|
+
set _sBound to _sLoB
|
|
79521
|
+
set _sTrue to _sLoB
|
|
79522
|
+
end if
|
|
79523
|
+
end if
|
|
79461
79524
|
set _sLo to 1
|
|
79462
|
-
repeat while _sLo <=
|
|
79525
|
+
repeat while _sLo <= _sBound
|
|
79463
79526
|
set _sHi to _sLo + _sChunk - 1
|
|
79464
|
-
if _sHi >
|
|
79527
|
+
if _sHi > _sBound then set _sHi to _sBound
|
|
79465
79528
|
set _sGot to false
|
|
79466
79529
|
repeat with _sTry from 1 to ${SNAPSHOT_SLICE_ATTEMPTS}
|
|
79467
79530
|
set _sIds to {}
|
|
@@ -79519,7 +79582,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79519
79582
|
-- PARTIAL under the existing rules and withholds the halves a
|
|
79520
79583
|
-- truncation would poison.
|
|
79521
79584
|
try
|
|
79522
|
-
set _sOverId to ((id of message (
|
|
79585
|
+
set _sOverId to ((id of message (_sBound + 1) of ${mbVar}) as string)
|
|
79523
79586
|
-- A specifier that CLAMPS rather than raising hands back the LAST
|
|
79524
79587
|
-- message instead of failing. That is not evidence of a truncation,
|
|
79525
79588
|
-- so only an id this enumeration did not already record counts.
|
|
@@ -79529,7 +79592,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79529
79592
|
end repeat
|
|
79530
79593
|
if not _sSeen then
|
|
79531
79594
|
if _sMiss is not "" then set _sMiss to _sMiss & ","
|
|
79532
|
-
set _sMiss to _sMiss & ((
|
|
79595
|
+
set _sMiss to _sMiss & ((_sBound + 1) as string) & "-end"
|
|
79533
79596
|
end if
|
|
79534
79597
|
end try
|
|
79535
79598
|
if _sMiss is not "" then
|
|
@@ -79544,7 +79607,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79544
79607
|
set _sPayload to _sPairs as string
|
|
79545
79608
|
set AppleScript's text item delimiters to _sTid
|
|
79546
79609
|
end if
|
|
79547
|
-
set _out to _out & "${SNAP_TAG}${FIELD_SEP}" & ${acctExpr} & "${FIELD_SEP}" & ${mbExpr} & "${FIELD_SEP}${phase}${FIELD_SEP}" & _sStatus & "${FIELD_SEP}" & _sPayload & "${FIELD_SEP}" & _sMiss & "${RECORD_SEP}"`;
|
|
79610
|
+
set _out to _out & "${SNAP_TAG}${FIELD_SEP}" & ${acctExpr} & "${FIELD_SEP}" & ${mbExpr} & "${FIELD_SEP}${phase}${FIELD_SEP}" & _sStatus & "${FIELD_SEP}" & _sPayload & "${FIELD_SEP}" & _sMiss & "${FIELD_SEP}" & (_sTrue as string) & "${RECORD_SEP}"`;
|
|
79548
79611
|
}
|
|
79549
79612
|
/**
|
|
79550
79613
|
* AppleScript capturing the message the op is ABOUT to touch into `_pre`,
|
|
@@ -79605,13 +79668,15 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79605
79668
|
continue;
|
|
79606
79669
|
}
|
|
79607
79670
|
if (f[0] === SNAP_TAG) {
|
|
79671
|
+
const measured = f[7] !== void 0 && f[7] !== "" ? Number(f[7]) : -1;
|
|
79608
79672
|
snaps.push({
|
|
79609
79673
|
account: f[1] ?? "",
|
|
79610
79674
|
mailbox: f[2] ?? "",
|
|
79611
79675
|
phase: f[3] === "after" ? "after" : "before",
|
|
79612
79676
|
status: f[4] ?? "",
|
|
79613
79677
|
payload: f[5] ?? "",
|
|
79614
|
-
miss: f[6] ?? ""
|
|
79678
|
+
miss: f[6] ?? "",
|
|
79679
|
+
...Number.isFinite(measured) && measured >= 0 ? { measuredLength: measured } : {}
|
|
79615
79680
|
});
|
|
79616
79681
|
continue;
|
|
79617
79682
|
}
|
|
@@ -79795,6 +79860,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79795
79860
|
const unrequested = disappeared.filter(
|
|
79796
79861
|
(e) => !requestedNumericIds.has(canonicalNumericId(e.id))
|
|
79797
79862
|
);
|
|
79863
|
+
const countStale = [b, a].filter((s) => s.measuredLength !== void 0).map((s) => ({ phase: s.phase, measuredLength: s.measuredLength }));
|
|
79798
79864
|
const holes = [b, a].filter((s) => s.miss !== "").map((s) => ({ phase: s.phase, ranges: s.miss }));
|
|
79799
79865
|
if (holes.length === 0) {
|
|
79800
79866
|
collateral.push({
|
|
@@ -79803,7 +79869,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79803
79869
|
snapshot: "ok",
|
|
79804
79870
|
disappeared,
|
|
79805
79871
|
unrequested,
|
|
79806
|
-
appeared
|
|
79872
|
+
appeared,
|
|
79873
|
+
...countStale.length ? { countStale } : {}
|
|
79807
79874
|
});
|
|
79808
79875
|
continue;
|
|
79809
79876
|
}
|
|
@@ -79815,6 +79882,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
79815
79882
|
account: g.account,
|
|
79816
79883
|
mailbox: g.mailbox,
|
|
79817
79884
|
snapshot: "partial",
|
|
79885
|
+
...countStale.length ? { countStale } : {},
|
|
79818
79886
|
skipReason: `Mail would not read ${holes.map((h) => `${h.ranges} (${h.phase})`).join(", ")} of this mailbox, so the snapshot has a hole in it. ` + (derivable.length > 0 ? `Still derivable and reported: ${derivable.join(" and ")}. ` : `Neither half of the diff is derivable from it. `) + `Anything the unread range could refute is omitted rather than guessed \u2014 an absent field here means "not computable", not "empty".`,
|
|
79819
79887
|
unobserved: holes,
|
|
79820
79888
|
...a.miss === "" ? { disappeared, unrequested } : {},
|
|
@@ -81983,10 +82051,11 @@ ${this.errorEmit(" ")}
|
|
|
81983
82051
|
* List all mailboxes for an account.
|
|
81984
82052
|
*/
|
|
81985
82053
|
listMailboxes(account, options = {}) {
|
|
81986
|
-
const
|
|
81987
|
-
const
|
|
82054
|
+
const local = isLocalStoreLabel(account);
|
|
82055
|
+
const targetAccount = local ? LOCAL_STORE_LABEL : this.resolveAccount(account);
|
|
82056
|
+
const listCommand = (iterExpr) => `
|
|
81988
82057
|
set mailboxList to {}
|
|
81989
|
-
repeat with mb in
|
|
82058
|
+
repeat with mb in ${iterExpr}
|
|
81990
82059
|
set mbName to name of mb
|
|
81991
82060
|
set mbUnread to unread count of mb
|
|
81992
82061
|
set mbCount to count of messages of mb
|
|
@@ -81995,7 +82064,8 @@ ${this.errorEmit(" ")}
|
|
|
81995
82064
|
set AppleScript's text item delimiters to "${RECORD_SEP}"
|
|
81996
82065
|
return mailboxList as text
|
|
81997
82066
|
`;
|
|
81998
|
-
const script =
|
|
82067
|
+
const script = local ? buildAppLevelScript(`${localMailboxBindingFragment()}
|
|
82068
|
+
${listCommand("_mbs")}`) : buildAccountScopedScript(targetAccount, listCommand("mailboxes"));
|
|
81999
82069
|
const result = executeAppleScript(script, { timeoutMs: options.timeoutMs ?? 6e4 });
|
|
82000
82070
|
if (!result.success) {
|
|
82001
82071
|
console.error(`Failed to list mailboxes: ${result.error}`);
|
|
@@ -84653,19 +84723,15 @@ function withErrorHandling(handler, errorPrefix) {
|
|
|
84653
84723
|
}
|
|
84654
84724
|
|
|
84655
84725
|
// src/tools/mailboxListing.ts
|
|
84656
|
-
var LOCAL_STORE_NAMES = ["on my mac", "on my computer", "local", "local folders"];
|
|
84657
|
-
function isLocalStoreName(name) {
|
|
84658
|
-
return LOCAL_STORE_NAMES.includes(name.trim().toLowerCase());
|
|
84659
|
-
}
|
|
84660
84726
|
function unlistableStoreError(account, error2, knownAccounts) {
|
|
84661
84727
|
const scope = account ? ` for "${account}"` : "";
|
|
84662
84728
|
const parts = [`Could not list mailboxes${scope}: ${error2 ?? "Mail declined the request"}`];
|
|
84663
84729
|
if (knownAccounts.length > 0) {
|
|
84664
84730
|
parts.push(`Accounts on this Mac: ${knownAccounts.join(", ")}.`);
|
|
84665
84731
|
}
|
|
84666
|
-
if (account &&
|
|
84732
|
+
if (account && isLocalStoreLabel(account)) {
|
|
84667
84733
|
parts.push(
|
|
84668
|
-
`"${account}"
|
|
84734
|
+
`"${account}" addresses Mail's LOCAL store, which IS listable \u2014 it is read at the application level rather than through an account, so this failure is not "no such account". Something went wrong reading the local store itself.`
|
|
84669
84735
|
);
|
|
84670
84736
|
}
|
|
84671
84737
|
return parts.join("\n\n");
|
|
@@ -86709,10 +86775,26 @@ ${r.base64}`,
|
|
|
86709
86775
|
);
|
|
86710
86776
|
}, "Error fetching attachment")
|
|
86711
86777
|
);
|
|
86778
|
+
function appendLocalStoreRows(rows, failedAccounts) {
|
|
86779
|
+
const checked = mailManager.listMailboxesChecked(LOCAL_STORE_LABEL);
|
|
86780
|
+
if (checked.failed) {
|
|
86781
|
+
console.error(`list-mailboxes failed for "${LOCAL_STORE_LABEL}": ${checked.error}`);
|
|
86782
|
+
failedAccounts.push(LOCAL_STORE_LABEL);
|
|
86783
|
+
return;
|
|
86784
|
+
}
|
|
86785
|
+
for (const mb of checked.mailboxes) {
|
|
86786
|
+
rows.push({
|
|
86787
|
+
name: `${LOCAL_STORE_LABEL}/${mb.name}`,
|
|
86788
|
+
account: LOCAL_STORE_LABEL,
|
|
86789
|
+
unreadCount: mb.unreadCount,
|
|
86790
|
+
messageCount: mb.messageCount
|
|
86791
|
+
});
|
|
86792
|
+
}
|
|
86793
|
+
}
|
|
86712
86794
|
registerTool(
|
|
86713
86795
|
"list-mailboxes",
|
|
86714
86796
|
{
|
|
86715
|
-
description:
|
|
86797
|
+
description: 'Use when: discovering the mailbox/folder names (and unread/message counts) available in an account, e.g. before moving messages or searching a specific mailbox.\nReturns: each mailbox\'s name with its unread (and, for IMAP, total message) count, plus a count. A source that could not be read is NAMED \u2014 the result carries `partial: true` + `failedAccounts` and the list is a floor, not the complete set \u2014 and a listing Mail refused outright (e.g. an account that does not exist) returns an ERROR naming the accounts that do exist, never an empty list.\nDo not use when: you want the messages inside a mailbox (use list-messages or search-messages) or the list of accounts (use list-accounts).\nNote: Mail\'s local "On My Mac" mailboxes are not part of any account, so they are reported under the synthetic account label "On My Mac" \u2014 an unscoped call includes them, and `account: "On My Mac"` lists only them. They will not appear in list-accounts, which reports real accounts only.',
|
|
86716
86798
|
inputSchema: {
|
|
86717
86799
|
account: external_exports.string().optional().describe("Account to list mailboxes from")
|
|
86718
86800
|
},
|
|
@@ -86778,6 +86860,7 @@ ${list2}`, structured3);
|
|
|
86778
86860
|
});
|
|
86779
86861
|
}
|
|
86780
86862
|
}
|
|
86863
|
+
appendLocalStoreRows(rows, failedAccounts);
|
|
86781
86864
|
const partial2 = failedAccounts.length > 0;
|
|
86782
86865
|
const structured2 = {
|
|
86783
86866
|
mailboxes: rows,
|
|
@@ -86806,7 +86889,13 @@ ${list}${caveat}`, structured2);
|
|
|
86806
86889
|
)
|
|
86807
86890
|
);
|
|
86808
86891
|
}
|
|
86809
|
-
const
|
|
86892
|
+
const localFailures = [];
|
|
86893
|
+
if (account === void 0) appendLocalStoreRows(mailboxes, localFailures);
|
|
86894
|
+
const structured = {
|
|
86895
|
+
mailboxes,
|
|
86896
|
+
count: mailboxes.length,
|
|
86897
|
+
...localFailures.length ? { partial: true, failedAccounts: localFailures } : {}
|
|
86898
|
+
};
|
|
86810
86899
|
if (mailboxes.length === 0) {
|
|
86811
86900
|
return successResponse("No mailboxes found", structured);
|
|
86812
86901
|
}
|
package/package.json
CHANGED