icloud-mcp 2.5.0 → 2.7.0
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/LICENSE +21 -0
- package/README.md +235 -341
- package/index.js +30 -1138
- package/lib/caldav.js +628 -3
- package/lib/carddav.js +28 -2
- package/lib/data-paths.js +13 -0
- package/lib/digest.js +23 -8
- package/lib/imap.js +298 -85
- package/lib/reminders.js +316 -0
- package/lib/session.js +9 -7
- package/lib/smtp.js +53 -21
- package/lib/tools/calendar.js +333 -0
- package/lib/tools/contacts.js +135 -0
- package/lib/tools/mail.js +903 -0
- package/lib/tools/reminders.js +165 -0
- package/package.json +26 -7
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/settings.local.json +0 -35
- package/.mcp.json.example +0 -12
- package/run-digest.js +0 -57
package/lib/carddav.js
CHANGED
|
@@ -12,7 +12,7 @@ function getCredentials() {
|
|
|
12
12
|
return { user, auth: Buffer.from(`${user}:${pass}`).toString('base64') };
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
async function
|
|
15
|
+
async function defaultDavRequest(method, url, opts = {}) {
|
|
16
16
|
const { auth } = getCredentials();
|
|
17
17
|
const headers = {
|
|
18
18
|
Authorization: `Basic ${auth}`,
|
|
@@ -25,6 +25,17 @@ async function davRequest(method, url, opts = {}) {
|
|
|
25
25
|
return { status: res.status, etag: res.headers.get('etag'), body: text };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
let davRequest = defaultDavRequest;
|
|
29
|
+
|
|
30
|
+
export function setCardDavRequestForTests(fn) {
|
|
31
|
+
davRequest = fn || defaultDavRequest;
|
|
32
|
+
_discoveryCache = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function setCardDavDiscoveryForTests(cache) {
|
|
36
|
+
_discoveryCache = cache;
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
function propfindBody(props) {
|
|
29
40
|
return `<?xml version="1.0" encoding="UTF-8"?>\n<A:propfind xmlns:A="DAV:"><A:prop>${props}</A:prop></A:propfind>`;
|
|
30
41
|
}
|
|
@@ -387,7 +398,22 @@ export async function updateContact(contactId, fields) {
|
|
|
387
398
|
return { updated: true, contactId, etag: resp.etag };
|
|
388
399
|
}
|
|
389
400
|
|
|
390
|
-
export async function deleteContact(contactId) {
|
|
401
|
+
export async function deleteContact(contactId, dryRun = false) {
|
|
402
|
+
if (dryRun) {
|
|
403
|
+
const contact = await getContact(contactId);
|
|
404
|
+
return {
|
|
405
|
+
dryRun: true,
|
|
406
|
+
wouldDelete: true,
|
|
407
|
+
contactId,
|
|
408
|
+
changes: [{
|
|
409
|
+
action: 'delete_contact',
|
|
410
|
+
contactId,
|
|
411
|
+
fullName: contact.fullName || null,
|
|
412
|
+
emails: contact.emails || [],
|
|
413
|
+
phones: contact.phones || [],
|
|
414
|
+
}],
|
|
415
|
+
};
|
|
416
|
+
}
|
|
391
417
|
const { dataHost, addressBookPath } = await discover();
|
|
392
418
|
const url = `${dataHost}${addressBookPath}${contactId}.vcf`;
|
|
393
419
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Local state files (rules, move manifest, digest, session log).
|
|
2
|
+
// Defaults to the home directory so nothing is written inside the repo.
|
|
3
|
+
// Override with ICLOUD_MCP_DATA_DIR for tests or a dedicated data folder.
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
|
|
7
|
+
export function dataDir() {
|
|
8
|
+
return process.env.ICLOUD_MCP_DATA_DIR || homedir();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function dataFile(filename) {
|
|
12
|
+
return join(dataDir(), filename);
|
|
13
|
+
}
|
package/lib/digest.js
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
// ─── Digest State ─────────────────────────────────────────────────────────────
|
|
2
2
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
3
|
-
import {
|
|
4
|
-
import { join } from 'path';
|
|
3
|
+
import { dataFile } from './data-paths.js';
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
function digestFile() {
|
|
6
|
+
return dataFile('.icloud-mcp-digest.json');
|
|
7
|
+
}
|
|
7
8
|
|
|
8
9
|
const DEFAULT_STATE = {
|
|
9
10
|
lastRun: null,
|
|
10
11
|
processedUids: [],
|
|
11
12
|
pendingActions: [],
|
|
12
|
-
skipCounts: {}
|
|
13
|
+
skipCounts: {},
|
|
14
|
+
dismissedReminders: [],
|
|
15
|
+
seenReminders: []
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
function readDigest() {
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
const file = digestFile();
|
|
20
|
+
if (!existsSync(file)) return { ...DEFAULT_STATE };
|
|
21
|
+
try { return JSON.parse(readFileSync(file, 'utf8')); }
|
|
18
22
|
catch { return { ...DEFAULT_STATE }; }
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
function writeDigest(data) {
|
|
22
|
-
writeFileSync(
|
|
26
|
+
writeFileSync(digestFile(), JSON.stringify(data, null, 2));
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
export function getDigestState() {
|
|
26
30
|
return readDigest();
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
export function updateDigestState({ processedUids, lastRun, pendingActions, skipCounts } = {}) {
|
|
33
|
+
export function updateDigestState({ processedUids, lastRun, pendingActions, skipCounts, dismissedReminders, seenReminders } = {}) {
|
|
30
34
|
const state = readDigest();
|
|
31
35
|
if (lastRun !== undefined) state.lastRun = lastRun;
|
|
32
36
|
if (processedUids !== undefined) {
|
|
@@ -41,6 +45,17 @@ export function updateDigestState({ processedUids, lastRun, pendingActions, skip
|
|
|
41
45
|
state.skipCounts[sender] = (state.skipCounts[sender] || 0) + count;
|
|
42
46
|
}
|
|
43
47
|
}
|
|
48
|
+
if (dismissedReminders !== undefined) {
|
|
49
|
+
// Replace, prune entries older than 30 days, cap at 500
|
|
50
|
+
const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
51
|
+
state.dismissedReminders = dismissedReminders
|
|
52
|
+
.filter(r => !r.dismissedAt || new Date(r.dismissedAt).getTime() > cutoff)
|
|
53
|
+
.slice(-500);
|
|
54
|
+
}
|
|
55
|
+
if (seenReminders !== undefined) {
|
|
56
|
+
// Replace wholesale — snapshot of current "claude" list, no accumulation
|
|
57
|
+
state.seenReminders = seenReminders;
|
|
58
|
+
}
|
|
44
59
|
writeDigest(state);
|
|
45
60
|
return state;
|
|
46
61
|
}
|