apple-mail-mcp 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -8
- package/build/cli.d.ts +24 -0
- package/build/cli.d.ts.map +1 -0
- package/build/cli.js +165 -0
- package/build/index.js +579 -209
- package/build/services/smtpMailer.d.ts +33 -2
- package/build/services/smtpMailer.d.ts.map +1 -1
- package/build/services/smtpMailer.js +39 -2
- package/build/tools/doctor.js +5 -5
- package/package.json +3 -2
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Connection settings come from environment variables; the password is read
|
|
11
11
|
* from the macOS Keychain via the `security` CLI by default so no secret is
|
|
12
|
-
* ever placed in config.
|
|
13
|
-
* opt
|
|
12
|
+
* ever placed in config. When SMTP is configured, `send-email` auto-prefers it
|
|
13
|
+
* over AppleScript (opt out per call with `transport: "applescript"`); see
|
|
14
|
+
* {@link shouldUseSmtp}.
|
|
14
15
|
*
|
|
15
16
|
* @module services/smtpMailer
|
|
16
17
|
*/
|
|
@@ -20,6 +21,7 @@ import type { AttachmentInput } from "../types.js";
|
|
|
20
21
|
export interface SmtpSendOptions {
|
|
21
22
|
to: string[];
|
|
22
23
|
subject: string;
|
|
24
|
+
/** Plain-text body. Always sent as the text/plain part. */
|
|
23
25
|
body: string;
|
|
24
26
|
cc?: string[];
|
|
25
27
|
bcc?: string[];
|
|
@@ -27,6 +29,13 @@ export interface SmtpSendOptions {
|
|
|
27
29
|
from?: string;
|
|
28
30
|
/** Files to attach: absolute paths and/or inline base64 content (B4). */
|
|
29
31
|
attachments?: AttachmentInput[];
|
|
32
|
+
/**
|
|
33
|
+
* Optional HTML body. When provided, the message is sent as
|
|
34
|
+
* multipart/alternative ({@link SmtpSendOptions.body} as the text/plain part,
|
|
35
|
+
* this as the text/html part) so clients pick the richer rendering while
|
|
36
|
+
* plain-text clients still get a clean fallback.
|
|
37
|
+
*/
|
|
38
|
+
htmlBody?: string;
|
|
30
39
|
}
|
|
31
40
|
/** Resolved SMTP connection configuration. */
|
|
32
41
|
export interface SmtpConfig {
|
|
@@ -57,6 +66,28 @@ export declare const SMTP_ENV: {
|
|
|
57
66
|
readonly keychainService: "APPLE_MAIL_MCP_SMTP_KEYCHAIN_SERVICE";
|
|
58
67
|
readonly keychainAccount: "APPLE_MAIL_MCP_SMTP_KEYCHAIN_ACCOUNT";
|
|
59
68
|
};
|
|
69
|
+
/**
|
|
70
|
+
* Cheap check for whether the SMTP transport is configured at all, i.e. the two
|
|
71
|
+
* required settings ({@link SMTP_ENV.host} and {@link SMTP_ENV.user}) are
|
|
72
|
+
* present. Used to auto-prefer SMTP over AppleScript when no transport is
|
|
73
|
+
* explicitly requested, and by `doctor`. Does NOT touch the Keychain or verify
|
|
74
|
+
* the password — a misconfigured password still surfaces a clear error at send
|
|
75
|
+
* time via {@link resolveSmtpConfig}.
|
|
76
|
+
*/
|
|
77
|
+
export declare function isSmtpConfigured(env?: NodeJS.ProcessEnv): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Decides whether `send-email` should use the SMTP transport for this call.
|
|
80
|
+
*
|
|
81
|
+
* - explicit `"smtp"` → always SMTP (config errors surface, no fallback);
|
|
82
|
+
* - explicit `"applescript"` → always the Mail.app path;
|
|
83
|
+
* - omitted → SMTP when configured, **except** when a non-email `account` label
|
|
84
|
+
* (a Mail.app account name such as `"Work"`) is supplied. That requests
|
|
85
|
+
* account-based sending, which only the AppleScript path can do, so we honor
|
|
86
|
+
* the caller's intent instead of silently switching their account. An
|
|
87
|
+
* `account` that is an email address is treated as a From override and does
|
|
88
|
+
* not block SMTP.
|
|
89
|
+
*/
|
|
90
|
+
export declare function shouldUseSmtp(transport: "applescript" | "smtp" | undefined, account: string | undefined, configured?: boolean): boolean;
|
|
60
91
|
/**
|
|
61
92
|
* Reads a password from the macOS login Keychain via the `security` CLI.
|
|
62
93
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smtpMailer.d.ts","sourceRoot":"","sources":["../../src/services/smtpMailer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"smtpMailer.d.ts","sourceRoot":"","sources":["../../src/services/smtpMailer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,UAAU,MAAM,YAAY,CAAC;AAIpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8CAA8C;AAC9C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8BAA8B;AAC9B,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;CASX,CAAC;AAEX;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE9E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,EAC7C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,UAAU,GAAE,OAA4B,GACvC,OAAO,CAMT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcpF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CA8ClF;AAqBD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,eAAe,EACrB,MAAM,CAAC,EAAE,UAAU,EACnB,eAAe,GAAE,OAAO,UAAU,CAAC,eAA4C,GAC9E,OAAO,CAAC,cAAc,CAAC,CA6CzB"}
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Connection settings come from environment variables; the password is read
|
|
11
11
|
* from the macOS Keychain via the `security` CLI by default so no secret is
|
|
12
|
-
* ever placed in config.
|
|
13
|
-
* opt
|
|
12
|
+
* ever placed in config. When SMTP is configured, `send-email` auto-prefers it
|
|
13
|
+
* over AppleScript (opt out per call with `transport: "applescript"`); see
|
|
14
|
+
* {@link shouldUseSmtp}.
|
|
14
15
|
*
|
|
15
16
|
* @module services/smtpMailer
|
|
16
17
|
*/
|
|
@@ -32,6 +33,39 @@ export const SMTP_ENV = {
|
|
|
32
33
|
keychainService: "APPLE_MAIL_MCP_SMTP_KEYCHAIN_SERVICE",
|
|
33
34
|
keychainAccount: "APPLE_MAIL_MCP_SMTP_KEYCHAIN_ACCOUNT",
|
|
34
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Cheap check for whether the SMTP transport is configured at all, i.e. the two
|
|
38
|
+
* required settings ({@link SMTP_ENV.host} and {@link SMTP_ENV.user}) are
|
|
39
|
+
* present. Used to auto-prefer SMTP over AppleScript when no transport is
|
|
40
|
+
* explicitly requested, and by `doctor`. Does NOT touch the Keychain or verify
|
|
41
|
+
* the password — a misconfigured password still surfaces a clear error at send
|
|
42
|
+
* time via {@link resolveSmtpConfig}.
|
|
43
|
+
*/
|
|
44
|
+
export function isSmtpConfigured(env = process.env) {
|
|
45
|
+
return Boolean(env[SMTP_ENV.host]?.trim() && env[SMTP_ENV.user]?.trim());
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Decides whether `send-email` should use the SMTP transport for this call.
|
|
49
|
+
*
|
|
50
|
+
* - explicit `"smtp"` → always SMTP (config errors surface, no fallback);
|
|
51
|
+
* - explicit `"applescript"` → always the Mail.app path;
|
|
52
|
+
* - omitted → SMTP when configured, **except** when a non-email `account` label
|
|
53
|
+
* (a Mail.app account name such as `"Work"`) is supplied. That requests
|
|
54
|
+
* account-based sending, which only the AppleScript path can do, so we honor
|
|
55
|
+
* the caller's intent instead of silently switching their account. An
|
|
56
|
+
* `account` that is an email address is treated as a From override and does
|
|
57
|
+
* not block SMTP.
|
|
58
|
+
*/
|
|
59
|
+
export function shouldUseSmtp(transport, account, configured = isSmtpConfigured()) {
|
|
60
|
+
if (transport === "smtp")
|
|
61
|
+
return true;
|
|
62
|
+
if (transport === "applescript")
|
|
63
|
+
return false;
|
|
64
|
+
if (!configured)
|
|
65
|
+
return false;
|
|
66
|
+
const isAccountLabel = Boolean(account && !account.includes("@"));
|
|
67
|
+
return !isAccountLabel;
|
|
68
|
+
}
|
|
35
69
|
/**
|
|
36
70
|
* Reads a password from the macOS login Keychain via the `security` CLI.
|
|
37
71
|
*
|
|
@@ -152,6 +186,7 @@ export async function sendViaSmtp(opts, config, createTransport = nodemailer.cre
|
|
|
152
186
|
secure: cfg.secure,
|
|
153
187
|
auth: { user: cfg.user, pass: cfg.pass },
|
|
154
188
|
});
|
|
189
|
+
const html = opts.htmlBody?.trim() ? opts.htmlBody : undefined;
|
|
155
190
|
try {
|
|
156
191
|
const info = await transporter.sendMail({
|
|
157
192
|
from: opts.from?.trim() || cfg.from,
|
|
@@ -160,6 +195,8 @@ export async function sendViaSmtp(opts, config, createTransport = nodemailer.cre
|
|
|
160
195
|
bcc: opts.bcc,
|
|
161
196
|
subject: opts.subject,
|
|
162
197
|
text: opts.body,
|
|
198
|
+
// When present, nodemailer emits multipart/alternative (text + html).
|
|
199
|
+
html,
|
|
163
200
|
attachments,
|
|
164
201
|
});
|
|
165
202
|
return { success: true, messageId: info.messageId };
|
package/build/tools/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { imapHealthCheck, IMAP_ENV, listImapAccountLabels } from "../services/imapClient.js";
|
|
2
|
-
import { SMTP_ENV } from "../services/smtpMailer.js";
|
|
2
|
+
import { SMTP_ENV, isSmtpConfigured } from "../services/smtpMailer.js";
|
|
3
3
|
export async function runDoctor(mailManager) {
|
|
4
4
|
const checks = [];
|
|
5
5
|
// 1. Mail.app reachability + Automation permission (existing health check).
|
|
@@ -56,10 +56,10 @@ export async function runDoctor(mailManager) {
|
|
|
56
56
|
const smtpHost = process.env[SMTP_ENV.host]?.trim();
|
|
57
57
|
checks.push({
|
|
58
58
|
name: "SMTP transport",
|
|
59
|
-
status:
|
|
60
|
-
detail:
|
|
61
|
-
? `configured (${smtpHost}); send-email transport:"
|
|
62
|
-
: `not configured — send-email uses AppleScript (subject to macOS 15+ blockquote wrapping). Set ${SMTP_ENV.host} to enable.`,
|
|
59
|
+
status: isSmtpConfigured() ? "ok" : "warn",
|
|
60
|
+
detail: isSmtpConfigured()
|
|
61
|
+
? `configured (${smtpHost}); send-email auto-prefers clean SMTP (no Mail.app Sent-folder copy; a non-email "account" label still routes to AppleScript). Pass transport:"applescript" to force Mail.app. The apple-mail-send CLI is also available.`
|
|
62
|
+
: `not configured — send-email uses AppleScript (subject to macOS 15+ blockquote wrapping). Set ${SMTP_ENV.host} and ${SMTP_ENV.user} (+ password via Keychain) to enable.`,
|
|
63
63
|
});
|
|
64
64
|
const healthy = !checks.some((c) => c.status === "fail");
|
|
65
65
|
return { healthy, checks };
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-mail-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "MCP server for Apple Mail - read, search, send, and manage emails via Claude and other AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"apple-mail-mcp": "build/index.js"
|
|
9
|
+
"apple-mail-mcp": "build/index.js",
|
|
10
|
+
"apple-mail-send": "build/cli.js"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
13
|
"build",
|