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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adam Zaidi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,341 +1,235 @@
|
|
|
1
|
-
# icloud-mcp
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
icloud-mcp
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
|
171
|
-
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
|
185
|
-
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
|
193
|
-
|
|
194
|
-
| `
|
|
195
|
-
| `
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
###
|
|
200
|
-
|
|
201
|
-
| Tool | Description |
|
|
202
|
-
|
|
203
|
-
| `
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
| Tool | Description |
|
|
238
|
-
|------|-------------|
|
|
239
|
-
| `get_move_status` | Check the status of the current or most recent bulk move; includes stale warning for operations >24h old |
|
|
240
|
-
| `abandon_move` | Abandon an in-progress move so a new one can start |
|
|
241
|
-
|
|
242
|
-
### Saved Rules
|
|
243
|
-
|
|
244
|
-
| Tool | Description |
|
|
245
|
-
|------|-------------|
|
|
246
|
-
| `create_rule` | Create a named rule with filters + action (move/delete/mark_read/mark_unread/flag/unflag) |
|
|
247
|
-
| `list_rules` | List all saved rules with last-run time and run count |
|
|
248
|
-
| `run_rule` | Run a specific rule by name; supports `dryRun` |
|
|
249
|
-
| `run_all_rules` | Run all saved rules in sequence; supports `dryRun` |
|
|
250
|
-
| `delete_rule` | Delete a saved rule by name |
|
|
251
|
-
|
|
252
|
-
### Contacts (CardDAV)
|
|
253
|
-
|
|
254
|
-
| Tool | Description |
|
|
255
|
-
|------|-------------|
|
|
256
|
-
| `list_contacts` | List contacts from iCloud Contacts; supports `limit`, `offset` for pagination |
|
|
257
|
-
| `search_contacts` | Search contacts by name, email, or phone number |
|
|
258
|
-
| `get_contact` | Get full details for a specific contact by ID |
|
|
259
|
-
| `create_contact` | Create a new contact; supports name, phones, emails, address, org, birthday, note |
|
|
260
|
-
| `update_contact` | Update an existing contact; only provided fields are changed |
|
|
261
|
-
| `delete_contact` | Permanently delete a contact |
|
|
262
|
-
|
|
263
|
-
### Calendar (CalDAV)
|
|
264
|
-
|
|
265
|
-
| Tool | Description |
|
|
266
|
-
|------|-------------|
|
|
267
|
-
| `list_calendars` | List all iCloud calendars with name, ID, and supported event types |
|
|
268
|
-
| `list_events` | List events in a calendar within a date range; supports `since`, `before`, `limit` |
|
|
269
|
-
| `get_event` | Get full details of a specific event by ID |
|
|
270
|
-
| `create_event` | Create a new event; supports title, start/end, timezone, all-day, description, location, recurrence |
|
|
271
|
-
| `update_event` | Update an existing event; only provided fields are changed |
|
|
272
|
-
| `delete_event` | Permanently delete a calendar event |
|
|
273
|
-
| `search_events` | Search for events by title across all calendars; supports date range |
|
|
274
|
-
|
|
275
|
-
### Session Log
|
|
276
|
-
|
|
277
|
-
| Tool | Description |
|
|
278
|
-
|------|-------------|
|
|
279
|
-
| `log_write` | Write a step to the session log |
|
|
280
|
-
| `log_read` | Read the session log |
|
|
281
|
-
| `log_clear` | Clear the session log and start fresh |
|
|
282
|
-
|
|
283
|
-
## Filters
|
|
284
|
-
|
|
285
|
-
`bulk_move`, `bulk_delete`, `bulk_flag`, `search_emails`, `count_emails`, and rules all accept any combination of these filters:
|
|
286
|
-
|
|
287
|
-
| Filter | Type | Description |
|
|
288
|
-
|--------|------|-------------|
|
|
289
|
-
| `sender` | string | Match exact sender email address |
|
|
290
|
-
| `domain` | string | Match any sender from this domain (e.g. `substack.com`) |
|
|
291
|
-
| `subject` | string | Keyword to match in subject |
|
|
292
|
-
| `before` | string | Only emails before this date (YYYY-MM-DD) |
|
|
293
|
-
| `since` | string | Only emails since this date (YYYY-MM-DD) |
|
|
294
|
-
| `unread` | boolean | `true` for unread only, `false` for read only |
|
|
295
|
-
| `flagged` | boolean | `true` for flagged only, `false` for unflagged only |
|
|
296
|
-
| `larger` | number | Only emails larger than this size in KB |
|
|
297
|
-
| `smaller` | number | Only emails smaller than this size in KB |
|
|
298
|
-
| `hasAttachment` | boolean | Only emails with attachments (requires narrow pre-filters โ scans up to 500 candidates) |
|
|
299
|
-
|
|
300
|
-
## Safe Move
|
|
301
|
-
|
|
302
|
-
All bulk move operations (`bulk_move`, `bulk_move_by_sender`, `bulk_move_by_domain`, `archive_older_than`) use a three-phase copy-verify-delete approach:
|
|
303
|
-
|
|
304
|
-
1. **Copy** โ all emails are copied to the destination in chunks
|
|
305
|
-
2. **Verify** โ every email is fingerprinted and confirmed present in the destination
|
|
306
|
-
3. **Delete** โ source emails are removed in a single EXPUNGE only after verification passes
|
|
307
|
-
|
|
308
|
-
A persistent manifest at `~/.icloud-mcp-move-manifest.json` tracks progress so a crash or dropped connection never results in data loss. Use `get_move_status` to inspect any operation and `abandon_move` to clear a stuck one.
|
|
309
|
-
|
|
310
|
-
## Example Usage
|
|
311
|
-
|
|
312
|
-
Once configured, you can ask Claude things like:
|
|
313
|
-
|
|
314
|
-
- *"Show me the top senders in my iCloud inbox"*
|
|
315
|
-
- *"What's eating the most storage in my inbox?"*
|
|
316
|
-
- *"How many unread emails do I have from substack.com?"*
|
|
317
|
-
- *"Find all emails in this thread and summarize the conversation"*
|
|
318
|
-
- *"Move all emails from substack.com older than 2023 to my Newsletters folder"*
|
|
319
|
-
- *"Archive everything in my inbox older than 1 year"*
|
|
320
|
-
- *"Delete all unread emails from linkedin.com before 2022"*
|
|
321
|
-
- *"What's the unsubscribe link for this newsletter?"*
|
|
322
|
-
- *"Show me the 3 largest attachments in my inbox this month"*
|
|
323
|
-
- *"Flag all unread emails from my bank"*
|
|
324
|
-
- *"Create a rule that moves all emails from spotify.com to bulk-mail/services"*
|
|
325
|
-
- *"Reply to the last email from John and cc Sarah"*
|
|
326
|
-
- *"Draft a follow-up email to the team about the Q1 report"*
|
|
327
|
-
- *"Find John Smith's phone number in my contacts"*
|
|
328
|
-
- *"Add a new contact: Jane Doe, jane@example.com, +1 555 123 4567"*
|
|
329
|
-
- *"What's on my calendar next week?"*
|
|
330
|
-
- *"Create an event: dentist appointment Monday at 10am Eastern"*
|
|
331
|
-
- *"Find all my calendar events about 'team meeting'"*
|
|
332
|
-
|
|
333
|
-
## Security
|
|
334
|
-
|
|
335
|
-
- Your credentials are stored only in your local Claude Desktop config file
|
|
336
|
-
- The server runs entirely on your machine โ no data is sent to any third party
|
|
337
|
-
- App-specific passwords can be revoked at any time from [appleid.apple.com](https://appleid.apple.com)
|
|
338
|
-
|
|
339
|
-
## License
|
|
340
|
-
|
|
341
|
-
MIT
|
|
1
|
+
# icloud-mcp
|
|
2
|
+
|
|
3
|
+
Maintained Model Context Protocol (MCP) server for iCloud Mail, Contacts, Calendar, and Reminders. Version **2.7.0** (86 tools).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Read, search, and paginate any mailbox, including threads, attachments, and unsubscribe links
|
|
8
|
+
- Send, reply, forward, and save drafts over SMTP
|
|
9
|
+
- Bulk move, delete, flag, and mark-read, with a safe copy-verify-delete pipeline for moves
|
|
10
|
+
- Saved rules, digest state, and a session log for long cleanups
|
|
11
|
+
- Contacts over CardDAV, calendars over CalDAV (including bulk create/update/delete and conflict checks), and Reminders
|
|
12
|
+
- `dryRun` on every delete, every move that removes the original, and every bulk operation. A dry run reports a `changes` list of exactly what would be affected and does not modify anything
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- Node.js 20 or newer
|
|
17
|
+
- Claude Desktop or Claude Code
|
|
18
|
+
- An iCloud account with an app-specific password
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
Install from npm:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g icloud-mcp
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or skip the install and let `npx` fetch it on demand (the configs below use this).
|
|
29
|
+
|
|
30
|
+
Confirm the server can reach iCloud:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
IMAP_USER="you@icloud.com" IMAP_PASSWORD="your-app-specific-password" npx -y icloud-mcp --doctor
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Claude Desktop
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"icloud-mail": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["-y", "icloud-mcp"],
|
|
44
|
+
"env": {
|
|
45
|
+
"IMAP_USER": "you@icloud.com",
|
|
46
|
+
"IMAP_PASSWORD": "your-app-specific-password"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If Claude Desktop cannot find `npx`, use its full path (`which npx`). With a global install, `"command": "icloud-mcp"` and no `args` also works. Quit Claude Desktop completely and reopen it.
|
|
54
|
+
|
|
55
|
+
### Claude Code
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
claude mcp add icloud-mail \
|
|
59
|
+
--scope user \
|
|
60
|
+
-e IMAP_USER=you@icloud.com \
|
|
61
|
+
-e IMAP_PASSWORD=your-app-specific-password \
|
|
62
|
+
-- npx -y icloud-mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### From source
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/adamzaidi/icloud-mcp.git
|
|
69
|
+
cd icloud-mcp
|
|
70
|
+
npm install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Point the configs above at `node /absolute/path/to/icloud-mcp/index.js` instead of `npx -y icloud-mcp`. To run from a checkout, copy `.mcp.json.example` to `.mcp.json` (that file is gitignored) and export `ICLOUD_EMAIL` and `ICLOUD_APP_PASSWORD` in your shell.
|
|
74
|
+
|
|
75
|
+
Additional IMAP accounts use `IMAP_ACCOUNT_N_USER`, `IMAP_ACCOUNT_N_PASSWORD`, `IMAP_ACCOUNT_N_HOST`, `IMAP_ACCOUNT_N_SMTP_HOST`, and `IMAP_ACCOUNT_N_NAME`.
|
|
76
|
+
|
|
77
|
+
## Local data
|
|
78
|
+
|
|
79
|
+
Rules, the move manifest, digest state, and the session log are written outside the repository, in your home directory:
|
|
80
|
+
|
|
81
|
+
- `~/.icloud-mcp-rules.json`
|
|
82
|
+
- `~/.icloud-mcp-move-manifest.json`
|
|
83
|
+
- `~/.icloud-mcp-digest.json`
|
|
84
|
+
- `~/.icloud-mcp-session.json`
|
|
85
|
+
|
|
86
|
+
Set `ICLOUD_MCP_DATA_DIR` to store those files somewhere else. Do not point it at the git checkout. Contact exports, CRM notes, `.env`, and digest output belong outside the repo; `.gitignore` already excludes the usual local folders.
|
|
87
|
+
|
|
88
|
+
## Available tools (86)
|
|
89
|
+
|
|
90
|
+
`dryRun: true` returns `{ dryRun: true, changes: [...] }` plus the existing count fields (`wouldDelete`, `wouldMove`, and so on). Omitted or false performs the change.
|
|
91
|
+
|
|
92
|
+
### Mail
|
|
93
|
+
|
|
94
|
+
| Tool | Description | dryRun |
|
|
95
|
+
|------|-------------|--------|
|
|
96
|
+
| `list_accounts` | List all configured email accounts (names and IMAP hosts). Use the account name in any mail tool's account parameter. | |
|
|
97
|
+
| `get_inbox_summary` | Get a summary of a mailbox including total, unread, and recent email counts | |
|
|
98
|
+
| `get_mailbox_summary` | Get total, unread, and recent email counts for any specific mailbox/folder | |
|
|
99
|
+
| `get_top_senders` | Get the top senders by email count from a sample of the inbox | |
|
|
100
|
+
| `get_unread_senders` | Get top senders of unread emails | |
|
|
101
|
+
| `get_emails_by_sender` | Get all emails from a specific sender | |
|
|
102
|
+
| `read_inbox` | Read emails from an inbox with pagination. Supports multiple accounts. | |
|
|
103
|
+
| `get_email` | Get full content of a specific email by UID | |
|
|
104
|
+
| `search_emails` | Search emails by keyword or targeted field queries, with optional filters for date, read status, domain, and more | |
|
|
105
|
+
| `count_emails` | Count how many emails match a set of filters without moving or deleting them. Use this before bulk_move or bulk_delete to preview how many emails will be affected. | |
|
|
106
|
+
| `bulk_move` | Move emails matching any combination of filters from one mailbox to another. Uses safe copy-verify-delete with fingerprint verification and a persistent manifest. Use dryRun: true to preview without making changes. | yes |
|
|
107
|
+
| `bulk_delete` | Delete emails matching any combination of filters. Processes in chunks of 500 with per-chunk timeouts for reliability. Use dryRun: true to preview without making changes. | yes |
|
|
108
|
+
| `bulk_flag` | Flag or unflag emails matching any combination of filters in bulk | yes |
|
|
109
|
+
| `bulk_delete_by_sender` | Delete all emails from a specific sender | yes |
|
|
110
|
+
| `bulk_move_by_sender` | Move all emails from a specific sender to a folder | yes |
|
|
111
|
+
| `bulk_delete_by_subject` | Delete all emails matching a subject pattern | yes |
|
|
112
|
+
| `bulk_mark_read` | Mark all emails as read, optionally filtered by sender | yes |
|
|
113
|
+
| `bulk_mark_unread` | Mark all emails as unread, optionally filtered by sender | yes |
|
|
114
|
+
| `delete_older_than` | Delete all emails older than a certain number of days | yes |
|
|
115
|
+
| `get_emails_by_date_range` | Get emails between two dates | |
|
|
116
|
+
| `flag_email` | Flag or unflag a single email | |
|
|
117
|
+
| `mark_as_read` | Mark a single email as read or unread | |
|
|
118
|
+
| `delete_email` | Delete a single email | yes |
|
|
119
|
+
| `move_email` | Move a single email to a different mailbox/folder | yes |
|
|
120
|
+
| `list_mailboxes` | List all mailboxes/folders in iCloud Mail | |
|
|
121
|
+
| `create_mailbox` | Create a new mailbox/folder | |
|
|
122
|
+
| `rename_mailbox` | Rename an existing mailbox/folder | |
|
|
123
|
+
| `delete_mailbox` | Delete a mailbox/folder. The folder must be empty first. | yes |
|
|
124
|
+
| `empty_trash` | Permanently delete all emails in the trash (Deleted Messages or Trash folder). Use dryRun: true to preview first. | yes |
|
|
125
|
+
| `get_move_status` | Check the status of the current or most recent bulk move operation. Shows progress, chunk statuses, and any failures. Call this to monitor a long-running move or inspect a failed one. | |
|
|
126
|
+
| `abandon_move` | Abandon an in-progress move operation so a new one can start. Only use if you are certain the operation should not be resumed. Emails already moved will not be returned to source. | |
|
|
127
|
+
| `log_write` | Write a step to the session log. Use this to record your plan before starting, and after each completed step. Helps maintain progress across long operations. | |
|
|
128
|
+
| `log_read` | Read the current session log to see what has been done so far. | |
|
|
129
|
+
| `log_clear` | Clear the session log and start fresh. Use this at the start of a new task. | |
|
|
130
|
+
| `list_attachments` | List all attachments in an email without downloading them. Returns filename, MIME type, size, and IMAP part ID for each attachment. | |
|
|
131
|
+
| `get_attachment` | Download a specific attachment from an email. Returns the file content as base64-encoded data. Use list_attachments first to get the partId. Maximum 20 MB per request; use offset+length for larger files. | |
|
|
132
|
+
| `get_unsubscribe_info` | Get the List-Unsubscribe header from an email, parsed into email and URL components. Useful for AI-assisted inbox cleanup. | |
|
|
133
|
+
| `mark_older_than_read` | Mark all unread emails older than N days as read. Useful for bulk triage of a cluttered inbox. | yes |
|
|
134
|
+
| `bulk_move_by_domain` | Move all emails from a specific domain to a folder. Convenience wrapper around bulk_move with a domain filter. | yes |
|
|
135
|
+
| `get_email_raw` | Get the raw RFC 2822 source of an email (full headers + MIME body) as base64-encoded data. Useful for debugging or export. Capped at 1 MB. | |
|
|
136
|
+
| `bulk_flag_by_sender` | Flag or unflag all emails from a specific sender | yes |
|
|
137
|
+
| `archive_older_than` | Safely move emails older than N days from a source mailbox to an archive folder. Uses the same safe copy-verify-delete pipeline as bulk_move. Use dryRun: true to preview. | yes |
|
|
138
|
+
| `get_storage_report` | Estimate storage usage by size bucket and identify top senders by email size. Uses SEARCH LARGER queries for bucketing and samples large emails for sender analysis. | |
|
|
139
|
+
| `get_thread` | Find all emails in the same thread as a given email. Uses subject matching + References/In-Reply-To header filtering. Note: iCloud does not support server-side threading โ results are approximate. | |
|
|
140
|
+
| `create_rule` | Create a saved rule that applies a specific action to emails matching a set of filters. Rules are stored persistently and can be run on demand or all at once with run_all_rules. | |
|
|
141
|
+
| `list_rules` | List all saved rules with their filters, actions, and run history. | |
|
|
142
|
+
| `run_rule` | Run a specific saved rule by name. Use dryRun: true to preview what would be affected without making changes. | yes |
|
|
143
|
+
| `delete_rule` | Delete a saved rule by name. | yes |
|
|
144
|
+
| `run_all_rules` | Run all saved rules in sequence. Use dryRun: true to preview all rules without making changes. | yes |
|
|
145
|
+
| `compose_email` | Compose and send a new email via iCloud SMTP. The From address is always your iCloud account. Supports plain text, HTML, or both (multipart/alternative). | |
|
|
146
|
+
| `reply_to_email` | Reply to an existing email. Automatically sets correct threading headers (In-Reply-To, References) and prefixes the subject with Re:. Supports plain text and/or HTML body. | |
|
|
147
|
+
| `forward_email` | Forward an existing email to one or more recipients. Fetches the original email body and includes it as a forwarded message block. Supports plain text and/or HTML note. | |
|
|
148
|
+
| `save_draft` | Save a draft email to your iCloud Drafts folder without sending it. Supports plain text, HTML, or both. The draft can be edited and sent later from Mail.app or iCloud.com. | |
|
|
149
|
+
| `get_digest_state` | 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. | |
|
|
150
|
+
| `update_digest_state` | 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. | |
|
|
151
|
+
|
|
152
|
+
### Contacts
|
|
153
|
+
|
|
154
|
+
| Tool | Description | dryRun |
|
|
155
|
+
|------|-------------|--------|
|
|
156
|
+
| `list_contacts` | List contacts from iCloud Contacts. Returns names, phones, emails, and other fields. | |
|
|
157
|
+
| `search_contacts` | Search iCloud Contacts by name, email address, or phone number. | |
|
|
158
|
+
| `get_contact` | Get full details for a specific contact by ID. Use list_contacts or search_contacts to find a contactId. | |
|
|
159
|
+
| `create_contact` | Create a new contact in iCloud Contacts. | |
|
|
160
|
+
| `update_contact` | Update an existing contact in iCloud Contacts. Only provided fields are changed; others are preserved. | |
|
|
161
|
+
| `delete_contact` | Delete a contact from iCloud Contacts permanently. | yes |
|
|
162
|
+
|
|
163
|
+
### Calendar
|
|
164
|
+
|
|
165
|
+
| Tool | Description | dryRun |
|
|
166
|
+
|------|-------------|--------|
|
|
167
|
+
| `list_calendars` | List all calendars in iCloud Calendar (e.g. Personal, Work, School). Returns calendarId, name, and supported event types. | |
|
|
168
|
+
| `create_calendar` | Create a new event calendar in iCloud Calendar. Fails if a calendar with that name already exists. | |
|
|
169
|
+
| `delete_calendar` | Delete an iCloud event calendar by exact name or calendarId. The calendar must have no events (past or future); delete them first with bulk_delete_events. Will not delete Reminders lists. | yes |
|
|
170
|
+
| `list_events` | List events in a specific iCloud calendar within a date range. Use list_calendars first to get a calendarId. | |
|
|
171
|
+
| `get_event` | Get full details of a specific calendar event by its ID. | |
|
|
172
|
+
| `create_event` | Create a new event in an iCloud calendar. For all-day events use allDay:true and YYYY-MM-DD for start/end. | |
|
|
173
|
+
| `update_event` | Update an existing calendar event. Only provided fields are changed; others are preserved. | |
|
|
174
|
+
| `delete_event` | Delete a calendar event permanently from iCloud Calendar. | yes |
|
|
175
|
+
| `search_events` | Search for events by title/summary across all calendars within an optional date range. | |
|
|
176
|
+
| `bulk_update_events` | Update multiple calendar events in one call. Each update object must include eventId and only the fields to change. | yes |
|
|
177
|
+
| `list_events_multi` | List events from multiple calendars in one call. Returns events grouped by calendar ID. | |
|
|
178
|
+
| `bulk_create_events` | Create multiple calendar events in one call. Much more efficient than calling create_event repeatedly. Each event in the array uses the same fields as create_event. | yes |
|
|
179
|
+
| `bulk_delete_events` | Delete multiple calendar events in one call. Much more efficient than calling delete_event repeatedly. | yes |
|
|
180
|
+
| `detect_conflicts` | Detect scheduling conflicts and tight gaps between events across multiple calendars. Compares all non-all-day events on the same date from different calendars. | |
|
|
181
|
+
|
|
182
|
+
### Reminders
|
|
183
|
+
|
|
184
|
+
| Tool | Description | dryRun |
|
|
185
|
+
|------|-------------|--------|
|
|
186
|
+
| `list_reminder_lists` | List all Reminders lists in iCloud Reminders (e.g. "Reminders", "Work", "Shopping"). Returns name, id, and count per list. | |
|
|
187
|
+
| `create_reminder_list` | Create a new Reminders list in iCloud Reminders. Fails if a list with that name already exists. | |
|
|
188
|
+
| `rename_reminder_list` | Rename a Reminders list. The old name must match exactly one list, and the new name must not already be in use. | yes |
|
|
189
|
+
| `delete_reminder_list` | Delete a Reminders list. The list must be empty first, and the name must match exactly one list. | yes |
|
|
190
|
+
| `list_reminders` | List reminders from iCloud Reminders. Omit listName to fetch from all lists. | |
|
|
191
|
+
| `get_reminder` | Get full details of a specific reminder by ID. | |
|
|
192
|
+
| `create_reminder` | Create a new reminder in iCloud Reminders. | |
|
|
193
|
+
| `update_reminder` | Update an existing reminder. Only provided fields are changed; others are preserved. | |
|
|
194
|
+
| `complete_reminder` | Mark a reminder as completed in iCloud Reminders. | |
|
|
195
|
+
| `delete_reminder` | Delete a reminder from iCloud Reminders permanently. | yes |
|
|
196
|
+
|
|
197
|
+
Listing reminders reads each property for the whole list in one batch. A list of zero or one reminder is coerced to an array before it is indexed. If Reminders.app stalls, the tool says the script timed out after 90 seconds instead of reporting an iCloud network timeout. A timed-out create, update, or delete may still have been applied.
|
|
198
|
+
|
|
199
|
+
### Email to calendar
|
|
200
|
+
|
|
201
|
+
| Tool | Description | dryRun |
|
|
202
|
+
|------|-------------|--------|
|
|
203
|
+
| `suggest_event_from_email` | Fetch an email and return its content formatted for calendar event extraction. After calling this tool, extract the event fields from the returned content (pay attention to _dateAnchor for resolving relative dates like "Tuesday"), present a summary to the user for confirmation, then call create_event. No API key required. | |
|
|
204
|
+
|
|
205
|
+
## Filters
|
|
206
|
+
|
|
207
|
+
`bulk_move`, `bulk_delete`, `bulk_flag`, `search_emails`, `count_emails`, and rules accept any combination of: `sender`, `domain`, `subject`, `before`, `since`, `unread`, `flagged`, `larger`, `smaller`, `hasAttachment`, and `account`.
|
|
208
|
+
|
|
209
|
+
A `domain` filter searches both the bare domain and `@domain`. iCloud's FROM search misses some senders when given only the bare domain. Subdomains are not matched: a filter of `example.com` does not find mail from `someone@mail.example.com`. When a keyword and a domain are both set, `search_emails` keeps both conditions.
|
|
210
|
+
|
|
211
|
+
## Safe move
|
|
212
|
+
|
|
213
|
+
`bulk_move`, `bulk_move_by_sender`, `bulk_move_by_domain`, and `archive_older_than` copy, verify fingerprints in the destination, then remove the source. `get_move_status` and `abandon_move` inspect or clear the manifest.
|
|
214
|
+
|
|
215
|
+
## Connections
|
|
216
|
+
|
|
217
|
+
An idle iCloud IMAP connection times out after 60 seconds of silence. The server logs the error and that tool call fails. The process stays up. Saving a draft uses its own IMAP connection and attaches the same handler.
|
|
218
|
+
|
|
219
|
+
## Tests
|
|
220
|
+
|
|
221
|
+
`npm test` runs the offline suite only. It mocks IMAP, CardDAV, CalDAV, and Reminders, and it does not contact iCloud or send mail.
|
|
222
|
+
|
|
223
|
+
`npm run test:live` runs the dummy-data suite in `tests/live-destructive.test.js`. It stays skipped unless `ICLOUD_MCP_LIVE=1`, `IMAP_USER`, and `IMAP_PASSWORD` are all set. It creates dummy contacts, reminders, calendar events, and messages appended into a temp folder, then deletes those dummies. It does not send mail. Set `LIVE_CALENDAR` to the calendar that should receive the dummy events. Calendar tests are skipped when that variable is unset. Delete tools run as a dry run first and skip the real delete unless that preview names exactly the dummies from this run.
|
|
224
|
+
|
|
225
|
+
`npm run test:send` runs `tests/test.js`. It stays skipped unless `ICLOUD_MCP_SEND=1`, `IMAP_USER`, and `IMAP_PASSWORD` are all set. Every message it sends goes only to the account in `IMAP_USER`. Reply, reply-all, and forward act on a seed that account just sent to itself, never on other inbox mail. Before each send, a guard checks every To, Cc, and Bcc recipient (case and surrounding whitespace ignored) and aborts the run if any address is anyone else.
|
|
226
|
+
|
|
227
|
+
`dryRun: true` on a delete, a move that removes the original, or a bulk change returns `{ dryRun: true, changes: [...] }` and does not write. Omit it, or pass false, to perform the change.
|
|
228
|
+
|
|
229
|
+
## Security
|
|
230
|
+
|
|
231
|
+
Credentials stay in your local MCP client config. The server runs on your machine. Revoke an app-specific password at [appleid.apple.com](https://appleid.apple.com).
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|