icloud-mcp 1.2.0 → 1.2.2
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/index.js +60 -22
- package/package.json +1 -1
- package/test.js +32 -2
package/index.js
CHANGED
|
@@ -166,9 +166,15 @@ async function bulkDeleteBySender(sender, mailbox = 'INBOX') {
|
|
|
166
166
|
await client.mailboxOpen(mailbox);
|
|
167
167
|
const uids = (await client.search({ from: sender }, { uid: true })) ?? [];
|
|
168
168
|
if (uids.length === 0) { await client.logout(); return { deleted: 0 }; }
|
|
169
|
-
|
|
169
|
+
const chunkSize = 250;
|
|
170
|
+
let deleted = 0;
|
|
171
|
+
for (let i = 0; i < uids.length; i += chunkSize) {
|
|
172
|
+
const chunk = uids.slice(i, i + chunkSize);
|
|
173
|
+
await client.messageDelete(chunk, { uid: true });
|
|
174
|
+
deleted += chunk.length;
|
|
175
|
+
}
|
|
170
176
|
await client.logout();
|
|
171
|
-
return { deleted
|
|
177
|
+
return { deleted, sender };
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
async function bulkMoveBySender(sender, targetMailbox, sourceMailbox = 'INBOX') {
|
|
@@ -177,9 +183,15 @@ async function bulkMoveBySender(sender, targetMailbox, sourceMailbox = 'INBOX')
|
|
|
177
183
|
await client.mailboxOpen(sourceMailbox);
|
|
178
184
|
const uids = (await client.search({ from: sender }, { uid: true })) ?? [];
|
|
179
185
|
if (uids.length === 0) { await client.logout(); return { moved: 0 }; }
|
|
180
|
-
|
|
186
|
+
const chunkSize = 250;
|
|
187
|
+
let moved = 0;
|
|
188
|
+
for (let i = 0; i < uids.length; i += chunkSize) {
|
|
189
|
+
const chunk = uids.slice(i, i + chunkSize);
|
|
190
|
+
await client.messageMove(chunk, targetMailbox, { uid: true });
|
|
191
|
+
moved += chunk.length;
|
|
192
|
+
}
|
|
181
193
|
await client.logout();
|
|
182
|
-
return { moved
|
|
194
|
+
return { moved, sender, targetMailbox };
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
async function bulkDeleteBySubject(subject, mailbox = 'INBOX') {
|
|
@@ -188,9 +200,15 @@ async function bulkDeleteBySubject(subject, mailbox = 'INBOX') {
|
|
|
188
200
|
await client.mailboxOpen(mailbox);
|
|
189
201
|
const uids = (await client.search({ subject }, { uid: true })) ?? [];
|
|
190
202
|
if (uids.length === 0) { await client.logout(); return { deleted: 0 }; }
|
|
191
|
-
|
|
203
|
+
const chunkSize = 250;
|
|
204
|
+
let deleted = 0;
|
|
205
|
+
for (let i = 0; i < uids.length; i += chunkSize) {
|
|
206
|
+
const chunk = uids.slice(i, i + chunkSize);
|
|
207
|
+
await client.messageDelete(chunk, { uid: true });
|
|
208
|
+
deleted += chunk.length;
|
|
209
|
+
}
|
|
192
210
|
await client.logout();
|
|
193
|
-
return { deleted
|
|
211
|
+
return { deleted, subject };
|
|
194
212
|
}
|
|
195
213
|
|
|
196
214
|
async function deleteOlderThan(days, mailbox = 'INBOX') {
|
|
@@ -201,9 +219,15 @@ async function deleteOlderThan(days, mailbox = 'INBOX') {
|
|
|
201
219
|
date.setDate(date.getDate() - days);
|
|
202
220
|
const uids = (await client.search({ before: date }, { uid: true })) ?? [];
|
|
203
221
|
if (uids.length === 0) { await client.logout(); return { deleted: 0 }; }
|
|
204
|
-
|
|
222
|
+
const chunkSize = 250;
|
|
223
|
+
let deleted = 0;
|
|
224
|
+
for (let i = 0; i < uids.length; i += chunkSize) {
|
|
225
|
+
const chunk = uids.slice(i, i + chunkSize);
|
|
226
|
+
await client.messageDelete(chunk, { uid: true });
|
|
227
|
+
deleted += chunk.length;
|
|
228
|
+
}
|
|
205
229
|
await client.logout();
|
|
206
|
-
return { deleted
|
|
230
|
+
return { deleted, olderThan: date.toISOString() };
|
|
207
231
|
}
|
|
208
232
|
|
|
209
233
|
async function getEmailsByDateRange(startDate, endDate, mailbox = 'INBOX', limit = 10) {
|
|
@@ -277,9 +301,15 @@ async function emptyTrash() {
|
|
|
277
301
|
await client.mailboxOpen('Deleted Messages');
|
|
278
302
|
const uids = (await client.search({ all: true }, { uid: true })) ?? [];
|
|
279
303
|
if (uids.length === 0) { await client.logout(); return { deleted: 0 }; }
|
|
280
|
-
|
|
304
|
+
const chunkSize = 250;
|
|
305
|
+
let deleted = 0;
|
|
306
|
+
for (let i = 0; i < uids.length; i += chunkSize) {
|
|
307
|
+
const chunk = uids.slice(i, i + chunkSize);
|
|
308
|
+
await client.messageDelete(chunk, { uid: true });
|
|
309
|
+
deleted += chunk.length;
|
|
310
|
+
}
|
|
281
311
|
await client.logout();
|
|
282
|
-
return { deleted
|
|
312
|
+
return { deleted };
|
|
283
313
|
}
|
|
284
314
|
|
|
285
315
|
async function createMailbox(name) {
|
|
@@ -400,10 +430,7 @@ async function searchEmails(query, mailbox = 'INBOX', limit = 10, filters = {})
|
|
|
400
430
|
await client.connect();
|
|
401
431
|
await client.mailboxOpen(mailbox);
|
|
402
432
|
|
|
403
|
-
// Build base text search
|
|
404
433
|
const textQuery = { or: [{ subject: query }, { from: query }, { body: query }] };
|
|
405
|
-
|
|
406
|
-
// Merge with additional filters if provided
|
|
407
434
|
const extraQuery = buildQuery(filters);
|
|
408
435
|
const finalQuery = Object.keys(extraQuery).length > 0 && !extraQuery.all
|
|
409
436
|
? { ...textQuery, ...extraQuery }
|
|
@@ -438,7 +465,6 @@ async function moveEmail(uid, targetMailbox, sourceMailbox = 'INBOX') {
|
|
|
438
465
|
return true;
|
|
439
466
|
}
|
|
440
467
|
|
|
441
|
-
// Build an IMAP search query from a filters object
|
|
442
468
|
function buildQuery(filters) {
|
|
443
469
|
const query = {};
|
|
444
470
|
if (filters.sender) query.from = filters.sender;
|
|
@@ -468,9 +494,15 @@ async function bulkMove(filters, targetMailbox, sourceMailbox = 'INBOX', dryRun
|
|
|
468
494
|
return { dryRun: true, wouldMove: uids.length, sourceMailbox, targetMailbox, filters };
|
|
469
495
|
}
|
|
470
496
|
if (uids.length === 0) { await client.logout(); return { moved: 0, sourceMailbox, targetMailbox }; }
|
|
471
|
-
|
|
497
|
+
const chunkSize = 250;
|
|
498
|
+
let moved = 0;
|
|
499
|
+
for (let i = 0; i < uids.length; i += chunkSize) {
|
|
500
|
+
const chunk = uids.slice(i, i + chunkSize);
|
|
501
|
+
await client.messageMove(chunk, targetMailbox, { uid: true });
|
|
502
|
+
moved += chunk.length;
|
|
503
|
+
}
|
|
472
504
|
await client.logout();
|
|
473
|
-
return { moved
|
|
505
|
+
return { moved, sourceMailbox, targetMailbox, filters };
|
|
474
506
|
}
|
|
475
507
|
|
|
476
508
|
async function bulkDelete(filters, sourceMailbox = 'INBOX', dryRun = false) {
|
|
@@ -484,9 +516,15 @@ async function bulkDelete(filters, sourceMailbox = 'INBOX', dryRun = false) {
|
|
|
484
516
|
return { dryRun: true, wouldDelete: uids.length, sourceMailbox, filters };
|
|
485
517
|
}
|
|
486
518
|
if (uids.length === 0) { await client.logout(); return { deleted: 0, sourceMailbox }; }
|
|
487
|
-
|
|
519
|
+
const chunkSize = 250;
|
|
520
|
+
let deleted = 0;
|
|
521
|
+
for (let i = 0; i < uids.length; i += chunkSize) {
|
|
522
|
+
const chunk = uids.slice(i, i + chunkSize);
|
|
523
|
+
await client.messageDelete(chunk, { uid: true });
|
|
524
|
+
deleted += chunk.length;
|
|
525
|
+
}
|
|
488
526
|
await client.logout();
|
|
489
|
-
return { deleted
|
|
527
|
+
return { deleted, sourceMailbox, filters };
|
|
490
528
|
}
|
|
491
529
|
|
|
492
530
|
async function countEmails(filters, mailbox = 'INBOX') {
|
|
@@ -501,7 +539,7 @@ async function countEmails(filters, mailbox = 'INBOX') {
|
|
|
501
539
|
|
|
502
540
|
async function main() {
|
|
503
541
|
const server = new Server(
|
|
504
|
-
{ name: 'icloud-mail', version: '1.
|
|
542
|
+
{ name: 'icloud-mail', version: '1.3.0' },
|
|
505
543
|
{ capabilities: { tools: {} } }
|
|
506
544
|
);
|
|
507
545
|
|
|
@@ -625,7 +663,7 @@ async function main() {
|
|
|
625
663
|
},
|
|
626
664
|
{
|
|
627
665
|
name: 'bulk_move',
|
|
628
|
-
description: 'Move emails matching any combination of filters from one mailbox to another.
|
|
666
|
+
description: 'Move emails matching any combination of filters from one mailbox to another. Processes in chunks of 250 for reliability. Use dryRun: true to preview without making changes.',
|
|
629
667
|
inputSchema: {
|
|
630
668
|
type: 'object',
|
|
631
669
|
properties: {
|
|
@@ -639,7 +677,7 @@ async function main() {
|
|
|
639
677
|
},
|
|
640
678
|
{
|
|
641
679
|
name: 'bulk_delete',
|
|
642
|
-
description: 'Delete emails matching any combination of filters.
|
|
680
|
+
description: 'Delete emails matching any combination of filters. Processes in chunks of 250 for reliability. Use dryRun: true to preview without making changes.',
|
|
643
681
|
inputSchema: {
|
|
644
682
|
type: 'object',
|
|
645
683
|
properties: {
|
|
@@ -937,4 +975,4 @@ process.on('unhandledRejection', (reason) => {
|
|
|
937
975
|
main().catch((err) => {
|
|
938
976
|
process.stderr.write(`Fatal error: ${err.message}\n${err.stack}\n`);
|
|
939
977
|
process.exit(1);
|
|
940
|
-
});
|
|
978
|
+
});
|
package/package.json
CHANGED
package/test.js
CHANGED
|
@@ -327,9 +327,39 @@ test('delete_mailbox', () => {
|
|
|
327
327
|
console.log(`\n → deleted: ${result.deleted}`);
|
|
328
328
|
});
|
|
329
329
|
|
|
330
|
+
// ─── Chunk Move Test (live) ───────────────────────────────────────────────────
|
|
331
|
+
console.log('\n📦 Chunk Move Test (live — newsletters ↔ test)');
|
|
332
|
+
|
|
333
|
+
test('bulk_move newsletters → test (chunked)', () => {
|
|
334
|
+
const beforeSource = callTool('count_emails', { mailbox: 'newsletters' });
|
|
335
|
+
assert(beforeSource.count > 0, 'newsletters should have emails');
|
|
336
|
+
console.log(`\n → newsletters before: ${beforeSource.count}`);
|
|
337
|
+
|
|
338
|
+
const moveResult = callTool('bulk_move', { sourceMailbox: 'newsletters', targetMailbox: 'test' });
|
|
339
|
+
console.log(`\n → moved: ${moveResult.moved}`);
|
|
340
|
+
assert(moveResult.moved === beforeSource.count, `moved count should match source (expected ${beforeSource.count}, got ${moveResult.moved})`);
|
|
341
|
+
|
|
342
|
+
const afterTarget = callTool('count_emails', { mailbox: 'test' });
|
|
343
|
+
console.log(`\n → test folder after: ${afterTarget.count}`);
|
|
344
|
+
assert(afterTarget.count === beforeSource.count, `test folder should have all ${beforeSource.count} emails`);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('bulk_move test → newsletters (restore)', () => {
|
|
348
|
+
const beforeSource = callTool('count_emails', { mailbox: 'test' });
|
|
349
|
+
assert(beforeSource.count > 0, 'test folder should have emails to restore');
|
|
350
|
+
console.log(`\n → test folder before restore: ${beforeSource.count}`);
|
|
351
|
+
|
|
352
|
+
const moveBack = callTool('bulk_move', { sourceMailbox: 'test', targetMailbox: 'newsletters' });
|
|
353
|
+
console.log(`\n → moved back: ${moveBack.moved}`);
|
|
354
|
+
assert(moveBack.moved === beforeSource.count, `should move all ${beforeSource.count} emails back`);
|
|
355
|
+
|
|
356
|
+
const afterRestore = callTool('count_emails', { mailbox: 'newsletters' });
|
|
357
|
+
console.log(`\n → newsletters restored: ${afterRestore.count}`);
|
|
358
|
+
assert(afterRestore.count === beforeSource.count, 'newsletters should be fully restored');
|
|
359
|
+
});
|
|
360
|
+
|
|
330
361
|
// ─── Destructive (skipped) ────────────────────────────────────────────────────
|
|
331
362
|
console.log('\n⚠️ Destructive Tests (skipped by default)');
|
|
332
|
-
console.log(' Skipping: bulk_move (live)');
|
|
333
363
|
console.log(' Skipping: bulk_delete (live)');
|
|
334
364
|
console.log(' Skipping: bulk_mark_read (live)');
|
|
335
365
|
console.log(' Skipping: bulk_mark_unread (live)');
|
|
@@ -346,4 +376,4 @@ console.log(`\n✅ Passed: ${passed}`);
|
|
|
346
376
|
console.log(`❌ Failed: ${failed}`);
|
|
347
377
|
console.log(`📊 Total: ${passed + failed}\n`);
|
|
348
378
|
|
|
349
|
-
if (failed > 0) process.exit(1);
|
|
379
|
+
if (failed > 0) process.exit(1);
|