apple-mail-mcp 2.20.9 → 2.20.10

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 (4) hide show
  1. package/README.md +27 -3
  2. package/build/cli.js +46 -2
  3. package/build/index.js +20455 -20380
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -237,12 +237,29 @@ Search for messages matching criteria. Searches all accounts by default.
237
237
  | `account` | string | No | Account to search in (omit to search all accounts) |
238
238
  | `isRead` | boolean | No | Filter by read status |
239
239
  | `isFlagged` | boolean | No | Filter by flagged status |
240
+ | `flagColor` | string | No | Filter to messages flagged with exactly this color — see [`flag-message`](#flag-message--unflag-message) for the palette. Implies `isFlagged: true`; pairing this with `isFlagged: false` is rejected. See the flag-color note below. |
240
241
  | `dateFrom` | string | No | Start date filter (e.g., "January 1, 2026") |
241
242
  | `dateTo` | string | No | End date filter (e.g., "March 1, 2026") |
242
243
  | `limit` | number | No | Max results, 1–500 (default: 50) |
243
244
  | `offset` | number | No | Newest matches to skip, ≥ 0 (for pagination). IMAP accounts only; without a `mailbox`, `offset` + `limit` may not exceed 5,000 |
244
245
 
245
- **Returns:** List of matching messages with ID, date, subject, sender, and read state.
246
+ **Returns:** List of matching messages with ID, date, subject, sender, read state,
247
+ and `flagColor` when flagged with an explicit color.
248
+
249
+ **Flag color as a label (#286).** Every row from both backends carries
250
+ `flagColor` (the palette name — `red`, `orange`, `yellow`, `green`, `blue`,
251
+ `purple`, `gray`) whenever the message is flagged with an explicit color;
252
+ omitted for an unflagged message or one flagged with Mail's plain default (no
253
+ color chosen). Combined with the `flagColor` filter above, a colored flag
254
+ works as a label across mailboxes: flag a set of messages with
255
+ `flag-message`/`batch-flag-messages`, then find exactly that set later with
256
+ `search-messages`, without moving anything into a dedicated folder. **Known
257
+ limitation, inherent to Apple's `$MailFlagBit0/1/2` IMAP wire encoding, not
258
+ specific to this server:** `red` (palette index 0) sets no keyword bits, so
259
+ over IMAP it is indistinguishable from a plain colorless flag, both when
260
+ reading `flagColor` back and when filtering on it. AppleScript-backed
261
+ accounts are unaffected (Mail's own `flag index` property is `0` for red, `-1`
262
+ for none).
246
263
 
247
264
  ⚠️ **Row dates.** Since 2.19.7 every row — on both the IMAP and AppleScript
248
265
  backends — carries both `dateSent` (the message's `Date:` header) and
@@ -392,7 +409,9 @@ List messages in a mailbox.
392
409
  | `from` | string | No | Filter by sender email address or name |
393
410
  | `unreadOnly` | boolean | No | Only show unread messages |
394
411
 
395
- **Returns:** List of messages with ID, date, subject, and sender.
412
+ **Returns:** List of messages with ID, date, subject, sender, and `flagColor`
413
+ when flagged with an explicit color (see the flag-color note under
414
+ [`search-messages`](#search-messages), which also supports filtering by it).
396
415
 
397
416
  ⚠️ **Row dates.** Since 2.19.7 every row — on both the **IMAP** and
398
417
  **AppleScript** backends — carries both `dateSent` (the message's `Date:`
@@ -995,7 +1014,12 @@ Flag or unflag a message. `flag-message` optionally takes a flag **color**; `unf
995
1014
 
996
1015
  **Flag colors** are an Apple Mail feature — the message's `flag index` (0 red, 1 orange, 2 yellow, 3 green, 4 blue, 5 purple, 6 gray), which is the property a Mail smart mailbox can match on. **The color is applied on both routes** (since 2.10.0): AppleScript sets the flag index directly, and for an **IMAP-routed** id (`imap:…`) the color is written as Mail.app's `$MailFlagBit0/1/2` keywords — a 3-bit field holding the same palette index. `\Flagged` on its own really is colorless, but those keywords ride alongside it in an ordinary `UID STORE`, so a smart mailbox keyed on flag color matches an IMAP-flagged message too. You do **not** need to resolve to a numeric id just to color a flag.
997
1016
 
998
- To **read** a color, the IMAP read path returns `flagColorIndex` in `structuredContent` — the same 0-6 palette index, omitted when the message carries no color bits. The AppleScript read path does not populate it.
1017
+ To **read** a color back, or **find** messages by color, use
1018
+ [`search-messages`](#search-messages) (or [`list-messages`](#list-messages) for
1019
+ read-back only) — both routes now return `flagColor` (the palette name) on
1020
+ every flagged-with-a-color row, and `search-messages` takes a `flagColor`
1021
+ filter using this same palette (#286). The IMAP route additionally still
1022
+ carries the raw 0-6 `flagColorIndex` for back-compat.
999
1023
 
1000
1024
  ---
1001
1025
 
package/build/cli.js CHANGED
@@ -58021,6 +58021,7 @@ var init_auditLog = __esm({
58021
58021
  // src/services/imapClient.ts
58022
58022
  var imapClient_exports = {};
58023
58023
  __export(imapClient_exports, {
58024
+ FLAG_COLOR_NAMES: () => FLAG_COLOR_NAMES,
58024
58025
  HEADER_WINDOW_BYTES: () => HEADER_WINDOW_BYTES,
58025
58026
  IMAP_ENV: () => IMAP_ENV,
58026
58027
  LARGE_MAILBOX_MESSAGES: () => LARGE_MAILBOX_MESSAGES,
@@ -58038,6 +58039,7 @@ __export(imapClient_exports, {
58038
58039
  decodedSizeWithoutBody: () => decodedSizeWithoutBody,
58039
58040
  dropAllPools: () => dropAllPools,
58040
58041
  encodeImapId: () => encodeImapId,
58042
+ flagColorName: () => flagColorName,
58041
58043
  imapAppendDraft: () => imapAppendDraft,
58042
58044
  imapAppendSentCopy: () => imapAppendSentCopy,
58043
58045
  imapBatchDelete: () => imapBatchDelete,
@@ -58324,8 +58326,25 @@ function buildCriteria(a, listMode) {
58324
58326
  if (a.isFlagged === false) c.unflagged = true;
58325
58327
  if (a.dateFrom) c.since = new Date(a.dateFrom);
58326
58328
  if (a.dateTo) c.before = new Date(a.dateTo);
58329
+ if (a.flagColorIndex !== void 0) {
58330
+ c.flagged = true;
58331
+ Object.assign(c, flagColorSearchTerm(a.flagColorIndex));
58332
+ }
58327
58333
  return c;
58328
58334
  }
58335
+ function flagColorSearchTerm(colorIndex) {
58336
+ const { set, clear } = mailFlagBitsFor(colorIndex);
58337
+ const terms = [
58338
+ ...set.map((kw) => ({ keyword: kw })),
58339
+ ...clear.map((kw) => ({ unKeyword: kw }))
58340
+ ];
58341
+ if (terms.length === 0) return {};
58342
+ if (terms.length === 1) return terms[0];
58343
+ const negated = terms.map(
58344
+ (t) => "keyword" in t ? { unKeyword: t.keyword } : { keyword: t.unKeyword }
58345
+ );
58346
+ return { not: { or: negated } };
58347
+ }
58329
58348
  function validDate(d) {
58330
58349
  if (!d) return void 0;
58331
58350
  const parsed = d instanceof Date ? d : new Date(d);
@@ -58366,6 +58385,7 @@ function isoOrEmpty(d) {
58366
58385
  }
58367
58386
  function structuredRow(m, account, path3) {
58368
58387
  const env = m.envelope ?? {};
58388
+ const flagColorIndex = mailFlagColorIndex(m.flags);
58369
58389
  return {
58370
58390
  id: encodeImapId(account, path3, m.uid),
58371
58391
  subject: env.subject || "(no subject)",
@@ -58382,7 +58402,19 @@ function structuredRow(m, account, path3) {
58382
58402
  dateReceived: isoOrEmpty(validDate(m.internalDate) ?? headerDate(m)),
58383
58403
  isRead: m.flags?.has("\\Seen") ?? false,
58384
58404
  isFlagged: m.flags?.has("\\Flagged") ?? false,
58385
- flagColorIndex: mailFlagColorIndex(m.flags),
58405
+ // flagColorIndex has been computed here since 2.19.x but never reached a
58406
+ // caller in friendly form (#286, @j5pu) — flagColor is the palette name
58407
+ // (e.g. "orange"), omitted whenever the index is (unflagged, or flagged
58408
+ // with Mail's plain default and no explicit color). flagColorIndex stays
58409
+ // for back-compat.
58410
+ // ⚠️ "red" (index 0) sets no $MailFlagBit keyword — it is wire-identical to
58411
+ // a plain colorless flag over IMAP, so a red-flagged message reads back
58412
+ // here with no flagColor (and flagColor: "red" in search-messages also
58413
+ // matches a plain flag). Inherent to Apple's bitfield encoding, not
58414
+ // fixable from the client side; the AppleScript route is unaffected (Mail's
58415
+ // own `flag index` is 0 for red, -1 for none).
58416
+ flagColorIndex,
58417
+ flagColor: flagColorName(flagColorIndex),
58386
58418
  mailbox: path3,
58387
58419
  account,
58388
58420
  // Derived from BODYSTRUCTURE, which the list/search fetch now requests.
@@ -59427,6 +59459,9 @@ function mailFlagColorIndex(flags) {
59427
59459
  }
59428
59460
  return any ? idx : void 0;
59429
59461
  }
59462
+ function flagColorName(index) {
59463
+ return index === void 0 ? void 0 : FLAG_COLOR_NAMES[index];
59464
+ }
59430
59465
  function flagOp(id, flag, add, deps) {
59431
59466
  const ref = decodeImapId(id);
59432
59467
  if (!ref) return Promise.resolve({ success: false, error: `Not an IMAP message id: "${id}".` });
@@ -59940,7 +59975,7 @@ async function imapThread(id, deps = {}, limit = 50) {
59940
59975
  true
59941
59976
  );
59942
59977
  }
59943
- var import_imapflow, IMAP_ENV, defaultConnect, SPECIAL_USE_ALIASES, NOT_DELETED, LARGE_MAILBOX_MESSAGES, FIRST_SEARCH_WINDOW, MAX_WINDOW, MAX_UNSCOPED_SEARCH_DEPTH, 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, imapMarkRead, imapMarkUnread, FALLBACK_TRASH_PATH, BASE64_TAIL_WINDOW, QP_EXACT_MAX_BYTES, imapBatchMarkRead, imapBatchMarkUnread, imapBatchFlag, imapBatchUnflag, imapBatchDelete;
59978
+ var import_imapflow, IMAP_ENV, defaultConnect, SPECIAL_USE_ALIASES, NOT_DELETED, LARGE_MAILBOX_MESSAGES, FIRST_SEARCH_WINDOW, MAX_WINDOW, MAX_UNSCOPED_SEARCH_DEPTH, 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;
59944
59979
  var init_imapClient = __esm({
59945
59980
  "src/services/imapClient.ts"() {
59946
59981
  "use strict";
@@ -60043,6 +60078,15 @@ var init_imapClient = __esm({
60043
60078
  MAX_RFC822_FILE_BYTES = MAX_COMPOSE_SOURCE_BYTES;
60044
60079
  HEADER_WINDOW_BYTES = 64 * 1024;
60045
60080
  MAIL_FLAG_BITS = ["$MailFlagBit0", "$MailFlagBit1", "$MailFlagBit2"];
60081
+ FLAG_COLOR_NAMES = [
60082
+ "red",
60083
+ "orange",
60084
+ "yellow",
60085
+ "green",
60086
+ "blue",
60087
+ "purple",
60088
+ "gray"
60089
+ ];
60046
60090
  imapMarkRead = (id, deps = {}) => flagOp(id, "\\Seen", true, deps);
60047
60091
  imapMarkUnread = (id, deps = {}) => flagOp(id, "\\Seen", false, deps);
60048
60092
  FALLBACK_TRASH_PATH = "Trash";