apple-mail-mcp 2.11.0 → 2.11.1

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.
Files changed (2) hide show
  1. package/build/index.js +29 -8
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -78071,11 +78071,17 @@ function sleep(ms) {
78071
78071
  }
78072
78072
  }
78073
78073
  }
78074
+ var PERMISSION_DENIED_PATTERN = /not authorized|not permitted|access.*denied/i;
78075
+ var PERMISSION_DENIED_MESSAGE = "Permission denied. Grant automation access in System Settings > Privacy & Security > Automation.";
78076
+ function isPermissionDenied(error2) {
78077
+ if (!error2) return false;
78078
+ return PERMISSION_DENIED_PATTERN.test(error2) || error2.includes(PERMISSION_DENIED_MESSAGE);
78079
+ }
78074
78080
  var ERROR_MAPPINGS = [
78075
78081
  // Permission errors
78076
78082
  {
78077
- pattern: /not authorized|not permitted|access.*denied/i,
78078
- message: "Permission denied. Grant automation access in System Settings > Privacy & Security > Automation."
78083
+ pattern: PERMISSION_DENIED_PATTERN,
78084
+ message: PERMISSION_DENIED_MESSAGE
78079
78085
  },
78080
78086
  // Application not running
78081
78087
  {
@@ -82841,7 +82847,7 @@ ${actionStmts.join("\n")}
82841
82847
  message: "Mail.app is accessible"
82842
82848
  });
82843
82849
  } else {
82844
- const errorHint = mailCheck.error?.includes("not authorized") ? " (check System Settings > Privacy & Security > Automation)" : "";
82850
+ const errorHint = isPermissionDenied(mailCheck.error) ? " (check System Settings > Privacy & Security > Automation)" : "";
82845
82851
  checks.push({
82846
82852
  name: "mail_app",
82847
82853
  passed: false,
@@ -82857,7 +82863,7 @@ ${actionStmts.join("\n")}
82857
82863
  message: "AppleScript automation permissions granted"
82858
82864
  });
82859
82865
  } else {
82860
- const isPermError = permCheck.error?.includes("not authorized") || permCheck.error?.includes("not permitted");
82866
+ const isPermError = isPermissionDenied(permCheck.error);
82861
82867
  checks.push({
82862
82868
  name: "permissions",
82863
82869
  passed: !isPermError,
@@ -83681,7 +83687,12 @@ function structuredRow(m, account, path) {
83681
83687
  flagColorIndex: mailFlagColorIndex(m.flags),
83682
83688
  mailbox: path,
83683
83689
  account,
83684
- hasAttachments: false,
83690
+ // Derived from BODYSTRUCTURE, which the list/search fetch now requests.
83691
+ // This was hardcoded `false` from 2.2.0 until 2.11.1 — indistinguishable to
83692
+ // a caller from "no attachments", so every IMAP-sourced message claimed to
83693
+ // have none. Falls back to false only when the fetch carried no
83694
+ // BODYSTRUCTURE at all.
83695
+ hasAttachments: bodyStructureHasAttachments(m.bodyStructure),
83685
83696
  // Message-ID (when the envelope carries it) is the strongest cross-/intra-
83686
83697
  // backend dedup key for the multi-account merge (imapMultiAccount.ts). The
83687
83698
  // AppleScript path does not expose it, so cross-backend dedup falls back to
@@ -83712,7 +83723,10 @@ async function run(args, listMode, deps) {
83712
83723
  const byUid = /* @__PURE__ */ new Map();
83713
83724
  for await (const msg of client.fetch(
83714
83725
  newest.join(","),
83715
- { envelope: true, flags: true },
83726
+ // BODYSTRUCTURE rides along so `hasAttachments` is computed rather
83727
+ // than assumed. Measured on 50 real messages: ~390ms -> ~465ms for
83728
+ // the fetch (~17%), same single round trip, no extra request.
83729
+ { envelope: true, flags: true, bodyStructure: true },
83716
83730
  { uid: true }
83717
83731
  )) {
83718
83732
  byUid.set(msg.uid, msg);
@@ -84206,7 +84220,8 @@ function collectAttachments(node, out = []) {
84206
84220
  if (!node) return out;
84207
84221
  const filename = node.dispositionParameters?.filename || node.parameters?.name;
84208
84222
  const disposition = node.disposition?.toLowerCase();
84209
- const isAttachment = !!node.part && (disposition === "attachment" || !!filename && disposition !== "inline");
84223
+ const isEmbeddedByReference = disposition === "inline" && !!node.id;
84224
+ const isAttachment = !!node.part && (disposition === "attachment" || !!filename && !isEmbeddedByReference);
84210
84225
  if (isAttachment) {
84211
84226
  out.push({
84212
84227
  part: node.part,
@@ -84218,6 +84233,9 @@ function collectAttachments(node, out = []) {
84218
84233
  for (const child of node.childNodes ?? []) collectAttachments(child, out);
84219
84234
  return out;
84220
84235
  }
84236
+ function bodyStructureHasAttachments(node) {
84237
+ return !!node && collectAttachments(node).length > 0;
84238
+ }
84221
84239
  async function streamToBuffer(content, maxBytes) {
84222
84240
  const chunks = [];
84223
84241
  let total = 0;
@@ -84389,7 +84407,10 @@ async function imapThread(id, deps = {}, limit = 50) {
84389
84407
  const msgs = [];
84390
84408
  for await (const msg of client.fetch(
84391
84409
  uids.join(","),
84392
- { envelope: true, flags: true },
84410
+ // Same reason as the list/search fetch: get-thread emits structured
84411
+ // rows too, so it needs BODYSTRUCTURE or its hasAttachments would
84412
+ // silently disagree with the same message seen via search.
84413
+ { envelope: true, flags: true, bodyStructure: true },
84393
84414
  { uid: true }
84394
84415
  )) {
84395
84416
  msgs.push(msg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-mail-mcp",
3
- "version": "2.11.0",
3
+ "version": "2.11.1",
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",