icloud-mcp 1.7.0 → 1.8.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 +56 -2
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1119,6 +1119,25 @@ function findTextPart(node) {
|
|
|
1119
1119
|
return null;
|
|
1120
1120
|
}
|
|
1121
1121
|
|
|
1122
|
+
function findAttachments(node, parts = []) {
|
|
1123
|
+
if (node.childNodes) {
|
|
1124
|
+
for (const child of node.childNodes) findAttachments(child, parts);
|
|
1125
|
+
} else {
|
|
1126
|
+
const filename = node.dispositionParameters?.filename ?? node.parameters?.name ?? null;
|
|
1127
|
+
const isTextBody = (node.type === 'text/plain' || node.type === 'text/html') && node.disposition !== 'attachment';
|
|
1128
|
+
if (node.disposition === 'attachment' || node.disposition === 'inline' || (filename && !isTextBody)) {
|
|
1129
|
+
parts.push({
|
|
1130
|
+
partId: node.part ?? 'TEXT',
|
|
1131
|
+
filename,
|
|
1132
|
+
mimeType: node.type ?? 'application/octet-stream',
|
|
1133
|
+
size: node.size ?? 0,
|
|
1134
|
+
disposition: node.disposition ?? 'attachment'
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
return parts;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1122
1141
|
// ─── Email content fetcher (MIME-aware) ───────────────────────────────────────
|
|
1123
1142
|
|
|
1124
1143
|
async function getEmailContent(uid, mailbox = 'INBOX') {
|
|
@@ -1205,6 +1224,22 @@ async function getEmailContent(uid, mailbox = 'INBOX') {
|
|
|
1205
1224
|
};
|
|
1206
1225
|
}
|
|
1207
1226
|
|
|
1227
|
+
async function listAttachments(uid, mailbox = 'INBOX') {
|
|
1228
|
+
const client = createRateLimitedClient();
|
|
1229
|
+
await client.connect();
|
|
1230
|
+
await client.mailboxOpen(mailbox);
|
|
1231
|
+
const meta = await client.fetchOne(uid, { envelope: true, bodyStructure: true }, { uid: true });
|
|
1232
|
+
await client.logout();
|
|
1233
|
+
if (!meta) return { uid, subject: null, attachmentCount: 0, attachments: [] };
|
|
1234
|
+
const attachments = meta.bodyStructure ? findAttachments(meta.bodyStructure) : [];
|
|
1235
|
+
return {
|
|
1236
|
+
uid: meta.uid,
|
|
1237
|
+
subject: meta.envelope.subject,
|
|
1238
|
+
attachmentCount: attachments.length,
|
|
1239
|
+
attachments
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1208
1243
|
async function flagEmail(uid, flagged, mailbox = 'INBOX') {
|
|
1209
1244
|
const client = createRateLimitedClient();
|
|
1210
1245
|
await client.connect();
|
|
@@ -1309,7 +1344,12 @@ function buildQuery(filters) {
|
|
|
1309
1344
|
if (filters.flagged === false) query.unflagged = true;
|
|
1310
1345
|
if (filters.larger) query.larger = filters.larger * 1024;
|
|
1311
1346
|
if (filters.smaller) query.smaller = filters.smaller * 1024;
|
|
1312
|
-
if (filters.hasAttachment)
|
|
1347
|
+
if (filters.hasAttachment) {
|
|
1348
|
+
query.or = [
|
|
1349
|
+
{ header: { 'Content-Disposition': 'attachment' } },
|
|
1350
|
+
{ header: { 'Content-Type': 'multipart/mixed' } }
|
|
1351
|
+
];
|
|
1352
|
+
}
|
|
1313
1353
|
if (Object.keys(query).length === 0) query.all = true;
|
|
1314
1354
|
return query;
|
|
1315
1355
|
}
|
|
@@ -1421,7 +1461,7 @@ function logClear() {
|
|
|
1421
1461
|
|
|
1422
1462
|
async function main() {
|
|
1423
1463
|
const server = new Server(
|
|
1424
|
-
{ name: 'icloud-mail', version: '1.
|
|
1464
|
+
{ name: 'icloud-mail', version: '1.8.0' },
|
|
1425
1465
|
{ capabilities: { tools: {} } }
|
|
1426
1466
|
);
|
|
1427
1467
|
|
|
@@ -1788,6 +1828,18 @@ async function main() {
|
|
|
1788
1828
|
name: 'log_clear',
|
|
1789
1829
|
description: 'Clear the session log and start fresh. Use this at the start of a new task.',
|
|
1790
1830
|
inputSchema: { type: 'object', properties: {} }
|
|
1831
|
+
},
|
|
1832
|
+
{
|
|
1833
|
+
name: 'list_attachments',
|
|
1834
|
+
description: 'List all attachments in an email without downloading them. Returns filename, MIME type, size, and IMAP part ID for each attachment.',
|
|
1835
|
+
inputSchema: {
|
|
1836
|
+
type: 'object',
|
|
1837
|
+
properties: {
|
|
1838
|
+
uid: { type: 'number', description: 'Email UID' },
|
|
1839
|
+
mailbox: { type: 'string', description: 'Mailbox name (default INBOX)' }
|
|
1840
|
+
},
|
|
1841
|
+
required: ['uid']
|
|
1842
|
+
}
|
|
1791
1843
|
}
|
|
1792
1844
|
]
|
|
1793
1845
|
}));
|
|
@@ -1817,6 +1869,8 @@ async function main() {
|
|
|
1817
1869
|
result = await withTimeout('read_inbox', TIMEOUT.FETCH, () => fetchEmails(args.mailbox || 'INBOX', args.limit || 10, args.onlyUnread || false, args.page || 1));
|
|
1818
1870
|
} else if (name === 'get_email') {
|
|
1819
1871
|
result = await withTimeout('get_email', TIMEOUT.FETCH, () => getEmailContent(args.uid, args.mailbox || 'INBOX'));
|
|
1872
|
+
} else if (name === 'list_attachments') {
|
|
1873
|
+
result = await withTimeout('list_attachments', TIMEOUT.FETCH, () => listAttachments(args.uid, args.mailbox || 'INBOX'));
|
|
1820
1874
|
} else if (name === 'search_emails') {
|
|
1821
1875
|
const { query, mailbox, limit, ...filters } = args;
|
|
1822
1876
|
result = await withTimeout('search_emails', TIMEOUT.FETCH, () => searchEmails(query, mailbox || 'INBOX', limit || 10, filters));
|