apple-mail-mcp 2.20.12 → 2.20.14
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 +1 -1
- package/build/index.js +200 -99
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -443,7 +443,7 @@ Send a new email immediately.
|
|
|
443
443
|
| `body` | string | Yes | Email body (plain text) |
|
|
444
444
|
| `cc` | string[] | No | CC recipients |
|
|
445
445
|
| `bcc` | string[] | No | BCC recipients |
|
|
446
|
-
| `account` | string | No | Mail.app account
|
|
446
|
+
| `account` | string | No | Mail.app account name or exact sender address (AppleScript path: resolved to one address of an enabled account and verified against Mail's `sender` before sending — no match, or Mail would use another sender, is an error and nothing is sent; the sender used is returned as `from`), or an email-form SMTP From override. An SMTP override must match `APPLE_MAIL_MCP_SMTP_USER`, `APPLE_MAIL_MCP_SMTP_FROM`, or an address in `APPLE_MAIL_MCP_SMTP_ALLOWED_FROM` |
|
|
447
447
|
| `attachments` | (string \| {filename, contentBase64})[] | No | Up to 20 attachments: absolute file paths inside the configured read roots (e.g., `"/Users/me/Documents/report.pdf"`) and/or inline `{filename, contentBase64}` objects up to 25 MiB decoded each |
|
|
448
448
|
| `transport` | `"applescript"` \| `"smtp"` | No | Send transport. If omitted, **SMTP is used automatically when configured** (otherwise AppleScript). Pass `"smtp"` to require clean MIME, or `"applescript"` to force the Mail.app path — see [SMTP transport](#smtp-transport) |
|
|
449
449
|
| `replyTo` | string | No | SMTP only: `Reply-To` header, when replies should go somewhere other than the From address. Ignored on the AppleScript transport |
|
package/build/index.js
CHANGED
|
@@ -65211,6 +65211,30 @@ function isInlineDisposition(headers) {
|
|
|
65211
65211
|
if (!dispHeader) return false;
|
|
65212
65212
|
return dispHeader.toLowerCase().startsWith("inline");
|
|
65213
65213
|
}
|
|
65214
|
+
function extractContentId(headers) {
|
|
65215
|
+
const raw = getHeader(headers, "Content-ID");
|
|
65216
|
+
if (!raw) return null;
|
|
65217
|
+
const id = raw.replace(/^<+/, "").replace(/>+$/, "").trim().toLowerCase();
|
|
65218
|
+
return id || null;
|
|
65219
|
+
}
|
|
65220
|
+
function isEmbeddedResource(headers, htmlBodies) {
|
|
65221
|
+
if (!isInlineDisposition(headers)) return false;
|
|
65222
|
+
const cid = extractContentId(headers);
|
|
65223
|
+
if (!cid) return false;
|
|
65224
|
+
const needle = `cid:${cid}`;
|
|
65225
|
+
return htmlBodies.some((html) => html.includes(needle));
|
|
65226
|
+
}
|
|
65227
|
+
function collectHtmlBodies(parts) {
|
|
65228
|
+
const bodies = [];
|
|
65229
|
+
for (const part of parts) {
|
|
65230
|
+
if (extractMimeType(part.headers) !== "text/html") continue;
|
|
65231
|
+
const encoding = getHeader(part.headers, "Content-Transfer-Encoding");
|
|
65232
|
+
bodies.push(
|
|
65233
|
+
decodePartText(decodeBody(part.body, encoding), extractCharset(part.headers)).toLowerCase()
|
|
65234
|
+
);
|
|
65235
|
+
}
|
|
65236
|
+
return bodies;
|
|
65237
|
+
}
|
|
65214
65238
|
function extractSize(headers) {
|
|
65215
65239
|
const dispHeader = getHeader(headers, "Content-Disposition");
|
|
65216
65240
|
if (dispHeader) {
|
|
@@ -65316,11 +65340,12 @@ function parseMimeAttachments(source) {
|
|
|
65316
65340
|
const boundary = extractBoundary(source);
|
|
65317
65341
|
if (!boundary) return [];
|
|
65318
65342
|
const parts = walkLeafParts(source, boundary);
|
|
65343
|
+
const htmlBodies = collectHtmlBodies(parts);
|
|
65319
65344
|
const attachments = [];
|
|
65320
65345
|
for (const part of parts) {
|
|
65321
65346
|
const filename = extractFilename(part.headers);
|
|
65322
65347
|
if (!filename) continue;
|
|
65323
|
-
if (
|
|
65348
|
+
if (isEmbeddedResource(part.headers, htmlBodies)) continue;
|
|
65324
65349
|
const encoding = getHeader(part.headers, "Content-Transfer-Encoding");
|
|
65325
65350
|
attachments.push({
|
|
65326
65351
|
name: filename,
|
|
@@ -82916,6 +82941,49 @@ function preflightAttachments(attachments) {
|
|
|
82916
82941
|
}
|
|
82917
82942
|
}
|
|
82918
82943
|
|
|
82944
|
+
// src/utils/senderAccount.ts
|
|
82945
|
+
function extractAddress(sender) {
|
|
82946
|
+
const angle = /<([^<>]*)>\s*$/.exec(sender);
|
|
82947
|
+
return (angle ? angle[1] : sender).trim();
|
|
82948
|
+
}
|
|
82949
|
+
var same = (a, b) => a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
82950
|
+
function describe(accounts) {
|
|
82951
|
+
const usable = accounts.filter((a) => a.enabled && a.addresses.length > 0);
|
|
82952
|
+
if (usable.length === 0) return "No enabled Mail account with an address was found.";
|
|
82953
|
+
return `Available: ${usable.map((a) => `${a.name} <${a.addresses.join(", ")}>`).join("; ")}`;
|
|
82954
|
+
}
|
|
82955
|
+
function pickSenderAddress(input, accounts) {
|
|
82956
|
+
const wanted = extractAddress(input);
|
|
82957
|
+
const enabled = accounts.filter((a) => a.enabled);
|
|
82958
|
+
const address = enabled.flatMap((a) => a.addresses).find((addr) => same(addr, wanted));
|
|
82959
|
+
if (address) return { address };
|
|
82960
|
+
const byName = enabled.filter((a) => same(a.name, input));
|
|
82961
|
+
if (byName.length > 1) {
|
|
82962
|
+
return {
|
|
82963
|
+
error: `Account name "${input}" matches ${byName.length} accounts; pass the exact sender address instead. ${describe(accounts)}`
|
|
82964
|
+
};
|
|
82965
|
+
}
|
|
82966
|
+
if (byName.length === 1) {
|
|
82967
|
+
const { addresses } = byName[0];
|
|
82968
|
+
if (addresses.length === 1) return { address: addresses[0] };
|
|
82969
|
+
return {
|
|
82970
|
+
error: `Account "${input}" has ${addresses.length === 0 ? "no address" : `${addresses.length} addresses`}; pass the exact sender address instead. ${describe(accounts)}`
|
|
82971
|
+
};
|
|
82972
|
+
}
|
|
82973
|
+
return {
|
|
82974
|
+
error: `No enabled Mail account or address matches "${input}", so nothing was sent or saved. ${describe(accounts)}`
|
|
82975
|
+
};
|
|
82976
|
+
}
|
|
82977
|
+
function senderGuardScript(safeAddress) {
|
|
82978
|
+
const check2 = safeAddress ? `
|
|
82979
|
+
if not (actualSender is "${safeAddress}" or actualSender ends with "<${safeAddress}>") then
|
|
82980
|
+
delete newMessage
|
|
82981
|
+
return "error:Mail.app would send as " & actualSender & " instead of ${safeAddress}, so nothing was sent or saved."
|
|
82982
|
+
end if` : "";
|
|
82983
|
+
return `
|
|
82984
|
+
set actualSender to sender of newMessage as text${check2}`;
|
|
82985
|
+
}
|
|
82986
|
+
|
|
82919
82987
|
// src/services/appleMailManager.ts
|
|
82920
82988
|
init_attachmentReadPolicy();
|
|
82921
82989
|
|
|
@@ -83123,6 +83191,9 @@ function extractFreshlyStalledMailboxes(raw) {
|
|
|
83123
83191
|
var GROUP_SEP = "";
|
|
83124
83192
|
var FIELD_SEP = "";
|
|
83125
83193
|
var RECORD_SEP = "";
|
|
83194
|
+
var ITEM_SEP = "";
|
|
83195
|
+
var SENT_PREFIX = "sent:";
|
|
83196
|
+
var DRAFT_PREFIX = "draft created:";
|
|
83126
83197
|
var DIAG_MARKER = "DIAG";
|
|
83127
83198
|
var DIAG_FIELD_SEP = "F";
|
|
83128
83199
|
var DIAG_ITEM_SEP = "M";
|
|
@@ -83499,6 +83570,13 @@ function looksLikeMailNotRunning(error3) {
|
|
|
83499
83570
|
error3
|
|
83500
83571
|
);
|
|
83501
83572
|
}
|
|
83573
|
+
function parseComposeOutput(output, prefix) {
|
|
83574
|
+
const out = output.trim();
|
|
83575
|
+
if (out.startsWith(prefix)) return { success: true, from: out.slice(prefix.length) };
|
|
83576
|
+
if (out.startsWith("error:")) return { success: false, error: out.slice("error:".length) };
|
|
83577
|
+
console.error(`Unexpected compose output: ${out.slice(0, 200)}`);
|
|
83578
|
+
return { success: false };
|
|
83579
|
+
}
|
|
83502
83580
|
function mailboxPathFragment(mailboxVar, outputVar) {
|
|
83503
83581
|
return `
|
|
83504
83582
|
set ${outputVar} to name of ${mailboxVar}
|
|
@@ -85546,8 +85624,9 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85546
85624
|
* @param body - Email body (plain text)
|
|
85547
85625
|
* @param cc - CC recipients
|
|
85548
85626
|
* @param bcc - BCC recipients
|
|
85549
|
-
* @param account - Account to send from
|
|
85550
|
-
*
|
|
85627
|
+
* @param account - Account name or exact address to send from; resolved to one
|
|
85628
|
+
* address and verified before sending (see `utils/senderAccount.ts`)
|
|
85629
|
+
* @returns `from` is the sender Mail.app actually used
|
|
85551
85630
|
*/
|
|
85552
85631
|
// ───────────────────────────────────────────────────────────────────
|
|
85553
85632
|
// KNOWN BUG: outgoing emails sent via AppleScript on macOS 15+ get wrapped
|
|
@@ -85593,7 +85672,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85593
85672
|
}
|
|
85594
85673
|
const mat = materializeAttachments(attachments);
|
|
85595
85674
|
try {
|
|
85596
|
-
return this.
|
|
85675
|
+
return this.composeWithCommands(
|
|
85676
|
+
"send",
|
|
85597
85677
|
recipientCommands,
|
|
85598
85678
|
safeSubject,
|
|
85599
85679
|
safeBody,
|
|
@@ -85604,38 +85684,55 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85604
85684
|
mat.cleanup();
|
|
85605
85685
|
}
|
|
85606
85686
|
}
|
|
85607
|
-
|
|
85608
|
-
|
|
85609
|
-
|
|
85610
|
-
|
|
85611
|
-
|
|
85612
|
-
|
|
85613
|
-
|
|
85614
|
-
|
|
85615
|
-
|
|
85616
|
-
|
|
85617
|
-
|
|
85618
|
-
send newMessage
|
|
85619
|
-
return "sent"
|
|
85620
|
-
`;
|
|
85621
|
-
} else {
|
|
85622
|
-
sendCommand = `
|
|
85623
|
-
set newMessage to make new outgoing message with properties {subject:"${safeSubject}", content:"${safeBody}", visible:true}
|
|
85687
|
+
/**
|
|
85688
|
+
* Build and run the AppleScript compose for send-email (`send`) and
|
|
85689
|
+
* create-draft (`draft`): pin the sender when `account` is given, verify it
|
|
85690
|
+
* before the message leaves, and report the sender Mail actually used.
|
|
85691
|
+
*/
|
|
85692
|
+
composeWithCommands(mode, recipientCommands, safeSubject, safeBody, account, attachmentCommands) {
|
|
85693
|
+
const sender = this.resolveSender(account);
|
|
85694
|
+
if ("error" in sender) return { success: false, error: sender.error };
|
|
85695
|
+
const prefix = mode === "send" ? SENT_PREFIX : DRAFT_PREFIX;
|
|
85696
|
+
const script = buildAppLevelScript(`
|
|
85697
|
+
set newMessage to make new outgoing message with properties {subject:"${safeSubject}", content:"${safeBody}", visible:${mode === "send"}}
|
|
85624
85698
|
tell newMessage
|
|
85625
85699
|
${recipientCommands}
|
|
85700
|
+
${sender.setSender}
|
|
85626
85701
|
${attachmentCommands}
|
|
85627
85702
|
end tell
|
|
85628
|
-
|
|
85629
|
-
|
|
85630
|
-
|
|
85631
|
-
|
|
85632
|
-
const script = buildAppLevelScript(sendCommand);
|
|
85703
|
+
${sender.guard}
|
|
85704
|
+
${mode === "send" ? "send newMessage" : ""}
|
|
85705
|
+
return "${prefix}" & actualSender
|
|
85706
|
+
`);
|
|
85633
85707
|
const result = executeAppleScript(script, { timeoutMs: 6e4, maxRetries: 1 });
|
|
85634
85708
|
if (!result.success) {
|
|
85635
|
-
console.error(
|
|
85636
|
-
|
|
85709
|
+
console.error(
|
|
85710
|
+
`Failed to ${mode === "send" ? "send email" : "create draft"}: ${result.error}`
|
|
85711
|
+
);
|
|
85712
|
+
return { success: false };
|
|
85637
85713
|
}
|
|
85638
|
-
return result.output
|
|
85714
|
+
return parseComposeOutput(result.output, prefix);
|
|
85715
|
+
}
|
|
85716
|
+
/**
|
|
85717
|
+
* Turn the caller's `account` into the AppleScript pieces that pin the
|
|
85718
|
+
* sender and verify it. No `account` → no pin, but the script still reports
|
|
85719
|
+
* the sender Mail picked. Uses the cached account list: a stale list can only
|
|
85720
|
+
* turn a valid account into a refusal, and the in-script guard re-checks the
|
|
85721
|
+
* real sender before anything leaves.
|
|
85722
|
+
*/
|
|
85723
|
+
resolveSender(account) {
|
|
85724
|
+
if (!account) return { setSender: "", guard: senderGuardScript(void 0) };
|
|
85725
|
+
const { accounts, failed } = this.listAccountsChecked({ timeoutMs: 3e4 });
|
|
85726
|
+
if (failed) {
|
|
85727
|
+
return { error: "Could not read Mail accounts to resolve the sender; nothing was sent." };
|
|
85728
|
+
}
|
|
85729
|
+
const picked = pickSenderAddress(account, accounts);
|
|
85730
|
+
if ("error" in picked) return { error: picked.error };
|
|
85731
|
+
const safeAddress = escapeForAppleScript(picked.address);
|
|
85732
|
+
return {
|
|
85733
|
+
setSender: `set sender to "${safeAddress}"`,
|
|
85734
|
+
guard: senderGuardScript(safeAddress)
|
|
85735
|
+
};
|
|
85639
85736
|
}
|
|
85640
85737
|
/**
|
|
85641
85738
|
* Send individual personalized emails to a list of recipients (mail merge).
|
|
@@ -85653,6 +85750,10 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85653
85750
|
sendSerialEmail(recipients, subject, body, account, delayMs = 500) {
|
|
85654
85751
|
const effectiveDelay = Math.min(Math.max(delayMs, 0), 1e4);
|
|
85655
85752
|
const results = [];
|
|
85753
|
+
const sender = this.resolveSender(account);
|
|
85754
|
+
if ("error" in sender) {
|
|
85755
|
+
return recipients.map((r) => ({ email: r.email, success: false, error: sender.error }));
|
|
85756
|
+
}
|
|
85656
85757
|
for (let i = 0; i < recipients.length; i++) {
|
|
85657
85758
|
const recipient = recipients[i];
|
|
85658
85759
|
try {
|
|
@@ -85664,7 +85765,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85664
85765
|
personalizedSubject = personalizedSubject.replace(placeholder, value);
|
|
85665
85766
|
personalizedBody = personalizedBody.replace(placeholder, value);
|
|
85666
85767
|
}
|
|
85667
|
-
const
|
|
85768
|
+
const sent = this.sendEmail(
|
|
85668
85769
|
[recipient.email],
|
|
85669
85770
|
personalizedSubject,
|
|
85670
85771
|
personalizedBody,
|
|
@@ -85674,8 +85775,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85674
85775
|
);
|
|
85675
85776
|
results.push({
|
|
85676
85777
|
email: recipient.email,
|
|
85677
|
-
success,
|
|
85678
|
-
error: success ? void 0 : "Failed to send email"
|
|
85778
|
+
success: sent.success,
|
|
85779
|
+
error: sent.success ? void 0 : sent.error ?? "Failed to send email"
|
|
85679
85780
|
});
|
|
85680
85781
|
} catch (error3) {
|
|
85681
85782
|
results.push({
|
|
@@ -85699,7 +85800,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85699
85800
|
* @param cc - CC recipients
|
|
85700
85801
|
* @param bcc - BCC recipients
|
|
85701
85802
|
* @param account - Account to create draft in
|
|
85702
|
-
* @returns
|
|
85803
|
+
* @returns `from` is the sender Mail.app actually used
|
|
85703
85804
|
*/
|
|
85704
85805
|
createDraft(to, subject, body, cc, bcc, account, attachments) {
|
|
85705
85806
|
const safeSubject = escapeForAppleScript(subject);
|
|
@@ -85724,7 +85825,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85724
85825
|
const mat = materializeAttachments(attachments);
|
|
85725
85826
|
const attachmentCommands = buildAttachmentCommands(mat.paths);
|
|
85726
85827
|
try {
|
|
85727
|
-
return this.
|
|
85828
|
+
return this.composeWithCommands(
|
|
85829
|
+
"draft",
|
|
85728
85830
|
recipientCommands,
|
|
85729
85831
|
safeSubject,
|
|
85730
85832
|
safeBody,
|
|
@@ -85735,37 +85837,6 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85735
85837
|
mat.cleanup();
|
|
85736
85838
|
}
|
|
85737
85839
|
}
|
|
85738
|
-
createDraftWithCommands(recipientCommands, safeSubject, safeBody, account, attachmentCommands) {
|
|
85739
|
-
let draftCommand;
|
|
85740
|
-
if (account) {
|
|
85741
|
-
const safeAccount = escapeForAppleScript(account);
|
|
85742
|
-
draftCommand = `
|
|
85743
|
-
set newMessage to make new outgoing message with properties {subject:"${safeSubject}", content:"${safeBody}", visible:false}
|
|
85744
|
-
tell newMessage
|
|
85745
|
-
${recipientCommands}
|
|
85746
|
-
set sender to "${safeAccount}"
|
|
85747
|
-
${attachmentCommands}
|
|
85748
|
-
end tell
|
|
85749
|
-
return "draft created"
|
|
85750
|
-
`;
|
|
85751
|
-
} else {
|
|
85752
|
-
draftCommand = `
|
|
85753
|
-
set newMessage to make new outgoing message with properties {subject:"${safeSubject}", content:"${safeBody}", visible:false}
|
|
85754
|
-
tell newMessage
|
|
85755
|
-
${recipientCommands}
|
|
85756
|
-
${attachmentCommands}
|
|
85757
|
-
end tell
|
|
85758
|
-
return "draft created"
|
|
85759
|
-
`;
|
|
85760
|
-
}
|
|
85761
|
-
const script = buildAppLevelScript(draftCommand);
|
|
85762
|
-
const result = executeAppleScript(script, { timeoutMs: 6e4, maxRetries: 1 });
|
|
85763
|
-
if (!result.success) {
|
|
85764
|
-
console.error(`Failed to create draft: ${result.error}`);
|
|
85765
|
-
return false;
|
|
85766
|
-
}
|
|
85767
|
-
return result.output.includes("draft created");
|
|
85768
|
-
}
|
|
85769
85840
|
/**
|
|
85770
85841
|
* Reply to a message.
|
|
85771
85842
|
*
|
|
@@ -86640,13 +86711,29 @@ ${this.errorEmit(" ")}
|
|
|
86640
86711
|
set msg to item 1 of matchingMsgs
|
|
86641
86712
|
set outputText to ""
|
|
86642
86713
|
set attCount to 0
|
|
86714
|
+
-- Each property is read inside its own try: \`MIME type of att\` throws
|
|
86715
|
+
-- "AppleEvent handler failed" for attachments Apple Mail itself composed,
|
|
86716
|
+
-- and one unguarded throw discards the whole list, so the MIME fallback
|
|
86717
|
+
-- answers "no attachments". An unreadable type or size degrades to a
|
|
86718
|
+
-- default; only a missing name drops the entry (it cannot be addressed).
|
|
86643
86719
|
repeat with att in mail attachments of msg
|
|
86644
|
-
set attName to
|
|
86645
|
-
set attType to
|
|
86646
|
-
set attSize to
|
|
86647
|
-
|
|
86648
|
-
|
|
86649
|
-
|
|
86720
|
+
set attName to ""
|
|
86721
|
+
set attType to "application/octet-stream"
|
|
86722
|
+
set attSize to "0"
|
|
86723
|
+
try
|
|
86724
|
+
set attName to name of att
|
|
86725
|
+
end try
|
|
86726
|
+
try
|
|
86727
|
+
set attType to MIME type of att
|
|
86728
|
+
end try
|
|
86729
|
+
try
|
|
86730
|
+
set attSize to file size of att as string
|
|
86731
|
+
end try
|
|
86732
|
+
if attName is not "" then
|
|
86733
|
+
if attCount > 0 then set outputText to outputText & "${RECORD_SEP}"
|
|
86734
|
+
set outputText to outputText & attName & "${FIELD_SEP}" & attType & "${FIELD_SEP}" & attSize
|
|
86735
|
+
set attCount to attCount + 1
|
|
86736
|
+
end if
|
|
86650
86737
|
end repeat
|
|
86651
86738
|
return outputText
|
|
86652
86739
|
end if
|
|
@@ -87496,12 +87583,9 @@ end tell`;
|
|
|
87496
87583
|
set accountList to {}
|
|
87497
87584
|
repeat with acct in accounts
|
|
87498
87585
|
set acctName to name of acct
|
|
87499
|
-
set acctEmail to email addresses of acct
|
|
87500
87586
|
set acctEnabled to enabled of acct
|
|
87501
|
-
set
|
|
87502
|
-
|
|
87503
|
-
set emailStr to item 1 of acctEmail
|
|
87504
|
-
end if
|
|
87587
|
+
set AppleScript's text item delimiters to "${ITEM_SEP}"
|
|
87588
|
+
set emailStr to (email addresses of acct) as text
|
|
87505
87589
|
set end of accountList to acctName & "${FIELD_SEP}" & emailStr & "${FIELD_SEP}" & acctEnabled
|
|
87506
87590
|
end repeat
|
|
87507
87591
|
set AppleScript's text item delimiters to "${RECORD_SEP}"
|
|
@@ -87522,9 +87606,11 @@ end tell`;
|
|
|
87522
87606
|
for (const item of items) {
|
|
87523
87607
|
const parts = item.split(FIELD_SEP);
|
|
87524
87608
|
if (parts.length < 3) continue;
|
|
87609
|
+
const addresses = parts[1].split(ITEM_SEP).filter(Boolean);
|
|
87525
87610
|
accounts.push({
|
|
87526
87611
|
name: parts[0],
|
|
87527
|
-
email:
|
|
87612
|
+
email: addresses[0] ?? "",
|
|
87613
|
+
addresses,
|
|
87528
87614
|
enabled: parts[2] === "true"
|
|
87529
87615
|
});
|
|
87530
87616
|
}
|
|
@@ -87772,7 +87858,7 @@ ${actionStmts.join("\n")}
|
|
|
87772
87858
|
const subject = overrides?.subject ?? template.subject;
|
|
87773
87859
|
const body = overrides?.body ?? template.body;
|
|
87774
87860
|
if (to.length === 0) return false;
|
|
87775
|
-
return this.createDraft(to, subject, body, cc);
|
|
87861
|
+
return this.createDraft(to, subject, body, cc).success;
|
|
87776
87862
|
}
|
|
87777
87863
|
// ===========================================================================
|
|
87778
87864
|
// Diagnostics
|
|
@@ -90175,6 +90261,9 @@ var SENT_COPY_SCHEMA = external_exports.boolean().optional().describe(
|
|
|
90175
90261
|
"SMTP only: whether a best-effort copy was filed to the account's Sent mailbox over IMAP (issue #220). Absent when no configured IMAP account matches the SMTP identity \u2014 not a failure, the feature simply wasn't engaged."
|
|
90176
90262
|
);
|
|
90177
90263
|
var SENT_COPY_ERROR_SCHEMA = external_exports.string().optional().describe("Present only when sentCopy is false: why the Sent-folder copy failed.");
|
|
90264
|
+
var SEND_ACCOUNT_SCHEMA = external_exports.string().optional().describe(
|
|
90265
|
+
"Account to send from. AppleScript transport: a Mail account name or an exact sender address, resolved to one address and verified before sending; if it matches no enabled account, or Mail would use a different sender, nothing is sent and an error is returned. Omitted \u2192 Mail's default sender (reported back in `from`). SMTP transport: an email-form From override (see README)."
|
|
90266
|
+
);
|
|
90178
90267
|
registerTool(
|
|
90179
90268
|
"send-email",
|
|
90180
90269
|
{
|
|
@@ -90185,7 +90274,7 @@ registerTool(
|
|
|
90185
90274
|
body: external_exports.string().min(1, "Body is required"),
|
|
90186
90275
|
cc: external_exports.array(external_exports.string()).optional().describe("CC recipients"),
|
|
90187
90276
|
bcc: external_exports.array(external_exports.string()).optional().describe("BCC recipients"),
|
|
90188
|
-
account:
|
|
90277
|
+
account: SEND_ACCOUNT_SCHEMA,
|
|
90189
90278
|
attachments: ATTACHMENTS_SCHEMA,
|
|
90190
90279
|
inReplyTo: IN_REPLY_TO_SCHEMA,
|
|
90191
90280
|
references: REFERENCES_SCHEMA,
|
|
@@ -90201,6 +90290,7 @@ registerTool(
|
|
|
90201
90290
|
recipients: external_exports.array(external_exports.string()).optional(),
|
|
90202
90291
|
attachmentCount: external_exports.number().optional(),
|
|
90203
90292
|
transport: external_exports.string().optional(),
|
|
90293
|
+
from: external_exports.string().optional().describe("AppleScript transport: the sender Mail.app used"),
|
|
90204
90294
|
messageId: external_exports.string().optional(),
|
|
90205
90295
|
sentCopy: SENT_COPY_SCHEMA,
|
|
90206
90296
|
sentCopyError: SENT_COPY_ERROR_SCHEMA
|
|
@@ -90252,16 +90342,20 @@ registerTool(
|
|
|
90252
90342
|
...result.sentCopyError !== void 0 ? { sentCopyError: result.sentCopyError } : {}
|
|
90253
90343
|
});
|
|
90254
90344
|
}
|
|
90255
|
-
const
|
|
90256
|
-
if (!success) {
|
|
90257
|
-
return errorResponse("Failed to send email. Check Mail.app configuration.");
|
|
90345
|
+
const sent = mailManager.sendEmail(to, subject, body, cc, bcc, account, attachments);
|
|
90346
|
+
if (!sent.success) {
|
|
90347
|
+
return errorResponse(sent.error ?? "Failed to send email. Check Mail.app configuration.");
|
|
90258
90348
|
}
|
|
90259
|
-
return successResponse(
|
|
90260
|
-
|
|
90261
|
-
|
|
90262
|
-
|
|
90263
|
-
|
|
90264
|
-
|
|
90349
|
+
return successResponse(
|
|
90350
|
+
`Email sent to ${to.join(", ")}${attachInfo}${sent.from ? ` from ${sent.from}` : ""}`,
|
|
90351
|
+
{
|
|
90352
|
+
ok: true,
|
|
90353
|
+
recipients: to,
|
|
90354
|
+
attachmentCount,
|
|
90355
|
+
transport: "applescript",
|
|
90356
|
+
from: sent.from
|
|
90357
|
+
}
|
|
90358
|
+
);
|
|
90265
90359
|
},
|
|
90266
90360
|
"Error sending email"
|
|
90267
90361
|
)
|
|
@@ -90279,7 +90373,7 @@ registerTool(
|
|
|
90279
90373
|
).min(1, "At least one recipient is required").max(100, "Cannot send to more than 100 recipients in a single batch").describe("List of recipients with personalization variables (max 100)"),
|
|
90280
90374
|
subject: external_exports.string().min(1, "Subject is required").describe("Subject line \u2014 use {{Key}} for placeholders"),
|
|
90281
90375
|
body: external_exports.string().min(1, "Body is required").describe("Email body \u2014 use {{Key}} for placeholders"),
|
|
90282
|
-
account:
|
|
90376
|
+
account: SEND_ACCOUNT_SCHEMA,
|
|
90283
90377
|
delayMs: external_exports.number().min(0).max(1e4).optional().describe("Delay between sends in ms (default: 500, max: 10000)")
|
|
90284
90378
|
},
|
|
90285
90379
|
outputSchema: {
|
|
@@ -90333,7 +90427,7 @@ registerTool(
|
|
|
90333
90427
|
cc: external_exports.array(external_exports.string()).optional().describe("CC recipients"),
|
|
90334
90428
|
bcc: external_exports.array(external_exports.string()).optional().describe("BCC recipients"),
|
|
90335
90429
|
account: external_exports.string().optional().describe(
|
|
90336
|
-
"Account to create draft in. With inReplyTo/references this must name a configured IMAP account (label or login); when omitted, the IMAP account matching the SMTP identity, else the only IMAP account, is used."
|
|
90430
|
+
"Account to create draft in: a Mail account name or an exact sender address, resolved and verified before the draft is saved (no match or a different sender \u2192 error, no draft). With inReplyTo/references this must name a configured IMAP account (label or login); when omitted, the IMAP account matching the SMTP identity, else the only IMAP account, is used."
|
|
90337
90431
|
),
|
|
90338
90432
|
attachments: ATTACHMENTS_SCHEMA,
|
|
90339
90433
|
inReplyTo: IN_REPLY_TO_SCHEMA,
|
|
@@ -90344,6 +90438,7 @@ registerTool(
|
|
|
90344
90438
|
recipients: external_exports.array(external_exports.string()).optional(),
|
|
90345
90439
|
attachmentCount: external_exports.number().optional(),
|
|
90346
90440
|
transport: external_exports.enum(["applescript", "imap"]).optional(),
|
|
90441
|
+
from: external_exports.string().optional().describe("AppleScript draft: the sender Mail.app used"),
|
|
90347
90442
|
account: external_exports.string().optional().describe("Threaded draft only: IMAP account it was filed in"),
|
|
90348
90443
|
mailbox: external_exports.string().optional().describe("Threaded draft only: Drafts mailbox path"),
|
|
90349
90444
|
messageId: external_exports.string().optional().describe("Threaded draft only: the draft's Message-ID"),
|
|
@@ -90366,18 +90461,24 @@ registerTool(
|
|
|
90366
90461
|
references
|
|
90367
90462
|
});
|
|
90368
90463
|
}
|
|
90369
|
-
const
|
|
90370
|
-
if (!success) {
|
|
90371
|
-
return errorResponse(
|
|
90464
|
+
const drafted = mailManager.createDraft(to, subject, body, cc, bcc, account, attachments);
|
|
90465
|
+
if (!drafted.success) {
|
|
90466
|
+
return errorResponse(
|
|
90467
|
+
drafted.error ?? "Failed to create draft. Check Mail.app configuration."
|
|
90468
|
+
);
|
|
90372
90469
|
}
|
|
90373
90470
|
const attachmentCount = attachments?.length ?? 0;
|
|
90374
90471
|
const attachInfo = attachmentCount ? ` with ${attachmentCount} attachment(s)` : "";
|
|
90375
|
-
return successResponse(
|
|
90376
|
-
|
|
90377
|
-
|
|
90378
|
-
|
|
90379
|
-
|
|
90380
|
-
|
|
90472
|
+
return successResponse(
|
|
90473
|
+
`Draft created for ${to.join(", ")}${attachInfo}${drafted.from ? ` from ${drafted.from}` : ""}`,
|
|
90474
|
+
{
|
|
90475
|
+
ok: true,
|
|
90476
|
+
recipients: to,
|
|
90477
|
+
attachmentCount,
|
|
90478
|
+
transport: "applescript",
|
|
90479
|
+
from: drafted.from
|
|
90480
|
+
}
|
|
90481
|
+
);
|
|
90381
90482
|
},
|
|
90382
90483
|
"Error creating draft"
|
|
90383
90484
|
)
|
package/package.json
CHANGED