apple-mail-mcp 2.10.5 → 2.10.7
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 +10 -1
- package/build/index.js +177 -95
- package/docs/IMAP-SETUP.md +17 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -485,6 +485,7 @@ for an explicitly-named IMAP account, never on an omitted account.
|
|
|
485
485
|
| `APPLE_MAIL_MCP_IMAP_ACCOUNTS` | No | — | JSON array of **additional** IMAP accounts for multi-account setups (see below) |
|
|
486
486
|
| `APPLE_MAIL_MCP_IMAP_IDLE` | No | `0` | Set `1` to enable IMAP IDLE push notifications (new-mail alerts) for every configured account |
|
|
487
487
|
| `APPLE_MAIL_MCP_IMAP_IDLE_MS` | No | `30000` | Idle timeout (ms) before a pooled IMAP connection is closed (`0` = never close) |
|
|
488
|
+
| `APPLE_MAIL_MCP_STATS_BUDGET_MS` | No | `25000` | Per-account wall-clock budget for `get-mail-stats` (minimum `1000`). Raise it for very large accounts |
|
|
488
489
|
|
|
489
490
|
**Multiple IMAP accounts (C2):** set `APPLE_MAIL_MCP_IMAP_ACCOUNTS` to a JSON array, e.g.
|
|
490
491
|
`[{"account":"Work","user":"me@co.com","host":"imap.co.com","keychainService":"imap.co.com"}]`.
|
|
@@ -1146,7 +1147,15 @@ Get mail statistics.
|
|
|
1146
1147
|
|-----------|------|----------|-------------|
|
|
1147
1148
|
| `account` | string | No | Limit to one account (uses fast IMAP `STATUS` when that account is IMAP-configured). Omit to merge across all accounts. |
|
|
1148
1149
|
|
|
1149
|
-
**Returns:** Total and per-account message/unread counts, plus recently received stats (24h, 7d, 30d).
|
|
1150
|
+
**Returns:** Total and per-account message/unread counts, plus recently received stats (24h, 7d, 30d). The scoped IMAP path also returns a `perMailbox` breakdown.
|
|
1151
|
+
|
|
1152
|
+
Gathering stats costs one IMAP `STATUS` per mailbox, and Gmail lists every label
|
|
1153
|
+
as a mailbox, so a large account is not instant. Accounts are counted
|
|
1154
|
+
**concurrently**, and each is bounded by `APPLE_MAIL_MCP_STATS_BUDGET_MS`
|
|
1155
|
+
(default `25000`). In the merged all-accounts path an account that fails or
|
|
1156
|
+
overruns is reported via `partial: true` + `failedAccounts` rather than being
|
|
1157
|
+
folded in as a silent zero; a scoped call to a single account returns an error
|
|
1158
|
+
naming the budget instead. Raise the budget if you have a very large account.
|
|
1150
1159
|
|
|
1151
1160
|
---
|
|
1152
1161
|
|
package/build/index.js
CHANGED
|
@@ -81653,9 +81653,8 @@ function sameImapAccount(left, right, deps) {
|
|
|
81653
81653
|
if (aliases.has(left) && aliases.has(right)) return true;
|
|
81654
81654
|
}
|
|
81655
81655
|
const specs = listImapAccountSpecs();
|
|
81656
|
-
const
|
|
81657
|
-
const
|
|
81658
|
-
const rightSpec = specs.find((spec) => matches(right, spec));
|
|
81656
|
+
const leftSpec = specs.find((spec) => specMatchesSelector(spec, left));
|
|
81657
|
+
const rightSpec = specs.find((spec) => specMatchesSelector(spec, right));
|
|
81659
81658
|
return leftSpec !== void 0 && leftSpec === rightSpec;
|
|
81660
81659
|
}
|
|
81661
81660
|
function depsForAccount(account, deps) {
|
|
@@ -81667,14 +81666,21 @@ function depsForAccount(account, deps) {
|
|
|
81667
81666
|
function depsForMessageRef(ref, deps) {
|
|
81668
81667
|
return depsForAccount(ref.account, deps);
|
|
81669
81668
|
}
|
|
81669
|
+
function specMatchesSelector(spec, selector) {
|
|
81670
|
+
return spec.accountLabel === selector || spec.user === selector || (spec.aliases?.includes(selector) ?? false);
|
|
81671
|
+
}
|
|
81670
81672
|
function str(v) {
|
|
81671
81673
|
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
81672
81674
|
}
|
|
81675
|
+
function imapIdentityKey(spec) {
|
|
81676
|
+
return `${spec.host.trim().toLowerCase()}:${spec.port}:${spec.user.trim()}`;
|
|
81677
|
+
}
|
|
81673
81678
|
function listImapAccountSpecs(env = process.env) {
|
|
81674
81679
|
const specs = [];
|
|
81680
|
+
const seen = /* @__PURE__ */ new Set();
|
|
81675
81681
|
const user = env[IMAP_ENV.user]?.trim();
|
|
81676
81682
|
if (user) {
|
|
81677
|
-
|
|
81683
|
+
const legacy = {
|
|
81678
81684
|
accountLabel: env[IMAP_ENV.account]?.trim() || user,
|
|
81679
81685
|
user,
|
|
81680
81686
|
host: env[IMAP_ENV.host]?.trim() || "imap.gmail.com",
|
|
@@ -81682,7 +81688,9 @@ function listImapAccountSpecs(env = process.env) {
|
|
|
81682
81688
|
password: env[IMAP_ENV.password],
|
|
81683
81689
|
keychainService: env[IMAP_ENV.keychainService]?.trim(),
|
|
81684
81690
|
keychainAccount: env[IMAP_ENV.keychainAccount]?.trim()
|
|
81685
|
-
}
|
|
81691
|
+
};
|
|
81692
|
+
specs.push(legacy);
|
|
81693
|
+
seen.add(imapIdentityKey(legacy));
|
|
81686
81694
|
}
|
|
81687
81695
|
const json = env[IMAP_ENV.accounts]?.trim();
|
|
81688
81696
|
if (json) {
|
|
@@ -81694,12 +81702,22 @@ function listImapAccountSpecs(env = process.env) {
|
|
|
81694
81702
|
const u = str(a.user);
|
|
81695
81703
|
if (!u) continue;
|
|
81696
81704
|
const label = str(a.account) || str(a.accountLabel) || u;
|
|
81697
|
-
|
|
81705
|
+
const host = str(a.host) || "imap.gmail.com";
|
|
81698
81706
|
const port = a.port ? Number(a.port) : 993;
|
|
81707
|
+
const key = imapIdentityKey({ host, port, user: u });
|
|
81708
|
+
if (seen.has(key)) {
|
|
81709
|
+
const owner = specs.find((s) => imapIdentityKey(s) === key);
|
|
81710
|
+
if (owner && owner.accountLabel !== label && !owner.aliases?.includes(label)) {
|
|
81711
|
+
(owner.aliases ??= []).push(label);
|
|
81712
|
+
}
|
|
81713
|
+
continue;
|
|
81714
|
+
}
|
|
81715
|
+
if (specs.some((s) => s.accountLabel === label)) continue;
|
|
81716
|
+
seen.add(key);
|
|
81699
81717
|
specs.push({
|
|
81700
81718
|
accountLabel: label,
|
|
81701
81719
|
user: u,
|
|
81702
|
-
host
|
|
81720
|
+
host,
|
|
81703
81721
|
port,
|
|
81704
81722
|
password: str(a.password),
|
|
81705
81723
|
keychainService: str(a.keychainService),
|
|
@@ -81737,7 +81755,7 @@ function specToConfig(spec) {
|
|
|
81737
81755
|
}
|
|
81738
81756
|
function isImapAccount(account, env = process.env) {
|
|
81739
81757
|
if (!account) return false;
|
|
81740
|
-
return listImapAccountSpecs(env).some((s) => s
|
|
81758
|
+
return listImapAccountSpecs(env).some((s) => specMatchesSelector(s, account));
|
|
81741
81759
|
}
|
|
81742
81760
|
function shouldUseImap(account, env = process.env) {
|
|
81743
81761
|
return listImapAccountSpecs(env).length > 0 && (account === void 0 || isImapAccount(account, env));
|
|
@@ -81760,12 +81778,12 @@ function resolveImapConfig(env = process.env, account) {
|
|
|
81760
81778
|
const specs = listImapAccountSpecs(env);
|
|
81761
81779
|
if (specs.length === 0) {
|
|
81762
81780
|
throw new Error(
|
|
81763
|
-
`IMAP not configured. Set ${IMAP_ENV.user} (login address) to enable it. ${SETUP_HINT}`
|
|
81781
|
+
`IMAP not configured. Set ${IMAP_ENV.user} (login address), or ${IMAP_ENV.accounts} for multiple accounts, to enable it. ${SETUP_HINT}`
|
|
81764
81782
|
);
|
|
81765
81783
|
}
|
|
81766
81784
|
let spec;
|
|
81767
81785
|
if (account) {
|
|
81768
|
-
spec = specs.find((s) => s
|
|
81786
|
+
spec = specs.find((s) => specMatchesSelector(s, account));
|
|
81769
81787
|
if (!spec) {
|
|
81770
81788
|
throw new Error(
|
|
81771
81789
|
`No IMAP account matching "${account}". Configured: ${specs.map((s) => s.accountLabel).join(", ")}.`
|
|
@@ -81977,7 +81995,7 @@ function errText(e) {
|
|
|
81977
81995
|
var poolConnect = defaultConnect;
|
|
81978
81996
|
var pools = /* @__PURE__ */ new Map();
|
|
81979
81997
|
function poolKey(cfg) {
|
|
81980
|
-
return
|
|
81998
|
+
return imapIdentityKey(cfg);
|
|
81981
81999
|
}
|
|
81982
82000
|
function imapIdleMs() {
|
|
81983
82001
|
const raw = process.env.APPLE_MAIL_MCP_IMAP_IDLE_MS;
|
|
@@ -82035,7 +82053,7 @@ async function acquirePooled(cfg) {
|
|
|
82035
82053
|
}
|
|
82036
82054
|
}
|
|
82037
82055
|
async function imapHealthCheck(deps = {}) {
|
|
82038
|
-
if (!deps.config &&
|
|
82056
|
+
if (!deps.config && listImapAccountSpecs().length === 0) {
|
|
82039
82057
|
return { configured: false, ok: false };
|
|
82040
82058
|
}
|
|
82041
82059
|
let cfg;
|
|
@@ -82773,7 +82791,11 @@ async function runDoctor(mailManager2) {
|
|
|
82773
82791
|
checks.push({
|
|
82774
82792
|
name: `IMAP: ${label}`,
|
|
82775
82793
|
status: h.ok ? "ok" : "fail",
|
|
82776
|
-
|
|
82794
|
+
// `h.error` is optional on the health-check result, so interpolating it
|
|
82795
|
+
// bare printed the literal string "connection failed: undefined" for
|
|
82796
|
+
// any failure that carried no message (issue #138). Never render that:
|
|
82797
|
+
// an unexplained failure is still worth naming, but as words.
|
|
82798
|
+
detail: h.ok ? `connected to ${h.host}` : `connection failed: ${h.error ?? "the health check reported no detail"}. Check the Keychain password and host/port.`
|
|
82777
82799
|
});
|
|
82778
82800
|
}
|
|
82779
82801
|
}
|
|
@@ -83202,6 +83224,14 @@ var server = new McpServer(
|
|
|
83202
83224
|
// logging capability lets the IMAP IDLE watcher push new-mail notifications (B5).
|
|
83203
83225
|
{ capabilities: { logging: {} } }
|
|
83204
83226
|
);
|
|
83227
|
+
function registerTool(name, config2, cb) {
|
|
83228
|
+
const { outputSchema, ...rest } = config2;
|
|
83229
|
+
return server.registerTool(
|
|
83230
|
+
name,
|
|
83231
|
+
outputSchema ? { ...rest, outputSchema: external_exports.object(outputSchema).passthrough() } : rest,
|
|
83232
|
+
cb
|
|
83233
|
+
);
|
|
83234
|
+
}
|
|
83205
83235
|
var mailManager = new AppleMailManager();
|
|
83206
83236
|
registerResourcesAndPrompts(server, mailManager);
|
|
83207
83237
|
async function hybridBatchCounts(ids, appleFn, imapFn) {
|
|
@@ -83224,7 +83254,7 @@ async function hybridBatchCounts(ids, appleFn, imapFn) {
|
|
|
83224
83254
|
}
|
|
83225
83255
|
return { success, fail, errors };
|
|
83226
83256
|
}
|
|
83227
|
-
|
|
83257
|
+
registerTool(
|
|
83228
83258
|
"search-messages",
|
|
83229
83259
|
{
|
|
83230
83260
|
description: "Use when: finding messages by query/sender/subject/date/read/flag filters and you need their ids for follow-up operations.\nReturns: matching messages with id, date, subject, sender, and read state (plus partial-coverage diagnostics when some mailboxes were skipped).\nDo not use when: you want a plain mailbox listing without filters (use list-messages), already have an id and want the body (use get-message), or want a whole conversation (use get-thread).\nPrefer this first to obtain the message ids that get-message/mark-as-read/delete-message/move-message and the batch tools require.",
|
|
@@ -83336,7 +83366,7 @@ ${messageList}${coverageBlock}`,
|
|
|
83336
83366
|
"Error searching messages"
|
|
83337
83367
|
)
|
|
83338
83368
|
);
|
|
83339
|
-
|
|
83369
|
+
registerTool(
|
|
83340
83370
|
"get-message",
|
|
83341
83371
|
{
|
|
83342
83372
|
description: `Use when: reading the full body of one message whose id you already have (numeric or imap:\u2026); set preferHtml to get the HTML body instead of plain text.
|
|
@@ -83404,7 +83434,7 @@ ${body}`, {
|
|
|
83404
83434
|
"Error retrieving message"
|
|
83405
83435
|
)
|
|
83406
83436
|
);
|
|
83407
|
-
|
|
83437
|
+
registerTool(
|
|
83408
83438
|
"get-thread",
|
|
83409
83439
|
{
|
|
83410
83440
|
description: "Use when: you have one message id and want the whole conversation it belongs to, oldest-first. With an imap: id it threads by References/Message-ID; otherwise it groups by normalized subject.\nReturns: the thread's normalized subject and its messages (id, date, subject, sender, read state).\nDo not use when: you only need the single message (use get-message) or are searching by arbitrary criteria (use search-messages).",
|
|
@@ -83523,7 +83553,7 @@ ${list}${coverageBlock}`,
|
|
|
83523
83553
|
);
|
|
83524
83554
|
}, "Error retrieving thread")
|
|
83525
83555
|
);
|
|
83526
|
-
|
|
83556
|
+
registerTool(
|
|
83527
83557
|
"list-messages",
|
|
83528
83558
|
{
|
|
83529
83559
|
description: "Use when: browsing a mailbox's recent messages (optionally filtered by sender or unread-only) with pagination via limit/offset, and you need their ids.\nReturns: messages with id, date, subject, and sender (plus partial-coverage diagnostics when some mailboxes were skipped).\nDo not use when: you have specific search criteria like subject/date/flags (use search-messages) or already have an id and want the body (use get-message).\nLike search-messages, use this to obtain the ids that read/mark/delete/move and batch tools require.",
|
|
@@ -83588,7 +83618,7 @@ ${messageList}${coverageBlock}`,
|
|
|
83588
83618
|
);
|
|
83589
83619
|
}, "Error listing messages")
|
|
83590
83620
|
);
|
|
83591
|
-
|
|
83621
|
+
registerTool(
|
|
83592
83622
|
"send-email",
|
|
83593
83623
|
{
|
|
83594
83624
|
description: "Use when: the user has explicitly confirmed they want to send a single email now to the given recipients (to/cc/bcc are arrays), optionally with attachments and a chosen transport.\nReturns: a confirmation naming the recipients and attachment count.\nDo not use when: the user wants to review first (use create-draft), is replying to or forwarding an existing message (use reply-to-message / forward-message), or wants per-recipient personalized copies (use send-serial-email).\nSafety: this SENDS real email immediately and it cannot be unsent \u2014 require explicit user confirmation of the exact recipients, subject, and body before calling. Prefer create-draft when there is any doubt.",
|
|
@@ -83639,7 +83669,7 @@ server.registerTool(
|
|
|
83639
83669
|
});
|
|
83640
83670
|
}, "Error sending email")
|
|
83641
83671
|
);
|
|
83642
|
-
|
|
83672
|
+
registerTool(
|
|
83643
83673
|
"send-serial-email",
|
|
83644
83674
|
{
|
|
83645
83675
|
description: "Use when: the user has confirmed a mail-merge \u2014 sending individually personalized copies to many recipients (max 100), with {{Key}} placeholders in subject/body replaced per-recipient from each recipient's variables. Recipients do not see each other.\nReturns: a per-recipient sent/failed report with counts.\nDo not use when: sending one message to a shared recipient list (use send-email) or saving for review (use create-draft).\nSafety: this SENDS many real emails immediately and they cannot be unsent \u2014 require explicit user confirmation of the recipient list, the subject/body template, and the placeholder substitutions before calling.",
|
|
@@ -83695,7 +83725,7 @@ ${details}`,
|
|
|
83695
83725
|
}
|
|
83696
83726
|
}, "Error sending serial emails")
|
|
83697
83727
|
);
|
|
83698
|
-
|
|
83728
|
+
registerTool(
|
|
83699
83729
|
"create-draft",
|
|
83700
83730
|
{
|
|
83701
83731
|
description: "Use when: composing an email the user should review in Mail.app before sending \u2014 the safe default for any new message (to/cc/bcc are arrays, optional attachments).\nReturns: a confirmation that the draft was created, with recipients and attachment count.\nDo not use when: the user has already confirmed they want it sent now (use send-email).\nSafety: low risk \u2014 creates a draft only and sends nothing; the user must open Mail.app and send it themselves.",
|
|
@@ -83775,7 +83805,7 @@ async function sendForwardViaSmtp(id, to, body) {
|
|
|
83775
83805
|
if (result.success) return { sent: true };
|
|
83776
83806
|
return { sent: false, fallback: false, error: result.error ?? "unknown SMTP error" };
|
|
83777
83807
|
}
|
|
83778
|
-
|
|
83808
|
+
registerTool(
|
|
83779
83809
|
"reply-to-message",
|
|
83780
83810
|
{
|
|
83781
83811
|
description: "Use when: replying to an existing message by id, preserving its threading headers. Set replyAll for all recipients; set send=false to save as a draft instead of sending.\nReturns: a confirmation that the reply was sent or saved as a draft.\nDo not use when: composing a brand-new message (use send-email / create-draft) or forwarding to new recipients (use forward-message).\nSafety: with the default send=true this SENDS real email immediately and cannot be unsent \u2014 require explicit user confirmation of the recipients and body, or pass send=false to let the user review.",
|
|
@@ -83812,7 +83842,7 @@ server.registerTool(
|
|
|
83812
83842
|
});
|
|
83813
83843
|
}, "Error replying to message")
|
|
83814
83844
|
);
|
|
83815
|
-
|
|
83845
|
+
registerTool(
|
|
83816
83846
|
"forward-message",
|
|
83817
83847
|
{
|
|
83818
83848
|
description: "Use when: forwarding an existing message (by id) to new recipients (to is an array), with an optional body to prepend. Set send=false to save as a draft.\nReturns: a confirmation that the message was forwarded or saved as a draft.\nDo not use when: replying to the sender/recipients (use reply-to-message) or composing a new message (use send-email / create-draft).\nSafety: with the default send=true this SENDS real email immediately and cannot be unsent \u2014 require explicit user confirmation of the recipients and any prepended body, or pass send=false to let the user review.",
|
|
@@ -83854,7 +83884,7 @@ server.registerTool(
|
|
|
83854
83884
|
);
|
|
83855
83885
|
}, "Error forwarding message")
|
|
83856
83886
|
);
|
|
83857
|
-
|
|
83887
|
+
registerTool(
|
|
83858
83888
|
"mark-as-read",
|
|
83859
83889
|
{
|
|
83860
83890
|
description: "Use when: marking a single message (by id) as read.\nReturns: a confirmation that the message was marked read.\nDo not use when: marking several at once (use batch-mark-as-read) or marking unread (use mark-as-unread). Get the id from search-messages or list-messages first.",
|
|
@@ -83874,7 +83904,7 @@ server.registerTool(
|
|
|
83874
83904
|
"Error marking message as read"
|
|
83875
83905
|
)
|
|
83876
83906
|
);
|
|
83877
|
-
|
|
83907
|
+
registerTool(
|
|
83878
83908
|
"mark-as-unread",
|
|
83879
83909
|
{
|
|
83880
83910
|
description: "Use when: marking a single message (by id) as unread.\nReturns: a confirmation that the message was marked unread.\nDo not use when: marking several at once (use batch-mark-as-unread) or marking read (use mark-as-read). Get the id from search-messages or list-messages first.",
|
|
@@ -83894,7 +83924,7 @@ server.registerTool(
|
|
|
83894
83924
|
"Error marking message as unread"
|
|
83895
83925
|
)
|
|
83896
83926
|
);
|
|
83897
|
-
|
|
83927
|
+
registerTool(
|
|
83898
83928
|
"flag-message",
|
|
83899
83929
|
{
|
|
83900
83930
|
description: "Use when: flagging a single message (by id), optionally with a color (red/orange/yellow/green/blue/purple/gray).\nReturns: a confirmation that the message was flagged (and the color, when applied).\nDo not use when: flagging several at once (use batch-flag-messages) or removing a flag (use unflag-message). Get the id from search-messages or list-messages first.\nNote: the color is applied on both routes \u2014 AppleScript sets the flag index, IMAP writes the equivalent $MailFlagBit0/1/2 keywords Mail.app reads.",
|
|
@@ -83926,7 +83956,7 @@ server.registerTool(
|
|
|
83926
83956
|
});
|
|
83927
83957
|
}, "Error flagging message")
|
|
83928
83958
|
);
|
|
83929
|
-
|
|
83959
|
+
registerTool(
|
|
83930
83960
|
"unflag-message",
|
|
83931
83961
|
{
|
|
83932
83962
|
description: "Use when: removing the flag from a single message (by id).\nReturns: a confirmation that the message was unflagged.\nDo not use when: unflagging several at once (use batch-unflag-messages) or adding a flag (use flag-message). Get the id from search-messages or list-messages first.",
|
|
@@ -83946,7 +83976,7 @@ server.registerTool(
|
|
|
83946
83976
|
"Error unflagging message"
|
|
83947
83977
|
)
|
|
83948
83978
|
);
|
|
83949
|
-
|
|
83979
|
+
registerTool(
|
|
83950
83980
|
"delete-message",
|
|
83951
83981
|
{
|
|
83952
83982
|
description: "Use when: deleting a single message by id (moves it to Trash).\nReturns: a confirmation that the message was deleted.\nDo not use when: deleting several at once (use batch-delete-messages) or just filing it away (use move-message).\nSafety: destructive \u2014 require explicit user confirmation, and search-messages/list-messages first to confirm you have the right id before deleting.",
|
|
@@ -83969,7 +83999,7 @@ server.registerTool(
|
|
|
83969
83999
|
"Error deleting message"
|
|
83970
84000
|
)
|
|
83971
84001
|
);
|
|
83972
|
-
|
|
84002
|
+
registerTool(
|
|
83973
84003
|
"move-message",
|
|
83974
84004
|
{
|
|
83975
84005
|
description: "Use when: moving a single message (by id) into another mailbox/folder, e.g. archiving or filing.\nReturns: a confirmation naming the destination mailbox.\nDo not use when: moving several at once (use batch-move-messages) or deleting (use delete-message). Use list-mailboxes to confirm the destination name exists.\nSafety: moves a real message between folders \u2014 confirm the destination mailbox, and search-messages/list-messages first to confirm the id.",
|
|
@@ -83998,7 +84028,7 @@ server.registerTool(
|
|
|
83998
84028
|
"Error moving message"
|
|
83999
84029
|
)
|
|
84000
84030
|
);
|
|
84001
|
-
|
|
84031
|
+
registerTool(
|
|
84002
84032
|
"batch-delete-messages",
|
|
84003
84033
|
{
|
|
84004
84034
|
description: "Use when: deleting multiple messages in one call (1\u2013100 ids; moves them to Trash).\nReturns: counts of how many were deleted and how many failed.\nDo not use when: deleting just one (use delete-message) or filing messages away (use batch-move-messages).\nSafety: destructive and applies to many messages at once \u2014 require explicit user confirmation, and search-messages/list-messages first to confirm every id is correct before deleting.",
|
|
@@ -84023,7 +84053,7 @@ server.registerTool(
|
|
|
84023
84053
|
}
|
|
84024
84054
|
}, "Error batch deleting messages")
|
|
84025
84055
|
);
|
|
84026
|
-
|
|
84056
|
+
registerTool(
|
|
84027
84057
|
"batch-move-messages",
|
|
84028
84058
|
{
|
|
84029
84059
|
description: "Use when: moving multiple messages (1\u2013100 ids) into the same destination mailbox/folder in one call, e.g. bulk archiving.\nReturns: counts of how many were moved and how many failed.\nDo not use when: moving just one (use move-message) or deleting (use batch-delete-messages). Use list-mailboxes to confirm the destination name exists.\nSafety: moves many real messages at once \u2014 confirm the destination mailbox, and search-messages/list-messages first to confirm the ids.",
|
|
@@ -84056,7 +84086,7 @@ server.registerTool(
|
|
|
84056
84086
|
}
|
|
84057
84087
|
}, "Error batch moving messages")
|
|
84058
84088
|
);
|
|
84059
|
-
|
|
84089
|
+
registerTool(
|
|
84060
84090
|
"batch-mark-as-read",
|
|
84061
84091
|
{
|
|
84062
84092
|
description: "Use when: marking multiple messages (1\u2013100 ids) as read in one call.\nReturns: counts of how many were marked read and how many failed.\nDo not use when: marking just one (use mark-as-read) or marking unread (use batch-mark-as-unread). Get the ids from search-messages or list-messages first.",
|
|
@@ -84084,7 +84114,7 @@ server.registerTool(
|
|
|
84084
84114
|
}
|
|
84085
84115
|
}, "Error batch marking messages as read")
|
|
84086
84116
|
);
|
|
84087
|
-
|
|
84117
|
+
registerTool(
|
|
84088
84118
|
"batch-mark-as-unread",
|
|
84089
84119
|
{
|
|
84090
84120
|
description: "Use when: marking multiple messages (1\u2013100 ids) as unread in one call.\nReturns: counts of how many were marked unread and how many failed.\nDo not use when: marking just one (use mark-as-unread) or marking read (use batch-mark-as-read). Get the ids from search-messages or list-messages first.",
|
|
@@ -84115,7 +84145,7 @@ server.registerTool(
|
|
|
84115
84145
|
}
|
|
84116
84146
|
}, "Error batch marking messages as unread")
|
|
84117
84147
|
);
|
|
84118
|
-
|
|
84148
|
+
registerTool(
|
|
84119
84149
|
"batch-flag-messages",
|
|
84120
84150
|
{
|
|
84121
84151
|
description: "Use when: flagging multiple messages (1\u2013100 ids) in one call, optionally with a color (red/orange/yellow/green/blue/purple/gray).\nReturns: counts of how many were flagged and how many failed.\nDo not use when: flagging just one (use flag-message) or removing flags (use batch-unflag-messages). Get the ids from search-messages or list-messages first.\nNote: the color is applied on both routes \u2014 AppleScript sets the flag index, IMAP writes the equivalent $MailFlagBit0/1/2 keywords Mail.app reads \u2014 so a mixed batch of numeric and `imap:` ids all end up colored.",
|
|
@@ -84142,7 +84172,7 @@ server.registerTool(
|
|
|
84142
84172
|
}
|
|
84143
84173
|
}, "Error batch flagging messages")
|
|
84144
84174
|
);
|
|
84145
|
-
|
|
84175
|
+
registerTool(
|
|
84146
84176
|
"batch-unflag-messages",
|
|
84147
84177
|
{
|
|
84148
84178
|
description: "Use when: removing flags from multiple messages (1\u2013100 ids) in one call.\nReturns: counts of how many were unflagged and how many failed.\nDo not use when: unflagging just one (use unflag-message) or adding flags (use batch-flag-messages). Get the ids from search-messages or list-messages first.",
|
|
@@ -84170,7 +84200,7 @@ server.registerTool(
|
|
|
84170
84200
|
}
|
|
84171
84201
|
}, "Error batch unflagging messages")
|
|
84172
84202
|
);
|
|
84173
|
-
|
|
84203
|
+
registerTool(
|
|
84174
84204
|
"resolve-message-id",
|
|
84175
84205
|
{
|
|
84176
84206
|
description: "Use when: you have `imap:` message id(s) and genuinely need the numeric Mail.app id(s) \u2014 e.g. for reply-to-message/forward-message, which are numeric-id only. NOTE: as of 2.10.0 you no longer need this to apply a flag COLOR \u2014 flag-message/batch-flag-messages write the color over IMAP directly via Mail.app's $MailFlagBit0/1/2 keywords, so a smart mailbox keyed on flag color matches an IMAP-flagged message. Each imap: id is resolved via its RFC822 Message-ID.\nReturns: for each input id, its `numericId` (the AppleScript id) or null when it can't be resolved, plus the `messageId` used; and a `resolvedCount`.\nDo not use when: your ids are already numeric (they pass straight through), or you don't need a color \u2014 flag/move/mark tools operate on `imap:` ids directly.",
|
|
@@ -84208,7 +84238,7 @@ server.registerTool(
|
|
|
84208
84238
|
);
|
|
84209
84239
|
}, "Error resolving message ids")
|
|
84210
84240
|
);
|
|
84211
|
-
|
|
84241
|
+
registerTool(
|
|
84212
84242
|
"list-attachments",
|
|
84213
84243
|
{
|
|
84214
84244
|
description: "Use when: enumerating a message's attachments (by id) to discover their names, MIME types, and sizes \u2014 typically before saving or fetching one.\nReturns: each attachment's name, MIME type, and size, plus a count.\nDo not use when: you want the bytes (use fetch-attachment for inline base64, or save-attachment to write to disk). Get the message id from search-messages or list-messages first.",
|
|
@@ -84241,7 +84271,7 @@ ${attachmentList}`,
|
|
|
84241
84271
|
);
|
|
84242
84272
|
}, "Error listing attachments")
|
|
84243
84273
|
);
|
|
84244
|
-
|
|
84274
|
+
registerTool(
|
|
84245
84275
|
"save-attachment",
|
|
84246
84276
|
{
|
|
84247
84277
|
description: "Use when: writing one of a message's attachments to disk, by message id and attachmentName, into the savePath directory (saved as savePath/attachmentName).\nReturns: a confirmation of the saved file path.\nDo not use when: you don't know the attachment name (use list-attachments first) or want the bytes inline rather than on disk (use fetch-attachment).\nSafety: writes a file to disk \u2014 savePath must be a directory inside the configured allowed roots, and attachmentName may not contain path separators or '..'; calls outside those constraints are rejected.",
|
|
@@ -84289,7 +84319,7 @@ server.registerTool(
|
|
|
84289
84319
|
});
|
|
84290
84320
|
}, "Error saving attachment")
|
|
84291
84321
|
);
|
|
84292
|
-
|
|
84322
|
+
registerTool(
|
|
84293
84323
|
"fetch-attachment",
|
|
84294
84324
|
{
|
|
84295
84325
|
description: "Use when: retrieving an attachment's raw bytes inline as base64 (by message id and attachmentName), e.g. to process its contents without touching disk.\nReturns: the attachment's bytes base64-encoded, with its size and (for IMAP) MIME type.\nDo not use when: you don't know the attachment name (use list-attachments first) or you just want it saved to disk (use save-attachment).",
|
|
@@ -84329,7 +84359,7 @@ ${r.base64}`,
|
|
|
84329
84359
|
);
|
|
84330
84360
|
}, "Error fetching attachment")
|
|
84331
84361
|
);
|
|
84332
|
-
|
|
84362
|
+
registerTool(
|
|
84333
84363
|
"list-mailboxes",
|
|
84334
84364
|
{
|
|
84335
84365
|
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.\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).",
|
|
@@ -84402,7 +84432,7 @@ ${list}`, structured2);
|
|
|
84402
84432
|
${mailboxList}`, structured);
|
|
84403
84433
|
}, "Error listing mailboxes")
|
|
84404
84434
|
);
|
|
84405
|
-
|
|
84435
|
+
registerTool(
|
|
84406
84436
|
"get-unread-count",
|
|
84407
84437
|
{
|
|
84408
84438
|
description: "Use when: you only need the number of unread messages \u2014 INBOX by default, or scoped to one mailbox and/or account \u2014 without listing the messages themselves.\nReturns: the unread count for the requested scope (INBOX when no mailbox is given). If a source cannot be read the result carries `partial: true` + `failedAccounts`, and a total AppleScript failure returns an ERROR \u2014 a plain count is never a disguised transport failure.\nDo not use when: you need the actual unread messages and their ids (use list-messages with unreadOnly, or search-messages with isRead=false) or broader totals across every mailbox (use get-mail-stats).",
|
|
@@ -84466,7 +84496,7 @@ server.registerTool(
|
|
|
84466
84496
|
});
|
|
84467
84497
|
}, "Error getting unread count")
|
|
84468
84498
|
);
|
|
84469
|
-
|
|
84499
|
+
registerTool(
|
|
84470
84500
|
"create-mailbox",
|
|
84471
84501
|
{
|
|
84472
84502
|
description: "Use when: creating a new mailbox/folder in an account.\nReturns: a confirmation that the mailbox was created.\nDo not use when: renaming an existing one (use rename-mailbox) or deleting one (use delete-mailbox). Use list-mailboxes to see what already exists.\nSafety: creates a real folder in the mail account \u2014 confirm the name and target account first.",
|
|
@@ -84492,7 +84522,7 @@ server.registerTool(
|
|
|
84492
84522
|
return successResponse(`Mailbox "${name}" created`, { ok: true, name });
|
|
84493
84523
|
}, "Error creating mailbox")
|
|
84494
84524
|
);
|
|
84495
|
-
|
|
84525
|
+
registerTool(
|
|
84496
84526
|
"delete-mailbox",
|
|
84497
84527
|
{
|
|
84498
84528
|
description: "Use when: deleting a mailbox/folder from an account.\nReturns: a confirmation that the mailbox was deleted.\nDo not use when: renaming it (use rename-mailbox) or deleting messages within it (use delete-message / batch-delete-messages).\nSafety: destructive \u2014 deleting a mailbox removes the folder and any messages it contains. Require explicit user confirmation and use list-mailboxes first to confirm the exact name.",
|
|
@@ -84518,7 +84548,7 @@ server.registerTool(
|
|
|
84518
84548
|
return successResponse(`Mailbox "${name}" deleted`, { ok: true, name });
|
|
84519
84549
|
}, "Error deleting mailbox")
|
|
84520
84550
|
);
|
|
84521
|
-
|
|
84551
|
+
registerTool(
|
|
84522
84552
|
"rename-mailbox",
|
|
84523
84553
|
{
|
|
84524
84554
|
description: "Use when: renaming an existing mailbox/folder from oldName to newName within an account.\nReturns: a confirmation naming the old and new mailbox names.\nDo not use when: creating a new folder (use create-mailbox) or deleting one (use delete-mailbox). Use list-mailboxes to confirm the current name.\nSafety: renames a real folder in the mail account \u2014 confirm oldName matches exactly (case-sensitive) before calling.",
|
|
@@ -84556,7 +84586,7 @@ server.registerTool(
|
|
|
84556
84586
|
});
|
|
84557
84587
|
}, "Error renaming mailbox")
|
|
84558
84588
|
);
|
|
84559
|
-
|
|
84589
|
+
registerTool(
|
|
84560
84590
|
"list-smart-mailboxes",
|
|
84561
84591
|
{
|
|
84562
84592
|
description: "Use when: listing Apple Mail smart mailboxes (criteria-based virtual views), including on German-localized macOS where AppleScript's smart-mailbox terms do not compile.\nReturns: each smart mailbox's name and a short criteria summary.\nDo not use when: listing real folders/mailboxes (use list-mailboxes).",
|
|
@@ -84589,7 +84619,7 @@ ${lines}`, {
|
|
|
84589
84619
|
});
|
|
84590
84620
|
}, "Error listing smart mailboxes")
|
|
84591
84621
|
);
|
|
84592
|
-
|
|
84622
|
+
registerTool(
|
|
84593
84623
|
"create-smart-mailbox",
|
|
84594
84624
|
{
|
|
84595
84625
|
description: "Use when: creating an Apple Mail smart mailbox (a criteria-based virtual view) that matches a sender, subject, or body substring \u2014 works on German-localized macOS where AppleScript's smart-mailbox terms fail.\nReturns: confirmation of creation, or a note that a smart mailbox with that name already existed.\nDo not use when: creating a real folder (use create-mailbox).\nSafety: edits Apple Mail's SyncedSmartMailboxes.plist directly. It backs the file up (.bak) and writes atomically, and never rewrites your existing smart mailboxes. It does not quit Mail \u2014 quit Mail first for reliable results, since a running Mail may not show the new smart mailbox until relaunched and can overwrite plist edits it did not make.",
|
|
@@ -84632,7 +84662,7 @@ server.registerTool(
|
|
|
84632
84662
|
});
|
|
84633
84663
|
}, "Error creating smart mailbox")
|
|
84634
84664
|
);
|
|
84635
|
-
|
|
84665
|
+
registerTool(
|
|
84636
84666
|
"delete-smart-mailbox",
|
|
84637
84667
|
{
|
|
84638
84668
|
description: "Use when: deleting an Apple Mail smart mailbox (virtual view) by name.\nReturns: confirmation of deletion.\nDo not use when: deleting a real folder (use delete-mailbox) or messages (use delete-message / batch-delete-messages).\nSafety: destructive \u2014 removes the smart mailbox from Apple Mail's SyncedSmartMailboxes.plist. It backs the file up (.bak) and writes atomically, preserving every other smart mailbox, but the removal is not undoable in-app. Confirm the exact name with list-smart-mailboxes first, and quit Mail first for reliable results.",
|
|
@@ -84655,7 +84685,7 @@ server.registerTool(
|
|
|
84655
84685
|
});
|
|
84656
84686
|
}, "Error deleting smart mailbox")
|
|
84657
84687
|
);
|
|
84658
|
-
|
|
84688
|
+
registerTool(
|
|
84659
84689
|
"create-newsletter-smart-mailboxes",
|
|
84660
84690
|
{
|
|
84661
84691
|
description: `Use when: auto-discovering newsletter/bulk senders in your INBOX(es) and (optionally) creating a dedicated smart mailbox per sender (named "NL: <sender>"). Defaults to a safe dry run that only proposes.
|
|
@@ -84685,7 +84715,7 @@ ${lines || " (none met the threshold)"}`,
|
|
|
84685
84715
|
);
|
|
84686
84716
|
}, "Error creating newsletter smart mailboxes")
|
|
84687
84717
|
);
|
|
84688
|
-
|
|
84718
|
+
registerTool(
|
|
84689
84719
|
"list-accounts",
|
|
84690
84720
|
{
|
|
84691
84721
|
description: "Use when: discovering the configured Mail accounts (e.g. iCloud, Gmail) so you can pass an exact account name to other tools.\nReturns: the account names and a count. If the AppleScript transport fails (timeout / wedged Mail / missing Automation grant) this returns an ERROR rather than an empty list \u2014 an empty list always means Mail really has no accounts.\nDo not use when: you want the folders within an account (use list-mailboxes) or messages (use list-messages / search-messages).",
|
|
@@ -84713,7 +84743,7 @@ server.registerTool(
|
|
|
84713
84743
|
${accountList}`, structured);
|
|
84714
84744
|
}, "Error listing accounts")
|
|
84715
84745
|
);
|
|
84716
|
-
|
|
84746
|
+
registerTool(
|
|
84717
84747
|
"list-rules",
|
|
84718
84748
|
{
|
|
84719
84749
|
description: "Use when: discovering the Mail rules that exist and whether each is enabled or disabled, e.g. before enabling/disabling/deleting one.\nReturns: each rule's name and enabled/disabled state.\nDo not use when: you want to change a rule (use enable-rule / disable-rule / create-rule / delete-rule).",
|
|
@@ -84737,7 +84767,7 @@ server.registerTool(
|
|
|
84737
84767
|
${ruleList}`, structured);
|
|
84738
84768
|
}, "Error listing rules")
|
|
84739
84769
|
);
|
|
84740
|
-
|
|
84770
|
+
registerTool(
|
|
84741
84771
|
"enable-rule",
|
|
84742
84772
|
{
|
|
84743
84773
|
description: "Use when: turning on an existing Mail rule by name.\nReturns: a confirmation that the rule was enabled.\nDo not use when: turning a rule off (use disable-rule), creating one (use create-rule), or deleting one (use delete-rule). Use list-rules to confirm the exact rule name.",
|
|
@@ -84758,7 +84788,7 @@ server.registerTool(
|
|
|
84758
84788
|
return successResponse(`Rule "${name}" enabled`, { ok: true, name, enabled: true });
|
|
84759
84789
|
}, "Error enabling rule")
|
|
84760
84790
|
);
|
|
84761
|
-
|
|
84791
|
+
registerTool(
|
|
84762
84792
|
"disable-rule",
|
|
84763
84793
|
{
|
|
84764
84794
|
description: "Use when: turning off an existing Mail rule by name (without deleting it).\nReturns: a confirmation that the rule was disabled.\nDo not use when: turning a rule on (use enable-rule), creating one (use create-rule), or removing it permanently (use delete-rule). Use list-rules to confirm the exact rule name.",
|
|
@@ -84779,7 +84809,7 @@ server.registerTool(
|
|
|
84779
84809
|
return successResponse(`Rule "${name}" disabled`, { ok: true, name, enabled: false });
|
|
84780
84810
|
}, "Error disabling rule")
|
|
84781
84811
|
);
|
|
84782
|
-
|
|
84812
|
+
registerTool(
|
|
84783
84813
|
"create-rule",
|
|
84784
84814
|
{
|
|
84785
84815
|
description: "Use when: creating a new Mail rule with one or more conditions (field/operator/value) and at least one action (markRead, markFlagged, delete, or moveTo). Set matchAll to require all conditions vs. any.\nReturns: a confirmation naming the rule and its condition count.\nDo not use when: toggling an existing rule (use enable-rule / disable-rule) or removing one (use delete-rule). Use list-rules to avoid duplicating an existing rule.\nSafety: creates a rule that automatically acts on real mail (including delete/move actions) on an ongoing basis \u2014 confirm the conditions and actions with the user before calling.",
|
|
@@ -84821,7 +84851,7 @@ server.registerTool(
|
|
|
84821
84851
|
);
|
|
84822
84852
|
}, "Error creating rule")
|
|
84823
84853
|
);
|
|
84824
|
-
|
|
84854
|
+
registerTool(
|
|
84825
84855
|
"delete-rule",
|
|
84826
84856
|
{
|
|
84827
84857
|
description: "Use when: permanently removing a Mail rule by name.\nReturns: a confirmation that the rule was deleted.\nDo not use when: you only want to pause it (use disable-rule) or create one (use create-rule).\nSafety: destructive \u2014 the rule is removed permanently. Require explicit user confirmation and use list-rules first to confirm the exact name.",
|
|
@@ -84841,7 +84871,7 @@ server.registerTool(
|
|
|
84841
84871
|
return successResponse(`Rule "${name}" deleted`, { name, deleted: true });
|
|
84842
84872
|
}, "Error deleting rule")
|
|
84843
84873
|
);
|
|
84844
|
-
|
|
84874
|
+
registerTool(
|
|
84845
84875
|
"search-contacts",
|
|
84846
84876
|
{
|
|
84847
84877
|
description: "Use when: looking up a person in Contacts by name, organization, nickname, or email to find their email address(es)/phone(s) before composing or sending mail. Reads the macOS Contacts database directly (needs Full Disk Access; does NOT require Contacts.app to be running or an Automation / Apple-Events grant).\nReturns: matching contacts with their names, email addresses, and phone numbers.\nDo not use when: searching email messages (use search-messages) \u2014 this queries Contacts, not the mailbox.",
|
|
@@ -84871,7 +84901,7 @@ server.registerTool(
|
|
|
84871
84901
|
${contactList}`, structured);
|
|
84872
84902
|
}, "Error searching contacts")
|
|
84873
84903
|
);
|
|
84874
|
-
|
|
84904
|
+
registerTool(
|
|
84875
84905
|
"save-template",
|
|
84876
84906
|
{
|
|
84877
84907
|
description: "Use when: creating a reusable email template (name, subject, body, optional default to/cc), or updating one by passing its existing id. Subject/body may contain placeholders for later use.\nReturns: the saved template's name and id (reuse the id with use-template / get-template / delete-template).\nDo not use when: composing a one-off message (use create-draft / send-email) or filling in a template to send (use use-template).\nSafety: writes the template to the on-disk templates store (APPLE_MAIL_MCP_TEMPLATES_FILE) and persists across restarts; passing an existing id overwrites that template.",
|
|
@@ -84898,7 +84928,7 @@ server.registerTool(
|
|
|
84898
84928
|
});
|
|
84899
84929
|
}, "Error saving template")
|
|
84900
84930
|
);
|
|
84901
|
-
|
|
84931
|
+
registerTool(
|
|
84902
84932
|
"list-templates",
|
|
84903
84933
|
{
|
|
84904
84934
|
description: "Use when: discovering the saved email templates and their ids, e.g. before using or editing one.\nReturns: each template's id, name, and subject.\nDo not use when: you want a single template's full body (use get-template) or want to apply one (use use-template).",
|
|
@@ -84922,7 +84952,7 @@ server.registerTool(
|
|
|
84922
84952
|
${templateList}`, structured);
|
|
84923
84953
|
}, "Error listing templates")
|
|
84924
84954
|
);
|
|
84925
|
-
|
|
84955
|
+
registerTool(
|
|
84926
84956
|
"get-template",
|
|
84927
84957
|
{
|
|
84928
84958
|
description: "Use when: reading the full contents of one saved template by id \u2014 its name, subject, default to/cc, and body.\nReturns: the template's name, subject, default recipients, and body text.\nDo not use when: you don't have the id (use list-templates first) or want to apply the template into a draft (use use-template).",
|
|
@@ -84961,7 +84991,7 @@ ${template.body}`
|
|
|
84961
84991
|
});
|
|
84962
84992
|
}, "Error getting template")
|
|
84963
84993
|
);
|
|
84964
|
-
|
|
84994
|
+
registerTool(
|
|
84965
84995
|
"delete-template",
|
|
84966
84996
|
{
|
|
84967
84997
|
description: "Use when: permanently removing a saved email template by id.\nReturns: a confirmation that the template was deleted.\nDo not use when: you only want to view it (use get-template) or update it (use save-template with the existing id).\nSafety: destructive \u2014 removes the template from the on-disk store permanently. Require explicit user confirmation and use list-templates first to confirm the id.",
|
|
@@ -84981,7 +85011,7 @@ server.registerTool(
|
|
|
84981
85011
|
return successResponse(`Template "${id}" deleted`, { ok: true, id });
|
|
84982
85012
|
}, "Error deleting template")
|
|
84983
85013
|
);
|
|
84984
|
-
|
|
85014
|
+
registerTool(
|
|
84985
85015
|
"use-template",
|
|
84986
85016
|
{
|
|
84987
85017
|
description: "Use when: composing a new draft from a saved template (by id), optionally overriding the recipients, subject, or body. Creates a draft in Mail.app for the user to review and send.\nReturns: a confirmation that a draft was created from the template.\nDo not use when: you want to inspect the template without composing (use get-template) or send immediately without a draft (use send-email).",
|
|
@@ -85005,7 +85035,7 @@ server.registerTool(
|
|
|
85005
85035
|
return successResponse(`Draft created from template "${id}"`, { ok: true, id });
|
|
85006
85036
|
}, "Error using template")
|
|
85007
85037
|
);
|
|
85008
|
-
|
|
85038
|
+
registerTool(
|
|
85009
85039
|
"health-check",
|
|
85010
85040
|
{
|
|
85011
85041
|
description: "Use when: doing a quick check that Mail.app is reachable and the server's basic checks pass.\nReturns: an overall healthy/unhealthy status with a pass/fail line per check.\nDo not use when: you need detailed permission/account/IMAP/SMTP diagnostics with remediation steps (use doctor).",
|
|
@@ -85028,7 +85058,7 @@ server.registerTool(
|
|
|
85028
85058
|
${checkLines}`, { ...result });
|
|
85029
85059
|
}, "Error running health check")
|
|
85030
85060
|
);
|
|
85031
|
-
|
|
85061
|
+
registerTool(
|
|
85032
85062
|
"doctor",
|
|
85033
85063
|
{
|
|
85034
85064
|
description: "Use when: troubleshooting setup problems \u2014 diagnoses Mail.app automation permissions, account state, and the IMAP/SMTP backends with actionable remediation messages.\nReturns: a detailed diagnostic report (formatted text plus structured checks).\nDo not use when: you just want a quick up/down status (use health-check) or message counts (use get-mail-stats).",
|
|
@@ -85043,7 +85073,7 @@ server.registerTool(
|
|
|
85043
85073
|
return successResponse(formatDoctorReport(report), { ...report });
|
|
85044
85074
|
}, "Error running doctor")
|
|
85045
85075
|
);
|
|
85046
|
-
|
|
85076
|
+
registerTool(
|
|
85047
85077
|
"get-mail-stats",
|
|
85048
85078
|
{
|
|
85049
85079
|
description: "Use when: you want aggregate mailbox statistics \u2014 total and unread message counts, recently-received counts (last 24h/7d/30d), and (for the all-accounts path) a per-account breakdown.\nReturns: totals, unread counts, recent-activity counts, and per-account figures.\nDo not use when: you only need a single unread number (use get-unread-count) or want to list the messages themselves (use list-messages / search-messages).",
|
|
@@ -85056,12 +85086,42 @@ server.registerTool(
|
|
|
85056
85086
|
totalUnread: external_exports.number().optional(),
|
|
85057
85087
|
accounts: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
85058
85088
|
recentlyReceived: external_exports.object({}).passthrough().optional(),
|
|
85059
|
-
recent: external_exports.object({}).passthrough().optional()
|
|
85089
|
+
recent: external_exports.object({}).passthrough().optional(),
|
|
85090
|
+
// The scoped IMAP path spreads an ImapStats, which carries per-mailbox
|
|
85091
|
+
// STATUS rows. Declared so the shape is documented rather than merely
|
|
85092
|
+
// tolerated by the permissive advertisement (#135).
|
|
85093
|
+
perMailbox: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
85094
|
+
partial: external_exports.boolean().optional(),
|
|
85095
|
+
failedAccounts: external_exports.array(external_exports.string()).optional()
|
|
85060
85096
|
}
|
|
85061
85097
|
},
|
|
85062
85098
|
withErrorHandling(async ({ account }) => {
|
|
85099
|
+
const budgetMs = Math.max(1e3, Number(process.env.APPLE_MAIL_MCP_STATS_BUDGET_MS ?? 25e3));
|
|
85100
|
+
const withBudget = async (work, label) => {
|
|
85101
|
+
let timer;
|
|
85102
|
+
try {
|
|
85103
|
+
return await Promise.race([
|
|
85104
|
+
work,
|
|
85105
|
+
new Promise((_, reject) => {
|
|
85106
|
+
timer = setTimeout(
|
|
85107
|
+
() => reject(new Error(`${label} timed out after ${budgetMs}ms`)),
|
|
85108
|
+
budgetMs
|
|
85109
|
+
);
|
|
85110
|
+
})
|
|
85111
|
+
]);
|
|
85112
|
+
} finally {
|
|
85113
|
+
if (timer) clearTimeout(timer);
|
|
85114
|
+
}
|
|
85115
|
+
};
|
|
85063
85116
|
if (account !== void 0 && isImapAccount(account)) {
|
|
85064
|
-
|
|
85117
|
+
let s;
|
|
85118
|
+
try {
|
|
85119
|
+
s = await withBudget(imapMailStats({ account }), `IMAP mail-stats for "${account}"`);
|
|
85120
|
+
} catch (e) {
|
|
85121
|
+
return errorResponse(
|
|
85122
|
+
`Could not read mail statistics for "${account}": ${String(e)}. Gathering stats costs one IMAP STATUS per mailbox, so a very large account can exceed the ${budgetMs}ms budget \u2014 raise APPLE_MAIL_MCP_STATS_BUDGET_MS, or run the "doctor" tool if the connection itself is the problem.`
|
|
85123
|
+
);
|
|
85124
|
+
}
|
|
85065
85125
|
const lines2 = [
|
|
85066
85126
|
`\u{1F4CA} Mail Statistics \u2014 ${account} (IMAP)`,
|
|
85067
85127
|
`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
@@ -85081,40 +85141,55 @@ server.registerTool(
|
|
|
85081
85141
|
const recent = { last24h: 0, last7d: 0, last30d: 0 };
|
|
85082
85142
|
const perAccount = [];
|
|
85083
85143
|
const sources = planCountSources(mailManager.listAccounts(), resolveImapConfigs());
|
|
85084
|
-
|
|
85085
|
-
|
|
85144
|
+
const failedAccounts = [];
|
|
85145
|
+
const settled = await Promise.all(
|
|
85146
|
+
sources.filter((s) => s.kind === "imap").map(async (src) => {
|
|
85086
85147
|
try {
|
|
85087
|
-
const
|
|
85088
|
-
|
|
85089
|
-
|
|
85090
|
-
|
|
85091
|
-
|
|
85092
|
-
recent.last30d += s.recent.last30d;
|
|
85093
|
-
perAccount.push({
|
|
85094
|
-
name: src.label,
|
|
85095
|
-
totalMessages: s.totalMessages,
|
|
85096
|
-
unreadMessages: s.totalUnread,
|
|
85097
|
-
backend: "imap"
|
|
85098
|
-
});
|
|
85148
|
+
const stats2 = await withBudget(
|
|
85149
|
+
imapMailStats({ config: src.config }),
|
|
85150
|
+
`IMAP mail-stats for "${src.label}"`
|
|
85151
|
+
);
|
|
85152
|
+
return { label: src.label, stats: stats2 };
|
|
85099
85153
|
} catch (e) {
|
|
85100
85154
|
console.error(`IMAP mail-stats failed for "${src.label}": ${String(e)}`);
|
|
85155
|
+
return { label: src.label, stats: void 0 };
|
|
85101
85156
|
}
|
|
85102
|
-
}
|
|
85103
|
-
|
|
85104
|
-
|
|
85105
|
-
|
|
85106
|
-
|
|
85107
|
-
|
|
85108
|
-
}
|
|
85109
|
-
totalMessages += m;
|
|
85110
|
-
totalUnread += u;
|
|
85111
|
-
perAccount.push({
|
|
85112
|
-
name: src.label,
|
|
85113
|
-
totalMessages: m,
|
|
85114
|
-
unreadMessages: u,
|
|
85115
|
-
backend: "applescript"
|
|
85116
|
-
});
|
|
85157
|
+
})
|
|
85158
|
+
);
|
|
85159
|
+
for (const r of settled) {
|
|
85160
|
+
if (!r.stats) {
|
|
85161
|
+
failedAccounts.push(r.label);
|
|
85162
|
+
continue;
|
|
85117
85163
|
}
|
|
85164
|
+
const s = r.stats;
|
|
85165
|
+
totalMessages += s.totalMessages;
|
|
85166
|
+
totalUnread += s.totalUnread;
|
|
85167
|
+
recent.last24h += s.recent.last24h;
|
|
85168
|
+
recent.last7d += s.recent.last7d;
|
|
85169
|
+
recent.last30d += s.recent.last30d;
|
|
85170
|
+
perAccount.push({
|
|
85171
|
+
name: r.label,
|
|
85172
|
+
totalMessages: s.totalMessages,
|
|
85173
|
+
unreadMessages: s.totalUnread,
|
|
85174
|
+
backend: "imap"
|
|
85175
|
+
});
|
|
85176
|
+
}
|
|
85177
|
+
for (const src of sources) {
|
|
85178
|
+
if (src.kind === "imap") continue;
|
|
85179
|
+
let m = 0;
|
|
85180
|
+
let u = 0;
|
|
85181
|
+
for (const mb of mailManager.listMailboxes(src.account.name)) {
|
|
85182
|
+
m += mb.messageCount;
|
|
85183
|
+
u += mb.unreadCount;
|
|
85184
|
+
}
|
|
85185
|
+
totalMessages += m;
|
|
85186
|
+
totalUnread += u;
|
|
85187
|
+
perAccount.push({
|
|
85188
|
+
name: src.label,
|
|
85189
|
+
totalMessages: m,
|
|
85190
|
+
unreadMessages: u,
|
|
85191
|
+
backend: "applescript"
|
|
85192
|
+
});
|
|
85118
85193
|
}
|
|
85119
85194
|
const lines2 = [
|
|
85120
85195
|
`\u{1F4CA} Mail Statistics (merged: IMAP + AppleScript)`,
|
|
@@ -85132,11 +85207,18 @@ server.registerTool(
|
|
|
85132
85207
|
(a) => ` ${a.name}: ${a.totalMessages} messages (${a.unreadMessages} unread) [${a.backend}]`
|
|
85133
85208
|
)
|
|
85134
85209
|
];
|
|
85210
|
+
if (failedAccounts.length > 0) {
|
|
85211
|
+
lines2.push(
|
|
85212
|
+
``,
|
|
85213
|
+
`\u26A0\uFE0F PARTIAL: ${failedAccounts.length} account(s) could not be read (${failedAccounts.join(", ")}), so the real totals are higher. They either failed or exceeded the ${budgetMs}ms budget \u2014 raise APPLE_MAIL_MCP_STATS_BUDGET_MS if an account is simply large, or run the "doctor" tool to check the connection.`
|
|
85214
|
+
);
|
|
85215
|
+
}
|
|
85135
85216
|
return successResponse(lines2.join("\n"), {
|
|
85136
85217
|
totalMessages,
|
|
85137
85218
|
totalUnread,
|
|
85138
85219
|
accounts: perAccount,
|
|
85139
|
-
recent
|
|
85220
|
+
recent,
|
|
85221
|
+
...failedAccounts.length > 0 ? { partial: true, failedAccounts } : {}
|
|
85140
85222
|
});
|
|
85141
85223
|
}
|
|
85142
85224
|
const stats = mailManager.getMailStats();
|
|
@@ -85164,7 +85246,7 @@ server.registerTool(
|
|
|
85164
85246
|
return successResponse(lines.join("\n"), { ...stats });
|
|
85165
85247
|
}, "Error getting mail statistics")
|
|
85166
85248
|
);
|
|
85167
|
-
|
|
85249
|
+
registerTool(
|
|
85168
85250
|
"get-sync-status",
|
|
85169
85251
|
{
|
|
85170
85252
|
description: "Use when: checking whether Mail.app is running and actively syncing, e.g. to explain why new mail hasn't appeared yet.\nReturns: whether Mail.app is running and whether sync activity was detected.\nDo not use when: you need message counts (use get-mail-stats) or a full setup diagnosis (use doctor).",
|
package/docs/IMAP-SETUP.md
CHANGED
|
@@ -199,6 +199,20 @@ Each array entry accepts: `account`, `user`, `host`, `port`, `password`
|
|
|
199
199
|
(discouraged — prefer Keychain), `keychainService`, `keychainAccount`. Each
|
|
200
200
|
account keeps its own pooled IMAP connection.
|
|
201
201
|
|
|
202
|
+
**The array alone is enough.** You do not need the legacy single-account vars:
|
|
203
|
+
listing every account in `APPLE_MAIL_MCP_IMAP_ACCOUNTS` and setting none of
|
|
204
|
+
`APPLE_MAIL_MCP_IMAP_USER` / `_ACCOUNT` / `_HOST` is fully supported, and the
|
|
205
|
+
first array entry becomes the default account. (Before 2.10.7 that shape worked
|
|
206
|
+
for every tool but made `doctor` report `connection failed: undefined` for each
|
|
207
|
+
account — see #138.)
|
|
208
|
+
|
|
209
|
+
**Don't declare the same mailbox twice.** If the legacy vars already describe a
|
|
210
|
+
mailbox, do not also give it an array entry — even under a different `account`
|
|
211
|
+
nickname. An account's identity is its resolved `(host, port, user)`, not its
|
|
212
|
+
label, so the duplicate is recognised and collapsed rather than counted twice
|
|
213
|
+
(the extra nickname still works as an alias). Before 2.10.7 it was counted
|
|
214
|
+
twice, inflating `get-unread-count` and `get-mail-stats`.
|
|
215
|
+
|
|
202
216
|
---
|
|
203
217
|
|
|
204
218
|
## Step 4 (optional) — SMTP sending
|
|
@@ -333,16 +347,17 @@ GUI is ignoring.
|
|
|
333
347
|
| Variable | Purpose |
|
|
334
348
|
|----------|---------|
|
|
335
349
|
| `APPLE_MAIL_MCP_DEFAULT_ACCOUNT` | Account used when a tool omits `account` (name or email). |
|
|
336
|
-
| `APPLE_MAIL_MCP_IMAP_USER` | Primary IMAP login
|
|
350
|
+
| `APPLE_MAIL_MCP_IMAP_USER` | Primary IMAP login. Setting it enables IMAP — but so does `APPLE_MAIL_MCP_IMAP_ACCOUNTS` on its own; either is sufficient. |
|
|
337
351
|
| `APPLE_MAIL_MCP_IMAP_ACCOUNT` | Mail.app account name to match for routing (default = USER). |
|
|
338
352
|
| `APPLE_MAIL_MCP_IMAP_HOST` | IMAP host (default `imap.gmail.com`). |
|
|
339
353
|
| `APPLE_MAIL_MCP_IMAP_PORT` | IMAP port (default `993`, implicit TLS). |
|
|
340
354
|
| `APPLE_MAIL_MCP_IMAP_PASSWORD` | Password (discouraged; prefer Keychain). |
|
|
341
355
|
| `APPLE_MAIL_MCP_IMAP_KEYCHAIN_SERVICE` | Keychain item service/server name. |
|
|
342
356
|
| `APPLE_MAIL_MCP_IMAP_KEYCHAIN_ACCOUNT` | Keychain item account (default = USER). |
|
|
343
|
-
| `APPLE_MAIL_MCP_IMAP_ACCOUNTS` | JSON array of
|
|
357
|
+
| `APPLE_MAIL_MCP_IMAP_ACCOUNTS` | JSON array of accounts (multi-account). Sufficient on its own; also enables IMAP. |
|
|
344
358
|
| `APPLE_MAIL_MCP_IMAP_IDLE` | `1` to enable IMAP IDLE new-mail push. |
|
|
345
359
|
| `APPLE_MAIL_MCP_IMAP_IDLE_MS` | Pooled-connection idle timeout in ms (default `30000`; `0` = never close). |
|
|
360
|
+
| `APPLE_MAIL_MCP_STATS_BUDGET_MS` | Per-account wall-clock budget for `get-mail-stats` in ms (default `25000`, minimum `1000`). |
|
|
346
361
|
| `APPLE_MAIL_MCP_SMTP_HOST` | SMTP host; setting it enables `transport:"smtp"`. |
|
|
347
362
|
| `APPLE_MAIL_MCP_SMTP_PORT` | SMTP port (`465` if secure, else `587`). |
|
|
348
363
|
| `APPLE_MAIL_MCP_SMTP_SECURE` | `true` for implicit TLS (465); else STARTTLS. |
|
package/package.json
CHANGED