@marlinjai/email-mcp 1.2.2 → 1.2.4

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/README.md CHANGED
@@ -202,6 +202,13 @@ pnpm test:watch
202
202
  pnpm test:integration
203
203
  ```
204
204
 
205
+ ## Support
206
+
207
+ If this project is useful to you, consider supporting its development:
208
+
209
+ - [GitHub Sponsors](https://github.com/sponsors/marlinjai)
210
+ - [Buy Me a Coffee](https://buymeacoffee.com/marlinjai)
211
+
205
212
  ## License
206
213
 
207
214
  MIT
package/dist/index.js CHANGED
@@ -8387,6 +8387,10 @@ var init_adapter3 = __esm({
8387
8387
  } catch {
8388
8388
  folder = requestedFolder;
8389
8389
  }
8390
+ try {
8391
+ await this.client.noop();
8392
+ } catch {
8393
+ }
8390
8394
  let lock;
8391
8395
  try {
8392
8396
  lock = await this.client.getMailboxLock(folder);
@@ -8394,8 +8398,15 @@ var init_adapter3 = __esm({
8394
8398
  throw formatImapError(error48, `Failed to open folder "${folder}"`);
8395
8399
  }
8396
8400
  try {
8401
+ let realMessageCount = -1;
8402
+ try {
8403
+ const status = await this.client.status(folder, { messages: true });
8404
+ realMessageCount = status.messages ?? -1;
8405
+ } catch {
8406
+ }
8397
8407
  const mailboxExists = this.client.mailbox?.exists ?? -1;
8398
- if (mailboxExists === 0) {
8408
+ const effectiveCount = Math.max(realMessageCount, mailboxExists);
8409
+ if (effectiveCount === 0) {
8399
8410
  return [];
8400
8411
  }
8401
8412
  const criteria = this.buildSearchCriteria(query);
@@ -8408,7 +8419,9 @@ var init_adapter3 = __esm({
8408
8419
  );
8409
8420
  allUids = Array.isArray(searchResult) ? searchResult : [];
8410
8421
  } catch (searchError) {
8411
- if (!hasCriteria) {
8422
+ const errorMsg = String(searchError?.responseText || searchError?.message || "").toLowerCase();
8423
+ const isInvalidMessage = errorMsg.includes("invalid message");
8424
+ if (isInvalidMessage || !hasCriteria) {
8412
8425
  allUids = await this.collectUidsViaFetch(query);
8413
8426
  } else {
8414
8427
  throw searchError;
@@ -8426,18 +8439,47 @@ var init_adapter3 = __esm({
8426
8439
  }
8427
8440
  /**
8428
8441
  * Fallback UID collection when UID SEARCH fails (e.g. iCloud "Invalid message number").
8429
- * Uses FETCH 1:* with sequence numbers to collect UIDs directly, optionally filtering
8430
- * by flag-based criteria (unread, starred).
8442
+ * Uses a multi-level fallback chain:
8443
+ * 1. FETCH 1:* with sequence numbers
8444
+ * 2. FETCH 1:N using mailbox.exists as explicit range
8445
+ * 3. Individual sequence number fetches (slowest but most resilient)
8431
8446
  */
8432
8447
  async collectUidsViaFetch(query) {
8433
8448
  if (!this.client) return [];
8434
8449
  const uids = [];
8435
- for await (const msg of this.client.fetch("1:*", { uid: true, flags: true })) {
8436
- if (query.unreadOnly && msg.flags?.has("\\Seen")) continue;
8437
- if (query.starredOnly && !msg.flags?.has("\\Flagged")) continue;
8438
- uids.push(msg.uid);
8450
+ try {
8451
+ for await (const msg of this.client.fetch("1:*", { uid: true, flags: true })) {
8452
+ if (query.unreadOnly && msg.flags?.has("\\Seen")) continue;
8453
+ if (query.starredOnly && !msg.flags?.has("\\Flagged")) continue;
8454
+ uids.push(msg.uid);
8455
+ }
8456
+ return uids;
8457
+ } catch {
8458
+ const exists = this.client.mailbox?.exists ?? 0;
8459
+ if (exists > 0) {
8460
+ try {
8461
+ for await (const msg of this.client.fetch(`1:${exists}`, { uid: true, flags: true })) {
8462
+ if (query.unreadOnly && msg.flags?.has("\\Seen")) continue;
8463
+ if (query.starredOnly && !msg.flags?.has("\\Flagged")) continue;
8464
+ uids.push(msg.uid);
8465
+ }
8466
+ return uids;
8467
+ } catch {
8468
+ }
8469
+ }
8470
+ const count = exists > 0 ? exists : 50;
8471
+ for (let seq = 1; seq <= count; seq++) {
8472
+ try {
8473
+ for await (const msg of this.client.fetch(String(seq), { uid: true, flags: true })) {
8474
+ if (query.unreadOnly && msg.flags?.has("\\Seen")) continue;
8475
+ if (query.starredOnly && !msg.flags?.has("\\Flagged")) continue;
8476
+ uids.push(msg.uid);
8477
+ }
8478
+ } catch {
8479
+ }
8480
+ }
8481
+ return uids;
8439
8482
  }
8440
- return uids;
8441
8483
  }
8442
8484
  /**
8443
8485
  * Fetches email data for a set of UIDs, either with full body or lightweight headers.
@@ -8624,7 +8666,8 @@ var init_adapter3 = __esm({
8624
8666
  }
8625
8667
  async deleteEmail(emailId, permanent, sourceFolder) {
8626
8668
  if (!this.client) throw new Error("Not connected");
8627
- const folder = sourceFolder || "INBOX";
8669
+ const folder = sourceFolder ? await this.resolveFolder(sourceFolder) : "INBOX";
8670
+ const trashFolder = await this.resolveFolder("Trash");
8628
8671
  let lock;
8629
8672
  try {
8630
8673
  lock = await this.client.getMailboxLock(folder);
@@ -8632,10 +8675,10 @@ var init_adapter3 = __esm({
8632
8675
  throw formatImapError(error48, `Failed to open folder "${folder}"`);
8633
8676
  }
8634
8677
  try {
8635
- if (permanent) {
8678
+ if (permanent || folder === trashFolder) {
8636
8679
  await this.client.messageDelete(emailId, { uid: true });
8637
8680
  } else {
8638
- await this.client.messageMove(emailId, "Trash", { uid: true });
8681
+ await this.client.messageMove(emailId, trashFolder, { uid: true });
8639
8682
  }
8640
8683
  } finally {
8641
8684
  lock.release();
@@ -8668,7 +8711,9 @@ var init_adapter3 = __esm({
8668
8711
  async batchDelete(emailIds, permanent, sourceFolder) {
8669
8712
  if (!this.client) throw new Error("Not connected");
8670
8713
  const result = { succeeded: [], failed: [] };
8671
- const folder = sourceFolder || "INBOX";
8714
+ const folder = sourceFolder ? await this.resolveFolder(sourceFolder) : "INBOX";
8715
+ const trashFolder = await this.resolveFolder("Trash");
8716
+ const shouldPermanentDelete = permanent || folder === trashFolder;
8672
8717
  let lock;
8673
8718
  try {
8674
8719
  lock = await this.client.getMailboxLock(folder);
@@ -8677,19 +8722,19 @@ var init_adapter3 = __esm({
8677
8722
  }
8678
8723
  try {
8679
8724
  const uidRange = emailIds.join(",");
8680
- if (permanent) {
8725
+ if (shouldPermanentDelete) {
8681
8726
  await this.client.messageDelete(uidRange, { uid: true });
8682
8727
  } else {
8683
- await this.client.messageMove(uidRange, "Trash", { uid: true });
8728
+ await this.client.messageMove(uidRange, trashFolder, { uid: true });
8684
8729
  }
8685
8730
  result.succeeded = [...emailIds];
8686
8731
  } catch (error48) {
8687
8732
  for (const id of emailIds) {
8688
8733
  try {
8689
- if (permanent) {
8734
+ if (shouldPermanentDelete) {
8690
8735
  await this.client.messageDelete(id, { uid: true });
8691
8736
  } else {
8692
- await this.client.messageMove(id, "Trash", { uid: true });
8737
+ await this.client.messageMove(id, trashFolder, { uid: true });
8693
8738
  }
8694
8739
  result.succeeded.push(id);
8695
8740
  } catch (e) {