apple-mail-mcp 1.0.0 → 1.1.1
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/README.md +254 -21
- package/build/index.js +247 -8
- package/build/services/appleMailManager.d.ts +106 -8
- package/build/services/appleMailManager.d.ts.map +1 -1
- package/build/services/appleMailManager.js +473 -37
- package/build/types.d.ts +37 -0
- package/build/types.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -77,24 +77,49 @@ On first use, macOS will ask for permission to automate Mail.app. Click "OK" to
|
|
|
77
77
|
|
|
78
78
|
## Features
|
|
79
79
|
|
|
80
|
+
### Messages
|
|
81
|
+
|
|
80
82
|
| Feature | Description |
|
|
81
83
|
|---------|-------------|
|
|
82
|
-
| **List Messages** | List messages
|
|
83
|
-
| **Search Messages** |
|
|
84
|
-
| **Read Messages** | Get full email content |
|
|
84
|
+
| **List Messages** | List messages with pagination, sender filter, date display |
|
|
85
|
+
| **Search Messages** | Search by sender, subject, content, date range, read/flagged status — across all accounts |
|
|
86
|
+
| **Read Messages** | Get full email content (plain text or HTML) |
|
|
85
87
|
| **Send Email** | Compose and send new emails |
|
|
86
88
|
| **Create Draft** | Save emails to Drafts folder |
|
|
87
89
|
| **Reply** | Reply to messages (with reply-all support) |
|
|
88
90
|
| **Forward** | Forward messages to new recipients |
|
|
89
|
-
| **Mark Read/Unread** | Change read status |
|
|
90
|
-
| **Flag/Unflag** | Flag or unflag messages |
|
|
91
|
-
| **Delete Messages** | Move messages to trash |
|
|
92
|
-
| **Move Messages** | Organize into mailboxes |
|
|
93
|
-
| **List
|
|
91
|
+
| **Mark Read/Unread** | Change read status (single or batch) |
|
|
92
|
+
| **Flag/Unflag** | Flag or unflag messages (single or batch) |
|
|
93
|
+
| **Delete Messages** | Move messages to trash (single or batch) |
|
|
94
|
+
| **Move Messages** | Organize into mailboxes (single or batch) |
|
|
95
|
+
| **List Attachments** | View attachment metadata (name, type, size) |
|
|
96
|
+
| **Save Attachment** | Save attachments to disk |
|
|
97
|
+
|
|
98
|
+
### Mailbox & Account Management
|
|
99
|
+
|
|
100
|
+
| Feature | Description |
|
|
101
|
+
|---------|-------------|
|
|
102
|
+
| **List Mailboxes** | Show all folders with message/unread counts |
|
|
103
|
+
| **Create/Delete/Rename Mailbox** | Full mailbox lifecycle management |
|
|
94
104
|
| **List Accounts** | Show configured accounts |
|
|
95
105
|
| **Unread Count** | Get unread counts per mailbox |
|
|
106
|
+
|
|
107
|
+
### Rules, Contacts & Templates
|
|
108
|
+
|
|
109
|
+
| Feature | Description |
|
|
110
|
+
|---------|-------------|
|
|
111
|
+
| **List Rules** | View all mail rules and their enabled status |
|
|
112
|
+
| **Enable/Disable Rules** | Toggle mail rules on or off |
|
|
113
|
+
| **Search Contacts** | Look up contacts from Contacts.app by name |
|
|
114
|
+
| **Email Templates** | Save, list, use, and delete reusable email templates |
|
|
115
|
+
|
|
116
|
+
### Diagnostics
|
|
117
|
+
|
|
118
|
+
| Feature | Description |
|
|
119
|
+
|---------|-------------|
|
|
96
120
|
| **Health Check** | Verify Mail.app connectivity |
|
|
97
|
-
| **Statistics** | Message and unread counts |
|
|
121
|
+
| **Statistics** | Message and unread counts per account, recently received stats |
|
|
122
|
+
| **Sync Status** | Check if Mail.app is actively syncing |
|
|
98
123
|
|
|
99
124
|
---
|
|
100
125
|
|
|
@@ -106,13 +131,19 @@ This section documents all available tools. AI agents should use these tool name
|
|
|
106
131
|
|
|
107
132
|
#### `search-messages`
|
|
108
133
|
|
|
109
|
-
Search for messages matching criteria.
|
|
134
|
+
Search for messages matching criteria. Searches all accounts by default.
|
|
110
135
|
|
|
111
136
|
| Parameter | Type | Required | Description |
|
|
112
137
|
|-----------|------|----------|-------------|
|
|
113
138
|
| `query` | string | No | Text to search in subject/sender |
|
|
139
|
+
| `from` | string | No | Filter by sender email address |
|
|
140
|
+
| `subject` | string | No | Filter by subject line |
|
|
114
141
|
| `mailbox` | string | No | Mailbox to search in (default: INBOX) |
|
|
115
|
-
| `account` | string | No | Account to search in |
|
|
142
|
+
| `account` | string | No | Account to search in (omit to search all accounts) |
|
|
143
|
+
| `isRead` | boolean | No | Filter by read status |
|
|
144
|
+
| `isFlagged` | boolean | No | Filter by flagged status |
|
|
145
|
+
| `dateFrom` | string | No | Start date filter (e.g., "January 1, 2026") |
|
|
146
|
+
| `dateTo` | string | No | End date filter (e.g., "March 1, 2026") |
|
|
116
147
|
| `limit` | number | No | Max results (default: 50) |
|
|
117
148
|
|
|
118
149
|
---
|
|
@@ -124,8 +155,9 @@ Get the full content of a message.
|
|
|
124
155
|
| Parameter | Type | Required | Description |
|
|
125
156
|
|-----------|------|----------|-------------|
|
|
126
157
|
| `id` | string | Yes | Message ID |
|
|
158
|
+
| `preferHtml` | boolean | No | Return HTML source instead of plain text |
|
|
127
159
|
|
|
128
|
-
**Returns:** Subject line and plain text
|
|
160
|
+
**Returns:** Subject line and message body (plain text by default, HTML if `preferHtml` is true and HTML content is available).
|
|
129
161
|
|
|
130
162
|
---
|
|
131
163
|
|
|
@@ -138,8 +170,11 @@ List messages in a mailbox.
|
|
|
138
170
|
| `mailbox` | string | No | Mailbox name (default: INBOX) |
|
|
139
171
|
| `account` | string | No | Account name |
|
|
140
172
|
| `limit` | number | No | Max messages (default: 50) |
|
|
173
|
+
| `offset` | number | No | Number of messages to skip (for pagination) |
|
|
174
|
+
| `from` | string | No | Filter by sender email address or name |
|
|
175
|
+
| `unreadOnly` | boolean | No | Only show unread messages |
|
|
141
176
|
|
|
142
|
-
**Returns:** List of messages with ID,
|
|
177
|
+
**Returns:** List of messages with ID, date, subject, and sender.
|
|
143
178
|
|
|
144
179
|
---
|
|
145
180
|
|
|
@@ -271,6 +306,62 @@ Move a message to a different mailbox.
|
|
|
271
306
|
|
|
272
307
|
---
|
|
273
308
|
|
|
309
|
+
#### `list-attachments`
|
|
310
|
+
|
|
311
|
+
List attachments on a message.
|
|
312
|
+
|
|
313
|
+
| Parameter | Type | Required | Description |
|
|
314
|
+
|-----------|------|----------|-------------|
|
|
315
|
+
| `id` | string | Yes | Message ID |
|
|
316
|
+
|
|
317
|
+
**Returns:** List of attachments with name, MIME type, and size.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
#### `save-attachment`
|
|
322
|
+
|
|
323
|
+
Save a message attachment to disk.
|
|
324
|
+
|
|
325
|
+
| Parameter | Type | Required | Description |
|
|
326
|
+
|-----------|------|----------|-------------|
|
|
327
|
+
| `id` | string | Yes | Message ID |
|
|
328
|
+
| `attachmentName` | string | Yes | Filename of the attachment |
|
|
329
|
+
| `savePath` | string | Yes | Directory to save to |
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### Batch Operations
|
|
334
|
+
|
|
335
|
+
All batch operations accept an array of message IDs and return per-item success/failure results.
|
|
336
|
+
|
|
337
|
+
#### `batch-delete-messages`
|
|
338
|
+
|
|
339
|
+
| Parameter | Type | Required | Description |
|
|
340
|
+
|-----------|------|----------|-------------|
|
|
341
|
+
| `ids` | string[] | Yes | Message IDs to delete |
|
|
342
|
+
|
|
343
|
+
#### `batch-move-messages`
|
|
344
|
+
|
|
345
|
+
| Parameter | Type | Required | Description |
|
|
346
|
+
|-----------|------|----------|-------------|
|
|
347
|
+
| `ids` | string[] | Yes | Message IDs to move |
|
|
348
|
+
| `mailbox` | string | Yes | Destination mailbox |
|
|
349
|
+
| `account` | string | No | Account containing mailbox |
|
|
350
|
+
|
|
351
|
+
#### `batch-mark-as-read` / `batch-mark-as-unread`
|
|
352
|
+
|
|
353
|
+
| Parameter | Type | Required | Description |
|
|
354
|
+
|-----------|------|----------|-------------|
|
|
355
|
+
| `ids` | string[] | Yes | Message IDs |
|
|
356
|
+
|
|
357
|
+
#### `batch-flag-messages` / `batch-unflag-messages`
|
|
358
|
+
|
|
359
|
+
| Parameter | Type | Required | Description |
|
|
360
|
+
|-----------|------|----------|-------------|
|
|
361
|
+
| `ids` | string[] | Yes | Message IDs |
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
274
365
|
### Mailbox Operations
|
|
275
366
|
|
|
276
367
|
#### `list-mailboxes`
|
|
@@ -281,7 +372,7 @@ List all mailboxes for an account.
|
|
|
281
372
|
|-----------|------|----------|-------------|
|
|
282
373
|
| `account` | string | No | Account to list from |
|
|
283
374
|
|
|
284
|
-
**Returns:** List of mailbox names with unread counts.
|
|
375
|
+
**Returns:** List of mailbox names with message and unread counts.
|
|
285
376
|
|
|
286
377
|
---
|
|
287
378
|
|
|
@@ -296,6 +387,40 @@ Get unread message count.
|
|
|
296
387
|
|
|
297
388
|
---
|
|
298
389
|
|
|
390
|
+
#### `create-mailbox`
|
|
391
|
+
|
|
392
|
+
Create a new mailbox.
|
|
393
|
+
|
|
394
|
+
| Parameter | Type | Required | Description |
|
|
395
|
+
|-----------|------|----------|-------------|
|
|
396
|
+
| `name` | string | Yes | Mailbox name |
|
|
397
|
+
| `account` | string | No | Account to create in |
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
#### `delete-mailbox`
|
|
402
|
+
|
|
403
|
+
Delete a mailbox.
|
|
404
|
+
|
|
405
|
+
| Parameter | Type | Required | Description |
|
|
406
|
+
|-----------|------|----------|-------------|
|
|
407
|
+
| `name` | string | Yes | Mailbox name |
|
|
408
|
+
| `account` | string | No | Account containing mailbox |
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
#### `rename-mailbox`
|
|
413
|
+
|
|
414
|
+
Rename a mailbox (creates new, moves messages, deletes old).
|
|
415
|
+
|
|
416
|
+
| Parameter | Type | Required | Description |
|
|
417
|
+
|-----------|------|----------|-------------|
|
|
418
|
+
| `oldName` | string | Yes | Current mailbox name |
|
|
419
|
+
| `newName` | string | Yes | New mailbox name |
|
|
420
|
+
| `account` | string | No | Account containing mailbox |
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
299
424
|
### Account Operations
|
|
300
425
|
|
|
301
426
|
#### `list-accounts`
|
|
@@ -304,7 +429,105 @@ List all configured Mail accounts.
|
|
|
304
429
|
|
|
305
430
|
**Parameters:** None
|
|
306
431
|
|
|
307
|
-
**Returns:** List of account names
|
|
432
|
+
**Returns:** List of account names and email addresses.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
### Rules
|
|
437
|
+
|
|
438
|
+
#### `list-rules`
|
|
439
|
+
|
|
440
|
+
List all mail rules.
|
|
441
|
+
|
|
442
|
+
**Parameters:** None
|
|
443
|
+
|
|
444
|
+
**Returns:** List of rule names and enabled status.
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
#### `enable-rule` / `disable-rule`
|
|
449
|
+
|
|
450
|
+
Enable or disable a mail rule.
|
|
451
|
+
|
|
452
|
+
| Parameter | Type | Required | Description |
|
|
453
|
+
|-----------|------|----------|-------------|
|
|
454
|
+
| `name` | string | Yes | Rule name |
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
### Contacts
|
|
459
|
+
|
|
460
|
+
#### `search-contacts`
|
|
461
|
+
|
|
462
|
+
Search contacts in Contacts.app.
|
|
463
|
+
|
|
464
|
+
| Parameter | Type | Required | Description |
|
|
465
|
+
|-----------|------|----------|-------------|
|
|
466
|
+
| `query` | string | Yes | Name to search for |
|
|
467
|
+
| `limit` | number | No | Max results (default: 10) |
|
|
468
|
+
|
|
469
|
+
**Returns:** List of contacts with name, email addresses, and phone numbers.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
### Templates
|
|
474
|
+
|
|
475
|
+
Email templates are stored in memory for the duration of the server session.
|
|
476
|
+
|
|
477
|
+
#### `save-template`
|
|
478
|
+
|
|
479
|
+
Save or update an email template.
|
|
480
|
+
|
|
481
|
+
| Parameter | Type | Required | Description |
|
|
482
|
+
|-----------|------|----------|-------------|
|
|
483
|
+
| `name` | string | Yes | Template name |
|
|
484
|
+
| `subject` | string | Yes | Default subject line |
|
|
485
|
+
| `body` | string | Yes | Template body |
|
|
486
|
+
| `to` | string[] | No | Default recipients |
|
|
487
|
+
| `cc` | string[] | No | Default CC recipients |
|
|
488
|
+
| `id` | string | No | Template ID (for updating) |
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
#### `list-templates`
|
|
493
|
+
|
|
494
|
+
List all saved templates.
|
|
495
|
+
|
|
496
|
+
**Parameters:** None
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
#### `get-template`
|
|
501
|
+
|
|
502
|
+
Get a template by ID.
|
|
503
|
+
|
|
504
|
+
| Parameter | Type | Required | Description |
|
|
505
|
+
|-----------|------|----------|-------------|
|
|
506
|
+
| `id` | string | Yes | Template ID |
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
#### `delete-template`
|
|
511
|
+
|
|
512
|
+
Delete a template.
|
|
513
|
+
|
|
514
|
+
| Parameter | Type | Required | Description |
|
|
515
|
+
|-----------|------|----------|-------------|
|
|
516
|
+
| `id` | string | Yes | Template ID |
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
#### `use-template`
|
|
521
|
+
|
|
522
|
+
Create a draft from a template, with optional overrides.
|
|
523
|
+
|
|
524
|
+
| Parameter | Type | Required | Description |
|
|
525
|
+
|-----------|------|----------|-------------|
|
|
526
|
+
| `id` | string | Yes | Template ID |
|
|
527
|
+
| `to` | string[] | No | Override recipients |
|
|
528
|
+
| `cc` | string[] | No | Override CC |
|
|
529
|
+
| `subject` | string | No | Override subject |
|
|
530
|
+
| `body` | string | No | Override body |
|
|
308
531
|
|
|
309
532
|
---
|
|
310
533
|
|
|
@@ -322,11 +545,21 @@ Verify Mail.app connectivity and permissions.
|
|
|
322
545
|
|
|
323
546
|
#### `get-mail-stats`
|
|
324
547
|
|
|
325
|
-
Get mail statistics
|
|
548
|
+
Get mail statistics.
|
|
549
|
+
|
|
550
|
+
**Parameters:** None
|
|
551
|
+
|
|
552
|
+
**Returns:** Total and per-account message/unread counts, plus recently received stats (24h, 7d, 30d).
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
#### `get-sync-status`
|
|
557
|
+
|
|
558
|
+
Check Mail.app sync activity.
|
|
326
559
|
|
|
327
560
|
**Parameters:** None
|
|
328
561
|
|
|
329
|
-
**Returns:**
|
|
562
|
+
**Returns:** Whether sync is detected, pending uploads, recent activity, and seconds since last change.
|
|
330
563
|
|
|
331
564
|
---
|
|
332
565
|
|
|
@@ -350,7 +583,7 @@ AI: [calls get-message with id="..."]
|
|
|
350
583
|
|
|
351
584
|
### Working with Accounts
|
|
352
585
|
|
|
353
|
-
By default, operations use
|
|
586
|
+
By default, operations use Mail.app's configured default send account. Search operations check all accounts when no account is specified. To work with specific accounts:
|
|
354
587
|
|
|
355
588
|
```
|
|
356
589
|
User: "What email accounts do I have?"
|
|
@@ -429,10 +662,10 @@ If installed from source, use this configuration:
|
|
|
429
662
|
| Limitation | Reason |
|
|
430
663
|
|------------|--------|
|
|
431
664
|
| macOS only | Apple Mail and AppleScript are macOS-specific |
|
|
432
|
-
|
|
|
433
|
-
| No attachments |
|
|
434
|
-
| Message ID scope | Message IDs are searched across all mailboxes (may be slow with large mailboxes) |
|
|
665
|
+
| No sending HTML email | Emails are sent as plain text; reading HTML content is supported |
|
|
666
|
+
| No adding attachments | Can list and save existing attachments, but cannot attach files to outgoing emails |
|
|
435
667
|
| No smart mailboxes | Cannot access Smart Mailboxes via AppleScript |
|
|
668
|
+
| In-memory templates | Email templates are not persisted across server restarts |
|
|
436
669
|
|
|
437
670
|
### Backslash Escaping (Important for AI Agents)
|
|
438
671
|
|
package/build/index.js
CHANGED
|
@@ -86,28 +86,34 @@ server.tool("search-messages", {
|
|
|
86
86
|
from: z.string().optional().describe("Filter by sender email address"),
|
|
87
87
|
subject: z.string().optional().describe("Filter by subject line"),
|
|
88
88
|
mailbox: z.string().optional().describe("Mailbox to search in (e.g., 'INBOX')"),
|
|
89
|
-
account: z.string().optional().describe("Account to search in"),
|
|
89
|
+
account: z.string().optional().describe("Account to search in (omit to search all accounts)"),
|
|
90
90
|
isRead: z.boolean().optional().describe("Filter by read status"),
|
|
91
91
|
isFlagged: z.boolean().optional().describe("Filter by flagged status"),
|
|
92
|
+
dateFrom: z.string().optional().describe("Start date filter (e.g., 'January 1, 2026')"),
|
|
93
|
+
dateTo: z.string().optional().describe("End date filter (e.g., 'March 1, 2026')"),
|
|
92
94
|
limit: z.number().optional().describe("Maximum number of results (default: 50)"),
|
|
93
|
-
}, withErrorHandling(({ query, mailbox, account, limit = 50 }) => {
|
|
94
|
-
const messages = mailManager.searchMessages(query, mailbox, account, limit);
|
|
95
|
+
}, withErrorHandling(({ query, mailbox, account, limit = 50, dateFrom, dateTo }) => {
|
|
96
|
+
const messages = mailManager.searchMessages(query, mailbox, account, limit, dateFrom, dateTo);
|
|
95
97
|
if (messages.length === 0) {
|
|
96
98
|
return successResponse("No messages found matching criteria");
|
|
97
99
|
}
|
|
98
100
|
const messageList = messages
|
|
99
|
-
.map((m) => ` - ${m.subject} (from: ${m.sender}) [${m.isRead ? "read" : "unread"}]`)
|
|
101
|
+
.map((m) => ` - ID: ${m.id} | ${m.dateReceived.toLocaleDateString()} | ${m.subject} (from: ${m.sender}) [${m.isRead ? "read" : "unread"}]`)
|
|
100
102
|
.join("\n");
|
|
101
103
|
return successResponse(`Found ${messages.length} message(s):\n${messageList}`);
|
|
102
104
|
}, "Error searching messages"));
|
|
103
105
|
// --- get-message ---
|
|
104
106
|
server.tool("get-message", {
|
|
105
107
|
id: z.string().min(1, "Message ID is required"),
|
|
106
|
-
|
|
108
|
+
preferHtml: z.boolean().optional().describe("Return HTML source instead of plain text"),
|
|
109
|
+
}, withErrorHandling(({ id, preferHtml }) => {
|
|
107
110
|
const content = mailManager.getMessageContent(id);
|
|
108
111
|
if (!content) {
|
|
109
112
|
return errorResponse(`Message with ID "${id}" not found`);
|
|
110
113
|
}
|
|
114
|
+
if (preferHtml && content.htmlContent) {
|
|
115
|
+
return successResponse(`Subject: ${content.subject}\n\n${content.htmlContent}`);
|
|
116
|
+
}
|
|
111
117
|
return successResponse(`Subject: ${content.subject}\n\n${content.plainText}`);
|
|
112
118
|
}, "Error retrieving message"));
|
|
113
119
|
// --- list-messages ---
|
|
@@ -115,13 +121,17 @@ server.tool("list-messages", {
|
|
|
115
121
|
mailbox: z.string().optional().describe("Mailbox to list messages from (default: INBOX)"),
|
|
116
122
|
account: z.string().optional().describe("Account to list messages from"),
|
|
117
123
|
limit: z.number().optional().describe("Maximum number of messages (default: 50)"),
|
|
124
|
+
offset: z.number().optional().describe("Number of messages to skip (for pagination)"),
|
|
125
|
+
from: z.string().optional().describe("Filter by sender email address or name"),
|
|
118
126
|
unreadOnly: z.boolean().optional().describe("Only show unread messages"),
|
|
119
|
-
}, withErrorHandling(({ mailbox, account, limit = 50 }) => {
|
|
120
|
-
const messages = mailManager.listMessages(mailbox, account, limit);
|
|
127
|
+
}, withErrorHandling(({ mailbox, account, limit = 50, offset = 0, from }) => {
|
|
128
|
+
const messages = mailManager.listMessages(mailbox, account, limit, from, offset);
|
|
121
129
|
if (messages.length === 0) {
|
|
122
130
|
return successResponse("No messages found");
|
|
123
131
|
}
|
|
124
|
-
const messageList = messages
|
|
132
|
+
const messageList = messages
|
|
133
|
+
.map((m) => ` - ID: ${m.id} | ${m.dateReceived.toLocaleDateString()} | ${m.subject} (from: ${m.sender})`)
|
|
134
|
+
.join("\n");
|
|
125
135
|
return successResponse(`Found ${messages.length} message(s):\n${messageList}`);
|
|
126
136
|
}, "Error listing messages"));
|
|
127
137
|
// --- send-email ---
|
|
@@ -210,6 +220,16 @@ server.tool("flag-message", {
|
|
|
210
220
|
}
|
|
211
221
|
return successResponse("Message flagged");
|
|
212
222
|
}, "Error flagging message"));
|
|
223
|
+
// --- unflag-message ---
|
|
224
|
+
server.tool("unflag-message", {
|
|
225
|
+
id: z.string().min(1, "Message ID is required"),
|
|
226
|
+
}, withErrorHandling(({ id }) => {
|
|
227
|
+
const success = mailManager.unflagMessage(id);
|
|
228
|
+
if (!success) {
|
|
229
|
+
return errorResponse(`Failed to unflag message "${id}"`);
|
|
230
|
+
}
|
|
231
|
+
return successResponse("Message unflagged");
|
|
232
|
+
}, "Error unflagging message"));
|
|
213
233
|
// --- delete-message ---
|
|
214
234
|
server.tool("delete-message", {
|
|
215
235
|
id: z.string().min(1, "Message ID is required"),
|
|
@@ -285,6 +305,57 @@ server.tool("batch-mark-as-read", {
|
|
|
285
305
|
return successResponse(`Marked ${successCount} message(s) as read, ${failCount} failed`);
|
|
286
306
|
}
|
|
287
307
|
}, "Error batch marking messages as read"));
|
|
308
|
+
// --- batch-mark-as-unread ---
|
|
309
|
+
server.tool("batch-mark-as-unread", {
|
|
310
|
+
ids: z.array(z.string()).min(1, "At least one message ID is required"),
|
|
311
|
+
}, withErrorHandling(({ ids }) => {
|
|
312
|
+
const results = mailManager.batchMarkAsUnread(ids);
|
|
313
|
+
const successCount = results.filter((r) => r.success).length;
|
|
314
|
+
const failCount = results.length - successCount;
|
|
315
|
+
if (failCount === 0) {
|
|
316
|
+
return successResponse(`Successfully marked ${successCount} message(s) as unread`);
|
|
317
|
+
}
|
|
318
|
+
else if (successCount === 0) {
|
|
319
|
+
return errorResponse(`Failed to mark all ${failCount} message(s) as unread`);
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
return successResponse(`Marked ${successCount} message(s) as unread, ${failCount} failed`);
|
|
323
|
+
}
|
|
324
|
+
}, "Error batch marking messages as unread"));
|
|
325
|
+
// --- batch-flag-messages ---
|
|
326
|
+
server.tool("batch-flag-messages", {
|
|
327
|
+
ids: z.array(z.string()).min(1, "At least one message ID is required"),
|
|
328
|
+
}, withErrorHandling(({ ids }) => {
|
|
329
|
+
const results = mailManager.batchFlagMessages(ids);
|
|
330
|
+
const successCount = results.filter((r) => r.success).length;
|
|
331
|
+
const failCount = results.length - successCount;
|
|
332
|
+
if (failCount === 0) {
|
|
333
|
+
return successResponse(`Successfully flagged ${successCount} message(s)`);
|
|
334
|
+
}
|
|
335
|
+
else if (successCount === 0) {
|
|
336
|
+
return errorResponse(`Failed to flag all ${failCount} message(s)`);
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
return successResponse(`Flagged ${successCount} message(s), ${failCount} failed`);
|
|
340
|
+
}
|
|
341
|
+
}, "Error batch flagging messages"));
|
|
342
|
+
// --- batch-unflag-messages ---
|
|
343
|
+
server.tool("batch-unflag-messages", {
|
|
344
|
+
ids: z.array(z.string()).min(1, "At least one message ID is required"),
|
|
345
|
+
}, withErrorHandling(({ ids }) => {
|
|
346
|
+
const results = mailManager.batchUnflagMessages(ids);
|
|
347
|
+
const successCount = results.filter((r) => r.success).length;
|
|
348
|
+
const failCount = results.length - successCount;
|
|
349
|
+
if (failCount === 0) {
|
|
350
|
+
return successResponse(`Successfully unflagged ${successCount} message(s)`);
|
|
351
|
+
}
|
|
352
|
+
else if (successCount === 0) {
|
|
353
|
+
return errorResponse(`Failed to unflag all ${failCount} message(s)`);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
return successResponse(`Unflagged ${successCount} message(s), ${failCount} failed`);
|
|
357
|
+
}
|
|
358
|
+
}, "Error batch unflagging messages"));
|
|
288
359
|
// --- list-attachments ---
|
|
289
360
|
server.tool("list-attachments", {
|
|
290
361
|
id: z.string().min(1, "Message ID is required"),
|
|
@@ -301,6 +372,18 @@ server.tool("list-attachments", {
|
|
|
301
372
|
.join("\n");
|
|
302
373
|
return successResponse(`Found ${attachments.length} attachment(s):\n${attachmentList}`);
|
|
303
374
|
}, "Error listing attachments"));
|
|
375
|
+
// --- save-attachment ---
|
|
376
|
+
server.tool("save-attachment", {
|
|
377
|
+
id: z.string().min(1, "Message ID is required"),
|
|
378
|
+
attachmentName: z.string().min(1, "Attachment name is required"),
|
|
379
|
+
savePath: z.string().min(1, "Save directory path is required"),
|
|
380
|
+
}, withErrorHandling(({ id, attachmentName, savePath }) => {
|
|
381
|
+
const success = mailManager.saveAttachment(id, attachmentName, savePath);
|
|
382
|
+
if (!success) {
|
|
383
|
+
return errorResponse(`Failed to save attachment "${attachmentName}"`);
|
|
384
|
+
}
|
|
385
|
+
return successResponse(`Attachment "${attachmentName}" saved to ${savePath}`);
|
|
386
|
+
}, "Error saving attachment"));
|
|
304
387
|
// =============================================================================
|
|
305
388
|
// Mailbox Tools
|
|
306
389
|
// =============================================================================
|
|
@@ -324,6 +407,40 @@ server.tool("get-unread-count", {
|
|
|
324
407
|
const location = mailbox ? ` in "${mailbox}"` : "";
|
|
325
408
|
return successResponse(`${count} unread message(s)${location}`);
|
|
326
409
|
}, "Error getting unread count"));
|
|
410
|
+
// --- create-mailbox ---
|
|
411
|
+
server.tool("create-mailbox", {
|
|
412
|
+
name: z.string().min(1, "Mailbox name is required"),
|
|
413
|
+
account: z.string().optional().describe("Account to create the mailbox in"),
|
|
414
|
+
}, withErrorHandling(({ name, account }) => {
|
|
415
|
+
const success = mailManager.createMailbox(name, account);
|
|
416
|
+
if (!success) {
|
|
417
|
+
return errorResponse(`Failed to create mailbox "${name}"`);
|
|
418
|
+
}
|
|
419
|
+
return successResponse(`Mailbox "${name}" created`);
|
|
420
|
+
}, "Error creating mailbox"));
|
|
421
|
+
// --- delete-mailbox ---
|
|
422
|
+
server.tool("delete-mailbox", {
|
|
423
|
+
name: z.string().min(1, "Mailbox name is required"),
|
|
424
|
+
account: z.string().optional().describe("Account containing the mailbox"),
|
|
425
|
+
}, withErrorHandling(({ name, account }) => {
|
|
426
|
+
const success = mailManager.deleteMailbox(name, account);
|
|
427
|
+
if (!success) {
|
|
428
|
+
return errorResponse(`Failed to delete mailbox "${name}"`);
|
|
429
|
+
}
|
|
430
|
+
return successResponse(`Mailbox "${name}" deleted`);
|
|
431
|
+
}, "Error deleting mailbox"));
|
|
432
|
+
// --- rename-mailbox ---
|
|
433
|
+
server.tool("rename-mailbox", {
|
|
434
|
+
oldName: z.string().min(1, "Current mailbox name is required"),
|
|
435
|
+
newName: z.string().min(1, "New mailbox name is required"),
|
|
436
|
+
account: z.string().optional().describe("Account containing the mailbox"),
|
|
437
|
+
}, withErrorHandling(({ oldName, newName, account }) => {
|
|
438
|
+
const success = mailManager.renameMailbox(oldName, newName, account);
|
|
439
|
+
if (!success) {
|
|
440
|
+
return errorResponse(`Failed to rename mailbox "${oldName}" to "${newName}"`);
|
|
441
|
+
}
|
|
442
|
+
return successResponse(`Mailbox renamed from "${oldName}" to "${newName}"`);
|
|
443
|
+
}, "Error renaming mailbox"));
|
|
327
444
|
// =============================================================================
|
|
328
445
|
// Account Tools
|
|
329
446
|
// =============================================================================
|
|
@@ -337,6 +454,128 @@ server.tool("list-accounts", {}, withErrorHandling(() => {
|
|
|
337
454
|
return successResponse(`Found ${accounts.length} account(s):\n${accountList}`);
|
|
338
455
|
}, "Error listing accounts"));
|
|
339
456
|
// =============================================================================
|
|
457
|
+
// Mail Rules Tools
|
|
458
|
+
// =============================================================================
|
|
459
|
+
// --- list-rules ---
|
|
460
|
+
server.tool("list-rules", {}, withErrorHandling(() => {
|
|
461
|
+
const rules = mailManager.listRules();
|
|
462
|
+
if (rules.length === 0) {
|
|
463
|
+
return successResponse("No mail rules found");
|
|
464
|
+
}
|
|
465
|
+
const ruleList = rules
|
|
466
|
+
.map((r) => ` - ${r.name} [${r.enabled ? "enabled" : "disabled"}]`)
|
|
467
|
+
.join("\n");
|
|
468
|
+
return successResponse(`Found ${rules.length} rule(s):\n${ruleList}`);
|
|
469
|
+
}, "Error listing rules"));
|
|
470
|
+
// --- enable-rule ---
|
|
471
|
+
server.tool("enable-rule", {
|
|
472
|
+
name: z.string().min(1, "Rule name is required"),
|
|
473
|
+
}, withErrorHandling(({ name }) => {
|
|
474
|
+
const success = mailManager.setRuleEnabled(name, true);
|
|
475
|
+
if (!success) {
|
|
476
|
+
return errorResponse(`Failed to enable rule "${name}"`);
|
|
477
|
+
}
|
|
478
|
+
return successResponse(`Rule "${name}" enabled`);
|
|
479
|
+
}, "Error enabling rule"));
|
|
480
|
+
// --- disable-rule ---
|
|
481
|
+
server.tool("disable-rule", {
|
|
482
|
+
name: z.string().min(1, "Rule name is required"),
|
|
483
|
+
}, withErrorHandling(({ name }) => {
|
|
484
|
+
const success = mailManager.setRuleEnabled(name, false);
|
|
485
|
+
if (!success) {
|
|
486
|
+
return errorResponse(`Failed to disable rule "${name}"`);
|
|
487
|
+
}
|
|
488
|
+
return successResponse(`Rule "${name}" disabled`);
|
|
489
|
+
}, "Error disabling rule"));
|
|
490
|
+
// =============================================================================
|
|
491
|
+
// Contacts Tools
|
|
492
|
+
// =============================================================================
|
|
493
|
+
// --- search-contacts ---
|
|
494
|
+
server.tool("search-contacts", {
|
|
495
|
+
query: z.string().min(1, "Search query is required"),
|
|
496
|
+
}, withErrorHandling(({ query }) => {
|
|
497
|
+
const contacts = mailManager.searchContacts(query);
|
|
498
|
+
if (contacts.length === 0) {
|
|
499
|
+
return successResponse("No contacts found");
|
|
500
|
+
}
|
|
501
|
+
const contactList = contacts
|
|
502
|
+
.map((c) => {
|
|
503
|
+
const emails = c.emails.length > 0 ? c.emails.join(", ") : "no email";
|
|
504
|
+
return ` - ${c.name} (${emails})`;
|
|
505
|
+
})
|
|
506
|
+
.join("\n");
|
|
507
|
+
return successResponse(`Found ${contacts.length} contact(s):\n${contactList}`);
|
|
508
|
+
}, "Error searching contacts"));
|
|
509
|
+
// =============================================================================
|
|
510
|
+
// Email Template Tools
|
|
511
|
+
// =============================================================================
|
|
512
|
+
// --- save-template ---
|
|
513
|
+
server.tool("save-template", {
|
|
514
|
+
name: z.string().min(1, "Template name is required"),
|
|
515
|
+
subject: z.string().min(1, "Subject is required"),
|
|
516
|
+
body: z.string().min(1, "Body is required"),
|
|
517
|
+
to: z.array(z.string()).optional().describe("Default recipients"),
|
|
518
|
+
cc: z.array(z.string()).optional().describe("Default CC recipients"),
|
|
519
|
+
id: z.string().optional().describe("Template ID (for updating existing template)"),
|
|
520
|
+
}, withErrorHandling(({ name, subject, body, to, cc, id }) => {
|
|
521
|
+
const template = mailManager.saveTemplate(name, subject, body, to, cc, id);
|
|
522
|
+
return successResponse(`Template "${template.name}" saved with ID: ${template.id}`);
|
|
523
|
+
}, "Error saving template"));
|
|
524
|
+
// --- list-templates ---
|
|
525
|
+
server.tool("list-templates", {}, withErrorHandling(() => {
|
|
526
|
+
const templates = mailManager.listTemplates();
|
|
527
|
+
if (templates.length === 0) {
|
|
528
|
+
return successResponse("No templates saved");
|
|
529
|
+
}
|
|
530
|
+
const templateList = templates
|
|
531
|
+
.map((t) => ` - [${t.id}] ${t.name} — "${t.subject}"`)
|
|
532
|
+
.join("\n");
|
|
533
|
+
return successResponse(`Found ${templates.length} template(s):\n${templateList}`);
|
|
534
|
+
}, "Error listing templates"));
|
|
535
|
+
// --- get-template ---
|
|
536
|
+
server.tool("get-template", {
|
|
537
|
+
id: z.string().min(1, "Template ID is required"),
|
|
538
|
+
}, withErrorHandling(({ id }) => {
|
|
539
|
+
const template = mailManager.getTemplate(id);
|
|
540
|
+
if (!template) {
|
|
541
|
+
return errorResponse(`Template "${id}" not found`);
|
|
542
|
+
}
|
|
543
|
+
const lines = [
|
|
544
|
+
`Name: ${template.name}`,
|
|
545
|
+
`Subject: ${template.subject}`,
|
|
546
|
+
template.to ? `To: ${template.to.join(", ")}` : null,
|
|
547
|
+
template.cc ? `CC: ${template.cc.join(", ")}` : null,
|
|
548
|
+
`\n${template.body}`,
|
|
549
|
+
]
|
|
550
|
+
.filter(Boolean)
|
|
551
|
+
.join("\n");
|
|
552
|
+
return successResponse(lines);
|
|
553
|
+
}, "Error getting template"));
|
|
554
|
+
// --- delete-template ---
|
|
555
|
+
server.tool("delete-template", {
|
|
556
|
+
id: z.string().min(1, "Template ID is required"),
|
|
557
|
+
}, withErrorHandling(({ id }) => {
|
|
558
|
+
const success = mailManager.deleteTemplate(id);
|
|
559
|
+
if (!success) {
|
|
560
|
+
return errorResponse(`Template "${id}" not found`);
|
|
561
|
+
}
|
|
562
|
+
return successResponse(`Template "${id}" deleted`);
|
|
563
|
+
}, "Error deleting template"));
|
|
564
|
+
// --- use-template ---
|
|
565
|
+
server.tool("use-template", {
|
|
566
|
+
id: z.string().min(1, "Template ID is required"),
|
|
567
|
+
to: z.array(z.string()).optional().describe("Override recipients"),
|
|
568
|
+
cc: z.array(z.string()).optional().describe("Override CC recipients"),
|
|
569
|
+
subject: z.string().optional().describe("Override subject"),
|
|
570
|
+
body: z.string().optional().describe("Override body"),
|
|
571
|
+
}, withErrorHandling(({ id, to, cc, subject, body }) => {
|
|
572
|
+
const success = mailManager.useTemplate(id, { to, cc, subject, body });
|
|
573
|
+
if (!success) {
|
|
574
|
+
return errorResponse(`Failed to use template "${id}". Template not found or no recipients.`);
|
|
575
|
+
}
|
|
576
|
+
return successResponse(`Draft created from template "${id}"`);
|
|
577
|
+
}, "Error using template"));
|
|
578
|
+
// =============================================================================
|
|
340
579
|
// Diagnostics Tools
|
|
341
580
|
// =============================================================================
|
|
342
581
|
// --- health-check ---
|