apple-mail-mcp 2.20.16 → 2.20.18

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 +129 -28
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -65193,16 +65193,112 @@ function getHeader(headers, name2) {
65193
65193
  if (!match) return null;
65194
65194
  return match[1].replace(/\r?\n[ \t]+/g, " ").trim();
65195
65195
  }
65196
+ function decodeBytes(bytes, charset) {
65197
+ try {
65198
+ return new TextDecoder(charset.trim().toLowerCase()).decode(bytes);
65199
+ } catch {
65200
+ return bytes.toString("utf8");
65201
+ }
65202
+ }
65203
+ function parseParams(value) {
65204
+ const pieces = [];
65205
+ let current = "";
65206
+ let quoted = false;
65207
+ for (let i = 0; i < value.length; i++) {
65208
+ const ch = value[i];
65209
+ if (ch === "\\" && quoted && i + 1 < value.length) {
65210
+ current += ch + value[++i];
65211
+ continue;
65212
+ }
65213
+ if (ch === '"') quoted = !quoted;
65214
+ if (ch === ";" && !quoted) {
65215
+ pieces.push(current);
65216
+ current = "";
65217
+ } else {
65218
+ current += ch;
65219
+ }
65220
+ }
65221
+ pieces.push(current);
65222
+ const params = /* @__PURE__ */ new Map();
65223
+ for (const piece of pieces.slice(1)) {
65224
+ const eq = piece.indexOf("=");
65225
+ if (eq < 0) continue;
65226
+ let val = piece.slice(eq + 1).trim();
65227
+ if (val.length >= 2 && val.startsWith('"') && val.endsWith('"')) {
65228
+ val = val.slice(1, -1).replace(/\\(.)/g, "$1");
65229
+ }
65230
+ params.set(piece.slice(0, eq).trim().toLowerCase(), val);
65231
+ }
65232
+ return params;
65233
+ }
65234
+ function decodeEncodedWords(text) {
65235
+ return text.replace(/(\?=)\s+(?==\?)/g, "$1").replace(
65236
+ /=\?([^?\s]+)\?([bq])\?([^?]*)\?=/gi,
65237
+ (_all, charset, kind, body) => {
65238
+ const bytes = kind.toLowerCase() === "b" ? Buffer.from(body, "base64") : Buffer.from(
65239
+ body.replace(/_/g, " ").replace(
65240
+ /=([0-9a-f]{2})/gi,
65241
+ (_m, hex) => String.fromCharCode(parseInt(hex, 16))
65242
+ ),
65243
+ "latin1"
65244
+ );
65245
+ return decodeBytes(bytes, charset.split("*")[0]);
65246
+ }
65247
+ );
65248
+ }
65249
+ function decodeExtendedValue(segments) {
65250
+ let charset = "utf-8";
65251
+ const chunks = [];
65252
+ segments.forEach((segment, index) => {
65253
+ let value = segment.value;
65254
+ if (!segment.encoded) {
65255
+ chunks.push(Buffer.from(value, "utf8"));
65256
+ return;
65257
+ }
65258
+ if (index === 0) {
65259
+ const head = value.match(/^([^']*)'[^']*'([\s\S]*)$/);
65260
+ if (head) {
65261
+ charset = head[1] || charset;
65262
+ value = head[2];
65263
+ }
65264
+ }
65265
+ chunks.push(
65266
+ Buffer.from(
65267
+ value.replace(
65268
+ /%([0-9a-f]{2})/gi,
65269
+ (_m, hex) => String.fromCharCode(parseInt(hex, 16))
65270
+ ),
65271
+ "latin1"
65272
+ )
65273
+ );
65274
+ });
65275
+ return decodeBytes(Buffer.concat(chunks), charset);
65276
+ }
65277
+ function paramValue(params, name2) {
65278
+ const extended = params.get(`${name2}*`);
65279
+ if (extended !== void 0) return decodeExtendedValue([{ value: extended, encoded: true }]);
65280
+ const segments = [];
65281
+ for (let i = 0; ; i++) {
65282
+ const encoded = params.get(`${name2}*${i}*`);
65283
+ const plain = params.get(`${name2}*${i}`);
65284
+ if (encoded !== void 0) segments.push({ value: encoded, encoded: true });
65285
+ else if (plain !== void 0) segments.push({ value: plain, encoded: false });
65286
+ else break;
65287
+ }
65288
+ if (segments.length > 0) return decodeExtendedValue(segments);
65289
+ const value = params.get(name2);
65290
+ return value === void 0 ? null : decodeEncodedWords(value);
65291
+ }
65196
65292
  function extractFilename(headers) {
65197
65293
  const dispHeader = getHeader(headers, "Content-Disposition");
65198
65294
  if (dispHeader) {
65199
- const fnMatch = dispHeader.match(/filename="?([^";\r\n]+)"?/i);
65200
- if (fnMatch) return fnMatch[1].trim();
65295
+ const filename = paramValue(parseParams(dispHeader), "filename")?.trim();
65296
+ if (filename) return filename;
65201
65297
  }
65202
65298
  const ctHeader = getHeader(headers, "Content-Type");
65203
65299
  if (ctHeader) {
65204
- const nameMatch = ctHeader.match(/name="?([^";\r\n]+)"?/i);
65205
- if (nameMatch) return nameMatch[1].trim();
65300
+ const name2 = paramValue(parseParams(ctHeader), "name")?.trim();
65301
+ if (name2) return name2;
65206
65302
  }
65207
65303
  return null;
65208
65304
  }
@@ -65217,23 +65313,33 @@ function extractContentId(headers) {
65217
65313
  const id = raw.replace(/^<+/, "").replace(/>+$/, "").trim().toLowerCase();
65218
65314
  return id || null;
65219
65315
  }
65220
- function isEmbeddedResource(headers, htmlBodies) {
65316
+ function isEmbeddedResource(headers, referenced) {
65221
65317
  if (!isInlineDisposition(headers)) return false;
65222
65318
  const cid = extractContentId(headers);
65223
65319
  if (!cid) return false;
65224
- const needle = `cid:${cid}`;
65225
- return htmlBodies.some((html) => html.includes(needle));
65320
+ return referenced().has(cid);
65226
65321
  }
65227
- function collectHtmlBodies(parts) {
65228
- const bodies = [];
65322
+ function cidReferences(html) {
65323
+ const refs = /* @__PURE__ */ new Set();
65324
+ for (const match of html.matchAll(/cid:([^\s"'<>()]+)/gi)) {
65325
+ let ref = match[1];
65326
+ try {
65327
+ ref = decodeURIComponent(ref);
65328
+ } catch {
65329
+ }
65330
+ refs.add(ref.trim().toLowerCase());
65331
+ }
65332
+ return refs;
65333
+ }
65334
+ function collectCidReferences(parts) {
65335
+ const refs = /* @__PURE__ */ new Set();
65229
65336
  for (const part of parts) {
65230
65337
  if (extractMimeType(part.headers) !== "text/html") continue;
65231
65338
  const encoding = getHeader(part.headers, "Content-Transfer-Encoding");
65232
- bodies.push(
65233
- decodePartText(decodeBody(part.body, encoding), extractCharset(part.headers)).toLowerCase()
65234
- );
65339
+ const html = decodePartText(decodeBody(part.body, encoding), extractCharset(part.headers));
65340
+ for (const ref of cidReferences(html)) refs.add(ref);
65235
65341
  }
65236
- return bodies;
65342
+ return refs;
65237
65343
  }
65238
65344
  function extractSize(headers) {
65239
65345
  const dispHeader = getHeader(headers, "Content-Disposition");
@@ -65340,12 +65446,13 @@ function parseMimeAttachments(source) {
65340
65446
  const boundary = extractBoundary(source);
65341
65447
  if (!boundary) return [];
65342
65448
  const parts = walkLeafParts(source, boundary);
65343
- const htmlBodies = collectHtmlBodies(parts);
65449
+ let refs;
65450
+ const referenced = () => refs ??= collectCidReferences(parts);
65344
65451
  const attachments = [];
65345
65452
  for (const part of parts) {
65346
65453
  const filename = extractFilename(part.headers);
65347
65454
  if (!filename) continue;
65348
- if (isEmbeddedResource(part.headers, htmlBodies)) continue;
65455
+ if (isEmbeddedResource(part.headers, referenced)) continue;
65349
65456
  const encoding = getHeader(part.headers, "Content-Transfer-Encoding");
65350
65457
  attachments.push({
65351
65458
  name: filename,
@@ -65432,7 +65539,7 @@ var init_mimeParse = __esm({
65432
65539
  });
65433
65540
 
65434
65541
  // src/utils/headers.ts
65435
- function decodeEncodedWords(value) {
65542
+ function decodeEncodedWords2(value) {
65436
65543
  if (!value.includes("=?")) return value;
65437
65544
  const joined = value.replace(/(\?=)\s+(=\?)/g, "$1$2");
65438
65545
  return joined.replace(
@@ -65522,7 +65629,7 @@ function parseHeaderBlock(input) {
65522
65629
  const all = (name2) => headers.filter((h) => h.name.toLowerCase() === name2.toLowerCase()).map((h) => h.value);
65523
65630
  const decoded = (name2) => {
65524
65631
  const v = first(name2);
65525
- return v === void 0 ? void 0 : decodeEncodedWords(v);
65632
+ return v === void 0 ? void 0 : decodeEncodedWords2(v);
65526
65633
  };
65527
65634
  const dateHeader = first("Date") || void 0;
65528
65635
  let date3;
@@ -83437,16 +83544,10 @@ function buildMessageRowLoop(opts) {
83437
83544
  -- outside date range; skip
83438
83545
  else` : "";
83439
83546
  const dateClose = dateFilter ? `end if` : "";
83440
- const attBulk = withAttachments ? `
83441
- set _atts to mail attachments of _msgs` : "";
83442
83547
  const attRow = withAttachments ? `
83443
83548
  set msgHasAtt to "false"
83444
83549
  try
83445
- if _bulkOK then
83446
- if (count of (item _i of _atts)) > 0 then set msgHasAtt to "true"
83447
- else
83448
- if (count of mail attachments of (item _i of _msgs)) > 0 then set msgHasAtt to "true"
83449
- end if
83550
+ if (count of mail attachments of (item _i of _msgs)) > 0 then set msgHasAtt to "true"
83450
83551
  end try` : "";
83451
83552
  const attField = withAttachments ? ` & "${FIELD_SEP}" & msgHasAtt` : "";
83452
83553
  return `
@@ -83459,7 +83560,7 @@ function buildMessageRowLoop(opts) {
83459
83560
  set _dates to date received of _msgs
83460
83561
  set _reads to read status of _msgs
83461
83562
  set _flags to flagged status of _msgs
83462
- set _flagColors to flag index of _msgs${attBulk}
83563
+ set _flagColors to flag index of _msgs
83463
83564
  on error
83464
83565
  set _bulkOK to false
83465
83566
  end try
@@ -84821,7 +84922,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
84821
84922
  ignoring case
84822
84923
  if _wantNames contains (name of mb) then
84823
84924
  try
84824
- ${buildMessageRowLoop({ collection: `messages of mb ${searchCondition}`, limit, dedup: true, dateFilter, trailing: ` & "${FIELD_SEP}" & mbName` })}
84925
+ ${buildMessageRowLoop({ collection: `messages of mb ${searchCondition}`, limit, dedup: true, dateFilter, withAttachments: true, trailing: ` & "${FIELD_SEP}" & mbName` })}
84825
84926
  on error _errMsg number _errNum
84826
84927
  set _timedOut to true
84827
84928
  set _notSearched to _notSearched & mbName & "${DIAG_ITEM_SEP}"
@@ -84843,7 +84944,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
84843
84944
  set _notSearched to "${escapeForAppleScript(targetMailbox)}${DIAG_ITEM_SEP}"
84844
84945
  else
84845
84946
  try
84846
- ${buildMessageRowLoop({ collection: `messages of theMailbox ${searchCondition}`, limit, dateFilter })}
84947
+ ${buildMessageRowLoop({ collection: `messages of theMailbox ${searchCondition}`, limit, dateFilter, withAttachments: true })}
84847
84948
  on error _errMsg number _errNum
84848
84949
  set _timedOut to true
84849
84950
  set _notSearched to "${escapeForAppleScript(targetMailbox)}${DIAG_ITEM_SEP}"
@@ -84881,7 +84982,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
84881
84982
  set _skipped to _skipped & mbName & " (" & (mbCount as string) & ")${DIAG_ITEM_SEP}"
84882
84983
  else
84883
84984
  try
84884
- ${buildMessageRowLoop({ collection: `messages of mb ${searchCondition}`, limit, dedup: true, dateFilter, trailing: ` & "${FIELD_SEP}" & mbName` })}
84985
+ ${buildMessageRowLoop({ collection: `messages of mb ${searchCondition}`, limit, dedup: true, dateFilter, withAttachments: true, trailing: ` & "${FIELD_SEP}" & mbName` })}
84885
84986
  on error _errMsg number _errNum
84886
84987
  set _timedOut to true
84887
84988
  set _notSearched to _notSearched & mbName & "${DIAG_ITEM_SEP}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-mail-mcp",
3
- "version": "2.20.16",
3
+ "version": "2.20.18",
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",