apple-mail-mcp 2.19.10 → 2.19.12
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 +5 -3
- package/build/cli.js +6 -0
- package/build/index.js +62 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -826,7 +826,7 @@ Reply to an existing message.
|
|
|
826
826
|
| Parameter | Type | Required | Description |
|
|
827
827
|
| ----------- | ------- | -------- | ---------------------------------------------------------------------------------------------- |
|
|
828
828
|
| `id` | string | Yes | Message ID to reply to |
|
|
829
|
-
| `body` | string | Yes | Reply body
|
|
829
|
+
| `body` | string | Yes | Reply body (plain text; HTML tags such as `<br>` are not rendered) |
|
|
830
830
|
| `replyAll` | boolean | No | Reply to all recipients (default: false) |
|
|
831
831
|
| `send` | boolean | No | Send immediately (default: true, false = save as draft) |
|
|
832
832
|
| `transport` | string | No | `smtp` or `applescript`; omitted prefers configured SMTP when sending. Drafts use AppleScript. |
|
|
@@ -853,6 +853,8 @@ Reply to an existing message.
|
|
|
853
853
|
|
|
854
854
|
**Delivery:** `imap:` IDs are read directly from their encoded account, mailbox, and UID. Numeric IDs are read through Mail.app. With SMTP configured, replies use clean MIME with the original `Message-ID` in `In-Reply-To` and the full `References` chain. Only the original plain-text body is quoted; the new text is not. Pass `transport: "smtp"` to require this path, or `transport: "applescript"` to use Mail.app explicitly.
|
|
855
855
|
|
|
856
|
+
**AppleScript transport quoting (v2.19.12):** every draft (`send: false`) and any send that falls back to AppleScript (SMTP not configured) also gets the original quoted — an "On \<date\>, \<sender\> wrote:" attribution line followed by the `> `-prefixed original body, built the same way as the SMTP path. Mail's own `reply`/`forward` AppleScript commands generate this quote internally but expose no way to read it back, so it's built here instead of relying on Mail's (unreadable) copy. Unlike SMTP, this path has no sending-identity restriction — it's read-only source lookup, not sending.
|
|
857
|
+
|
|
856
858
|
**Failure behavior:** once SMTP is selected, a missing password, unreadable source, missing reply address or `Message-ID`, or SMTP failure returns an error. It does not silently switch to AppleScript or create an unthreaded new message. With SMTP unconfigured and transport omitted, AppleScript remains available. `send: false` saves a Mail.app draft; combining it with `transport: "smtp"` is rejected before composing.
|
|
857
859
|
|
|
858
860
|
The direct IMAP source read is bounded to 25 MiB, including MIME attachments. Its account login must match the SMTP login, configured From, or an explicitly configured `APPLE_MAIL_MCP_SMTP_ALLOWED_FROM` identity; otherwise the call fails rather than sending from an unrelated account. The SMTP configuration still represents a single sending identity, not a per-account SMTP registry.
|
|
@@ -871,11 +873,11 @@ Forward a message to new recipients.
|
|
|
871
873
|
| ----------- | -------- | -------- | ---------------------------------------------------------------------------------------------- |
|
|
872
874
|
| `id` | string | Yes | Message ID to forward |
|
|
873
875
|
| `to` | string[] | Yes | Recipients to forward to |
|
|
874
|
-
| `body` | string | No | Message to prepend
|
|
876
|
+
| `body` | string | No | Message to prepend (plain text) |
|
|
875
877
|
| `send` | boolean | No | Send immediately (default: true, false = save as draft) |
|
|
876
878
|
| `transport` | string | No | `smtp` or `applescript`; omitted prefers configured SMTP when sending. Drafts use AppleScript. |
|
|
877
879
|
|
|
878
|
-
**Delivery:** uses the same source lookup, transport selection, 25 MiB source limit, account-identity check, and failure behavior as `reply-to-message`. A forward deliberately starts a new conversation, so it has no `In-Reply-To` or `References` headers. The existing plain-text forwarding behavior is unchanged: original attachments are not reattached. See [SMTP transport](#smtp-transport).
|
|
880
|
+
**Delivery:** uses the same source lookup, transport selection, 25 MiB source limit, account-identity check, and failure behavior as `reply-to-message`. A forward deliberately starts a new conversation, so it has no `In-Reply-To` or `References` headers. The existing plain-text forwarding behavior is unchanged: original attachments are not reattached. See [SMTP transport](#smtp-transport). On the AppleScript transport, a `body` is merged with the forwarded-message header block and original text the same way the SMTP path builds it (see `reply-to-message`'s AppleScript transport quoting note); with no `body`, Mail's own forward content is left untouched.
|
|
879
881
|
|
|
880
882
|
SMTP forwarding requires a readable plain-text original. HTML-only IMAP messages and failed Mail.app body reads return an error before sending instead of silently omitting the original content. Explicitly select `transport: "applescript"` to forward these with Mail.app; no automatic fallback occurs. An intentionally empty plain-text message is still valid.
|
|
881
883
|
|
package/build/cli.js
CHANGED
|
@@ -58409,6 +58409,12 @@ async function fetchMailboxMatches(client, path, criteria, newestCount) {
|
|
|
58409
58409
|
const found = await client.search(criteria, { uid: true });
|
|
58410
58410
|
const uids = Array.isArray(found) ? found : [];
|
|
58411
58411
|
if (uids.length === 0 || newestCount === 0) return { messages: [], total: uids.length };
|
|
58412
|
+
const status = await client.status(path, { messages: true });
|
|
58413
|
+
if (typeof status.messages === "number" && uids.length > status.messages) {
|
|
58414
|
+
throw new Error(
|
|
58415
|
+
`IMAP SEARCH on "${path}" reported ${uids.length} matches, more than the mailbox's own ${status.messages} messages \u2014 discarding as corrupted rather than trusting it (see #246).`
|
|
58416
|
+
);
|
|
58417
|
+
}
|
|
58412
58418
|
const newest = uids.slice().reverse().slice(0, newestCount);
|
|
58413
58419
|
const byUid = /* @__PURE__ */ new Map();
|
|
58414
58420
|
for await (const msg of client.fetch(
|
package/build/index.js
CHANGED
|
@@ -65738,6 +65738,12 @@ async function fetchMailboxMatches(client, path, criteria, newestCount) {
|
|
|
65738
65738
|
const found = await client.search(criteria, { uid: true });
|
|
65739
65739
|
const uids = Array.isArray(found) ? found : [];
|
|
65740
65740
|
if (uids.length === 0 || newestCount === 0) return { messages: [], total: uids.length };
|
|
65741
|
+
const status = await client.status(path, { messages: true });
|
|
65742
|
+
if (typeof status.messages === "number" && uids.length > status.messages) {
|
|
65743
|
+
throw new Error(
|
|
65744
|
+
`IMAP SEARCH on "${path}" reported ${uids.length} matches, more than the mailbox's own ${status.messages} messages \u2014 discarding as corrupted rather than trusting it (see #246).`
|
|
65745
|
+
);
|
|
65746
|
+
}
|
|
65741
65747
|
const newest = uids.slice().reverse().slice(0, newestCount);
|
|
65742
65748
|
const byUid = /* @__PURE__ */ new Map();
|
|
65743
65749
|
for await (const msg of client.fetch(
|
|
@@ -86841,10 +86847,6 @@ function buildReplyOptions(args) {
|
|
|
86841
86847
|
);
|
|
86842
86848
|
cc = extra.length ? extra : void 0;
|
|
86843
86849
|
}
|
|
86844
|
-
const attribution = buildAttribution(original);
|
|
86845
|
-
const quoted = originalPlainText.trim() ? `
|
|
86846
|
-
|
|
86847
|
-
${attribution}${quoteBody(originalPlainText)}` : "";
|
|
86848
86850
|
const references = dedupe(
|
|
86849
86851
|
original.messageId ? [...original.references, original.messageId] : original.references
|
|
86850
86852
|
);
|
|
@@ -86852,12 +86854,19 @@ ${attribution}${quoteBody(originalPlainText)}` : "";
|
|
|
86852
86854
|
to,
|
|
86853
86855
|
cc,
|
|
86854
86856
|
subject: withSubjectPrefix(original.subject, "Re:"),
|
|
86855
|
-
body:
|
|
86857
|
+
body: buildReplyBody(body, original, originalPlainText),
|
|
86856
86858
|
inReplyTo: original.messageId,
|
|
86857
86859
|
references: references.length ? references : void 0,
|
|
86858
86860
|
from
|
|
86859
86861
|
};
|
|
86860
86862
|
}
|
|
86863
|
+
function buildReplyBody(body, original, originalPlainText) {
|
|
86864
|
+
const attribution = buildAttribution(original);
|
|
86865
|
+
const quoted = originalPlainText.trim() ? `
|
|
86866
|
+
|
|
86867
|
+
${attribution}${quoteBody(originalPlainText)}` : "";
|
|
86868
|
+
return `${body}${quoted}`;
|
|
86869
|
+
}
|
|
86861
86870
|
function buildAttribution(original) {
|
|
86862
86871
|
const who = original.from[0] ?? original.replyTo[0] ?? "the sender";
|
|
86863
86872
|
return original.date ? `On ${original.date}, ${who} wrote:
|
|
@@ -86866,6 +86875,14 @@ function buildAttribution(original) {
|
|
|
86866
86875
|
}
|
|
86867
86876
|
function buildForwardOptions(args) {
|
|
86868
86877
|
const { original, originalPlainText, to, body, from } = args;
|
|
86878
|
+
return {
|
|
86879
|
+
to: dedupe(to),
|
|
86880
|
+
subject: withSubjectPrefix(original.subject, "Fwd:"),
|
|
86881
|
+
body: buildForwardBody(original, originalPlainText, body),
|
|
86882
|
+
from
|
|
86883
|
+
};
|
|
86884
|
+
}
|
|
86885
|
+
function buildForwardBody(original, originalPlainText, body) {
|
|
86869
86886
|
const headerBlock = [
|
|
86870
86887
|
"---------- Forwarded message ----------",
|
|
86871
86888
|
original.from.length ? `From: ${original.from.join(", ")}` : "",
|
|
@@ -86877,14 +86894,9 @@ function buildForwardOptions(args) {
|
|
|
86877
86894
|
const prefix = body?.trim() ? `${body}
|
|
86878
86895
|
|
|
86879
86896
|
` : "";
|
|
86880
|
-
return {
|
|
86881
|
-
to: dedupe(to),
|
|
86882
|
-
subject: withSubjectPrefix(original.subject, "Fwd:"),
|
|
86883
|
-
body: `${prefix}${headerBlock}
|
|
86897
|
+
return `${prefix}${headerBlock}
|
|
86884
86898
|
|
|
86885
|
-
${originalPlainText}
|
|
86886
|
-
from
|
|
86887
|
-
};
|
|
86899
|
+
${originalPlainText}`;
|
|
86888
86900
|
}
|
|
86889
86901
|
|
|
86890
86902
|
// src/tools/compose.ts
|
|
@@ -87022,6 +87034,31 @@ async function readOriginal(deps, id, cfg) {
|
|
|
87022
87034
|
const content = deps.mail.getMessageContent(id);
|
|
87023
87035
|
return { original: parseOriginalHeaders(raw), plainText: content?.plainText ?? null };
|
|
87024
87036
|
}
|
|
87037
|
+
async function readOriginalForQuote(deps, id) {
|
|
87038
|
+
if (decodeImapId(id)) {
|
|
87039
|
+
try {
|
|
87040
|
+
const source = await deps.imapSource(id);
|
|
87041
|
+
const original = parseOriginalHeaders(source.raw);
|
|
87042
|
+
if (source.subject !== void 0) original.subject = source.subject;
|
|
87043
|
+
return { original, plainText: extractTextBody(source.raw) ?? "" };
|
|
87044
|
+
} catch {
|
|
87045
|
+
return null;
|
|
87046
|
+
}
|
|
87047
|
+
}
|
|
87048
|
+
const raw = deps.mail.getRawSource(id);
|
|
87049
|
+
if (!raw) return null;
|
|
87050
|
+
const content = deps.mail.getMessageContent(id);
|
|
87051
|
+
return { original: parseOriginalHeaders(raw), plainText: content?.plainText ?? "" };
|
|
87052
|
+
}
|
|
87053
|
+
async function replyComposeBody(deps, id, body) {
|
|
87054
|
+
const source = await readOriginalForQuote(deps, id);
|
|
87055
|
+
return source ? buildReplyBody(body, source.original, source.plainText) : body;
|
|
87056
|
+
}
|
|
87057
|
+
async function forwardComposeBody(deps, id, body) {
|
|
87058
|
+
if (!body) return body;
|
|
87059
|
+
const source = await readOriginalForQuote(deps, id);
|
|
87060
|
+
return source ? buildForwardBody(source.original, source.plainText, body) : body;
|
|
87061
|
+
}
|
|
87025
87062
|
async function runCompose(deps, args) {
|
|
87026
87063
|
const { id, send, transport: transport2 } = args;
|
|
87027
87064
|
const verb = args.kind === "reply" ? "reply to" : "forward";
|
|
@@ -87092,7 +87129,17 @@ async function runCompose(deps, args) {
|
|
|
87092
87129
|
return errorResponse(
|
|
87093
87130
|
`Failed to ${verb} message "${id}": ${resolved.error ?? "message not found"}`
|
|
87094
87131
|
);
|
|
87095
|
-
const outcome = args.kind === "reply" ? deps.mail.replyToMessage(
|
|
87132
|
+
const outcome = args.kind === "reply" ? deps.mail.replyToMessage(
|
|
87133
|
+
resolved.numericId,
|
|
87134
|
+
await replyComposeBody(deps, id, args.body),
|
|
87135
|
+
args.replyAll,
|
|
87136
|
+
send
|
|
87137
|
+
) : deps.mail.forwardMessage(
|
|
87138
|
+
resolved.numericId,
|
|
87139
|
+
args.to,
|
|
87140
|
+
await forwardComposeBody(deps, id, args.body),
|
|
87141
|
+
send
|
|
87142
|
+
);
|
|
87096
87143
|
if (!outcome.success)
|
|
87097
87144
|
return errorResponse(
|
|
87098
87145
|
`Failed to ${verb} message "${id}": ${outcome.error ?? "Mail.app compose failed"}`
|
|
@@ -88852,7 +88899,7 @@ registerTool(
|
|
|
88852
88899
|
inputSchema: {
|
|
88853
88900
|
id: MESSAGE_ID_SCHEMA,
|
|
88854
88901
|
transport: COMPOSE_TRANSPORT_SCHEMA,
|
|
88855
|
-
body: external_exports.string().min(1, "Reply body is required"),
|
|
88902
|
+
body: external_exports.string().min(1, "Reply body is required").describe("Reply body (plain text; HTML tags such as <br> are not rendered)"),
|
|
88856
88903
|
replyAll: external_exports.boolean().optional().default(false).describe("Reply to all recipients"),
|
|
88857
88904
|
send: external_exports.boolean().optional().default(true).describe("Send immediately (false = save as draft)")
|
|
88858
88905
|
},
|
|
@@ -88876,7 +88923,7 @@ registerTool(
|
|
|
88876
88923
|
id: MESSAGE_ID_SCHEMA,
|
|
88877
88924
|
transport: COMPOSE_TRANSPORT_SCHEMA,
|
|
88878
88925
|
to: external_exports.array(external_exports.string()).min(1, "At least one recipient is required"),
|
|
88879
|
-
body: external_exports.string().optional().describe("Optional message to prepend"),
|
|
88926
|
+
body: external_exports.string().optional().describe("Optional message to prepend (plain text)"),
|
|
88880
88927
|
send: external_exports.boolean().optional().default(true).describe("Send immediately (false = save as draft)")
|
|
88881
88928
|
},
|
|
88882
88929
|
outputSchema: {
|
package/package.json
CHANGED