apple-tools-mcp 3.0.0 → 3.0.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.
@@ -44,6 +44,17 @@ export const RECURRENCE_FREQUENCIES = ["daily", "weekly", "monthly", "yearly"];
44
44
  export const RECURRENCE_DAYS = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"];
45
45
  export const RSVP_RESPONSES = ["accept", "decline", "tentative"];
46
46
 
47
+ // Regression (2026-09-23): calendar_edit's classic-AppleScript fallback and
48
+ // calendar_rsvp both ran atmFindEvent (see findEventHandler below) under a
49
+ // flat 60000ms runAppleScript timeout -- exactly matching the MCP client
50
+ // SDK's own default 60s CallTool timeout, so the client could give up with
51
+ // a raw "Request timed out" before the server ever got to answer, even
52
+ // though the underlying operation kept running and generally succeeded
53
+ // (same class of bug already fixed for mail_send/reply/forward). Trimmed
54
+ // under that ceiling to leave real margin, including for atmFindEvent's own
55
+ // retry loop (~8s worst case).
56
+ export const CALENDAR_FIND_TIMEOUT_MS = 45000;
57
+
47
58
  const RSVP_STATUS = {
48
59
  accept: "accepted",
49
60
  decline: "declined",
@@ -163,16 +174,36 @@ export function defaultEndParts(start, allDay) {
163
174
  };
164
175
  }
165
176
 
177
+ /**
178
+ * calendar_edit / calendar_rsvp's classic-AppleScript fallback both resolve
179
+ * an event by uid through this handler. Regression (2026-09-23): RSVPing to
180
+ * an event created moments earlier through the daemon's separate EventKit
181
+ * session ("no event with that id was found") failed on the first lookup
182
+ * even though the event genuinely existed -- Calendar.app's classic
183
+ * AppleScript object model can lag behind a write made through EventKit.
184
+ * An EventKit-based fix isn't available here: writeOnly EventKit can only
185
+ * re-query an event via the exact in-memory EKEvent object from the
186
+ * session that created it (see describeEventKitWriteFailure below), which
187
+ * doesn't exist for a real invitation synced in from elsewhere. Retrying
188
+ * the classic lookup a few times gives Calendar.app's own model a chance
189
+ * to catch up, well inside callers' existing 60s budget.
190
+ */
166
191
  function findEventHandler() {
167
192
  return `on atmFindEvent(theUid)
168
- tell application "Calendar"
169
- repeat with cal in calendars
170
- try
171
- set hits to (every event of cal whose uid is theUid)
172
- if (count of hits) > 0 then return item 1 of hits
173
- end try
174
- end repeat
175
- end tell
193
+ set attemptsLeft to 5
194
+ repeat
195
+ tell application "Calendar"
196
+ repeat with cal in calendars
197
+ try
198
+ set hits to (every event of cal whose uid is theUid)
199
+ if (count of hits) > 0 then return item 1 of hits
200
+ end try
201
+ end repeat
202
+ end tell
203
+ set attemptsLeft to attemptsLeft - 1
204
+ if attemptsLeft is 0 then exit repeat
205
+ delay 2
206
+ end repeat
176
207
  error "EVENT_NOT_FOUND"
177
208
  end atmFindEvent`;
178
209
  }
@@ -503,11 +534,19 @@ function addRow(c) {
503
534
  var local = type === 0 ? "yes" : "no";
504
535
  rows.push(name + "<<>>" + writable + "<<>>" + local + "<<>>" + type + "<<>>" + srcName);
505
536
  }
537
+ var listError = "";
506
538
  try {
507
539
  var cals = store.calendarsForEntityType($.EKEntityTypeEvent);
508
540
  for (var i = 0; i < cals.count; i++) addRow(cals.objectAtIndex(i));
509
- } catch (e) {}
510
- try { addRow(store.defaultCalendarForNewEvents); } catch (e) {}
541
+ } catch (e) {
542
+ listError = String(e);
543
+ }
544
+ try { addRow(store.defaultCalendarForNewEvents); } catch (e) {
545
+ if (!listError) listError = String(e);
546
+ }
547
+ if (rows.length === 0 && listError) {
548
+ throw new Error("EVENTKIT_LIST_FAILED: " + listError);
549
+ }
511
550
  rows.join("|||");`;
512
551
  }
513
552
 
@@ -659,26 +698,37 @@ set AppleScript's text item delimiters to "|||"
659
698
  return outputList as string`;
