icloud-mcp 1.4.1 → 1.4.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.
Files changed (3) hide show
  1. package/README.md +35 -7
  2. package/index.js +145 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -43,9 +43,39 @@ Then find the install location:
43
43
  npm root -g
44
44
  ```
45
45
 
46
- This will return a path like `/opt/homebrew/lib/node_modules` or `/usr/local/lib/node_modules`.
46
+ The path varies by setup:
47
47
 
48
- ### 3. Configure Claude Desktop
48
+ | Setup | Typical path |
49
+ |-------|-------------|
50
+ | Mac with Homebrew Node | `/opt/homebrew/lib/node_modules` |
51
+ | Mac with system Node | `/usr/local/lib/node_modules` |
52
+ | nvm | `~/.nvm/versions/node/v20.x.x/lib/node_modules` |
53
+
54
+ ### 3. Verify your setup
55
+
56
+ Before configuring Claude Desktop, run the doctor command to confirm everything is working:
57
+
58
+ ```bash
59
+ IMAP_USER="you@icloud.com" IMAP_PASSWORD="your-app-specific-password" node $(npm root -g)/icloud-mcp/index.js --doctor
60
+ ```
61
+
62
+ You should see:
63
+
64
+ ```
65
+ icloud-mcp doctor
66
+ ─────────────────────────────
67
+ ✅ IMAP_USER is set
68
+ ✅ IMAP_PASSWORD is set
69
+ ✅ Connected to imap.mail.me.com:993
70
+ ✅ Authenticated as you@icloud.com
71
+ ✅ INBOX opened (12453 messages)
72
+ ─────────────────────────────
73
+ All checks passed. Ready to use with Claude Desktop.
74
+ ```
75
+
76
+ If any step fails, a plain-English explanation and suggested fix will be shown.
77
+
78
+ ### 4. Configure Claude Desktop
49
79
 
50
80
  Open your Claude Desktop config file:
51
81
 
@@ -53,7 +83,7 @@ Open your Claude Desktop config file:
53
83
  open ~/Library/Application\ Support/Claude/claude_desktop_config.json
54
84
  ```
55
85
 
56
- Add the following under `mcpServers`, replacing the path with your npm root path from the previous step:
86
+ Add the following under `mcpServers`, replacing the path with your npm root from step 2:
57
87
 
58
88
  ```json
59
89
  {
@@ -70,9 +100,7 @@ Add the following under `mcpServers`, replacing the path with your npm root path
70
100
  }
71
101
  ```
72
102
 
73
- > **Note:** If your `npm root -g` returned a different path, replace `/opt/homebrew/lib/node_modules` with that path.
74
-
75
- ### 4. Add Custom Instructions (Recommended)
103
+ ### 5. Add Custom Instructions (Recommended)
76
104
 
77
105
  For large inbox operations, add the following to Claude Desktop's custom instructions to ensure Claude stays on track and checks in with you regularly. Go to **Claude Desktop → Settings → Custom Instructions** and add:
78
106
 
@@ -85,7 +113,7 @@ When using icloud-mail tools:
85
113
  5. If you are ever unsure what you have done so far, call log_read before proceeding
