apple-mail-mcp 2.13.1 → 2.14.0
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 +103 -12
- 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
|
}
|
|
@@ -81983,10 +82001,11 @@ ${this.errorEmit(" ")}
|
|
|
81983
82001
|
* List all mailboxes for an account.
|
|
81984
82002
|
*/
|
|
81985
82003
|
listMailboxes(account, options = {}) {
|
|
81986
|
-
const
|
|
81987
|
-
const
|
|
82004
|
+
const local = isLocalStoreLabel(account);
|
|
82005
|
+
const targetAccount = local ? LOCAL_STORE_LABEL : this.resolveAccount(account);
|
|
82006
|
+
const listCommand = (iterExpr) => `
|
|
81988
82007
|
set mailboxList to {}
|
|
81989
|
-
repeat with mb in
|
|
82008
|
+
repeat with mb in ${iterExpr}
|
|
81990
82009
|
set mbName to name of mb
|
|
81991
82010
|
set mbUnread to unread count of mb
|
|
81992
82011
|
set mbCount to count of messages of mb
|
|
@@ -81995,7 +82014,8 @@ ${this.errorEmit(" ")}
|
|
|
81995
82014
|
set AppleScript's text item delimiters to "${RECORD_SEP}"
|
|
81996
82015
|
return mailboxList as text
|
|
81997
82016
|
`;
|
|
81998
|
-
const script =
|
|
82017
|
+
const script = local ? buildAppLevelScript(`${localMailboxBindingFragment()}
|
|
82018
|
+
${listCommand("_mbs")}`) : buildAccountScopedScript(targetAccount, listCommand("mailboxes"));
|
|
81999
82019
|
const result = executeAppleScript(script, { timeoutMs: options.timeoutMs ?? 6e4 });
|
|
82000
82020
|
if (!result.success) {
|
|
82001
82021
|
console.error(`Failed to list mailboxes: ${result.error}`);
|
|
@@ -84652,6 +84672,21 @@ function withErrorHandling(handler, errorPrefix) {
|
|
|
84652
84672
|
};
|
|
84653
84673
|
}
|
|
84654
84674
|
|
|
84675
|
+
// src/tools/mailboxListing.ts
|
|
84676
|
+
function unlistableStoreError(account, error2, knownAccounts) {
|
|
84677
|
+
const scope = account ? ` for "${account}"` : "";
|
|
84678
|
+
const parts = [`Could not list mailboxes${scope}: ${error2 ?? "Mail declined the request"}`];
|
|
84679
|
+
if (knownAccounts.length > 0) {
|
|
84680
|
+
parts.push(`Accounts on this Mac: ${knownAccounts.join(", ")}.`);
|
|
84681
|
+
}
|
|
84682
|
+
if (account && isLocalStoreLabel(account)) {
|
|
84683
|
+
parts.push(
|
|
84684
|
+
`"${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.`
|
|
84685
|
+
);
|
|
84686
|
+
}
|
|
84687
|
+
return parts.join("\n\n");
|
|
84688
|
+
}
|
|
84689
|
+
|
|
84655
84690
|
// src/tools/batchResults.ts
|
|
84656
84691
|
async function hybridBatchCounts(ids, appleFn, imapFn) {
|
|
84657
84692
|
const distinctIds = [...new Set(ids)];
|
|
@@ -86690,16 +86725,36 @@ ${r.base64}`,
|
|
|
86690
86725
|
);
|
|
86691
86726
|
}, "Error fetching attachment")
|
|
86692
86727
|
);
|
|
86728
|
+
function appendLocalStoreRows(rows, failedAccounts) {
|
|
86729
|
+
const checked = mailManager.listMailboxesChecked(LOCAL_STORE_LABEL);
|
|
86730
|
+
if (checked.failed) {
|
|
86731
|
+
console.error(`list-mailboxes failed for "${LOCAL_STORE_LABEL}": ${checked.error}`);
|
|
86732
|
+
failedAccounts.push(LOCAL_STORE_LABEL);
|
|
86733
|
+
return;
|
|
86734
|
+
}
|
|
86735
|
+
for (const mb of checked.mailboxes) {
|
|
86736
|
+
rows.push({
|
|
86737
|
+
name: `${LOCAL_STORE_LABEL}/${mb.name}`,
|
|
86738
|
+
account: LOCAL_STORE_LABEL,
|
|
86739
|
+
unreadCount: mb.unreadCount,
|
|
86740
|
+
messageCount: mb.messageCount
|
|
86741
|
+
});
|
|
86742
|
+
}
|
|
86743
|
+
}
|
|
86693
86744
|
registerTool(
|
|
86694
86745
|
"list-mailboxes",
|
|
86695
86746
|
{
|
|
86696
|
-
description:
|
|
86747
|
+
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.',
|
|
86697
86748
|
inputSchema: {
|
|
86698
86749
|
account: external_exports.string().optional().describe("Account to list mailboxes from")
|
|
86699
86750
|
},
|
|
86700
86751
|
outputSchema: {
|
|
86701
86752
|
mailboxes: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
86702
|
-
count: external_exports.number().optional()
|
|
86753
|
+
count: external_exports.number().optional(),
|
|
86754
|
+
// Declared explicitly: the SDK stamps additionalProperties:false on a bare
|
|
86755
|
+
// zod shape, so an undeclared key makes the CLIENT reject the result.
|
|
86756
|
+
partial: external_exports.boolean().optional(),
|
|
86757
|
+
failedAccounts: external_exports.array(external_exports.string()).optional()
|
|
86703
86758
|
}
|
|
86704
86759
|
},
|
|
86705
86760
|
withErrorHandling(async ({ account }) => {
|
|
@@ -86721,6 +86776,7 @@ ${list2}`, structured3);
|
|
|
86721
86776
|
}
|
|
86722
86777
|
const configs = resolveImapConfigs();
|
|
86723
86778
|
const rows = [];
|
|
86779
|
+
const failedAccounts = [];
|
|
86724
86780
|
for (const config2 of configs) {
|
|
86725
86781
|
try {
|
|
86726
86782
|
const boxes = await imapListMailboxes({ config: config2 });
|
|
@@ -86734,11 +86790,18 @@ ${list2}`, structured3);
|
|
|
86734
86790
|
}
|
|
86735
86791
|
} catch (e) {
|
|
86736
86792
|
console.error(`IMAP list-mailboxes failed for "${config2.accountLabel}": ${String(e)}`);
|
|
86793
|
+
failedAccounts.push(config2.accountLabel);
|
|
86737
86794
|
}
|
|
86738
86795
|
}
|
|
86739
86796
|
const { appleScriptOnly } = partitionAccountsForCounts(mailManager.listAccounts(), configs);
|
|
86740
86797
|
for (const acct of appleScriptOnly) {
|
|
86741
|
-
|
|
86798
|
+
const checked = mailManager.listMailboxesChecked(acct.name);
|
|
86799
|
+
if (checked.failed) {
|
|
86800
|
+
console.error(`list-mailboxes failed for "${acct.name}": ${checked.error}`);
|
|
86801
|
+
failedAccounts.push(acct.name);
|
|
86802
|
+
continue;
|
|
86803
|
+
}
|
|
86804
|
+
for (const mb of checked.mailboxes) {
|
|
86742
86805
|
rows.push({
|
|
86743
86806
|
name: `${acct.name}/${mb.name}`,
|
|
86744
86807
|
account: acct.name,
|
|
@@ -86747,14 +86810,42 @@ ${list2}`, structured3);
|
|
|
86747
86810
|
});
|
|
86748
86811
|
}
|
|
86749
86812
|
}
|
|
86750
|
-
|
|
86751
|
-
|
|
86813
|
+
appendLocalStoreRows(rows, failedAccounts);
|
|
86814
|
+
const partial2 = failedAccounts.length > 0;
|
|
86815
|
+
const structured2 = {
|
|
86816
|
+
mailboxes: rows,
|
|
86817
|
+
count: rows.length,
|
|
86818
|
+
...partial2 ? { partial: partial2, failedAccounts } : {}
|
|
86819
|
+
};
|
|
86820
|
+
const caveat = partial2 ? `
|
|
86821
|
+
|
|
86822
|
+
PARTIAL \u2014 could not read: ${failedAccounts.join(", ")}. This list is incomplete.` : "";
|
|
86823
|
+
if (rows.length === 0) {
|
|
86824
|
+
return partial2 ? errorResponse(
|
|
86825
|
+
`Could not list mailboxes from any source. Failed: ${failedAccounts.join(", ")}.`
|
|
86826
|
+
) : successResponse("No mailboxes found", structured2);
|
|
86827
|
+
}
|
|
86752
86828
|
const list = rows.map((b) => ` - ${b.name} (${b.unreadCount} unread)`).join("\n");
|
|
86753
86829
|
return successResponse(`Found ${rows.length} mailbox(es):
|
|
86754
|
-
${list}`, structured2);
|
|
86830
|
+
${list}${caveat}`, structured2);
|
|
86755
86831
|
}
|
|
86756
|
-
const mailboxes = mailManager.
|
|
86757
|
-
|
|
86832
|
+
const { mailboxes, failed, error: error2 } = mailManager.listMailboxesChecked(account);
|
|
86833
|
+
if (failed) {
|
|
86834
|
+
return errorResponse(
|
|
86835
|
+
unlistableStoreError(
|
|
86836
|
+
account,
|
|
86837
|
+
error2,
|
|
86838
|
+
mailManager.listAccounts().map((a) => a.name)
|
|
86839
|
+
)
|
|
86840
|
+
);
|
|
86841
|
+
}
|
|
86842
|
+
const localFailures = [];
|
|
86843
|
+
if (account === void 0) appendLocalStoreRows(mailboxes, localFailures);
|
|
86844
|
+
const structured = {
|
|
86845
|
+
mailboxes,
|
|
86846
|
+
count: mailboxes.length,
|
|
86847
|
+
...localFailures.length ? { partial: true, failedAccounts: localFailures } : {}
|
|
86848
|
+
};
|
|
86758
86849
|
if (mailboxes.length === 0) {
|
|
86759
86850
|
return successResponse("No mailboxes found", structured);
|
|
86760
86851
|
}
|
package/package.json
CHANGED