apple-mail-mcp 2.8.6 → 2.8.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.
Files changed (2) hide show
  1. package/build/index.js +159 -95
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -20707,7 +20707,7 @@ var require_thread_stream = __commonJS({
20707
20707
  var { version: version3 } = require_package2();
20708
20708
  var { EventEmitter } = __require("events");
20709
20709
  var { Worker } = __require("worker_threads");
20710
- var { join: join5 } = __require("path");
20710
+ var { join: join6 } = __require("path");
20711
20711
  var { pathToFileURL } = __require("url");
20712
20712
  var { wait } = require_wait();
20713
20713
  var {
@@ -20758,7 +20758,7 @@ var require_thread_stream = __commonJS({
20758
20758
  function createWorker(stream, opts) {
20759
20759
  const { filename, workerData } = opts;
20760
20760
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
20761
- const toExecute = bundlerOverrides["thread-stream-worker"] || join5(__dirname, "lib", "worker.js");
20761
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join6(__dirname, "lib", "worker.js");
20762
20762
  const worker = new Worker(toExecute, {
20763
20763
  ...opts.workerOpts,
20764
20764
  name: opts.workerOpts?.name || "thread-stream",
@@ -21224,9 +21224,9 @@ var require_transport = __commonJS({
21224
21224
  "node_modules/.pnpm/pino@10.3.1/node_modules/pino/lib/transport.js"(exports, module) {
21225
21225
  "use strict";
21226
21226
  var { createRequire: createRequire2 } = __require("module");
21227
- var { existsSync: existsSync5 } = __require("node:fs");
21227
+ var { existsSync: existsSync6 } = __require("node:fs");
21228
21228
  var getCallers = require_caller();
21229
- var { join: join5, isAbsolute: isAbsolute3, sep: sep2 } = __require("node:path");
21229
+ var { join: join6, isAbsolute: isAbsolute3, sep: sep2 } = __require("node:path");
21230
21230
  var { fileURLToPath } = __require("node:url");
21231
21231
  var sleep2 = require_atomic_sleep();
21232
21232
  var onExit = require_on_exit_leak_free();
@@ -21298,7 +21298,7 @@ var require_transport = __commonJS({
21298
21298
  return false;
21299
21299
  }
21300
21300
  }
21301
- return isAbsolute3(path) && !existsSync5(path);
21301
+ return isAbsolute3(path) && !existsSync6(path);
21302
21302
  }
21303
21303
  function stripQuotes(value) {
21304
21304
  const first = value[0];
@@ -21379,7 +21379,7 @@ var require_transport = __commonJS({
21379
21379
  throw new Error("only one of target or targets can be specified");
21380
21380
  }
21381
21381
  if (targets) {
21382
- target = bundlerOverrides["pino-worker"] || join5(__dirname, "worker.js");
21382
+ target = bundlerOverrides["pino-worker"] || join6(__dirname, "worker.js");
21383
21383
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
21384
21384
  return {
21385
21385
  ...dest,
@@ -21397,7 +21397,7 @@ var require_transport = __commonJS({
21397
21397
  });
21398
21398
  });
21399
21399
  } else if (pipeline) {
21400
- target = bundlerOverrides["pino-worker"] || join5(__dirname, "worker.js");
21400
+ target = bundlerOverrides["pino-worker"] || join6(__dirname, "worker.js");
21401
21401
  options.pipelines = [pipeline.map((dest) => {
21402
21402
  return {
21403
21403
  ...dest,
@@ -21420,7 +21420,7 @@ var require_transport = __commonJS({
21420
21420
  return origin;
21421
21421
  }
21422
21422
  if (origin === "pino/file") {
21423
- return join5(__dirname, "..", "file.js");
21423
+ return join6(__dirname, "..", "file.js");
21424
21424
  }
21425
21425
  let fixTarget2;
21426
21426
  for (const filePath of callers) {
@@ -22400,7 +22400,7 @@ var require_safe_stable_stringify = __commonJS({
22400
22400
  return circularValue;
22401
22401
  }
22402
22402
  let res = "";
22403
- let join5 = ",";
22403
+ let join6 = ",";
22404
22404
  const originalIndentation = indentation;
22405
22405
  if (Array.isArray(value)) {
22406
22406
  if (value.length === 0) {
@@ -22414,7 +22414,7 @@ var require_safe_stable_stringify = __commonJS({
22414
22414
  indentation += spacer;
22415
22415
  res += `
22416
22416
  ${indentation}`;
22417
- join5 = `,
22417
+ join6 = `,
22418
22418
  ${indentation}`;
22419
22419
  }
22420
22420
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -22422,13 +22422,13 @@ ${indentation}`;
22422
22422
  for (; i < maximumValuesToStringify - 1; i++) {
22423
22423
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
22424
22424
  res += tmp2 !== void 0 ? tmp2 : "null";
22425
- res += join5;
22425
+ res += join6;
22426
22426
  }
22427
22427
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
22428
22428
  res += tmp !== void 0 ? tmp : "null";
22429
22429
  if (value.length - 1 > maximumBreadth) {
22430
22430
  const removedKeys = value.length - maximumBreadth - 1;
22431
- res += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
22431
+ res += `${join6}"... ${getItemCount(removedKeys)} not stringified"`;
22432
22432
  }
22433
22433
  if (spacer !== "") {
22434
22434
  res += `
@@ -22449,7 +22449,7 @@ ${originalIndentation}`;
22449
22449
  let separator = "";
22450
22450
  if (spacer !== "") {
22451
22451
  indentation += spacer;
22452
- join5 = `,
22452
+ join6 = `,
22453
22453
  ${indentation}`;
22454
22454
  whitespace = " ";
22455
22455
  }
@@ -22463,13 +22463,13 @@ ${indentation}`;
22463
22463
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
22464
22464
  if (tmp !== void 0) {
22465
22465
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
22466
- separator = join5;
22466
+ separator = join6;
22467
22467
  }
22468
22468
  }
22469
22469
  if (keyLength > maximumBreadth) {
22470
22470
  const removedKeys = keyLength - maximumBreadth;
22471
22471
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
22472
- separator = join5;
22472
+ separator = join6;
22473
22473
  }
22474
22474
  if (spacer !== "" && separator.length > 1) {
22475
22475
  res = `
@@ -22510,7 +22510,7 @@ ${originalIndentation}`;
22510
22510
  }
22511
22511
  const originalIndentation = indentation;
22512
22512
  let res = "";
22513
- let join5 = ",";
22513
+ let join6 = ",";
22514
22514
  if (Array.isArray(value)) {
22515
22515
  if (value.length === 0) {
22516
22516
  return "[]";
@@ -22523,7 +22523,7 @@ ${originalIndentation}`;
22523
22523
  indentation += spacer;
22524
22524
  res += `
22525
22525
  ${indentation}`;
22526
- join5 = `,
22526
+ join6 = `,
22527
22527
  ${indentation}`;
22528
22528
  }
22529
22529
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -22531,13 +22531,13 @@ ${indentation}`;
22531
22531
  for (; i < maximumValuesToStringify - 1; i++) {
22532
22532
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
22533
22533
  res += tmp2 !== void 0 ? tmp2 : "null";
22534
- res += join5;
22534
+ res += join6;
22535
22535
  }
22536
22536
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
22537
22537
  res += tmp !== void 0 ? tmp : "null";
22538
22538
  if (value.length - 1 > maximumBreadth) {
22539
22539
  const removedKeys = value.length - maximumBreadth - 1;
22540
- res += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
22540
+ res += `${join6}"... ${getItemCount(removedKeys)} not stringified"`;
22541
22541
  }
22542
22542
  if (spacer !== "") {
22543
22543
  res += `
@@ -22550,7 +22550,7 @@ ${originalIndentation}`;
22550
22550
  let whitespace = "";
22551
22551
  if (spacer !== "") {
22552
22552
  indentation += spacer;
22553
- join5 = `,
22553
+ join6 = `,
22554
22554
  ${indentation}`;
22555
22555
  whitespace = " ";
22556
22556
  }
@@ -22559,7 +22559,7 @@ ${indentation}`;
22559
22559
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
22560
22560
  if (tmp !== void 0) {
22561
22561
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
22562
- separator = join5;
22562
+ separator = join6;
22563
22563
  }
22564
22564
  }
22565
22565
  if (spacer !== "" && separator.length > 1) {
@@ -22617,20 +22617,20 @@ ${originalIndentation}`;
22617
22617
  indentation += spacer;
22618
22618
  let res2 = `
22619
22619
  ${indentation}`;
22620
- const join6 = `,
22620
+ const join7 = `,
22621
22621
  ${indentation}`;
22622
22622
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
22623
22623
  let i = 0;
22624
22624
  for (; i < maximumValuesToStringify - 1; i++) {
22625
22625
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
22626
22626
  res2 += tmp2 !== void 0 ? tmp2 : "null";
22627
- res2 += join6;
22627
+ res2 += join7;
22628
22628
  }
22629
22629
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
22630
22630
  res2 += tmp !== void 0 ? tmp : "null";
22631
22631
  if (value.length - 1 > maximumBreadth) {
22632
22632
  const removedKeys = value.length - maximumBreadth - 1;
22633
- res2 += `${join6}"... ${getItemCount(removedKeys)} not stringified"`;
22633
+ res2 += `${join7}"... ${getItemCount(removedKeys)} not stringified"`;
22634
22634
  }
22635
22635
  res2 += `
22636
22636
  ${originalIndentation}`;
@@ -22646,16 +22646,16 @@ ${originalIndentation}`;
22646
22646
  return '"[Object]"';
22647
22647
  }
22648
22648
  indentation += spacer;
22649
- const join5 = `,
22649
+ const join6 = `,
22650
22650
  ${indentation}`;
22651
22651
  let res = "";
22652
22652
  let separator = "";
22653
22653
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
22654
22654
  if (isTypedArrayWithEntries(value)) {
22655
- res += stringifyTypedArray(value, join5, maximumBreadth);
22655
+ res += stringifyTypedArray(value, join6, maximumBreadth);
22656
22656
  keys = keys.slice(value.length);
22657
22657
  maximumPropertiesToStringify -= value.length;
22658
- separator = join5;
22658
+ separator = join6;
22659
22659
  }
22660
22660
  if (deterministic) {
22661
22661
  keys = sort(keys, comparator);
@@ -22666,13 +22666,13 @@ ${indentation}`;
22666
22666
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
22667
22667
  if (tmp !== void 0) {
22668
22668
  res += `${separator}${strEscape(key2)}: ${tmp}`;
22669
- separator = join5;
22669
+ separator = join6;
22670
22670
  }
22671
22671
  }
22672
22672
  if (keyLength > maximumBreadth) {
22673
22673
  const removedKeys = keyLength - maximumBreadth;
22674
22674
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
22675
- separator = join5;
22675
+ separator = join6;
22676
22676
  }
22677
22677
  if (separator !== "") {
22678
22678
  res = `
@@ -76147,9 +76147,9 @@ var StdioServerTransport = class {
76147
76147
 
76148
76148
  // src/services/appleMailManager.ts
76149
76149
  import { spawnSync as spawnSync2 } from "child_process";
76150
- import { existsSync as existsSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync2, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
76151
- import { isAbsolute, resolve, sep, join as join3 } from "path";
76152
- import { homedir as homedir2 } from "os";
76150
+ import { existsSync as existsSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync2, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
76151
+ import { isAbsolute, resolve, sep, join as join4 } from "path";
76152
+ import { homedir as homedir3 } from "os";
76153
76153
 
76154
76154
  // src/utils/applescript.ts
76155
76155
  import { execSync, spawnSync } from "child_process";
@@ -76685,6 +76685,114 @@ function materializeAttachments(attachments) {
76685
76685
  };
76686
76686
  }
76687
76687
 
76688
+ // src/utils/contactsDb.ts
76689
+ import { existsSync as existsSync2, readdirSync } from "fs";
76690
+ import { join as join3 } from "path";
76691
+ import { homedir as homedir2 } from "os";
76692
+ function loadSqlite() {
76693
+ try {
76694
+ const mod = __require("node:sqlite");
76695
+ if (!mod || typeof mod.DatabaseSync !== "function") {
76696
+ console.error(
76697
+ "[contactsDb] node:sqlite unavailable (no DatabaseSync export); returning no contacts"
76698
+ );
76699
+ return null;
76700
+ }
76701
+ return mod;
76702
+ } catch (err) {
76703
+ console.error(
76704
+ `[contactsDb] node:sqlite not available (requires Node >= 22.5); returning no contacts: ${err instanceof Error ? err.message : String(err)}`
76705
+ );
76706
+ return null;
76707
+ }
76708
+ }
76709
+ function resolveContactsDbPaths(baseDir) {
76710
+ const root = baseDir ?? join3(homedir2(), "Library", "Application Support", "AddressBook");
76711
+ const paths = [];
76712
+ const topLevel = join3(root, "AddressBook-v22.abcddb");
76713
+ if (existsSync2(topLevel)) paths.push(topLevel);
76714
+ const sourcesDir = join3(root, "Sources");
76715
+ if (existsSync2(sourcesDir)) {
76716
+ let entries = [];
76717
+ try {
76718
+ entries = readdirSync(sourcesDir);
76719
+ } catch {
76720
+ entries = [];
76721
+ }
76722
+ for (const entry of entries) {
76723
+ const candidate = join3(sourcesDir, entry, "AddressBook-v22.abcddb");
76724
+ if (existsSync2(candidate)) paths.push(candidate);
76725
+ }
76726
+ }
76727
+ return paths;
76728
+ }
76729
+ var RECORD_SQL = `
76730
+ SELECT r.Z_PK pk,
76731
+ TRIM(COALESCE(r.ZFIRSTNAME,'')||' '||COALESCE(r.ZLASTNAME,'')) fullname,
76732
+ r.ZORGANIZATION org, r.ZNICKNAME nick
76733
+ FROM ZABCDRECORD r
76734
+ WHERE lower(TRIM(COALESCE(r.ZFIRSTNAME,'')||' '||COALESCE(r.ZLASTNAME,''))) LIKE ?
76735
+ OR lower(COALESCE(r.ZORGANIZATION,'')) LIKE ?
76736
+ OR lower(COALESCE(r.ZNICKNAME,'')) LIKE ?
76737
+ OR r.Z_PK IN (SELECT ZOWNER FROM ZABCDEMAILADDRESS WHERE lower(COALESCE(ZADDRESS,'')) LIKE ?)
76738
+ `;
76739
+ var EMAILS_SQL = `SELECT ZADDRESS AS v FROM ZABCDEMAILADDRESS WHERE ZOWNER = ? AND ZADDRESS IS NOT NULL`;
76740
+ var PHONES_SQL = `SELECT ZFULLNUMBER AS v FROM ZABCDPHONENUMBER WHERE ZOWNER = ? AND ZFULLNUMBER IS NOT NULL`;
76741
+ function displayName(row) {
76742
+ const fullname = typeof row.fullname === "string" ? row.fullname.trim() : "";
76743
+ if (fullname) return fullname;
76744
+ const org = typeof row.org === "string" ? row.org.trim() : "";
76745
+ if (org) return org;
76746
+ const nick = typeof row.nick === "string" ? row.nick.trim() : "";
76747
+ if (nick) return nick;
76748
+ return "(no name)";
76749
+ }
76750
+ function searchOneDb(sqlite, dbPath, like) {
76751
+ let db = null;
76752
+ try {
76753
+ db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
76754
+ const records = db.prepare(RECORD_SQL).all(like, like, like, like);
76755
+ const emailStmt = db.prepare(EMAILS_SQL);
76756
+ const phoneStmt = db.prepare(PHONES_SQL);
76757
+ const contacts = [];
76758
+ for (const rec of records) {
76759
+ const pk = rec.pk;
76760
+ const emails = emailStmt.all(pk).map((r) => r.v).filter((v) => typeof v === "string" && v.length > 0);
76761
+ const phones = phoneStmt.all(pk).map((r) => r.v).filter((v) => typeof v === "string" && v.length > 0);
76762
+ contacts.push({ name: displayName(rec), emails, phones });
76763
+ }
76764
+ return contacts;
76765
+ } catch (err) {
76766
+ console.error(
76767
+ `[contactsDb] skipping unreadable Contacts DB ${dbPath}: ${err instanceof Error ? err.message : String(err)}`
76768
+ );
76769
+ return [];
76770
+ } finally {
76771
+ try {
76772
+ db?.close();
76773
+ } catch {
76774
+ }
76775
+ }
76776
+ }
76777
+ function searchContactsDb(query, opts) {
76778
+ if (!query || !query.trim()) return [];
76779
+ const sqlite = loadSqlite();
76780
+ if (!sqlite) return [];
76781
+ const dbPaths = opts?.dbPaths ?? resolveContactsDbPaths();
76782
+ const like = `%${query.toLowerCase()}%`;
76783
+ const seen = /* @__PURE__ */ new Set();
76784
+ const results = [];
76785
+ for (const dbPath of dbPaths) {
76786
+ for (const contact of searchOneDb(sqlite, dbPath, like)) {
76787
+ const key = `${contact.name} ${[...contact.emails].sort().join(",")}`;
76788
+ if (seen.has(key)) continue;
76789
+ seen.add(key);
76790
+ results.push(contact);
76791
+ }
76792
+ }
76793
+ return results;
76794
+ }
76795
+
76688
76796
  // src/services/appleMailManager.ts
76689
76797
  function getMailboxScanThreshold() {
76690
76798
  const raw = process.env.APPLE_MAIL_MAX_SEARCH_MAILBOX;
@@ -76740,7 +76848,7 @@ function splitSearchDiagnostics(output, account) {
76740
76848
  }
76741
76849
  return { payload, diagnostics };
76742
76850
  }
76743
- var ALLOWED_SAVE_ROOTS = [homedir2(), "/tmp", "/private/tmp", "/Volumes"];
76851
+ var ALLOWED_SAVE_ROOTS = [homedir3(), "/tmp", "/private/tmp", "/Volumes"];
76744
76852
  function isPathWithinAllowedRoots(resolvedPath) {
76745
76853
  return ALLOWED_SAVE_ROOTS.some((root) => {
76746
76854
  const base = root.endsWith(sep) ? root.slice(0, -1) : root;
@@ -76784,7 +76892,7 @@ function buildAttachmentCommands(attachments) {
76784
76892
  if (!isAbsolute(filePath)) {
76785
76893
  throw new Error(`Attachment path must be absolute: "${filePath}"`);
76786
76894
  }
76787
- if (!existsSync2(filePath)) {
76895
+ if (!existsSync3(filePath)) {
76788
76896
  throw new Error(`Attachment file not found: "${filePath}"`);
76789
76897
  }
76790
76898
  }
@@ -78606,7 +78714,7 @@ var AppleMailManager = class {
78606
78714
  let dir = null;
78607
78715
  try {
78608
78716
  dir = mkdtempSync2("/private/tmp/amcp-fetch-");
78609
- const dest = join3(dir, attachmentName.replace(/[/\\]/g, "_"));
78717
+ const dest = join4(dir, attachmentName.replace(/[/\\]/g, "_"));
78610
78718
  const ok = this.saveAttachment(id, attachmentName, dest);
78611
78719
  if (!ok) {
78612
78720
  return {
@@ -79040,60 +79148,16 @@ ${actionStmts.join("\n")}
79040
79148
  // Contacts Integration
79041
79149
  // ===========================================================================
79042
79150
  /**
79043
- * Search contacts by name or email.
79151
+ * Search contacts by name, organization, nickname, or email address.
79152
+ *
79153
+ * Reads the macOS Contacts (AddressBook) SQLite databases directly via Full
79154
+ * Disk Access — it does NOT drive Contacts.app over AppleScript, so it raises
79155
+ * no Automation / Apple-Events permission prompt (the old path would hang on
79156
+ * that unanswerable prompt on headless/scheduled hosts). See
79157
+ * {@link searchContactsDb}.
79044
79158
  */
79045
79159
  searchContacts(query) {
79046
- const safeQuery = escapeForAppleScript(query);
79047
- const script = `
79048
- tell application "Contacts"
79049
- set matchedContacts to {}
79050
- set foundPeople to (every person whose name contains "${safeQuery}") & (every person whose value of emails contains "${safeQuery}")
79051
-
79052
- -- Deduplicate by tracking IDs
79053
- set seenIds to {}
79054
- repeat with p in foundPeople
79055
- set pid to id of p
79056
- if seenIds does not contain pid then
79057
- set end of seenIds to pid
79058
- -- Per-person try: one malformed contact (e.g. no emails) must not
79059
- -- abort the whole search and return an empty list (audit finding #13).
79060
- try
79061
- set pName to name of p
79062
- set pEmails to ""
79063
- repeat with e in emails of p
79064
- if pEmails is not "" then set pEmails to pEmails & ","
79065
- set pEmails to pEmails & (value of e)
79066
- end repeat
79067
- set pPhones to ""
79068
- repeat with ph in phones of p
79069
- if pPhones is not "" then set pPhones to pPhones & ","
79070
- set pPhones to pPhones & (value of ph)
79071
- end repeat
79072
- set end of matchedContacts to pName & "${FIELD_SEP}" & pEmails & "${FIELD_SEP}" & pPhones
79073
- end try
79074
- end if
79075
- end repeat
79076
-
79077
- set AppleScript's text item delimiters to "${RECORD_SEP}"
79078
- return matchedContacts as text
79079
- end tell
79080
- `;
79081
- const result = executeAppleScript(script);
79082
- if (!result.success || !result.output.trim()) {
79083
- return [];
79084
- }
79085
- const items = result.output.split(RECORD_SEP);
79086
- const contacts = [];
79087
- for (const item of items) {
79088
- const parts = item.split(FIELD_SEP);
79089
- if (parts.length < 3) continue;
79090
- contacts.push({
79091
- name: parts[0],
79092
- emails: parts[1] ? parts[1].split(",").filter(Boolean) : [],
79093
- phones: parts[2] ? parts[2].split(",").filter(Boolean) : []
79094
- });
79095
- }
79096
- return contacts;
79160
+ return searchContactsDb(query);
79097
79161
  }
79098
79162
  // ===========================================================================
79099
79163
  // Email Templates
@@ -79408,7 +79472,7 @@ import { resolve as resolvePath, join as joinPath } from "path";
79408
79472
  var import_nodemailer = __toESM(require_nodemailer(), 1);
79409
79473
  import { execFileSync } from "child_process";
79410
79474
  import { isAbsolute as isAbsolute2 } from "path";
79411
- import { existsSync as existsSync3 } from "fs";
79475
+ import { existsSync as existsSync4 } from "fs";
79412
79476
 
79413
79477
  // src/utils/docsUrls.ts
79414
79478
  var SETUP_GUIDE_URL = "https://github.com/sweetrb/apple-mail-mcp/blob/main/docs/IMAP-SETUP.md";
@@ -79484,7 +79548,7 @@ function buildAttachments(attachments) {
79484
79548
  return attachments.map((a) => {
79485
79549
  if (typeof a === "string") {
79486
79550
  if (!isAbsolute2(a)) throw new Error(`Attachment path must be absolute: "${a}"`);
79487
- if (!existsSync3(a)) throw new Error(`Attachment file not found: "${a}"`);
79551
+ if (!existsSync4(a)) throw new Error(`Attachment file not found: "${a}"`);
79488
79552
  return { path: a };
79489
79553
  }
79490
79554
  if (!a.filename || !a.contentBase64) {
@@ -81078,18 +81142,18 @@ var ImapIdleWatcher = class {
81078
81142
  };
81079
81143
 
81080
81144
  // src/services/fileConfig.ts
81081
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
81082
- import { join as join4 } from "path";
81083
- import { homedir as homedir3 } from "os";
81145
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
81146
+ import { join as join5 } from "path";
81147
+ import { homedir as homedir4 } from "os";
81084
81148
  function fileConfigPath(env = process.env) {
81085
81149
  const override = env.APPLE_MAIL_MCP_CONFIG_FILE;
81086
81150
  if (override && override.trim()) return override.trim();
81087
- return join4(homedir3(), "Library", "Application Support", "apple-mail-mcp", "config.json");
81151
+ return join5(homedir4(), "Library", "Application Support", "apple-mail-mcp", "config.json");
81088
81152
  }
81089
81153
  function loadFileConfig(env = process.env, path = fileConfigPath(env)) {
81090
81154
  const applied = [];
81091
81155
  try {
81092
- if (!existsSync4(path)) return applied;
81156
+ if (!existsSync5(path)) return applied;
81093
81157
  const parsed = JSON.parse(readFileSync3(path, "utf8"));
81094
81158
  if (!parsed || typeof parsed !== "object") return applied;
81095
81159
  for (const [k, v] of Object.entries(parsed)) {
@@ -82697,7 +82761,7 @@ server.registerTool(
82697
82761
  server.registerTool(
82698
82762
  "search-contacts",
82699
82763
  {
82700
- description: "Use when: looking up a person in Contacts.app by name to find their email address(es) before composing or sending mail.\nReturns: matching contacts with their names and email addresses.\nDo not use when: searching email messages (use search-messages) \u2014 this queries Contacts, not the mailbox.",
82764
+ description: "Use when: looking up a person in Contacts by name, organization, nickname, or email to find their email address(es)/phone(s) before composing or sending mail. Reads the macOS Contacts database directly (needs Full Disk Access; does NOT require Contacts.app to be running or an Automation / Apple-Events grant).\nReturns: matching contacts with their names, email addresses, and phone numbers.\nDo not use when: searching email messages (use search-messages) \u2014 this queries Contacts, not the mailbox.",
82701
82765
  inputSchema: {
82702
82766
  query: external_exports.string().min(1, "Search query is required")
82703
82767
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-mail-mcp",
3
- "version": "2.8.6",
3
+ "version": "2.8.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",