apple-mail-mcp 2.20.25 → 2.20.27

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 CHANGED
@@ -501,6 +501,10 @@ Two differences to know when SMTP is auto-preferred:
501
501
  or `sentCopy: false` with `sentCopyError`; the field is **absent** when no
502
502
  configured IMAP account matches the SMTP identity, which is a skip, not a
503
503
  failure. A copy that fails never fails the send — the mail has already gone.
504
+ Before appending, the Sent mailbox is searched for that `Message-ID`; if the
505
+ submission server already filed it (Gmail's SMTP always does — the server
506
+ waits up to ~3 s for it there), nothing is appended and the result adds
507
+ `sentCopyAlreadyFiled: true` alongside `sentCopy: true`.
504
508
  Use `transport: "applescript"` if you want Mail.app itself to file the copy.
505
509
  - **`account` is a From override, not account selection.** Over SMTP, `account`
506
510
  is used as the From address only when it is an email address; a Mail.app
package/build/cli.js CHANGED
@@ -58670,6 +58670,8 @@ __export(imapClient_exports, {
58670
58670
  MAX_RFC822_FILE_BYTES: () => MAX_RFC822_FILE_BYTES,
58671
58671
  MAX_RFC822_INLINE_BYTES: () => MAX_RFC822_INLINE_BYTES,
58672
58672
  MAX_UNSCOPED_SEARCH_DEPTH: () => MAX_UNSCOPED_SEARCH_DEPTH,
58673
+ SENT_COPY_GMAIL_POLL_INTERVAL_MS: () => SENT_COPY_GMAIL_POLL_INTERVAL_MS,
58674
+ SENT_COPY_GMAIL_POLL_MS: () => SENT_COPY_GMAIL_POLL_MS,
58673
58675
  __resetPool: () => __resetPool,
58674
58676
  __setPoolConnect: () => __setPoolConnect,
58675
58677
  base64DecodedLength: () => base64DecodedLength,
@@ -58680,6 +58682,7 @@ __export(imapClient_exports, {
58680
58682
  decodedSizeWithoutBody: () => decodedSizeWithoutBody,
58681
58683
  dropAllPools: () => dropAllPools,
58682
58684
  encodeImapId: () => encodeImapId,
58685
+ extractRawMessageId: () => extractRawMessageId,
58683
58686
  flagColorName: () => flagColorName,
58684
58687
  imapAppendDraft: () => imapAppendDraft,
58685
58688
  imapAppendSentCopy: () => imapAppendSentCopy,
@@ -58712,6 +58715,7 @@ __export(imapClient_exports, {
58712
58715
  imapThread: () => imapThread,
58713
58716
  imapUnflagMessage: () => imapUnflagMessage,
58714
58717
  imapUnreadCount: () => imapUnreadCount,
58718
+ isGmailHost: () => isGmailHost,
58715
58719
  isImapAccount: () => isImapAccount,
58716
58720
  listImapAccountLabels: () => listImapAccountLabels,
58717
58721
  mailFlagBitsFor: () => mailFlagBitsFor,
@@ -59840,11 +59844,32 @@ function imapRenameMailbox(oldName, newName, deps = {}) {
59840
59844
  }
59841
59845
  });
59842
59846
  }
59843
- async function imapAppendSentCopy(smtpUser, raw, deps = {}) {
59847
+ function isGmailHost(host) {
59848
+ return Boolean(host && GMAIL_HOST.test(host.trim()));
59849
+ }
59850
+ function extractRawMessageId(raw) {
59851
+ const text = typeof raw === "string" ? raw : raw.toString("latin1");
59852
+ const end = text.search(/\r?\n\r?\n/);
59853
+ const header = (end === -1 ? text : text.slice(0, end)).replace(/\r?\n[ \t]+/g, " ");
59854
+ const m = /^message-id:[ \t]*(<[^<>\s]+>)/im.exec(header);
59855
+ return m ? m[1] : null;
59856
+ }
59857
+ async function imapAppendSentCopy(smtpUser, raw, deps = {}, opts = {}) {
59844
59858
  if (!deps.config && !isImapAccount(smtpUser)) return { attempted: false };
59859
+ const messageId = extractRawMessageId(raw);
59860
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
59861
+ const now = opts.now ?? Date.now;
59845
59862
  try {
59846
- return await withClient({ ...deps, account: deps.account ?? smtpUser }, async (client) => {
59863
+ return await withClient({ ...deps, account: deps.account ?? smtpUser }, async (client, cfg) => {
59847
59864
  const path3 = await resolveMailboxPath(client, "sent", "list");
59865
+ if (messageId) {
59866
+ const gmail = isGmailHost(cfg.host) || isGmailHost(opts.smtpHost);
59867
+ const budget = gmail ? opts.gmailPollMs ?? SENT_COPY_GMAIL_POLL_MS : 0;
59868
+ const interval = Math.max(1, opts.gmailPollIntervalMs ?? SENT_COPY_GMAIL_POLL_INTERVAL_MS);
59869
+ if (await sentCopyPresent(client, path3, messageId, budget, interval, sleep, now)) {
59870
+ return { attempted: true, success: true, mailbox: path3, alreadyFiled: true };
59871
+ }
59872
+ }
59848
59873
  const res = await client.append(path3, raw, ["\\Seen"]);
59849
59874
  if (!res) throw new Error("server rejected the APPEND (IMAP NO/BAD)");
59850
59875
  return { attempted: true, success: true, mailbox: path3 };
@@ -59853,6 +59878,28 @@ async function imapAppendSentCopy(smtpUser, raw, deps = {}) {
59853
59878
  return { attempted: true, success: false, error: errText(e) };
59854
59879
  }
59855
59880
  }
59881
+ async function sentCopyPresent(client, path3, messageId, budget, interval, sleep, now) {
59882
+ const deadline = now() + budget;
59883
+ for (; ; ) {
59884
+ try {
59885
+ const lock = await client.getMailboxLock(path3, { readOnly: true });
59886
+ try {
59887
+ const found = await client.search(
59888
+ { header: { "message-id": messageId }, ...NOT_DELETED },
59889
+ { uid: true }
59890
+ );
59891
+ if (Array.isArray(found) && found.length > 0) return true;
59892
+ } finally {
59893
+ lock.release();
59894
+ }
59895
+ } catch {
59896
+ return false;
59897
+ }
59898
+ const left = deadline - now();
59899
+ if (left <= 0) return false;
59900
+ await sleep(Math.min(interval, left));
59901
+ }
59902
+ }
59856
59903
  function resolveDraftImapAccount(account, smtpUser, env = process.env) {
59857
59904
  const specs = listImapAccountSpecs(env);
59858
59905
  const why = "Mail.app's AppleScript cannot set In-Reply-To/References on a draft, so a threaded draft is filed over IMAP into the account's Drafts mailbox";
@@ -60620,7 +60667,7 @@ async function imapThread(id, deps = {}, limit = 50) {
60620
60667
  true
60621
60668
  );
60622
60669
  }
60623
- var import_imapflow, IMAP_ENV, defaultConnect, SPECIAL_USE_ALIASES, NOT_DELETED, LARGE_MAILBOX_MESSAGES, FIRST_SEARCH_WINDOW, MAX_WINDOW, MAX_UNSCOPED_SEARCH_DEPTH, COLOR_FETCH_CHUNK, ROW_QUERY, ROW_QUERY_NO_STRUCTURE, ROW_QUERY_HEADERS_ONLY, DELETED_MARGIN, poolConnect, pools, connecting, MAX_COMPOSE_SOURCE_BYTES, MAX_RFC822_INLINE_BYTES, MAX_RFC822_FILE_BYTES, HEADER_WINDOW_BYTES, MAIL_FLAG_BITS, FLAG_COLOR_NAMES, imapMarkRead, imapMarkUnread, FALLBACK_TRASH_PATH, BASE64_TAIL_WINDOW, QP_EXACT_MAX_BYTES, imapBatchMarkRead, imapBatchMarkUnread, imapBatchFlag, imapBatchUnflag, imapBatchDelete;
60670
+ var import_imapflow, IMAP_ENV, defaultConnect, SPECIAL_USE_ALIASES, NOT_DELETED, LARGE_MAILBOX_MESSAGES, FIRST_SEARCH_WINDOW, MAX_WINDOW, MAX_UNSCOPED_SEARCH_DEPTH, COLOR_FETCH_CHUNK, ROW_QUERY, ROW_QUERY_NO_STRUCTURE, ROW_QUERY_HEADERS_ONLY, DELETED_MARGIN, poolConnect, pools, connecting, SENT_COPY_GMAIL_POLL_MS, SENT_COPY_GMAIL_POLL_INTERVAL_MS, GMAIL_HOST, MAX_COMPOSE_SOURCE_BYTES, MAX_RFC822_INLINE_BYTES, MAX_RFC822_FILE_BYTES, HEADER_WINDOW_BYTES, MAIL_FLAG_BITS, FLAG_COLOR_NAMES, imapMarkRead, imapMarkUnread, FALLBACK_TRASH_PATH, BASE64_TAIL_WINDOW, QP_EXACT_MAX_BYTES, imapBatchMarkRead, imapBatchMarkUnread, imapBatchFlag, imapBatchUnflag, imapBatchDelete;
60624
60671
  var init_imapClient = __esm({
60625
60672
  "src/services/imapClient.ts"() {
60626
60673
  "use strict";
@@ -60719,6 +60766,9 @@ var init_imapClient = __esm({
60719
60766
  poolConnect = defaultConnect;
60720
60767
  pools = /* @__PURE__ */ new Map();
60721
60768
  connecting = /* @__PURE__ */ new Map();
60769
+ SENT_COPY_GMAIL_POLL_MS = 3e3;
60770
+ SENT_COPY_GMAIL_POLL_INTERVAL_MS = 500;
60771
+ GMAIL_HOST = /(^|\.)(gmail|googlemail)\.com$/i;
60722
60772
  MAX_COMPOSE_SOURCE_BYTES = 25 * 1024 * 1024;
60723
60773
  MAX_RFC822_INLINE_BYTES = 6 * 1024 * 1024;
60724
60774
  MAX_RFC822_FILE_BYTES = MAX_COMPOSE_SOURCE_BYTES;
@@ -60787,11 +60837,12 @@ import { execFileSync } from "child_process";
60787
60837
  function errText2(e) {
60788
60838
  return e instanceof Error ? e.message : String(e);
60789
60839
  }
60790
- async function defaultAppendSentCopy(smtpUser, raw) {
60840
+ async function defaultAppendSentCopy(smtpUser, raw, smtpHost) {
60791
60841
  const { imapAppendSentCopy: imapAppendSentCopy2 } = await Promise.resolve().then(() => (init_imapClient(), imapClient_exports));
60792
- const result = await imapAppendSentCopy2(smtpUser, raw);
60842
+ const result = await imapAppendSentCopy2(smtpUser, raw, {}, { smtpHost });
60793
60843
  if (!result.attempted) return {};
60794
- return result.success ? { sentCopy: true } : { sentCopy: false, sentCopyError: result.error };
60844
+ if (!result.success) return { sentCopy: false, sentCopyError: result.error };
60845
+ return result.alreadyFiled ? { sentCopy: true, sentCopyAlreadyFiled: true } : { sentCopy: true };
60795
60846
  }
60796
60847
  function buildRawMime(mail) {
60797
60848
  return new mail_composer_default(mail).compile().build();
@@ -60922,7 +60973,10 @@ async function sendViaSmtp(opts, config, createTransport2 = nodemailer_default.c
60922
60973
  requireTLS,
60923
60974
  auth: { user: cfg.user, pass: cfg.pass }
60924
60975
  });
60925
- const mailOptions = buildMailOptions(opts, requestedFrom || cfg.from, attachments);
60976
+ const mailOptions = {
60977
+ ...buildMailOptions(opts, requestedFrom || cfg.from, attachments),
60978
+ date: /* @__PURE__ */ new Date()
60979
+ };
60926
60980
  try {
60927
60981
  const info = await transporter.sendMail(mailOptions);
60928
60982
  let copyFields = {};
@@ -60932,7 +60986,7 @@ async function sendViaSmtp(opts, config, createTransport2 = nodemailer_default.c
60932
60986
  keepBcc: true,
60933
60987
  messageId: info.messageId
60934
60988
  });
60935
- copyFields = await appendSentCopy(cfg.user, raw);
60989
+ copyFields = await appendSentCopy(cfg.user, raw, cfg.host);
60936
60990
  } catch (copyError) {
60937
60991
  copyFields = { sentCopy: false, sentCopyError: errText2(copyError) };
60938
60992
  }
package/build/index.js CHANGED
@@ -65470,11 +65470,12 @@ import { execFileSync } from "child_process";
65470
65470
  function errText(e) {
65471
65471
  return e instanceof Error ? e.message : String(e);
65472
65472
  }
65473
- async function defaultAppendSentCopy(smtpUser, raw) {
65473
+ async function defaultAppendSentCopy(smtpUser, raw, smtpHost) {
65474
65474
  const { imapAppendSentCopy: imapAppendSentCopy2 } = await Promise.resolve().then(() => (init_imapClient(), imapClient_exports));
65475
- const result = await imapAppendSentCopy2(smtpUser, raw);
65475
+ const result = await imapAppendSentCopy2(smtpUser, raw, {}, { smtpHost });
65476
65476
  if (!result.attempted) return {};
65477
- return result.success ? { sentCopy: true } : { sentCopy: false, sentCopyError: result.error };
65477
+ if (!result.success) return { sentCopy: false, sentCopyError: result.error };
65478
+ return result.alreadyFiled ? { sentCopy: true, sentCopyAlreadyFiled: true } : { sentCopy: true };
65478
65479
  }
65479
65480
  function buildRawMime(mail) {
65480
65481
  return new mail_composer_default(mail).compile().build();
@@ -65622,7 +65623,10 @@ async function sendViaSmtp(opts, config2, createTransport2 = nodemailer_default.
65622
65623
  requireTLS,
65623
65624
  auth: { user: cfg.user, pass: cfg.pass }
65624
65625
  });
65625
- const mailOptions = buildMailOptions(opts, requestedFrom || cfg.from, attachments);
65626
+ const mailOptions = {
65627
+ ...buildMailOptions(opts, requestedFrom || cfg.from, attachments),
65628
+ date: /* @__PURE__ */ new Date()
65629
+ };
65626
65630
  try {
65627
65631
  const info = await transporter.sendMail(mailOptions);
65628
65632
  let copyFields = {};
@@ -65632,7 +65636,7 @@ async function sendViaSmtp(opts, config2, createTransport2 = nodemailer_default.
65632
65636
  keepBcc: true,
65633
65637
  messageId: info.messageId
65634
65638
  });
65635
- copyFields = await appendSentCopy(cfg.user, raw);
65639
+ copyFields = await appendSentCopy(cfg.user, raw, cfg.host);
65636
65640
  } catch (copyError) {
65637
65641
  copyFields = { sentCopy: false, sentCopyError: errText(copyError) };
65638
65642
  }
@@ -66423,6 +66427,8 @@ __export(imapClient_exports, {
66423
66427
  MAX_RFC822_FILE_BYTES: () => MAX_RFC822_FILE_BYTES,
66424
66428
  MAX_RFC822_INLINE_BYTES: () => MAX_RFC822_INLINE_BYTES,
66425
66429
  MAX_UNSCOPED_SEARCH_DEPTH: () => MAX_UNSCOPED_SEARCH_DEPTH,
66430
+ SENT_COPY_GMAIL_POLL_INTERVAL_MS: () => SENT_COPY_GMAIL_POLL_INTERVAL_MS,
66431
+ SENT_COPY_GMAIL_POLL_MS: () => SENT_COPY_GMAIL_POLL_MS,
66426
66432
  __resetPool: () => __resetPool,
66427
66433
  __setPoolConnect: () => __setPoolConnect,
66428
66434
  base64DecodedLength: () => base64DecodedLength,
@@ -66433,6 +66439,7 @@ __export(imapClient_exports, {
66433
66439
  decodedSizeWithoutBody: () => decodedSizeWithoutBody,
66434
66440
  dropAllPools: () => dropAllPools,
66435
66441
  encodeImapId: () => encodeImapId,
66442
+ extractRawMessageId: () => extractRawMessageId,
66436
66443
  flagColorName: () => flagColorName,
66437
66444
  imapAppendDraft: () => imapAppendDraft,
66438
66445
  imapAppendSentCopy: () => imapAppendSentCopy,
@@ -66465,6 +66472,7 @@ __export(imapClient_exports, {
66465
66472
  imapThread: () => imapThread,
66466
66473
  imapUnflagMessage: () => imapUnflagMessage,
66467
66474
  imapUnreadCount: () => imapUnreadCount,
66475
+ isGmailHost: () => isGmailHost,
66468
66476
  isImapAccount: () => isImapAccount,
66469
66477
  listImapAccountLabels: () => listImapAccountLabels,
66470
66478
  mailFlagBitsFor: () => mailFlagBitsFor,
@@ -67593,11 +67601,32 @@ function imapRenameMailbox(oldName, newName, deps = {}) {
67593
67601
  }
67594
67602
  });
67595
67603
  }
67596
- async function imapAppendSentCopy(smtpUser, raw, deps = {}) {
67604
+ function isGmailHost(host) {
67605
+ return Boolean(host && GMAIL_HOST.test(host.trim()));
67606
+ }
67607
+ function extractRawMessageId(raw) {
67608
+ const text = typeof raw === "string" ? raw : raw.toString("latin1");
67609
+ const end = text.search(/\r?\n\r?\n/);
67610
+ const header = (end === -1 ? text : text.slice(0, end)).replace(/\r?\n[ \t]+/g, " ");
67611
+ const m = /^message-id:[ \t]*(<[^<>\s]+>)/im.exec(header);
67612
+ return m ? m[1] : null;
67613
+ }
67614
+ async function imapAppendSentCopy(smtpUser, raw, deps = {}, opts = {}) {
67597
67615
  if (!deps.config && !isImapAccount(smtpUser)) return { attempted: false };
67616
+ const messageId = extractRawMessageId(raw);
67617
+ const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
67618
+ const now = opts.now ?? Date.now;
67598
67619
  try {
67599
- return await withClient({ ...deps, account: deps.account ?? smtpUser }, async (client) => {
67620
+ return await withClient({ ...deps, account: deps.account ?? smtpUser }, async (client, cfg) => {
67600
67621
  const path3 = await resolveMailboxPath(client, "sent", "list");
67622
+ if (messageId) {
67623
+ const gmail = isGmailHost(cfg.host) || isGmailHost(opts.smtpHost);
67624
+ const budget = gmail ? opts.gmailPollMs ?? SENT_COPY_GMAIL_POLL_MS : 0;
67625
+ const interval = Math.max(1, opts.gmailPollIntervalMs ?? SENT_COPY_GMAIL_POLL_INTERVAL_MS);
67626
+ if (await sentCopyPresent(client, path3, messageId, budget, interval, sleep2, now)) {
67627
+ return { attempted: true, success: true, mailbox: path3, alreadyFiled: true };
67628
+ }
67629
+ }
67601
67630
  const res = await client.append(path3, raw, ["\\Seen"]);
67602
67631
  if (!res) throw new Error("server rejected the APPEND (IMAP NO/BAD)");
67603
67632
  return { attempted: true, success: true, mailbox: path3 };
@@ -67606,6 +67635,28 @@ async function imapAppendSentCopy(smtpUser, raw, deps = {}) {
67606
67635
  return { attempted: true, success: false, error: errText2(e) };
67607
67636
  }
67608
67637
  }
67638
+ async function sentCopyPresent(client, path3, messageId, budget, interval, sleep2, now) {
67639
+ const deadline = now() + budget;
67640
+ for (; ; ) {
67641
+ try {
67642
+ const lock = await client.getMailboxLock(path3, { readOnly: true });
67643
+ try {
67644
+ const found = await client.search(
67645
+ { header: { "message-id": messageId }, ...NOT_DELETED },
67646
+ { uid: true }
67647
+ );
67648
+ if (Array.isArray(found) && found.length > 0) return true;
67649
+ } finally {
67650
+ lock.release();
67651
+ }
67652
+ } catch {
67653
+ return false;
67654
+ }
67655
+ const left = deadline - now();
67656
+ if (left <= 0) return false;
67657
+ await sleep2(Math.min(interval, left));
67658
+ }
67659
+ }
67609
67660
  function resolveDraftImapAccount(account, smtpUser, env = process.env) {
67610
67661
  const specs = listImapAccountSpecs(env);
67611
67662
  const why = "Mail.app's AppleScript cannot set In-Reply-To/References on a draft, so a threaded draft is filed over IMAP into the account's Drafts mailbox";
@@ -68373,7 +68424,7 @@ async function imapThread(id, deps = {}, limit = 50) {
68373
68424
  true
68374
68425
  );
68375
68426
  }
68376
- var import_imapflow, IMAP_ENV, defaultConnect, SPECIAL_USE_ALIASES, NOT_DELETED, LARGE_MAILBOX_MESSAGES, FIRST_SEARCH_WINDOW, MAX_WINDOW, MAX_UNSCOPED_SEARCH_DEPTH, COLOR_FETCH_CHUNK, ROW_QUERY, ROW_QUERY_NO_STRUCTURE, ROW_QUERY_HEADERS_ONLY, DELETED_MARGIN, poolConnect, pools, connecting, MAX_COMPOSE_SOURCE_BYTES, MAX_RFC822_INLINE_BYTES, MAX_RFC822_FILE_BYTES, HEADER_WINDOW_BYTES, MAIL_FLAG_BITS, FLAG_COLOR_NAMES, imapMarkRead, imapMarkUnread, FALLBACK_TRASH_PATH, BASE64_TAIL_WINDOW, QP_EXACT_MAX_BYTES, imapBatchMarkRead, imapBatchMarkUnread, imapBatchFlag, imapBatchUnflag, imapBatchDelete;
68427
+ var import_imapflow, IMAP_ENV, defaultConnect, SPECIAL_USE_ALIASES, NOT_DELETED, LARGE_MAILBOX_MESSAGES, FIRST_SEARCH_WINDOW, MAX_WINDOW, MAX_UNSCOPED_SEARCH_DEPTH, COLOR_FETCH_CHUNK, ROW_QUERY, ROW_QUERY_NO_STRUCTURE, ROW_QUERY_HEADERS_ONLY, DELETED_MARGIN, poolConnect, pools, connecting, SENT_COPY_GMAIL_POLL_MS, SENT_COPY_GMAIL_POLL_INTERVAL_MS, GMAIL_HOST, MAX_COMPOSE_SOURCE_BYTES, MAX_RFC822_INLINE_BYTES, MAX_RFC822_FILE_BYTES, HEADER_WINDOW_BYTES, MAIL_FLAG_BITS, FLAG_COLOR_NAMES, imapMarkRead, imapMarkUnread, FALLBACK_TRASH_PATH, BASE64_TAIL_WINDOW, QP_EXACT_MAX_BYTES, imapBatchMarkRead, imapBatchMarkUnread, imapBatchFlag, imapBatchUnflag, imapBatchDelete;
68377
68428
  var init_imapClient = __esm({
68378
68429
  "src/services/imapClient.ts"() {
68379
68430
  "use strict";
@@ -68472,6 +68523,9 @@ var init_imapClient = __esm({
68472
68523
  poolConnect = defaultConnect;
68473
68524
  pools = /* @__PURE__ */ new Map();
68474
68525
  connecting = /* @__PURE__ */ new Map();
68526
+ SENT_COPY_GMAIL_POLL_MS = 3e3;
68527
+ SENT_COPY_GMAIL_POLL_INTERVAL_MS = 500;
68528
+ GMAIL_HOST = /(^|\.)(gmail|googlemail)\.com$/i;
68475
68529
  MAX_COMPOSE_SOURCE_BYTES = 25 * 1024 * 1024;
68476
68530
  MAX_RFC822_INLINE_BYTES = 6 * 1024 * 1024;
68477
68531
  MAX_RFC822_FILE_BYTES = MAX_COMPOSE_SOURCE_BYTES;
@@ -85682,7 +85736,8 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
85682
85736
  */
85683
85737
  resolveById(id, innerAction, hint, opts = {}) {
85684
85738
  const remembered = this.idLocationIndex.get(String(id));
85685
- const loc = hint?.account && hint?.mailbox ? { account: hint.account, mailbox: hint.mailbox, explicit: true } : remembered ? { ...remembered, explicit: false } : void 0;
85739
+ const usable = hint?.account && remembered && remembered.account !== hint.account ? void 0 : remembered;
85740
+ const loc = hint?.account && hint?.mailbox ? { account: hint.account, mailbox: hint.mailbox, explicit: true } : usable ? { ...usable, explicit: false } : void 0;
85686
85741
  if (loc) {
85687
85742
  const scoped = this.resolveScopedById(id, loc, innerAction);
85688
85743
  if (scoped.kind === "hit") return scoped.output;
@@ -89276,7 +89331,8 @@ async function runCompose(deps, args) {
89276
89331
  // Best-effort Sent-folder copy (issue #220) — same field shape as
89277
89332
  // send-email, since it's the same sendViaSmtp underneath.
89278
89333
  ...result.sentCopy !== void 0 ? { sentCopy: result.sentCopy } : {},
89279
- ...result.sentCopyError !== void 0 ? { sentCopyError: result.sentCopyError } : {}
89334
+ ...result.sentCopyError !== void 0 ? { sentCopyError: result.sentCopyError } : {},
89335
+ ...result.sentCopyAlreadyFiled ? { sentCopyAlreadyFiled: true } : {}
89280
89336
  }
89281
89337
  );
89282
89338
  } catch (error3) {
@@ -91046,6 +91102,9 @@ var SENT_COPY_SCHEMA = external_exports.boolean().optional().describe(
91046
91102
  "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."
91047
91103
  );
91048
91104
  var SENT_COPY_ERROR_SCHEMA = external_exports.string().optional().describe("Present only when sentCopy is false: why the Sent-folder copy failed.");
91105
+ var SENT_COPY_ALREADY_FILED_SCHEMA = external_exports.boolean().optional().describe(
91106
+ "Present (true) only when sentCopy is true because the Sent mailbox already held this Message-ID (e.g. Gmail's SMTP files its own copy), so none was appended."
91107
+ );
91049
91108
  var SEND_ACCOUNT_SCHEMA = external_exports.string().optional().describe(
91050
91109
  "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)."
91051
91110
  );
@@ -91078,7 +91137,8 @@ registerTool(
91078
91137
  from: external_exports.string().optional().describe("AppleScript transport: the sender Mail.app used"),
91079
91138
  messageId: external_exports.string().optional(),
91080
91139
  sentCopy: SENT_COPY_SCHEMA,
91081
- sentCopyError: SENT_COPY_ERROR_SCHEMA
91140
+ sentCopyError: SENT_COPY_ERROR_SCHEMA,
91141
+ sentCopyAlreadyFiled: SENT_COPY_ALREADY_FILED_SCHEMA
91082
91142
  }
91083
91143
  },
91084
91144
  withErrorHandling(
@@ -91116,7 +91176,7 @@ registerTool(
91116
91176
  if (!result.success) {
91117
91177
  return errorResponse(result.error ?? "Failed to send email via SMTP.");
91118
91178
  }
91119
- const copyNote = result.sentCopy === true ? " (Sent-folder copy filed)" : result.sentCopy === false ? ` (Sent-folder copy NOT filed: ${result.sentCopyError ?? "unknown error"})` : "";
91179
+ const copyNote = result.sentCopy === true ? result.sentCopyAlreadyFiled ? " (Sent-folder copy already filed by the server)" : " (Sent-folder copy filed)" : result.sentCopy === false ? ` (Sent-folder copy NOT filed: ${result.sentCopyError ?? "unknown error"})` : "";
91120
91180
  return successResponse(`Email sent via SMTP to ${to.join(", ")}${attachInfo}${copyNote}`, {
91121
91181
  ok: true,
91122
91182
  recipients: to,
@@ -91124,7 +91184,8 @@ registerTool(
91124
91184
  transport: "smtp",
91125
91185
  ...result.messageId ? { messageId: result.messageId } : {},
91126
91186
  ...result.sentCopy !== void 0 ? { sentCopy: result.sentCopy } : {},
91127
- ...result.sentCopyError !== void 0 ? { sentCopyError: result.sentCopyError } : {}
91187
+ ...result.sentCopyError !== void 0 ? { sentCopyError: result.sentCopyError } : {},
91188
+ ...result.sentCopyAlreadyFiled ? { sentCopyAlreadyFiled: true } : {}
91128
91189
  });
91129
91190
  }
91130
91191
  const sent = mailManager.sendEmail(to, subject, body, cc, bcc, account, attachments);
@@ -91333,7 +91394,8 @@ registerTool(
91333
91394
  id: external_exports.string().optional(),
91334
91395
  attachmentCount: external_exports.number().optional(),
91335
91396
  sentCopy: SENT_COPY_SCHEMA,
91336
- sentCopyError: SENT_COPY_ERROR_SCHEMA
91397
+ sentCopyError: SENT_COPY_ERROR_SCHEMA,
91398
+ sentCopyAlreadyFiled: SENT_COPY_ALREADY_FILED_SCHEMA
91337
91399
  }
91338
91400
  },
91339
91401
  withErrorHandling((args) => runReply(composeDeps, args), "Error replying to message")
@@ -91359,7 +91421,8 @@ registerTool(
91359
91421
  id: external_exports.string().optional(),
91360
91422
  attachmentCount: external_exports.number().optional(),
91361
91423
  sentCopy: SENT_COPY_SCHEMA,
91362
- sentCopyError: SENT_COPY_ERROR_SCHEMA
91424
+ sentCopyError: SENT_COPY_ERROR_SCHEMA,
91425
+ sentCopyAlreadyFiled: SENT_COPY_ALREADY_FILED_SCHEMA
91363
91426
  }
91364
91427
  },
91365
91428
  withErrorHandling((args) => runForward(composeDeps, args), "Error forwarding message")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-mail-mcp",
3
- "version": "2.20.25",
3
+ "version": "2.20.27",
4
4
  "description": "MCP server for Apple Mail - read, search, send, and manage emails via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",