apple-mail-mcp 2.10.6 → 2.10.7

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/build/index.js CHANGED
@@ -81653,9 +81653,8 @@ function sameImapAccount(left, right, deps) {
81653
81653
  if (aliases.has(left) && aliases.has(right)) return true;
81654
81654
  }
81655
81655
  const specs = listImapAccountSpecs();
81656
- const matches = (selector, spec) => spec.accountLabel === selector || spec.user === selector;
81657
- const leftSpec = specs.find((spec) => matches(left, spec));
81658
- const rightSpec = specs.find((spec) => matches(right, spec));
81656
+ const leftSpec = specs.find((spec) => specMatchesSelector(spec, left));
81657
+ const rightSpec = specs.find((spec) => specMatchesSelector(spec, right));
81659
81658
  return leftSpec !== void 0 && leftSpec === rightSpec;
81660
81659
  }
81661
81660
  function depsForAccount(account, deps) {
@@ -81667,14 +81666,21 @@ function depsForAccount(account, deps) {
81667
81666
  function depsForMessageRef(ref, deps) {
81668
81667
  return depsForAccount(ref.account, deps);
81669
81668
  }
81669
+ function specMatchesSelector(spec, selector) {
81670
+ return spec.accountLabel === selector || spec.user === selector || (spec.aliases?.includes(selector) ?? false);
81671
+ }
81670
81672
  function str(v) {
81671
81673
  return typeof v === "string" && v.trim() ? v.trim() : void 0;
81672
81674
  }
81675
+ function imapIdentityKey(spec) {
81676
+ return `${spec.host.trim().toLowerCase()}:${spec.port}:${spec.user.trim()}`;
81677
+ }
81673
81678
  function listImapAccountSpecs(env = process.env) {
81674
81679
  const specs = [];
81680
+ const seen = /* @__PURE__ */ new Set();
81675
81681
  const user = env[IMAP_ENV.user]?.trim();
81676
81682
  if (user) {
81677
- specs.push({
81683
+ const legacy = {
81678
81684
  accountLabel: env[IMAP_ENV.account]?.trim() || user,
81679
81685
  user,
81680
81686
  host: env[IMAP_ENV.host]?.trim() || "imap.gmail.com",
@@ -81682,7 +81688,9 @@ function listImapAccountSpecs(env = process.env) {
81682
81688
  password: env[IMAP_ENV.password],
81683
81689
  keychainService: env[IMAP_ENV.keychainService]?.trim(),
81684
81690
  keychainAccount: env[IMAP_ENV.keychainAccount]?.trim()
81685
- });
81691
+ };
81692
+ specs.push(legacy);
81693
+ seen.add(imapIdentityKey(legacy));
81686
81694
  }
81687
81695
  const json = env[IMAP_ENV.accounts]?.trim();
81688
81696
  if (json) {
@@ -81694,12 +81702,22 @@ function listImapAccountSpecs(env = process.env) {
81694
81702
  const u = str(a.user);
81695
81703
  if (!u) continue;
81696
81704
  const label = str(a.account) || str(a.accountLabel) || u;
81697
- if (specs.some((s) => s.accountLabel === label)) continue;
81705
+ const host = str(a.host) || "imap.gmail.com";
81698
81706
  const port = a.port ? Number(a.port) : 993;
81707
+ const key = imapIdentityKey({ host, port, user: u });
81708
+ if (seen.has(key)) {
81709
+ const owner = specs.find((s) => imapIdentityKey(s) === key);
81710
+ if (owner && owner.accountLabel !== label && !owner.aliases?.includes(label)) {
81711
+ (owner.aliases ??= []).push(label);
81712
+ }
81713
+ continue;
81714
+ }
81715
+ if (specs.some((s) => s.accountLabel === label)) continue;
81716
+ seen.add(key);
81699
81717
  specs.push({
81700
81718
  accountLabel: label,
81701
81719
  user: u,
81702
- host: str(a.host) || "imap.gmail.com",
81720
+ host,
81703
81721
  port,
81704
81722
  password: str(a.password),
81705
81723
  keychainService: str(a.keychainService),
@@ -81737,7 +81755,7 @@ function specToConfig(spec) {
81737
81755
  }
81738
81756
  function isImapAccount(account, env = process.env) {
81739
81757
  if (!account) return false;
81740
- return listImapAccountSpecs(env).some((s) => s.accountLabel === account || s.user === account);
81758
+ return listImapAccountSpecs(env).some((s) => specMatchesSelector(s, account));
81741
81759
  }
81742
81760
  function shouldUseImap(account, env = process.env) {
81743
81761
  return listImapAccountSpecs(env).length > 0 && (account === void 0 || isImapAccount(account, env));
@@ -81760,12 +81778,12 @@ function resolveImapConfig(env = process.env, account) {
81760
81778
  const specs = listImapAccountSpecs(env);
81761
81779
  if (specs.length === 0) {
81762
81780
  throw new Error(
81763
- `IMAP not configured. Set ${IMAP_ENV.user} (login address) to enable it. ${SETUP_HINT}`
81781
+ `IMAP not configured. Set ${IMAP_ENV.user} (login address), or ${IMAP_ENV.accounts} for multiple accounts, to enable it. ${SETUP_HINT}`
81764
81782
  );
81765
81783
  }
81766
81784
  let spec;
81767
81785
  if (account) {
81768
- spec = specs.find((s) => s.accountLabel === account || s.user === account);
81786
+ spec = specs.find((s) => specMatchesSelector(s, account));
81769
81787
  if (!spec) {
81770
81788
  throw new Error(
81771
81789
  `No IMAP account matching "${account}". Configured: ${specs.map((s) => s.accountLabel).join(", ")}.`
@@ -81977,7 +81995,7 @@ function errText(e) {
81977
81995
  var poolConnect = defaultConnect;
81978
81996
  var pools = /* @__PURE__ */ new Map();
81979
81997
  function poolKey(cfg) {
81980
- return `${cfg.host}:${cfg.port}:${cfg.user}`;
81998
+ return imapIdentityKey(cfg);
81981
81999
  }
81982
82000
  function imapIdleMs() {
81983
82001
  const raw = process.env.APPLE_MAIL_MCP_IMAP_IDLE_MS;
@@ -82035,7 +82053,7 @@ async function acquirePooled(cfg) {
82035
82053
  }
82036
82054
  }
82037
82055
  async function imapHealthCheck(deps = {}) {
82038
- if (!deps.config && !process.env[IMAP_ENV.user]?.trim()) {
82056
+ if (!deps.config && listImapAccountSpecs().length === 0) {
82039
82057
  return { configured: false, ok: false };
82040
82058
  }
82041
82059
  let cfg;
@@ -82773,7 +82791,11 @@ async function runDoctor(mailManager2) {
82773
82791
  checks.push({
82774
82792
  name: `IMAP: ${label}`,
82775
82793
  status: h.ok ? "ok" : "fail",
82776
- detail: h.ok ? `connected to ${h.host}` : `connection failed: ${h.error}. Check the Keychain password and host/port.`
82794
+ // `h.error` is optional on the health-check result, so interpolating it
82795
+ // bare printed the literal string "connection failed: undefined" for
82796
+ // any failure that carried no message (issue #138). Never render that:
82797
+ // an unexplained failure is still worth naming, but as words.
82798
+ detail: h.ok ? `connected to ${h.host}` : `connection failed: ${h.error ?? "the health check reported no detail"}. Check the Keychain password and host/port.`
82777
82799
  });
82778
82800
  }
82779
82801
  }
@@ -199,6 +199,20 @@ Each array entry accepts: `account`, `user`, `host`, `port`, `password`
199
199
  (discouraged — prefer Keychain), `keychainService`, `keychainAccount`. Each
200
200
  account keeps its own pooled IMAP connection.
201
201
 
202
+ **The array alone is enough.** You do not need the legacy single-account vars:
203
+ listing every account in `APPLE_MAIL_MCP_IMAP_ACCOUNTS` and setting none of
204
+ `APPLE_MAIL_MCP_IMAP_USER` / `_ACCOUNT` / `_HOST` is fully supported, and the
205
+ first array entry becomes the default account. (Before 2.10.7 that shape worked
206
+ for every tool but made `doctor` report `connection failed: undefined` for each
207
+ account — see #138.)
208
+
209
+ **Don't declare the same mailbox twice.** If the legacy vars already describe a
210
+ mailbox, do not also give it an array entry — even under a different `account`
211
+ nickname. An account's identity is its resolved `(host, port, user)`, not its
212
+ label, so the duplicate is recognised and collapsed rather than counted twice
213
+ (the extra nickname still works as an alias). Before 2.10.7 it was counted
214
+ twice, inflating `get-unread-count` and `get-mail-stats`.
215
+
202
216
  ---
203
217
 
204
218
  ## Step 4 (optional) — SMTP sending
@@ -333,14 +347,14 @@ GUI is ignoring.
333
347
  | Variable | Purpose |
334
348
  |----------|---------|
335
349
  | `APPLE_MAIL_MCP_DEFAULT_ACCOUNT` | Account used when a tool omits `account` (name or email). |
336
- | `APPLE_MAIL_MCP_IMAP_USER` | Primary IMAP login; setting it enables IMAP. |
350
+ | `APPLE_MAIL_MCP_IMAP_USER` | Primary IMAP login. Setting it enables IMAP — but so does `APPLE_MAIL_MCP_IMAP_ACCOUNTS` on its own; either is sufficient. |
337
351
  | `APPLE_MAIL_MCP_IMAP_ACCOUNT` | Mail.app account name to match for routing (default = USER). |
338
352
  | `APPLE_MAIL_MCP_IMAP_HOST` | IMAP host (default `imap.gmail.com`). |
339
353
  | `APPLE_MAIL_MCP_IMAP_PORT` | IMAP port (default `993`, implicit TLS). |
340
354
  | `APPLE_MAIL_MCP_IMAP_PASSWORD` | Password (discouraged; prefer Keychain). |
341
355
  | `APPLE_MAIL_MCP_IMAP_KEYCHAIN_SERVICE` | Keychain item service/server name. |
342
356
  | `APPLE_MAIL_MCP_IMAP_KEYCHAIN_ACCOUNT` | Keychain item account (default = USER). |
343
- | `APPLE_MAIL_MCP_IMAP_ACCOUNTS` | JSON array of additional accounts (multi-account). |
357
+ | `APPLE_MAIL_MCP_IMAP_ACCOUNTS` | JSON array of accounts (multi-account). Sufficient on its own; also enables IMAP. |
344
358
  | `APPLE_MAIL_MCP_IMAP_IDLE` | `1` to enable IMAP IDLE new-mail push. |
345
359
  | `APPLE_MAIL_MCP_IMAP_IDLE_MS` | Pooled-connection idle timeout in ms (default `30000`; `0` = never close). |
346
360
  | `APPLE_MAIL_MCP_STATS_BUDGET_MS` | Per-account wall-clock budget for `get-mail-stats` in ms (default `25000`, minimum `1000`). |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-mail-mcp",
3
- "version": "2.10.6",
3
+ "version": "2.10.7",
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",