@sempervirens-labs/apple-mail-mcp 1.0.0 → 1.1.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/.claude/settings.local.json +3 -1
- package/README.md +139 -17
- package/package.json +1 -1
- package/src/applescript/mail.ts +260 -0
- package/src/index.ts +147 -0
- package/src/tools.ts +179 -0
package/README.md
CHANGED
|
@@ -10,6 +10,11 @@ An MCP (Model Context Protocol) server for Apple Mail on macOS. Allows AI assist
|
|
|
10
10
|
- **Search Emails** - Search by subject, sender, or content
|
|
11
11
|
- **Unread Count** - Get unread email counts
|
|
12
12
|
- **Send Email** - Compose and send emails with CC/BCC support
|
|
13
|
+
- **Archive Email** - Move emails to the Archive mailbox
|
|
14
|
+
- **Delete Email** - Move emails to Trash
|
|
15
|
+
- **Mark as Read/Unread** - Change read status of emails
|
|
16
|
+
- **Create Draft** - Create new draft emails
|
|
17
|
+
- **Create Draft Reply** - Create draft replies to existing emails
|
|
13
18
|
|
|
14
19
|
## Requirements
|
|
15
20
|
|
|
@@ -19,18 +24,22 @@ An MCP (Model Context Protocol) server for Apple Mail on macOS. Allows AI assist
|
|
|
19
24
|
|
|
20
25
|
## Installation
|
|
21
26
|
|
|
22
|
-
### From
|
|
27
|
+
### From npm (Recommended)
|
|
23
28
|
|
|
24
29
|
```bash
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
# Install globally
|
|
31
|
+
bun add -g @sempervirens-labs/apple-mail-mcp
|
|
32
|
+
|
|
33
|
+
# Or run directly without installation
|
|
34
|
+
bunx @sempervirens-labs/apple-mail-mcp
|
|
28
35
|
```
|
|
29
36
|
|
|
30
|
-
###
|
|
37
|
+
### From Source
|
|
31
38
|
|
|
32
39
|
```bash
|
|
33
|
-
|
|
40
|
+
git clone https://github.com/rbouschery/apple-mail-mcp.git
|
|
41
|
+
cd apple-mail-mcp
|
|
42
|
+
bun install
|
|
34
43
|
```
|
|
35
44
|
|
|
36
45
|
## Configuration
|
|
@@ -43,21 +52,21 @@ Add to your `~/.claude/settings.json`:
|
|
|
43
52
|
{
|
|
44
53
|
"mcpServers": {
|
|
45
54
|
"apple-mail": {
|
|
46
|
-
"command": "
|
|
47
|
-
"args": ["
|
|
55
|
+
"command": "bunx",
|
|
56
|
+
"args": ["@sempervirens-labs/apple-mail-mcp"]
|
|
48
57
|
}
|
|
49
58
|
}
|
|
50
59
|
}
|
|
51
60
|
```
|
|
52
61
|
|
|
53
|
-
Or if
|
|
62
|
+
Or if running from source:
|
|
54
63
|
|
|
55
64
|
```json
|
|
56
65
|
{
|
|
57
66
|
"mcpServers": {
|
|
58
67
|
"apple-mail": {
|
|
59
|
-
"command": "
|
|
60
|
-
"args": ["apple-mail-mcp"]
|
|
68
|
+
"command": "bun",
|
|
69
|
+
"args": ["run", "/path/to/apple-mail-mcp/src/index.ts"]
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
72
|
}
|
|
@@ -73,8 +82,8 @@ Add to your Cursor MCP settings (Settings > MCP Servers):
|
|
|
73
82
|
{
|
|
74
83
|
"mcpServers": {
|
|
75
84
|
"apple-mail": {
|
|
76
|
-
"command": "
|
|
77
|
-
"args": ["
|
|
85
|
+
"command": "bunx",
|
|
86
|
+
"args": ["@sempervirens-labs/apple-mail-mcp"]
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
89
|
}
|
|
@@ -86,8 +95,8 @@ Or via Cursor's settings UI:
|
|
|
86
95
|
3. Click "Add Server"
|
|
87
96
|
4. Enter:
|
|
88
97
|
- **Name**: `apple-mail`
|
|
89
|
-
- **Command**: `
|
|
90
|
-
- **Arguments**:
|
|
98
|
+
- **Command**: `bunx`
|
|
99
|
+
- **Arguments**: `@sempervirens-labs/apple-mail-mcp`
|
|
91
100
|
|
|
92
101
|
### Claude Desktop
|
|
93
102
|
|
|
@@ -97,8 +106,8 @@ Add to your `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
97
106
|
{
|
|
98
107
|
"mcpServers": {
|
|
99
108
|
"apple-mail": {
|
|
100
|
-
"command": "
|
|
101
|
-
"args": ["
|
|
109
|
+
"command": "bunx",
|
|
110
|
+
"args": ["@sempervirens-labs/apple-mail-mcp"]
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
}
|
|
@@ -216,6 +225,119 @@ Send an email using Apple Mail.
|
|
|
216
225
|
}
|
|
217
226
|
```
|
|
218
227
|
|
|
228
|
+
### `mail_archive`
|
|
229
|
+
|
|
230
|
+
Archive an email by moving it to the Archive mailbox.
|
|
231
|
+
|
|
232
|
+
| Parameter | Type | Required | Default | Description |
|
|
233
|
+
|-----------|------|----------|---------|-------------|
|
|
234
|
+
| `messageId` | number | **Yes** | - | Email ID (from mail_get_emails or mail_search) |
|
|
235
|
+
| `account` | string | No | - | Account name |
|
|
236
|
+
| `mailbox` | string | No | "INBOX" | Current mailbox of the email |
|
|
237
|
+
|
|
238
|
+
**Example response:**
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"success": true,
|
|
242
|
+
"message": "Message archived successfully"
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### `mail_delete`
|
|
247
|
+
|
|
248
|
+
Delete an email by moving it to Trash.
|
|
249
|
+
|
|
250
|
+
| Parameter | Type | Required | Default | Description |
|
|
251
|
+
|-----------|------|----------|---------|-------------|
|
|
252
|
+
| `messageId` | number | **Yes** | - | Email ID (from mail_get_emails or mail_search) |
|
|
253
|
+
| `account` | string | No | - | Account name |
|
|
254
|
+
| `mailbox` | string | No | "INBOX" | Current mailbox of the email |
|
|
255
|
+
|
|
256
|
+
**Example response:**
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"success": true,
|
|
260
|
+
"message": "Message deleted successfully"
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### `mail_mark_read`
|
|
265
|
+
|
|
266
|
+
Mark an email as read.
|
|
267
|
+
|
|
268
|
+
| Parameter | Type | Required | Default | Description |
|
|
269
|
+
|-----------|------|----------|---------|-------------|
|
|
270
|
+
| `messageId` | number | **Yes** | - | Email ID (from mail_get_emails or mail_search) |
|
|
271
|
+
| `account` | string | No | - | Account name |
|
|
272
|
+
| `mailbox` | string | No | "INBOX" | Mailbox where the email is located |
|
|
273
|
+
|
|
274
|
+
**Example response:**
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"success": true,
|
|
278
|
+
"message": "Message marked as read"
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### `mail_mark_unread`
|
|
283
|
+
|
|
284
|
+
Mark an email as unread.
|
|
285
|
+
|
|
286
|
+
| Parameter | Type | Required | Default | Description |
|
|
287
|
+
|-----------|------|----------|---------|-------------|
|
|
288
|
+
| `messageId` | number | **Yes** | - | Email ID (from mail_get_emails or mail_search) |
|
|
289
|
+
| `account` | string | No | - | Account name |
|
|
290
|
+
| `mailbox` | string | No | "INBOX" | Mailbox where the email is located |
|
|
291
|
+
|
|
292
|
+
**Example response:**
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"success": true,
|
|
296
|
+
"message": "Message marked as unread"
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### `mail_create_draft`
|
|
301
|
+
|
|
302
|
+
Create a new draft email.
|
|
303
|
+
|
|
304
|
+
| Parameter | Type | Required | Description |
|
|
305
|
+
|-----------|------|----------|-------------|
|
|
306
|
+
| `to` | string or string[] | No | Recipient email(s) |
|
|
307
|
+
| `subject` | string | **Yes** | Email subject |
|
|
308
|
+
| `body` | string | **Yes** | Email body |
|
|
309
|
+
| `cc` | string or string[] | No | CC recipient(s) |
|
|
310
|
+
| `bcc` | string or string[] | No | BCC recipient(s) |
|
|
311
|
+
| `from` | string | No | Sender (must be configured account) |
|
|
312
|
+
|
|
313
|
+
**Example response:**
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"success": true,
|
|
317
|
+
"message": "Draft created successfully"
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### `mail_create_draft_reply`
|
|
322
|
+
|
|
323
|
+
Create a draft reply to an existing email.
|
|
324
|
+
|
|
325
|
+
| Parameter | Type | Required | Default | Description |
|
|
326
|
+
|-----------|------|----------|---------|-------------|
|
|
327
|
+
| `messageId` | number | **Yes** | - | Email ID to reply to (from mail_get_emails or mail_search) |
|
|
328
|
+
| `body` | string | **Yes** | - | Reply body content |
|
|
329
|
+
| `replyAll` | boolean | No | false | Reply to all recipients |
|
|
330
|
+
| `account` | string | No | - | Account name |
|
|
331
|
+
| `mailbox` | string | No | "INBOX" | Mailbox where the original email is located |
|
|
332
|
+
|
|
333
|
+
**Example response:**
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"success": true,
|
|
337
|
+
"message": "Draft reply created successfully"
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
219
341
|
## Permissions
|
|
220
342
|
|
|
221
343
|
On first use, macOS will prompt you to grant permissions:
|
package/package.json
CHANGED
package/src/applescript/mail.ts
CHANGED
|
@@ -354,3 +354,263 @@ end tell
|
|
|
354
354
|
return { success: false, message: error.message };
|
|
355
355
|
}
|
|
356
356
|
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Archive an email by moving it to the Archive mailbox
|
|
360
|
+
*/
|
|
361
|
+
export function archiveEmail(options: {
|
|
362
|
+
messageId: number;
|
|
363
|
+
account?: string;
|
|
364
|
+
mailbox?: string;
|
|
365
|
+
}): { success: boolean; message: string } {
|
|
366
|
+
const { messageId, account, mailbox = "INBOX" } = options;
|
|
367
|
+
|
|
368
|
+
const accountPart = account
|
|
369
|
+
? `mailbox "${mailbox}" of account "${account}"`
|
|
370
|
+
: `mailbox "${mailbox}"`;
|
|
371
|
+
|
|
372
|
+
const script = `
|
|
373
|
+
tell application "Mail"
|
|
374
|
+
try
|
|
375
|
+
set theMailbox to ${accountPart}
|
|
376
|
+
set theMessage to (first message of theMailbox whose id is ${messageId})
|
|
377
|
+
set theAccount to account of theMailbox
|
|
378
|
+
|
|
379
|
+
-- Find the Archive mailbox for this account
|
|
380
|
+
set archiveMailbox to missing value
|
|
381
|
+
repeat with mb in mailboxes of theAccount
|
|
382
|
+
if name of mb is "Archive" or name of mb is "All Mail" then
|
|
383
|
+
set archiveMailbox to mb
|
|
384
|
+
exit repeat
|
|
385
|
+
end if
|
|
386
|
+
end repeat
|
|
387
|
+
|
|
388
|
+
if archiveMailbox is missing value then
|
|
389
|
+
return "ERROR:No Archive mailbox found for this account"
|
|
390
|
+
end if
|
|
391
|
+
|
|
392
|
+
move theMessage to archiveMailbox
|
|
393
|
+
return "Message archived successfully"
|
|
394
|
+
on error errMsg
|
|
395
|
+
return "ERROR:" & errMsg
|
|
396
|
+
end try
|
|
397
|
+
end tell
|
|
398
|
+
`;
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
const result = runAppleScript(script);
|
|
402
|
+
if (result.startsWith("ERROR:")) {
|
|
403
|
+
return { success: false, message: result.substring(6) };
|
|
404
|
+
}
|
|
405
|
+
return { success: true, message: result || "Message archived successfully" };
|
|
406
|
+
} catch (error: any) {
|
|
407
|
+
return { success: false, message: error.message };
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Delete an email by moving it to the Trash mailbox
|
|
413
|
+
*/
|
|
414
|
+
export function deleteEmail(options: {
|
|
415
|
+
messageId: number;
|
|
416
|
+
account?: string;
|
|
417
|
+
mailbox?: string;
|
|
418
|
+
}): { success: boolean; message: string } {
|
|
419
|
+
const { messageId, account, mailbox = "INBOX" } = options;
|
|
420
|
+
|
|
421
|
+
const accountPart = account
|
|
422
|
+
? `mailbox "${mailbox}" of account "${account}"`
|
|
423
|
+
: `mailbox "${mailbox}"`;
|
|
424
|
+
|
|
425
|
+
const script = `
|
|
426
|
+
tell application "Mail"
|
|
427
|
+
try
|
|
428
|
+
set theMailbox to ${accountPart}
|
|
429
|
+
set theMessage to (first message of theMailbox whose id is ${messageId})
|
|
430
|
+
delete theMessage
|
|
431
|
+
return "Message deleted successfully"
|
|
432
|
+
on error errMsg
|
|
433
|
+
return "ERROR:" & errMsg
|
|
434
|
+
end try
|
|
435
|
+
end tell
|
|
436
|
+
`;
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
const result = runAppleScript(script);
|
|
440
|
+
if (result.startsWith("ERROR:")) {
|
|
441
|
+
return { success: false, message: result.substring(6) };
|
|
442
|
+
}
|
|
443
|
+
return { success: true, message: result || "Message deleted successfully" };
|
|
444
|
+
} catch (error: any) {
|
|
445
|
+
return { success: false, message: error.message };
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Mark an email as read
|
|
451
|
+
*/
|
|
452
|
+
export function markAsRead(options: {
|
|
453
|
+
messageId: number;
|
|
454
|
+
account?: string;
|
|
455
|
+
mailbox?: string;
|
|
456
|
+
}): { success: boolean; message: string } {
|
|
457
|
+
const { messageId, account, mailbox = "INBOX" } = options;
|
|
458
|
+
|
|
459
|
+
const accountPart = account
|
|
460
|
+
? `mailbox "${mailbox}" of account "${account}"`
|
|
461
|
+
: `mailbox "${mailbox}"`;
|
|
462
|
+
|
|
463
|
+
const script = `
|
|
464
|
+
tell application "Mail"
|
|
465
|
+
try
|
|
466
|
+
set theMailbox to ${accountPart}
|
|
467
|
+
set theMessage to (first message of theMailbox whose id is ${messageId})
|
|
468
|
+
set read status of theMessage to true
|
|
469
|
+
return "Message marked as read"
|
|
470
|
+
on error errMsg
|
|
471
|
+
return "ERROR:" & errMsg
|
|
472
|
+
end try
|
|
473
|
+
end tell
|
|
474
|
+
`;
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
const result = runAppleScript(script);
|
|
478
|
+
if (result.startsWith("ERROR:")) {
|
|
479
|
+
return { success: false, message: result.substring(6) };
|
|
480
|
+
}
|
|
481
|
+
return { success: true, message: result || "Message marked as read" };
|
|
482
|
+
} catch (error: any) {
|
|
483
|
+
return { success: false, message: error.message };
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Mark an email as unread
|
|
489
|
+
*/
|
|
490
|
+
export function markAsUnread(options: {
|
|
491
|
+
messageId: number;
|
|
492
|
+
account?: string;
|
|
493
|
+
mailbox?: string;
|
|
494
|
+
}): { success: boolean; message: string } {
|
|
495
|
+
const { messageId, account, mailbox = "INBOX" } = options;
|
|
496
|
+
|
|
497
|
+
const accountPart = account
|
|
498
|
+
? `mailbox "${mailbox}" of account "${account}"`
|
|
499
|
+
: `mailbox "${mailbox}"`;
|
|
500
|
+
|
|
501
|
+
const script = `
|
|
502
|
+
tell application "Mail"
|
|
503
|
+
try
|
|
504
|
+
set theMailbox to ${accountPart}
|
|
505
|
+
set theMessage to (first message of theMailbox whose id is ${messageId})
|
|
506
|
+
set read status of theMessage to false
|
|
507
|
+
return "Message marked as unread"
|
|
508
|
+
on error errMsg
|
|
509
|
+
return "ERROR:" & errMsg
|
|
510
|
+
end try
|
|
511
|
+
end tell
|
|
512
|
+
`;
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
const result = runAppleScript(script);
|
|
516
|
+
if (result.startsWith("ERROR:")) {
|
|
517
|
+
return { success: false, message: result.substring(6) };
|
|
518
|
+
}
|
|
519
|
+
return { success: true, message: result || "Message marked as unread" };
|
|
520
|
+
} catch (error: any) {
|
|
521
|
+
return { success: false, message: error.message };
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Create a draft email
|
|
527
|
+
*/
|
|
528
|
+
export function createDraft(options: {
|
|
529
|
+
to?: string | string[];
|
|
530
|
+
subject: string;
|
|
531
|
+
body: string;
|
|
532
|
+
cc?: string | string[];
|
|
533
|
+
bcc?: string | string[];
|
|
534
|
+
from?: string;
|
|
535
|
+
}): { success: boolean; message: string } {
|
|
536
|
+
const { to, subject, body, cc, bcc, from } = options;
|
|
537
|
+
|
|
538
|
+
const toList = to ? (Array.isArray(to) ? to : [to]) : [];
|
|
539
|
+
const ccList = cc ? (Array.isArray(cc) ? cc : [cc]) : [];
|
|
540
|
+
const bccList = bcc ? (Array.isArray(bcc) ? bcc : [bcc]) : [];
|
|
541
|
+
|
|
542
|
+
// Build recipient parts
|
|
543
|
+
const toRecipients = toList.map(addr => `make new to recipient at end of to recipients with properties {address:"${addr}"}`).join("\n ");
|
|
544
|
+
const ccRecipients = ccList.map(addr => `make new cc recipient at end of cc recipients with properties {address:"${addr}"}`).join("\n ");
|
|
545
|
+
const bccRecipients = bccList.map(addr => `make new bcc recipient at end of bcc recipients with properties {address:"${addr}"}`).join("\n ");
|
|
546
|
+
|
|
547
|
+
const fromPart = from ? `, sender:"${from}"` : "";
|
|
548
|
+
|
|
549
|
+
const script = `
|
|
550
|
+
tell application "Mail"
|
|
551
|
+
set newMessage to make new outgoing message with properties {subject:"${subject.replace(/"/g, '\\"')}", content:"${body.replace(/"/g, '\\"').replace(/\n/g, "\\n")}", visible:true${fromPart}}
|
|
552
|
+
tell newMessage
|
|
553
|
+
${toRecipients}
|
|
554
|
+
${ccRecipients ? ccRecipients : ""}
|
|
555
|
+
${bccRecipients ? bccRecipients : ""}
|
|
556
|
+
end tell
|
|
557
|
+
-- Save as draft by not sending, just leaving it open
|
|
558
|
+
return "Draft created successfully"
|
|
559
|
+
end tell
|
|
560
|
+
`;
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
const result = runAppleScript(script);
|
|
564
|
+
return { success: true, message: result || "Draft created successfully" };
|
|
565
|
+
} catch (error: any) {
|
|
566
|
+
return { success: false, message: error.message };
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Create a draft reply to an existing email
|
|
572
|
+
*/
|
|
573
|
+
export function createDraftReply(options: {
|
|
574
|
+
messageId: number;
|
|
575
|
+
body: string;
|
|
576
|
+
replyAll?: boolean;
|
|
577
|
+
account?: string;
|
|
578
|
+
mailbox?: string;
|
|
579
|
+
}): { success: boolean; message: string } {
|
|
580
|
+
const { messageId, body, replyAll = false, account, mailbox = "INBOX" } = options;
|
|
581
|
+
|
|
582
|
+
const accountPart = account
|
|
583
|
+
? `mailbox "${mailbox}" of account "${account}"`
|
|
584
|
+
: `mailbox "${mailbox}"`;
|
|
585
|
+
|
|
586
|
+
const replyCommand = replyAll ? "reply theMessage with opening window and reply to all" : "reply theMessage with opening window";
|
|
587
|
+
|
|
588
|
+
const script = `
|
|
589
|
+
tell application "Mail"
|
|
590
|
+
try
|
|
591
|
+
set theMailbox to ${accountPart}
|
|
592
|
+
set theMessage to (first message of theMailbox whose id is ${messageId})
|
|
593
|
+
|
|
594
|
+
set replyMessage to ${replyCommand}
|
|
595
|
+
|
|
596
|
+
-- Prepend the new body to the reply
|
|
597
|
+
set currentContent to content of replyMessage
|
|
598
|
+
set content of replyMessage to "${body.replace(/"/g, '\\"').replace(/\n/g, "\\n")}" & return & return & currentContent
|
|
599
|
+
|
|
600
|
+
return "Draft reply created successfully"
|
|
601
|
+
on error errMsg
|
|
602
|
+
return "ERROR:" & errMsg
|
|
603
|
+
end try
|
|
604
|
+
end tell
|
|
605
|
+
`;
|
|
606
|
+
|
|
607
|
+
try {
|
|
608
|
+
const result = runAppleScript(script);
|
|
609
|
+
if (result.startsWith("ERROR:")) {
|
|
610
|
+
return { success: false, message: result.substring(6) };
|
|
611
|
+
}
|
|
612
|
+
return { success: true, message: result || "Draft reply created successfully" };
|
|
613
|
+
} catch (error: any) {
|
|
614
|
+
return { success: false, message: error.message };
|
|
615
|
+
}
|
|
616
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,12 @@ import {
|
|
|
15
15
|
searchEmails,
|
|
16
16
|
getUnreadCount,
|
|
17
17
|
sendEmail,
|
|
18
|
+
archiveEmail,
|
|
19
|
+
deleteEmail,
|
|
20
|
+
markAsRead,
|
|
21
|
+
markAsUnread,
|
|
22
|
+
createDraft,
|
|
23
|
+
createDraftReply,
|
|
18
24
|
} from "./applescript/mail.js";
|
|
19
25
|
|
|
20
26
|
// Create MCP server
|
|
@@ -147,6 +153,147 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
147
153
|
};
|
|
148
154
|
}
|
|
149
155
|
|
|
156
|
+
case "mail_archive": {
|
|
157
|
+
const messageId = args?.messageId as number;
|
|
158
|
+
if (!messageId) {
|
|
159
|
+
throw new Error("Required field: messageId");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result = archiveEmail({
|
|
163
|
+
messageId,
|
|
164
|
+
account: args?.account as string | undefined,
|
|
165
|
+
mailbox: (args?.mailbox as string) || "INBOX",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: JSON.stringify(result, null, 2),
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case "mail_delete": {
|
|
179
|
+
const messageId = args?.messageId as number;
|
|
180
|
+
if (!messageId) {
|
|
181
|
+
throw new Error("Required field: messageId");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const result = deleteEmail({
|
|
185
|
+
messageId,
|
|
186
|
+
account: args?.account as string | undefined,
|
|
187
|
+
mailbox: (args?.mailbox as string) || "INBOX",
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
content: [
|
|
192
|
+
{
|
|
193
|
+
type: "text",
|
|
194
|
+
text: JSON.stringify(result, null, 2),
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
case "mail_mark_read": {
|
|
201
|
+
const messageId = args?.messageId as number;
|
|
202
|
+
if (!messageId) {
|
|
203
|
+
throw new Error("Required field: messageId");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const result = markAsRead({
|
|
207
|
+
messageId,
|
|
208
|
+
account: args?.account as string | undefined,
|
|
209
|
+
mailbox: (args?.mailbox as string) || "INBOX",
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: "text",
|
|
216
|
+
text: JSON.stringify(result, null, 2),
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
case "mail_mark_unread": {
|
|
223
|
+
const messageId = args?.messageId as number;
|
|
224
|
+
if (!messageId) {
|
|
225
|
+
throw new Error("Required field: messageId");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const result = markAsUnread({
|
|
229
|
+
messageId,
|
|
230
|
+
account: args?.account as string | undefined,
|
|
231
|
+
mailbox: (args?.mailbox as string) || "INBOX",
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
content: [
|
|
236
|
+
{
|
|
237
|
+
type: "text",
|
|
238
|
+
text: JSON.stringify(result, null, 2),
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
case "mail_create_draft": {
|
|
245
|
+
const subject = args?.subject as string;
|
|
246
|
+
const body = args?.body as string;
|
|
247
|
+
|
|
248
|
+
if (!subject || !body) {
|
|
249
|
+
throw new Error("Required fields: subject, body");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const result = createDraft({
|
|
253
|
+
to: args?.to as string | string[] | undefined,
|
|
254
|
+
subject,
|
|
255
|
+
body,
|
|
256
|
+
cc: args?.cc as string | string[] | undefined,
|
|
257
|
+
bcc: args?.bcc as string | string[] | undefined,
|
|
258
|
+
from: args?.from as string | undefined,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: JSON.stringify(result, null, 2),
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
case "mail_create_draft_reply": {
|
|
272
|
+
const messageId = args?.messageId as number;
|
|
273
|
+
const body = args?.body as string;
|
|
274
|
+
|
|
275
|
+
if (!messageId || !body) {
|
|
276
|
+
throw new Error("Required fields: messageId, body");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const result = createDraftReply({
|
|
280
|
+
messageId,
|
|
281
|
+
body,
|
|
282
|
+
replyAll: (args?.replyAll as boolean) || false,
|
|
283
|
+
account: args?.account as string | undefined,
|
|
284
|
+
mailbox: (args?.mailbox as string) || "INBOX",
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: "text",
|
|
291
|
+
text: JSON.stringify(result, null, 2),
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
150
297
|
default:
|
|
151
298
|
throw new Error(`Unknown tool: ${name}`);
|
|
152
299
|
}
|
package/src/tools.ts
CHANGED
|
@@ -146,6 +146,179 @@ export const MAIL_SEND: Tool = {
|
|
|
146
146
|
},
|
|
147
147
|
};
|
|
148
148
|
|
|
149
|
+
export const MAIL_ARCHIVE: Tool = {
|
|
150
|
+
name: "mail_archive",
|
|
151
|
+
description: "Archive an email in Apple Mail by moving it to the Archive mailbox",
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
messageId: {
|
|
156
|
+
type: "number",
|
|
157
|
+
description: "The ID of the email message to archive (obtained from mail_get_emails or mail_search)",
|
|
158
|
+
},
|
|
159
|
+
account: {
|
|
160
|
+
type: "string",
|
|
161
|
+
description: "The name of the email account",
|
|
162
|
+
},
|
|
163
|
+
mailbox: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: "The name of the mailbox/folder where the email currently is (default: INBOX)",
|
|
166
|
+
default: "INBOX",
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
required: ["messageId"],
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const MAIL_DELETE: Tool = {
|
|
174
|
+
name: "mail_delete",
|
|
175
|
+
description: "Delete an email in Apple Mail by moving it to the Trash mailbox",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
type: "object",
|
|
178
|
+
properties: {
|
|
179
|
+
messageId: {
|
|
180
|
+
type: "number",
|
|
181
|
+
description: "The ID of the email message to delete (obtained from mail_get_emails or mail_search)",
|
|
182
|
+
},
|
|
183
|
+
account: {
|
|
184
|
+
type: "string",
|
|
185
|
+
description: "The name of the email account",
|
|
186
|
+
},
|
|
187
|
+
mailbox: {
|
|
188
|
+
type: "string",
|
|
189
|
+
description: "The name of the mailbox/folder where the email currently is (default: INBOX)",
|
|
190
|
+
default: "INBOX",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
required: ["messageId"],
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export const MAIL_MARK_READ: Tool = {
|
|
198
|
+
name: "mail_mark_read",
|
|
199
|
+
description: "Mark an email as read in Apple Mail",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
messageId: {
|
|
204
|
+
type: "number",
|
|
205
|
+
description: "The ID of the email message to mark as read (obtained from mail_get_emails or mail_search)",
|
|
206
|
+
},
|
|
207
|
+
account: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "The name of the email account",
|
|
210
|
+
},
|
|
211
|
+
mailbox: {
|
|
212
|
+
type: "string",
|
|
213
|
+
description: "The name of the mailbox/folder where the email is (default: INBOX)",
|
|
214
|
+
default: "INBOX",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
required: ["messageId"],
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export const MAIL_MARK_UNREAD: Tool = {
|
|
222
|
+
name: "mail_mark_unread",
|
|
223
|
+
description: "Mark an email as unread in Apple Mail",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
type: "object",
|
|
226
|
+
properties: {
|
|
227
|
+
messageId: {
|
|
228
|
+
type: "number",
|
|
229
|
+
description: "The ID of the email message to mark as unread (obtained from mail_get_emails or mail_search)",
|
|
230
|
+
},
|
|
231
|
+
account: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: "The name of the email account",
|
|
234
|
+
},
|
|
235
|
+
mailbox: {
|
|
236
|
+
type: "string",
|
|
237
|
+
description: "The name of the mailbox/folder where the email is (default: INBOX)",
|
|
238
|
+
default: "INBOX",
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
required: ["messageId"],
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export const MAIL_CREATE_DRAFT: Tool = {
|
|
246
|
+
name: "mail_create_draft",
|
|
247
|
+
description: "Create a new draft email in Apple Mail",
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
to: {
|
|
252
|
+
oneOf: [
|
|
253
|
+
{ type: "string" },
|
|
254
|
+
{ type: "array", items: { type: "string" } },
|
|
255
|
+
],
|
|
256
|
+
description: "Email address(es) of the recipient(s)",
|
|
257
|
+
},
|
|
258
|
+
subject: {
|
|
259
|
+
type: "string",
|
|
260
|
+
description: "The email subject line",
|
|
261
|
+
},
|
|
262
|
+
body: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "The email body content",
|
|
265
|
+
},
|
|
266
|
+
cc: {
|
|
267
|
+
oneOf: [
|
|
268
|
+
{ type: "string" },
|
|
269
|
+
{ type: "array", items: { type: "string" } },
|
|
270
|
+
],
|
|
271
|
+
description: "Email address(es) for CC recipients",
|
|
272
|
+
},
|
|
273
|
+
bcc: {
|
|
274
|
+
oneOf: [
|
|
275
|
+
{ type: "string" },
|
|
276
|
+
{ type: "array", items: { type: "string" } },
|
|
277
|
+
],
|
|
278
|
+
description: "Email address(es) for BCC recipients",
|
|
279
|
+
},
|
|
280
|
+
from: {
|
|
281
|
+
type: "string",
|
|
282
|
+
description: "The sender email address (must be a configured account)",
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
required: ["subject", "body"],
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
export const MAIL_CREATE_DRAFT_REPLY: Tool = {
|
|
290
|
+
name: "mail_create_draft_reply",
|
|
291
|
+
description: "Create a draft reply to an existing email in Apple Mail",
|
|
292
|
+
inputSchema: {
|
|
293
|
+
type: "object",
|
|
294
|
+
properties: {
|
|
295
|
+
messageId: {
|
|
296
|
+
type: "number",
|
|
297
|
+
description: "The ID of the email message to reply to (obtained from mail_get_emails or mail_search)",
|
|
298
|
+
},
|
|
299
|
+
body: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description: "The reply body content",
|
|
302
|
+
},
|
|
303
|
+
replyAll: {
|
|
304
|
+
type: "boolean",
|
|
305
|
+
description: "Whether to reply to all recipients (default: false)",
|
|
306
|
+
default: false,
|
|
307
|
+
},
|
|
308
|
+
account: {
|
|
309
|
+
type: "string",
|
|
310
|
+
description: "The name of the email account",
|
|
311
|
+
},
|
|
312
|
+
mailbox: {
|
|
313
|
+
type: "string",
|
|
314
|
+
description: "The name of the mailbox/folder where the original email is (default: INBOX)",
|
|
315
|
+
default: "INBOX",
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
required: ["messageId", "body"],
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
149
322
|
export const tools: Tool[] = [
|
|
150
323
|
MAIL_LIST_ACCOUNTS,
|
|
151
324
|
MAIL_LIST_MAILBOXES,
|
|
@@ -153,4 +326,10 @@ export const tools: Tool[] = [
|
|
|
153
326
|
MAIL_SEARCH,
|
|
154
327
|
MAIL_GET_UNREAD_COUNT,
|
|
155
328
|
MAIL_SEND,
|
|
329
|
+
MAIL_ARCHIVE,
|
|
330
|
+
MAIL_DELETE,
|
|
331
|
+
MAIL_MARK_READ,
|
|
332
|
+
MAIL_MARK_UNREAD,
|
|
333
|
+
MAIL_CREATE_DRAFT,
|
|
334
|
+
MAIL_CREATE_DRAFT_REPLY,
|
|
156
335
|
];
|