86
114
  ```
87
115
 
88
- ### 5. Restart Claude Desktop
116
+ ### 6. Restart Claude Desktop
89
117
 
90
118
  Fully quit Claude Desktop (Cmd+Q) and reopen it. You should now be able to manage your iCloud inbox through Claude.
91
119
 
package/index.js CHANGED
@@ -15,8 +15,12 @@ const IMAP_USER = process.env.IMAP_USER;
15
15
  const IMAP_PASSWORD = process.env.IMAP_PASSWORD;
16
16
 
17
17
  if (!IMAP_USER || !IMAP_PASSWORD) {
18
- process.stderr.write('Error: IMAP_USER and IMAP_PASSWORD environment variables are required\n');
19
- process.exit(1);
18
+ if (process.argv.includes('--doctor')) {
19
+ // Doctor will handle missing credentials with friendly output
20
+ } else {
21
+ process.stderr.write('Error: IMAP_USER and IMAP_PASSWORD environment variables are required\n');
22
+ process.exit(1);
23
+ }
20
24
  }
21
25
 
22
26
  function createClient() {
@@ -977,7 +981,7 @@ function logClear() {
977
981
 
978
982
  async function main() {
979
983
  const server = new Server(
980
- { name: 'icloud-mail', version: '1.4.1' },
984
+ { name: 'icloud-mail', version: '1.6.0' },
981
985
  { capabilities: { tools: {} } }
982
986
  );
983
987
 
@@ -1426,7 +1430,7 @@ async function main() {
1426
1430
  }
1427
1431
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1428
1432
  } catch (error) {
1429
- return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true };
1433
+ return { content: [{ type: 'text', text: `Error: ${friendlyError(error)}` }], isError: true };
1430
1434
  }
1431
1435
  });
1432
1436
 
@@ -1435,6 +1439,132 @@ async function main() {
1435
1439
  process.stderr.write('iCloud Mail MCP Server running\n');
1436
1440
  }
1437
1441
 
1442
+ // ─── Friendly error messages ──────────────────────────────────────────────────
1443
+
1444
+ function friendlyError(err) {
1445
+ const msg = err.message ?? '';
1446
+
1447
+ if (msg.includes('AUTHENTICATIONFAILED') || msg.includes('Invalid credentials') || msg.includes('Authentication failed')) {
1448
+ return [
1449
+ 'Authentication failed.',
1450
+ '→ Make sure IMAP_PASSWORD is an app-specific password, not your regular iCloud password.',
1451
+ '→ Generate one at: appleid.apple.com → Sign-In and Security → App-Specific Passwords',
1452
+ '→ Also check that IMAP_USER is your full iCloud email address (e.g. you@icloud.com)'
1453
+ ].join('\n');
1454
+ }
1455
+
1456
+ if (msg.includes('ECONNREFUSED') || msg.includes('ENOTFOUND') || msg.includes('ENETUNREACH')) {
1457
+ return [
1458
+ 'Could not reach imap.mail.me.com:993.',
1459
+ '→ Check your internet connection.',
1460
+ '→ If you are behind a firewall or VPN, port 993 may be blocked.'
1461
+ ].join('\n');
1462
+ }
1463
+
1464
+ if (msg.includes('ETIMEDOUT') || msg.includes('socket hang up')) {
1465
+ return [
1466
+ 'Connection to iCloud timed out.',
1467
+ '→ Check your internet connection and try again.',
1468
+ '→ iCloud IMAP can be slow under load — this is usually transient.'
1469
+ ].join('\n');
1470
+ }
1471
+
1472
+ if (msg.includes('ECONNRESET')) {
1473
+ return [
1474
+ 'iCloud closed the connection unexpectedly.',
1475
+ '→ This is usually transient. Try again in a few seconds.'
1476
+ ].join('\n');
1477
+ }
1478
+
1479
+ if (msg.includes('Mailbox does not exist') || msg.includes('does not exist') || msg.includes('NONEXISTENT')) {
1480
+ return [
1481
+ `Mailbox not found: ${msg}`,
1482
+ '→ Check the folder name is correct — iCloud folder names are case-sensitive.',
1483
+ '→ Use list_mailboxes to see all available folders.'
1484
+ ].join('\n');
1485
+ }
1486
+
1487
+ // Fall through — return original message
1488
+ return msg;
1489
+ }
1490
+
1491
+ // ─── Doctor command ───────────────────────────────────────────────────────────
1492
+
1493
+ async function runDoctor() {
1494
+ const divider = '─'.repeat(45);
1495
+ process.stdout.write(`\nicloud-mcp doctor\n${divider}\n`);
1496
+
1497
+ const checks = [
1498
+ {
1499
+ label: 'IMAP_USER is set',
1500
+ run: () => {
1501
+ if (!IMAP_USER) throw new Error('IMAP_USER environment variable is not set.\n→ Add it to your Claude Desktop config env block.');
1502
+ }
1503
+ },
1504
+ {
1505
+ label: 'IMAP_PASSWORD is set',
1506
+ run: () => {
1507
+ if (!IMAP_PASSWORD) throw new Error('IMAP_PASSWORD environment variable is not set.\n→ Add it to your Claude Desktop config env block.');
1508
+ }
1509
+ },
1510
+ {
1511
+ label: 'IMAP_USER looks like an email address',
1512
+ run: () => {
1513
+ if (!IMAP_USER?.includes('@')) throw new Error(`IMAP_USER "${IMAP_USER}" doesn't look like an email address.\n→ Use your full iCloud address, e.g. you@icloud.com`);
1514
+ }
1515
+ },
1516
+ {
1517
+ label: `Connected to imap.mail.me.com:993`,
1518
+ run: async () => {
1519
+ const client = createClient();
1520
+ await client.connect();
1521
+ await client.logout();
1522
+ }
1523
+ },
1524
+ {
1525
+ label: `Authenticated as ${IMAP_USER}`,
1526
+ run: async () => {
1527
+ // Auth is validated as part of connect — if we reach here it passed.
1528
+ // This check exists to give a clearer label in the output.
1529
+ }
1530
+ },
1531
+ {
1532
+ label: 'INBOX opened',
1533
+ run: async () => {
1534
+ const client = createClient();
1535
+ await client.connect();
1536
+ const mb = await client.mailboxOpen('INBOX');
1537
+ await client.logout();
1538
+ return `${mb.exists.toLocaleString()} messages`;
1539
+ }
1540
+ }
1541
+ ];
1542
+
1543
+ let allPassed = true;
1544
+
1545
+ for (const check of checks) {
1546
+ try {
1547
+ const detail = await check.run();
1548
+ const suffix = detail ? ` (${detail})` : '';
1549
+ process.stdout.write(`✅ ${check.label}${suffix}\n`);
1550
+ } catch (err) {
1551
+ process.stdout.write(`❌ ${check.label}\n ${friendlyError(err).replace(/\n/g, '\n ')}\n`);
1552
+ allPassed = false;
1553
+ break; // No point continuing after a failure
1554
+ }
1555
+ }
1556
+
1557
+ process.stdout.write(`${divider}\n`);
1558
+ if (allPassed) {
1559
+ process.stdout.write('All checks passed. Ready to use with Claude Desktop.\n\n');
1560
+ process.exit(0);
1561
+ } else {
1562
+ process.stdout.write('Setup is not complete. Fix the issue above and run --doctor again.\n\n');
1563
+ process.exit(1);
1564
+ }
1565
+ }
1566
+
1567
+
1438
1568
  process.on('uncaughtException', (err) => {
1439
1569
  process.stderr.write(`Uncaught exception: ${err.message}\n${err.stack}\n`);
1440
1570
  process.exit(1);
@@ -1445,7 +1575,14 @@ process.on('unhandledRejection', (reason) => {
1445
1575
  process.exit(1);
1446
1576
  });
1447
1577
 
1448
- main().catch((err) => {
1449
- process.stderr.write(`Fatal error: ${err.message}\n${err.stack}\n`);
1450
- process.exit(1);
1451
- });
1578
+ if (process.argv.includes('--doctor')) {
1579
+ runDoctor().catch((err) => {
1580
+ process.stderr.write(`Doctor failed unexpectedly: ${err.message}\n`);
1581
+ process.exit(1);
1582
+ });
1583
+ } else {
1584
+ main().catch((err) => {
1585
+ process.stderr.write(`Fatal error: ${err.message}\n${err.stack}\n`);
1586
+ process.exit(1);
1587
+ });
1588
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icloud-mcp",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "A Model Context Protocol (MCP) server for iCloud Mail",
5
5
  "main": "index.js",
6
6
  "bin": {