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.
@@ -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. AppleScript remains the default transport SMTP is
13
- * opt-in per call (`transport: "smtp"`).
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;;;;;;;;;;;;;;;GAeG;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,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;CACjC;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;;;;;;;;;;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,CAyCzB"}
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. AppleScript remains the default transport SMTP is
13
- * opt-in per call (`transport: "smtp"`).
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 };
@@ -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: smtpHost ? "ok" : "warn",
60
- detail: smtpHost
61
- ? `configured (${smtpHost}); send-email transport:"smtp" is available`
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.2.0",
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",