icloud-mcp 2.3.0 → 2.4.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/index.js +31 -1
- package/lib/digest.js +46 -0
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import { logRead, logWrite, logClear } from './lib/session.js';
|
|
21
21
|
import { composeEmail, replyToEmail, forwardEmail, saveDraft } from './lib/smtp.js';
|
|
22
22
|
import { listContacts, searchContacts, getContact, createContact, updateContact, deleteContact } from './lib/carddav.js';
|
|
23
|
+
import { getDigestState, updateDigestState } from './lib/digest.js';
|
|
23
24
|
import { formatEmailForExtraction } from './lib/event-extractor.js';
|
|
24
25
|
import { listCalendars, listEvents, getEvent, createEvent, updateEvent, deleteEvent, searchEvents } from './lib/caldav.js';
|
|
25
26
|
|
|
@@ -39,7 +40,7 @@ if (!IMAP_USER || !IMAP_PASSWORD) {
|
|
|
39
40
|
|
|
40
41
|
async function main() {
|
|
41
42
|
const server = new Server(
|
|
42
|
-
{ name: 'icloud-mail', version: '2.
|
|
43
|
+
{ name: 'icloud-mail', version: '2.4.0' },
|
|
43
44
|
{ capabilities: { tools: {} } }
|
|
44
45
|
);
|
|
45
46
|
|
|
@@ -677,6 +678,25 @@ async function main() {
|
|
|
677
678
|
required: ['to', 'subject']
|
|
678
679
|
}
|
|
679
680
|
},
|
|
681
|
+
// ── Digest State ──
|
|
682
|
+
{
|
|
683
|
+
name: 'get_digest_state',
|
|
684
|
+
description: 'Get the current inbox digest state — last run timestamp, processed email UIDs (to skip on next run), pending actions, and per-sender skip counts for smart unsubscribe.',
|
|
685
|
+
inputSchema: { type: 'object', properties: {} }
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
name: 'update_digest_state',
|
|
689
|
+
description: 'Update the digest state after a run. Merges new processed UIDs into the existing list, updates lastRun, replaces pendingActions, and accumulates per-sender skip counts.',
|
|
690
|
+
inputSchema: {
|
|
691
|
+
type: 'object',
|
|
692
|
+
properties: {
|
|
693
|
+
lastRun: { type: 'string', description: 'ISO timestamp of this run' },
|
|
694
|
+
processedUids: { type: 'array', items: { type: 'number' }, description: 'Email UIDs processed in this run — merged with existing and capped at 5000' },
|
|
695
|
+
pendingActions: { type: 'array', description: 'Full replacement list of pending action items to track across runs (deadlines, waiting-for-reply, etc.). Each item: { type, subject, to/from, dueDate?, notes? }' },
|
|
696
|
+
skipCounts: { type: 'object', description: 'Map of sender address to skip count increment for this run, e.g. { "bestbuy@email.bestbuy.com": 3 }. Accumulated across runs for smart unsubscribe.' }
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
},
|
|
680
700
|
// ── CardDAV / Contacts ──
|
|
681
701
|
{
|
|
682
702
|
name: 'list_contacts',
|
|
@@ -986,6 +1006,16 @@ async function main() {
|
|
|
986
1006
|
result = logRead();
|
|
987
1007
|
} else if (name === 'log_clear') {
|
|
988
1008
|
result = logClear();
|
|
1009
|
+
// ── Digest state (synchronous, no timeout needed) ──
|
|
1010
|
+
} else if (name === 'get_digest_state') {
|
|
1011
|
+
result = getDigestState();
|
|
1012
|
+
} else if (name === 'update_digest_state') {
|
|
1013
|
+
result = updateDigestState({
|
|
1014
|
+
lastRun: args.lastRun,
|
|
1015
|
+
processedUids: args.processedUids,
|
|
1016
|
+
pendingActions: args.pendingActions,
|
|
1017
|
+
skipCounts: args.skipCounts
|
|
1018
|
+
});
|
|
989
1019
|
// ── Saved rules (synchronous CRUD; run_rule/run_all_rules use internal chunk timeouts) ──
|
|
990
1020
|
} else if (name === 'create_rule') {
|
|
991
1021
|
result = createRule(args.name, args.filters || {}, args.action, args.description || '');
|
package/lib/digest.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// ─── Digest State ─────────────────────────────────────────────────────────────
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
const DIGEST_FILE = join(homedir(), '.icloud-mcp-digest.json');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_STATE = {
|
|
9
|
+
lastRun: null,
|
|
10
|
+
processedUids: [],
|
|
11
|
+
pendingActions: [],
|
|
12
|
+
skipCounts: {}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function readDigest() {
|
|
16
|
+
if (!existsSync(DIGEST_FILE)) return { ...DEFAULT_STATE };
|
|
17
|
+
try { return JSON.parse(readFileSync(DIGEST_FILE, 'utf8')); }
|
|
18
|
+
catch { return { ...DEFAULT_STATE }; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function writeDigest(data) {
|
|
22
|
+
writeFileSync(DIGEST_FILE, JSON.stringify(data, null, 2));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getDigestState() {
|
|
26
|
+
return readDigest();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function updateDigestState({ processedUids, lastRun, pendingActions, skipCounts } = {}) {
|
|
30
|
+
const state = readDigest();
|
|
31
|
+
if (lastRun !== undefined) state.lastRun = lastRun;
|
|
32
|
+
if (processedUids !== undefined) {
|
|
33
|
+
// Merge with existing, deduplicate, cap at 5000 to prevent unbounded growth
|
|
34
|
+
const merged = [...new Set([...state.processedUids, ...processedUids])];
|
|
35
|
+
state.processedUids = merged.slice(-5000);
|
|
36
|
+
}
|
|
37
|
+
if (pendingActions !== undefined) state.pendingActions = pendingActions;
|
|
38
|
+
if (skipCounts !== undefined) {
|
|
39
|
+
// Accumulate skip counts per sender for smart unsubscribe
|
|
40
|
+
for (const [sender, count] of Object.entries(skipCounts)) {
|
|
41
|
+
state.skipCounts[sender] = (state.skipCounts[sender] || 0) + count;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
writeDigest(state);
|
|
45
|
+
return state;
|
|
46
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "icloud-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "A Model Context Protocol (MCP) server for iCloud Mail",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"author": "Adam Zaidi",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
+
"@anthropic-ai/sdk": "^0.78.0",
|
|
24
25
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
25
26
|
"fast-xml-parser": "^5.4.2",
|
|
26
27
|
"imapflow": "^1.2.10",
|