apple-mail-mcp 2.20.12 → 2.20.13
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 +152 -92
- 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
|
@@ -82916,6 +82916,49 @@ function preflightAttachments(attachments) {
|
|
|
82916
82916
|
}
|
|
82917
82917
|
}
|
|
82918
82918
|
|
|
82919
|
+
// src/utils/senderAccount.ts
|
|
82920
|
+
function extractAddress(sender) {
|
|
82921
|
+
const angle = /<([^<>]*)>\s*$/.exec(sender);
|
|
82922
|
+
return (angle ? angle[1] : sender).trim();
|
|
82923
|
+
}
|
|
82924
|
+
var same = (a, b) => a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
82925
|
+
function describe(accounts) {
|
|
82926
|
+
const usable = accounts.filter((a) => a.enabled && a.addresses.length > 0);
|
|
82927
|
+
if (usable.length === 0) return "No enabled Mail account with an address was found.";
|
|
82928
|
+
return `Available: ${usable.map((a) => `${a.name} <${a.addresses.join(", ")}>`).join("; ")}`;
|
|
82929
|
+
}
|
|
82930
|
+
function pickSenderAddress(input, accounts) {
|
|
82931
|
+
const wanted = extractAddress(input);
|
|
82932
|
+
const enabled = accounts.filter((a) => a.enabled);
|
|
82933
|
+
const address = enabled.flatMap((a) => a.addresses).find((addr) => same(addr, wanted));
|
|
82934
|
+
if (address) return { address };
|
|
82935
|
+
const byName = enabled.filter((a) => same(a.name, input));
|
|
82936
|
+
if (byName.length > 1) {
|
|
82937
|
+
return {
|
|
82938
|
+
error: `Account name "${input}" matches ${byName.length} accounts; pass the exact sender address instead. ${describe(accounts)}`
|
|
82939
|
+
};
|
|
82940
|
+
}
|
|
82941
|
+
if (byName.length === 1) {
|
|
82942
|
+
const { addresses } = byName[0];
|
|
82943
|
+
if (addresses.length === 1) return { address: addresses[0] };
|
|
82944
|
+
return {
|
|
82945
|
+
error: `Account "${input}" has ${addresses.length === 0 ? "no address" : `${addresses.length} addresses`}; pass the exact sender address instead. ${describe(accounts)}`
|
|
82946
|
+
};
|
|
82947
|
+
}
|
|
82948
|
+
return {
|
|
82949
|
+
error: `No enabled Mail account or address matches "${input}", so nothing was sent or saved. ${describe(accounts)}`
|
|
82950
|
+
};
|
|
82951
|
+
}
|
|
82952
|
+
function senderGuardScript(safeAddress) {
|
|
82953
|
+
const check2 = safeAddress ? `
|
|
82954
|
+
if not (actualSender is "${safeAddress}" or actualSender ends with "<${safeAddress}>") then
|
|
82955
|
+
delete newMessage
|
|
82956
|
+
return "error:Mail.app would send as " & actualSender & " instead of ${safeAddress}, so nothing was sent or saved."
|
|
82957
|
+
end if` : "";
|
|
82958
|
+
return `
|
|
82959
|
+
set actualSender to sender of newMessage as text${check2}`;
|
|
82960
|
+
}
|
|
82961
|
+
|
|
82919
82962
|
// src/services/appleMailManager.ts
|
|
82920
82963
|
init_attachmentReadPolicy();
|
|
82921
82964
|
|
|
@@ -83123,6 +83166,9 @@ function extractFreshlyStalledMailboxes(raw) {
|
|
|
83123
83166
|
var GROUP_SEP = "";
|
|
83124
83167
|
var FIELD_SEP = "";
|
|
83125
83168
|
var RECORD_SEP = "";
|
|
83169
|
+
var ITEM_SEP = "";
|
|
83170
|
+
var SENT_PREFIX = "sent:";
|
|
83171
|
+
var DRAFT_PREFIX = "draft created:";
|
|
83126
83172
|
var DIAG_MARKER = "DIAG";
|
|
83127
83173
|
var DIAG_FIELD_SEP = "F";
|
|
83128
83174
|
var DIAG_ITEM_SEP = "M";
|
|
@@ -83499,6 +83545,13 @@ function looksLikeMailNotRunning(error3) {
|
|
|
83499
83545
|
error3
|
|
83500
83546
|
);
|
|
83501
83547
|
}
|
|
83548
|
+
function parseComposeOutput(output, prefix) {
|
|
83549
|
+
const out = output.trim();
|
|
83550
|
+
if (out.startsWith(prefix)) return { success: true, from: out.slice(prefix.length) };
|
|
83551
|
+
if (out.startsWith("error:")) return { success: false, error: out.slice("error:".length) };
|
|
83552
|
+
console.error(`Unexpected compose output: ${out.slice(0, 200)}`);
|
|
83553
|
+
return { success: false };
|
|
83554
|
+
}
|
|
83502
83555
|
function mailboxPathFragment(mailboxVar, outputVar) {
|
|
83503
83556
|
return `
|
|
83504
83557
|
set ${outputVar} to name of ${mailboxVar}
|
|
@@ -85546,8 +85599,9 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85546
85599
|
* @param body - Email body (plain text)
|
|
85547
85600
|
* @param cc - CC recipients
|
|
85548
85601
|
* @param bcc - BCC recipients
|
|
85549
|
-
* @param account - Account to send from
|
|
85550
|
-
*
|
|
85602
|
+
* @param account - Account name or exact address to send from; resolved to one
|
|
85603
|
+
* address and verified before sending (see `utils/senderAccount.ts`)
|
|
85604
|
+
* @returns `from` is the sender Mail.app actually used
|
|
85551
85605
|
*/
|
|
85552
85606
|
// ───────────────────────────────────────────────────────────────────
|
|
85553
85607
|
// KNOWN BUG: outgoing emails sent via AppleScript on macOS 15+ get wrapped
|
|
@@ -85593,7 +85647,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85593
85647
|
}
|
|
85594
85648
|
const mat = materializeAttachments(attachments);
|
|
85595
85649
|
try {
|
|
85596
|
-
return this.
|
|
85650
|
+
return this.composeWithCommands(
|
|
85651
|
+
"send",
|
|
85597
85652
|
recipientCommands,
|
|
85598
85653
|
safeSubject,
|
|
85599
85654
|
safeBody,
|
|
@@ -85604,38 +85659,55 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85604
85659
|
mat.cleanup();
|
|
85605
85660
|
}
|
|
85606
85661
|
}
|
|
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}
|
|
85662
|
+
/**
|
|
85663
|
+
* Build and run the AppleScript compose for send-email (`send`) and
|
|
85664
|
+
* create-draft (`draft`): pin the sender when `account` is given, verify it
|
|
85665
|
+
* before the message leaves, and report the sender Mail actually used.
|
|
85666
|
+
*/
|
|
85667
|
+
composeWithCommands(mode, recipientCommands, safeSubject, safeBody, account, attachmentCommands) {
|
|
85668
|
+
const sender = this.resolveSender(account);
|
|
85669
|
+
if ("error" in sender) return { success: false, error: sender.error };
|
|
85670
|
+
const prefix = mode === "send" ? SENT_PREFIX : DRAFT_PREFIX;
|
|
85671
|
+
const script = buildAppLevelScript(`
|
|
85672
|
+
set newMessage to make new outgoing message with properties {subject:"${safeSubject}", content:"${safeBody}", visible:${mode === "send"}}
|
|
85624
85673
|
tell newMessage
|
|
85625
85674
|
${recipientCommands}
|
|
85675
|
+
${sender.setSender}
|
|
85626
85676
|
${attachmentCommands}
|
|
85627
85677
|
end tell
|
|
85628
|
-
|
|
85629
|
-
|
|
85630
|
-
|
|
85631
|
-
|
|
85632
|
-
const script = buildAppLevelScript(sendCommand);
|
|
85678
|
+
${sender.guard}
|
|
85679
|
+
${mode === "send" ? "send newMessage" : ""}
|
|
85680
|
+
return "${prefix}" & actualSender
|
|
85681
|
+
`);
|
|
85633
85682
|
const result = executeAppleScript(script, { timeoutMs: 6e4, maxRetries: 1 });
|
|
85634
85683
|
if (!result.success) {
|
|
85635
|
-
console.error(
|
|
85636
|
-
|
|
85684
|
+
console.error(
|
|
85685
|
+
`Failed to ${mode === "send" ? "send email" : "create draft"}: ${result.error}`
|
|
85686
|
+
);
|
|
85687
|
+
return { success: false };
|
|
85688
|
+
}
|
|
85689
|
+
return parseComposeOutput(result.output, prefix);
|
|
85690
|
+
}
|
|
85691
|
+
/**
|
|
85692
|
+
* Turn the caller's `account` into the AppleScript pieces that pin the
|
|
85693
|
+
* sender and verify it. No `account` → no pin, but the script still reports
|
|
85694
|
+
* the sender Mail picked. Uses the cached account list: a stale list can only
|
|
85695
|
+
* turn a valid account into a refusal, and the in-script guard re-checks the
|
|
85696
|
+
* real sender before anything leaves.
|
|
85697
|
+
*/
|
|
85698
|
+
resolveSender(account) {
|
|
85699
|
+
if (!account) return { setSender: "", guard: senderGuardScript(void 0) };
|
|
85700
|
+
const { accounts, failed } = this.listAccountsChecked({ timeoutMs: 3e4 });
|
|
85701
|
+
if (failed) {
|
|
85702
|
+
return { error: "Could not read Mail accounts to resolve the sender; nothing was sent." };
|
|
85637
85703
|
}
|
|
85638
|
-
|
|
85704
|
+
const picked = pickSenderAddress(account, accounts);
|
|
85705
|
+
if ("error" in picked) return { error: picked.error };
|
|
85706
|
+
const safeAddress = escapeForAppleScript(picked.address);
|
|
85707
|
+
return {
|
|
85708
|
+
setSender: `set sender to "${safeAddress}"`,
|
|
85709
|
+
guard: senderGuardScript(safeAddress)
|
|
85710
|
+
};
|
|
85639
85711
|
}
|
|
85640
85712
|
/**
|
|
85641
85713
|
* Send individual personalized emails to a list of recipients (mail merge).
|
|
@@ -85653,6 +85725,10 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85653
85725
|
sendSerialEmail(recipients, subject, body, account, delayMs = 500) {
|
|
85654
85726
|
const effectiveDelay = Math.min(Math.max(delayMs, 0), 1e4);
|
|
85655
85727
|
const results = [];
|
|
85728
|
+
const sender = this.resolveSender(account);
|
|
85729
|
+
if ("error" in sender) {
|
|
85730
|
+
return recipients.map((r) => ({ email: r.email, success: false, error: sender.error }));
|
|
85731
|
+
}
|
|
85656
85732
|
for (let i = 0; i < recipients.length; i++) {
|
|
85657
85733
|
const recipient = recipients[i];
|
|
85658
85734
|
try {
|
|
@@ -85664,7 +85740,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85664
85740
|
personalizedSubject = personalizedSubject.replace(placeholder, value);
|
|
85665
85741
|
personalizedBody = personalizedBody.replace(placeholder, value);
|
|
85666
85742
|
}
|
|
85667
|
-
const
|
|
85743
|
+
const sent = this.sendEmail(
|
|
85668
85744
|
[recipient.email],
|
|
85669
85745
|
personalizedSubject,
|
|
85670
85746
|
personalizedBody,
|
|
@@ -85674,8 +85750,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85674
85750
|
);
|
|
85675
85751
|
results.push({
|
|
85676
85752
|
email: recipient.email,
|
|
85677
|
-
success,
|
|
85678
|
-
error: success ? void 0 : "Failed to send email"
|
|
85753
|
+
success: sent.success,
|
|
85754
|
+
error: sent.success ? void 0 : sent.error ?? "Failed to send email"
|
|
85679
85755
|
});
|
|
85680
85756
|
} catch (error3) {
|
|
85681
85757
|
results.push({
|
|
@@ -85699,7 +85775,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85699
85775
|
* @param cc - CC recipients
|
|
85700
85776
|
* @param bcc - BCC recipients
|
|
85701
85777
|
* @param account - Account to create draft in
|
|
85702
|
-
* @returns
|
|
85778
|
+
* @returns `from` is the sender Mail.app actually used
|
|
85703
85779
|
*/
|
|
85704
85780
|
createDraft(to, subject, body, cc, bcc, account, attachments) {
|
|
85705
85781
|
const safeSubject = escapeForAppleScript(subject);
|
|
@@ -85724,7 +85800,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85724
85800
|
const mat = materializeAttachments(attachments);
|
|
85725
85801
|
const attachmentCommands = buildAttachmentCommands(mat.paths);
|
|
85726
85802
|
try {
|
|
85727
|
-
return this.
|
|
85803
|
+
return this.composeWithCommands(
|
|
85804
|
+
"draft",
|
|
85728
85805
|
recipientCommands,
|
|
85729
85806
|
safeSubject,
|
|
85730
85807
|
safeBody,
|
|
@@ -85735,37 +85812,6 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85735
85812
|
mat.cleanup();
|
|
85736
85813
|
}
|
|
85737
85814
|
}
|
|
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
85815
|
/**
|
|
85770
85816
|
* Reply to a message.
|
|
85771
85817
|
*
|
|
@@ -87496,12 +87542,9 @@ end tell`;
|
|
|
87496
87542
|
set accountList to {}
|
|
87497
87543
|
repeat with acct in accounts
|
|
87498
87544
|
set acctName to name of acct
|
|
87499
|
-
set acctEmail to email addresses of acct
|
|
87500
87545
|
set acctEnabled to enabled of acct
|
|
87501
|
-
set
|
|
87502
|
-
|
|
87503
|
-
set emailStr to item 1 of acctEmail
|
|
87504
|
-
end if
|
|
87546
|
+
set AppleScript's text item delimiters to "${ITEM_SEP}"
|
|
87547
|
+
set emailStr to (email addresses of acct) as text
|
|
87505
87548
|
set end of accountList to acctName & "${FIELD_SEP}" & emailStr & "${FIELD_SEP}" & acctEnabled
|
|
87506
87549
|
end repeat
|
|
87507
87550
|
set AppleScript's text item delimiters to "${RECORD_SEP}"
|
|
@@ -87522,9 +87565,11 @@ end tell`;
|
|
|
87522
87565
|
for (const item of items) {
|
|
87523
87566
|
const parts = item.split(FIELD_SEP);
|
|
87524
87567
|
if (parts.length < 3) continue;
|
|
87568
|
+
const addresses = parts[1].split(ITEM_SEP).filter(Boolean);
|
|
87525
87569
|
accounts.push({
|
|
87526
87570
|
name: parts[0],
|
|
87527
|
-
email:
|
|
87571
|
+
email: addresses[0] ?? "",
|
|
87572
|
+
addresses,
|
|
87528
87573
|
enabled: parts[2] === "true"
|
|
87529
87574
|
});
|
|
87530
87575
|
}
|
|
@@ -87772,7 +87817,7 @@ ${actionStmts.join("\n")}
|
|
|
87772
87817
|
const subject = overrides?.subject ?? template.subject;
|
|
87773
87818
|
const body = overrides?.body ?? template.body;
|
|
87774
87819
|
if (to.length === 0) return false;
|
|
87775
|
-
return this.createDraft(to, subject, body, cc);
|
|
87820
|
+
return this.createDraft(to, subject, body, cc).success;
|
|
87776
87821
|
}
|
|
87777
87822
|
// ===========================================================================
|
|
87778
87823
|
// Diagnostics
|
|
@@ -90175,6 +90220,9 @@ var SENT_COPY_SCHEMA = external_exports.boolean().optional().describe(
|
|
|
90175
90220
|
"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
90221
|
);
|
|
90177
90222
|
var SENT_COPY_ERROR_SCHEMA = external_exports.string().optional().describe("Present only when sentCopy is false: why the Sent-folder copy failed.");
|
|
90223
|
+
var SEND_ACCOUNT_SCHEMA = external_exports.string().optional().describe(
|
|
90224
|
+
"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)."
|
|
90225
|
+
);
|
|
90178
90226
|
registerTool(
|
|
90179
90227
|
"send-email",
|
|
90180
90228
|
{
|
|
@@ -90185,7 +90233,7 @@ registerTool(
|
|
|
90185
90233
|
body: external_exports.string().min(1, "Body is required"),
|
|
90186
90234
|
cc: external_exports.array(external_exports.string()).optional().describe("CC recipients"),
|
|
90187
90235
|
bcc: external_exports.array(external_exports.string()).optional().describe("BCC recipients"),
|
|
90188
|
-
account:
|
|
90236
|
+
account: SEND_ACCOUNT_SCHEMA,
|
|
90189
90237
|
attachments: ATTACHMENTS_SCHEMA,
|
|
90190
90238
|
inReplyTo: IN_REPLY_TO_SCHEMA,
|
|
90191
90239
|
references: REFERENCES_SCHEMA,
|
|
@@ -90201,6 +90249,7 @@ registerTool(
|
|
|
90201
90249
|
recipients: external_exports.array(external_exports.string()).optional(),
|
|
90202
90250
|
attachmentCount: external_exports.number().optional(),
|
|
90203
90251
|
transport: external_exports.string().optional(),
|
|
90252
|
+
from: external_exports.string().optional().describe("AppleScript transport: the sender Mail.app used"),
|
|
90204
90253
|
messageId: external_exports.string().optional(),
|
|
90205
90254
|
sentCopy: SENT_COPY_SCHEMA,
|
|
90206
90255
|
sentCopyError: SENT_COPY_ERROR_SCHEMA
|
|
@@ -90252,16 +90301,20 @@ registerTool(
|
|
|
90252
90301
|
...result.sentCopyError !== void 0 ? { sentCopyError: result.sentCopyError } : {}
|
|
90253
90302
|
});
|
|
90254
90303
|
}
|
|
90255
|
-
const
|
|
90256
|
-
if (!success) {
|
|
90257
|
-
return errorResponse("Failed to send email. Check Mail.app configuration.");
|
|
90304
|
+
const sent = mailManager.sendEmail(to, subject, body, cc, bcc, account, attachments);
|
|
90305
|
+
if (!sent.success) {
|
|
90306
|
+
return errorResponse(sent.error ?? "Failed to send email. Check Mail.app configuration.");
|
|
90258
90307
|
}
|
|
90259
|
-
return successResponse(
|
|
90260
|
-
|
|
90261
|
-
|
|
90262
|
-
|
|
90263
|
-
|
|
90264
|
-
|
|
90308
|
+
return successResponse(
|
|
90309
|
+
`Email sent to ${to.join(", ")}${attachInfo}${sent.from ? ` from ${sent.from}` : ""}`,
|
|
90310
|
+
{
|
|
90311
|
+
ok: true,
|
|
90312
|
+
recipients: to,
|
|
90313
|
+
attachmentCount,
|
|
90314
|
+
transport: "applescript",
|
|
90315
|
+
from: sent.from
|
|
90316
|
+
}
|
|
90317
|
+
);
|
|
90265
90318
|
},
|
|
90266
90319
|
"Error sending email"
|
|
90267
90320
|
)
|
|
@@ -90279,7 +90332,7 @@ registerTool(
|
|
|
90279
90332
|
).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
90333
|
subject: external_exports.string().min(1, "Subject is required").describe("Subject line \u2014 use {{Key}} for placeholders"),
|
|
90281
90334
|
body: external_exports.string().min(1, "Body is required").describe("Email body \u2014 use {{Key}} for placeholders"),
|
|
90282
|
-
account:
|
|
90335
|
+
account: SEND_ACCOUNT_SCHEMA,
|
|
90283
90336
|
delayMs: external_exports.number().min(0).max(1e4).optional().describe("Delay between sends in ms (default: 500, max: 10000)")
|
|
90284
90337
|
},
|
|
90285
90338
|
outputSchema: {
|
|
@@ -90333,7 +90386,7 @@ registerTool(
|
|
|
90333
90386
|
cc: external_exports.array(external_exports.string()).optional().describe("CC recipients"),
|
|
90334
90387
|
bcc: external_exports.array(external_exports.string()).optional().describe("BCC recipients"),
|
|
90335
90388
|
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."
|
|
90389
|
+
"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
90390
|
),
|
|
90338
90391
|
attachments: ATTACHMENTS_SCHEMA,
|
|
90339
90392
|
inReplyTo: IN_REPLY_TO_SCHEMA,
|
|
@@ -90344,6 +90397,7 @@ registerTool(
|
|
|
90344
90397
|
recipients: external_exports.array(external_exports.string()).optional(),
|
|
90345
90398
|
attachmentCount: external_exports.number().optional(),
|
|
90346
90399
|
transport: external_exports.enum(["applescript", "imap"]).optional(),
|
|
90400
|
+
from: external_exports.string().optional().describe("AppleScript draft: the sender Mail.app used"),
|
|
90347
90401
|
account: external_exports.string().optional().describe("Threaded draft only: IMAP account it was filed in"),
|
|
90348
90402
|
mailbox: external_exports.string().optional().describe("Threaded draft only: Drafts mailbox path"),
|
|
90349
90403
|
messageId: external_exports.string().optional().describe("Threaded draft only: the draft's Message-ID"),
|
|
@@ -90366,18 +90420,24 @@ registerTool(
|
|
|
90366
90420
|
references
|
|
90367
90421
|
});
|
|
90368
90422
|
}
|
|
90369
|
-
const
|
|
90370
|
-
if (!success) {
|
|
90371
|
-
return errorResponse(
|
|
90423
|
+
const drafted = mailManager.createDraft(to, subject, body, cc, bcc, account, attachments);
|
|
90424
|
+
if (!drafted.success) {
|
|
90425
|
+
return errorResponse(
|
|
90426
|
+
drafted.error ?? "Failed to create draft. Check Mail.app configuration."
|
|
90427
|
+
);
|
|
90372
90428
|
}
|
|
90373
90429
|
const attachmentCount = attachments?.length ?? 0;
|
|
90374
90430
|
const attachInfo = attachmentCount ? ` with ${attachmentCount} attachment(s)` : "";
|
|
90375
|
-
return successResponse(
|
|
90376
|
-
|
|
90377
|
-
|
|
90378
|
-
|
|
90379
|
-
|
|
90380
|
-
|
|
90431
|
+
return successResponse(
|
|
90432
|
+
`Draft created for ${to.join(", ")}${attachInfo}${drafted.from ? ` from ${drafted.from}` : ""}`,
|
|
90433
|
+
{
|
|
90434
|
+
ok: true,
|
|
90435
|
+
recipients: to,
|
|
90436
|
+
attachmentCount,
|
|
90437
|
+
transport: "applescript",
|
|
90438
|
+
from: drafted.from
|
|
90439
|
+
}
|
|
90440
|
+
);
|
|
90381
90441
|
},
|
|
90382
90442
|
"Error creating draft"
|
|
90383
90443
|
)
|
package/package.json
CHANGED