660
699
 
661
700
  const result = runAppleScript(script, { timeout: 30000, appName: "Calendar" });
662
- if (!result.ok) {
663
- return { ok: false, message: failure(action, "list calendars", result) };
664
- }
665
-
666
- let calendars = result.output
667
- .split("|||")
668
- .map((entry) => entry.trim())
669
- .filter((entry) => entry.length > 0)
670
- .map((entry) => {
671
- const [name, writable] = entry.split("<<>>");
672
- return { name: name || "", writable: writable !== "no" };
673
- });
674
701
 
702
+ // EventKit does not require Calendar.app to be running, so a real EventKit
703
+ // list can stand in when the classic AppleScript path fails (app closed).
704
+ // An empty EventKit string is not that list: the JXA helper used to swallow
705
+ // every error and still exit 0, which hid TCC / timeout / not-running.
675
706
  const ekList = runAppleScript(buildEventKitListCalendarsScript(), {
676
707
  timeout: 15000,
677
708
  appName: "Calendar",
678
709
  language: "JavaScript"
679
710
  });
680
- if (ekList.ok) {
681
- calendars = mergeCalendarSources(calendars, parseEventKitCalendarList(ekList.output));
711
+ const ekCalendars = ekList.ok ? parseEventKitCalendarList(ekList.output) : [];
712
+
713
+ if (!result.ok && ekCalendars.length === 0) {
714
+ return { ok: false, message: failure(action, "list calendars", result) };
715
+ }
716
+
717
+ let calendars = result.ok
718
+ ? result.output
719
+ .split("|||")
720
+ .map((entry) => entry.trim())
721
+ .filter((entry) => entry.length > 0)
722
+ .map((entry) => {
723
+ const [name, writable] = entry.split("<<>>");
724
+ return { name: name || "", writable: writable !== "no" };
725
+ })
726
+ : [];
727
+
728
+ if (ekCalendars.length > 0) {
729
+ calendars = result.ok
730
+ ? mergeCalendarSources(calendars, ekCalendars)
731
+ : ekCalendars;
682
732
  }
683
733
 
684
734
  if (calendars.length === 0) {
@@ -1115,7 +1165,7 @@ export function calendarEdit(args = {}) {
1115
1165
  return { ok: false, message: `${action} refused: ${e.message}` };
1116
1166
  }
1117
1167
 
1118
- const result = runAppleScript(script, { timeout: 60000, appName: "Calendar" });
1168
+ const result = runAppleScript(script, { timeout: CALENDAR_FIND_TIMEOUT_MS, appName: "Calendar" });
1119
1169
  if (!result.ok) return { ok: false, message: failure(action, summary, result, [updates.description || ""]) };
1120
1170
 
1121
1171
  return {
@@ -1284,10 +1334,16 @@ export function calendarRemove(args = {}) {
1284
1334
  }
1285
1335
 
1286
1336
  export function buildRsvpScript({ eventId, status, attendeeEmail }) {
1337
+ // Calendar.app's AppleScript dictionary defines `participation status` as
1338
+ // one of unknown/accepted/declined/tentative (confirmed via `sdef`/aete
1339
+ // dump) -- there is no "needs action" term, so that bare two-word phrase
1340
+ // was a syntax error (AppleScript parsed "needs" as an identifier and
1341
+ // choked on "action" where it expected "then"). `unknown` is the correct
1342
+ // term for "no answer yet".
1287
1343
  const match = attendeeEmail
1288
1344
  ? ` if (email of att) is ${asString(attendeeEmail)} then set theAttendee to att`
1289
1345
  : ` try
1290
- if (participation status of att) is needs action then set theAttendee to att
1346
+ if (participation status of att) is unknown then set theAttendee to att
1291
1347
  end try`;
1292
1348
 
1293
1349
  return `${findEventHandler()}
@@ -1344,7 +1400,7 @@ export function calendarRsvp(args = {}) {
1344
1400
 
1345
1401
  const result = runAppleScript(
1346
1402
  buildRsvpScript({ eventId, status: RSVP_STATUS[responseRaw], attendeeEmail }),
1347
- { timeout: 60000, appName: "Calendar" }
1403
+ { timeout: CALENDAR_FIND_TIMEOUT_MS, appName: "Calendar" }
1348
1404
  );
1349
1405
 
1350
1406
  if (!result.ok) {
package/lib/mailWrite.js CHANGED
@@ -182,6 +182,27 @@ export const MAIL_SE_PROBE_TIMEOUT_MS = 4000;
182
182
  export const SYSTEM_EVENTS_AUTOMATION_PROBE_TIMEOUT_MS = 30000;
183
183
  /** Compose osascript budget (make + keystroke + send). Far below the ~60s SE wedge. */
184
184
  export const MAIL_COMPOSE_TIMEOUT_MS = 25000;
185
+ // Regression (2026-09-23): atmFindMessage's `messages of <box> whose message
186
+ // id is X` scan is genuinely slow on a large mailbox (observed 30-38s on a
187
+ // ~68,500-message account, even matching in the first/fastest box checked --
188
+ // this is a known AppleScript `whose`-filter characteristic, not specific to
189
+ // one mailbox choice). mail_mark / mail_archive / mail_trash had no explicit
190
+ // timeout at all (defaulting to the shared 30s DEFAULT_SCRIPT_TIMEOUT_MS),
191
+ // so they timed out before that scan could finish on this account. Widened
192
+ // with real margin over the observed worst case, while staying under the
193
+ // MCP client SDK's 60s default CallTool timeout (mail_send's fix comment
194
+ // above SENT_VERIFY_ATTEMPTS has the full story on why that ceiling matters).
195
+ export const MAIL_FIND_TIMEOUT_MS = 50000;
196
+ // Whole tool call must answer before the MCP client's default 60s CallTool
197
+ // timeout. 55s leaves the process time to format that answer.
198
+ export const MCP_CALL_DEADLINE_MS = 55000;
199
+ // mail_reply / mail_forward run atmFindMessage and then the send inside one
200
+ // osascript. The find alone has been observed at 30-38s, so a combined
201
+ // budget at that observed ceiling kills the script after the lookup and
202
+ // before send. Match the find-only budget (50s): a 38s lookup still has
203
+ // ~12s left for send. Verification after the script returns is capped to
204
+ // whatever remains under MCP_CALL_DEADLINE_MS, not a second full window.
205
+ export const MAIL_FIND_AND_SEND_TIMEOUT_MS = MAIL_FIND_TIMEOUT_MS;
185
206
  export const MAIL_SE_APPLEEVENT_TIMEOUT_SEC = 8;
186
207
  export const MAIL_APPLEEVENT_TIMEOUT_SEC = 15;
187
208
  export const MAIL_SE_PROBE_APPLEEVENT_TIMEOUT_SEC = 3;
@@ -840,11 +861,41 @@ export const SENT_VERIFY_NOT_FOUND = "NOT_FOUND";
840
861
  /** Older hang-recovery scripts returned FOUND to mean Sent. Still accepted. */
841
862
  export const SENT_VERIFY_FOUND = "FOUND";
842
863
  export const SENT_VERIFY_TIMEOUT_MS = 15000;
843
- export const SENT_VERIFY_ATTEMPTS = 3;
844
- export const SENT_VERIFY_RETRY_MS = 400;
864
+ /** Skip a poll that could not return before the call deadline. */
865
+ export const SENT_VERIFY_MIN_POLL_MS = 1000;
866
+ // Regression (2026-09-23): 6 consecutive real sends on an iCloud/IMAP
867
+ // account all delivered successfully, but each took a different, unbounded
868
+ // amount of time before Mail made the message queryable in Sent via
869
+ // AppleScript (observed: 20s, 20s, 30s, 65s, 47s, ~64s), even though Mail's
870
+ // `send` call itself returned in a few seconds. The old budget (3 attempts
871
+ // x 400ms = ~1.2s) gave up long before that and reported a false-negative
872
+ // failure on messages that really were delivered.
873
+ //
874
+ // A larger budget alone cannot fully fix this: the MCP client SDK's own
875
+ // default CallTool timeout is 60s (McpError -32001 "Request timed out"),
876
+ // confirmed by testing an attempts=30/retryMs=4000 (~120s worst case)
877
+ // budget against a real client -- the client gave up and errored while the
878
+ // server was still polling and about to find the (already-delivered)
879
+ // message. Any purely-synchronous verification longer than that ceiling is
880
+ // self-defeating: it cannot outrun the client's own timeout, and going past
881
+ // it only means the client sees a raw protocol timeout instead of a useful
882
+ // answer. So the budget below stays safely under that ceiling (well under
883
+ // 60s total including the initial send), and a miss within budget is
884
+ // reported as *unconfirmed*, not failed -- honesty over false confidence,
885
+ // since 9/9 real sends observed here eventually succeeded.
886
+ //
887
+ // Attempt count and sleep are a ceiling, not a guaranteed wait. Each poll
888
+ // also runs under SENT_VERIFY_TIMEOUT_MS (15s), so 6 x 15s plus the sleeps
889
+ // can blow past the 60s client timeout if a poll hangs. verifyQueuedMessage
890
+ // stops at MCP_CALL_DEADLINE_MS and shrinks each poll to the time left.
891
+ export const SENT_VERIFY_ATTEMPTS = 6;
892
+ export const SENT_VERIFY_RETRY_MS = 2000;
845
893
 
846
894
  export const MAIL_VERIFY_MISS_GUIDANCE =
847
- "Mail reported the send call succeeded, but the message was not found in Sent or Outbox. Do not assume it was delivered.";
895
+ "Mail reported the send call succeeded, and this account's Sent folder has been observed to take up to a minute or more to " +
896
+ "reflect a new message (iCloud/IMAP sync lag), longer than it is safe to block a single tool call for. This is not a known " +
897
+ "failure -- treat it as unconfirmed. Check Sent (and Outbox) before deciding whether to retry; retrying a message that " +
898
+ "already landed sends a second copy.";
848
899
 
849
900
  function defaultSentVerifySleep(ms) {
850
901
  if (!ms || ms <= 0) return;
@@ -853,11 +904,13 @@ function defaultSentVerifySleep(ms) {
853
904
 
854
905
  /** Test hook: skip the retry delay without changing attempt count. */
855
906
  export const sentVerifyClock = {
856
- sleep: defaultSentVerifySleep
907
+ sleep: defaultSentVerifySleep,
908
+ now: () => Date.now()
857
909
  };
858
910
 
859
911
  export function resetSentVerifyClock() {
860
912
  sentVerifyClock.sleep = defaultSentVerifySleep;
913
+ sentVerifyClock.now = () => Date.now();
861
914
  }
862
915
 
863
916
  /**
@@ -1158,10 +1211,12 @@ function pickSentVerifyScript({
1158
1211
  * Compose matches require To + subject (Message-ID when available) — never
1159
1212
  * subject alone. Returns a hit object or null. Verify failure is not success.
1160
1213
  */
1161
- export function recoverIfInSent(match = {}) {
1214
+ export function recoverIfInSent(match = {}, { timeout = SENT_VERIFY_TIMEOUT_MS } = {}) {
1162
1215
  const script = pickSentVerifyScript(match);
1163
1216
  if (!script) return null;
1164
- const result = runAppleScript(script, { timeout: SENT_VERIFY_TIMEOUT_MS, appName: "Mail" });
1217
+ const pollTimeout = Math.min(SENT_VERIFY_TIMEOUT_MS, Math.max(0, timeout));
1218
+ if (pollTimeout < SENT_VERIFY_MIN_POLL_MS) return null;
1219
+ const result = runAppleScript(script, { timeout: pollTimeout, appName: "Mail" });
1165
1220
  if (!result.ok) return null;
1166
1221
  return parseSentVerifyOutput(result.output);
1167
1222
  }
@@ -1169,13 +1224,25 @@ export function recoverIfInSent(match = {}) {
1169
1224
  /**
1170
1225
  * Poll Sent/Outbox after a send that returned without throw.
1171
1226
  */
1172
- export function verifyQueuedMessage(match = {}, { attempts = SENT_VERIFY_ATTEMPTS, retryMs = SENT_VERIFY_RETRY_MS } = {}) {
1227
+ export function verifyQueuedMessage(match = {}, {
1228
+ attempts = SENT_VERIFY_ATTEMPTS,
1229
+ retryMs = SENT_VERIFY_RETRY_MS,
1230
+ deadlineMs = null
1231
+ } = {}) {
1232
+ const deadline = Number.isFinite(deadlineMs)
1233
+ ? deadlineMs
1234
+ : sentVerifyClock.now() + MCP_CALL_DEADLINE_MS;
1173
1235
  let last = null;
1174
1236
  const n = Math.max(1, attempts);
1175
1237
  for (let i = 0; i < n; i++) {
1176
- last = recoverIfInSent(match);
1238
+ const remaining = deadline - sentVerifyClock.now();
1239
+ if (remaining < SENT_VERIFY_MIN_POLL_MS) break;
1240
+ last = recoverIfInSent(match, { timeout: Math.min(SENT_VERIFY_TIMEOUT_MS, remaining) });
1177
1241
  if (last) return last;
1178
- if (i < n - 1) sentVerifyClock.sleep(retryMs);
1242
+ if (i >= n - 1) break;
1243
+ const sleepFor = Math.min(retryMs, deadline - sentVerifyClock.now() - SENT_VERIFY_MIN_POLL_MS);
1244
+ if (sleepFor <= 0) break;
1245
+ sentVerifyClock.sleep(sleepFor);
1179
1246
  }
1180
1247
  return last;
1181
1248
  }
@@ -1282,7 +1349,7 @@ function sendVerifiedSuccess({ action, successSummary, details, verified, recove
1282
1349
  }
1283
1350
 
1284
1351
  function verifyMissMessage(action, summary) {
1285
- return `${action} failed — attempted to ${summary}. ${MAIL_VERIFY_MISS_GUIDANCE}`;
1352
+ return `${action}: unconfirmed — attempted to ${summary}. ${MAIL_VERIFY_MISS_GUIDANCE}`;
1286
1353
  }
1287
1354
 
1288
1355
  function finalizeMailWrite({
@@ -1297,15 +1364,20 @@ function finalizeMailWrite({
1297
1364
  to = null,
1298
1365
  messageId = null,
1299
1366
  successSummary,
1300
- details
1367
+ details,
1368
+ deadlineMs = null
1301
1369
  }) {
1302
1370
  const match = { inReplyTo, subject, forwardTo, to, messageId };
1371
+ const deadline = Number.isFinite(deadlineMs)
1372
+ ? deadlineMs
1373
+ : sentVerifyClock.now() + MCP_CALL_DEADLINE_MS;
1374
+ const remaining = () => deadline - sentVerifyClock.now();
1303
1375
 
1304
1376
  if (result.ok) {
1305
1377
  if (!sendNow) {
1306
1378
  return { ok: true, delivered: false, mailbox: null, message: writeSuccessMessage(action, successSummary, details) };
1307
1379
  }
1308
- const verified = verifyQueuedMessage(match);
1380
+ const verified = verifyQueuedMessage(match, { deadlineMs: deadline });
1309
1381
  if (!verified) {
1310
1382
  return { ok: false, delivered: false, mailbox: null, message: verifyMissMessage(action, summary) };
1311
1383
  }
@@ -1313,7 +1385,7 @@ function finalizeMailWrite({
1313
1385
  }
1314
1386
 
1315
1387
  if (sendNow && isMailSendTimeout(result)) {
1316
- const recovered = recoverIfInSent(match);
1388
+ const recovered = recoverIfInSent(match, { timeout: remaining() });
1317
1389
  if (recovered) {
1318
1390
  return sendVerifiedSuccess({ action, successSummary, details, verified: recovered, recovered: true });
1319
1391
  }
@@ -1397,6 +1469,7 @@ export function mailCompose(args = {}, { draft = false, indexerMode = isIndexerM
1397
1469
  html: bodyFormat === "html"
1398
1470
  });
1399
1471
 
1472
+ const deadlineMs = sentVerifyClock.now() + MCP_CALL_DEADLINE_MS;
1400
1473
  const result = runAppleScript(script, { timeout: MAIL_COMPOSE_TIMEOUT_MS, appName: "Mail" });
1401
1474
  if (
1402
1475
  !result.ok &&
@@ -1420,7 +1493,8 @@ export function mailCompose(args = {}, { draft = false, indexerMode = isIndexerM
1420
1493
  cc: cc.addresses.join(", ") || undefined,
1421
1494
  bcc: bcc.addresses.length ? `${bcc.addresses.length} recipient(s)` : undefined,
1422
1495
  subject: truncate(subject.text, 150)
1423
- }
1496
+ },
1497
+ deadlineMs
1424
1498
  });
1425
1499
  }
1426
1500
 
@@ -1475,9 +1549,10 @@ export function mailReply(args = {}) {
1475
1549
  });
1476
1550
  if (!plan.proceed) return plannedWriteResult(plan);
1477
1551
 
1552
+ const deadlineMs = sentVerifyClock.now() + MCP_CALL_DEADLINE_MS;
1478
1553
  const result = runAppleScript(
1479
1554
  buildReplyScript({ messageId: resolved.messageId, body: body.text, replyAll, sendNow }),
1480
- { timeout: 60000, appName: "Mail" }
1555
+ { timeout: MAIL_FIND_AND_SEND_TIMEOUT_MS, appName: "Mail" }
1481
1556
  );
1482
1557
  return finalizeMailWrite({
1483
1558
  action,
@@ -1490,7 +1565,8 @@ export function mailReply(args = {}) {
1490
1565
  details: {
1491
1566
  message_id: resolved.messageId,
1492
1567
  reply_all: String(replyAll)
1493
- }
1568
+ },
1569
+ deadlineMs
1494
1570
  });
1495
1571
  }
1496
1572
 
@@ -1538,9 +1614,10 @@ export function mailForward(args = {}) {
1538
1614
  });
1539
1615
  if (!plan.proceed) return plannedWriteResult(plan);
1540
1616
 
1617
+ const deadlineMs = sentVerifyClock.now() + MCP_CALL_DEADLINE_MS;
1541
1618
  const result = runAppleScript(
1542
1619
  buildForwardScript({ messageId: resolved.messageId, to: to.addresses, body: body.text, sendNow }),
1543
- { timeout: 60000, appName: "Mail" }
1620
+ { timeout: MAIL_FIND_AND_SEND_TIMEOUT_MS, appName: "Mail" }
1544
1621
  );
1545
1622
  return finalizeMailWrite({
1546
1623
  action,
@@ -1554,7 +1631,8 @@ export function mailForward(args = {}) {
1554
1631
  details: {
1555
1632
  message_id: resolved.messageId,
1556
1633
  to: to.addresses.join(", ")
1557
- }
1634
+ },
1635
+ deadlineMs
1558
1636
  });
1559
1637
  }
1560
1638
 
@@ -1586,7 +1664,7 @@ export function mailMark(args = {}) {
1586
1664
  });
1587
1665
  if (!plan.proceed) return plannedWriteResult(plan);
1588
1666
 
1589
- const result = runAppleScript(buildMarkScript({ messageId: resolved.messageId, read: status === "read" }), { appName: "Mail" });
1667
+ const result = runAppleScript(buildMarkScript({ messageId: resolved.messageId, read: status === "read" }), { timeout: MAIL_FIND_TIMEOUT_MS, appName: "Mail" });
1590
1668
  if (!result.ok) return { ok: false, message: failure(action, summary, result, []) };
1591
1669
 
1592
1670
  return { ok: true, message: writeSuccessMessage(action, `marked as ${status}`, { message_id: resolved.messageId }) };
@@ -1637,7 +1715,7 @@ export function mailArchive(args = {}) {
1637
1715
  mailboxNames: ["Archive", "All Mail", "Archived"],
1638
1716
  allowDeleteFallback: false
1639
1717
  }),
1640
- { appName: "Mail" }
1718
+ { timeout: MAIL_FIND_TIMEOUT_MS, appName: "Mail" }
1641
1719
  );
1642
1720
  if (!result.ok) {
1643
1721
  if (result.kind === "not_found" && String(result.error).includes("ARCHIVE_MAILBOX_NOT_FOUND")) {
@@ -1670,7 +1748,7 @@ export function mailTrash(args = {}) {
1670
1748
  mailboxNames: ["Trash", "Deleted Messages", "Bin"],
1671
1749
  allowDeleteFallback: true
1672
1750
  }),
1673
- { appName: "Mail" }
1751
+ { timeout: MAIL_FIND_TIMEOUT_MS, appName: "Mail" }
1674
1752
  );
1675
1753
  if (!result.ok) return { ok: false, message: failure(action, summary, result, []) };
1676
1754
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-tools-mcp",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "MCP server for semantic search and write actions across Apple Mail, Messages, Calendar, and Contacts. Speaks stdio (local) or bearer-token-authenticated Streamable HTTP (LAN / Tailscale).",
5
5
  "type": "module",
6
6
  "main": "index.js",