@marlinjai/email-mcp 1.8.0 → 1.8.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.
@@ -892,6 +892,53 @@ function buildGmailQuery(query) {
892
892
  return parts.join(" ");
893
893
  }
894
894
 
895
+ // src/providers/mime.ts
896
+ import MailComposer from "nodemailer/lib/mail-composer/index.js";
897
+ import { convert } from "html-to-text";
898
+ function htmlToText(html) {
899
+ return convert(html, {
900
+ wordwrap: false,
901
+ selectors: [
902
+ { selector: "img", format: "skip" },
903
+ { selector: "a", options: { hideLinkHrefIfSameAsText: true } }
904
+ ]
905
+ }).trim();
906
+ }
907
+ function normalizeBody(body) {
908
+ const html = body.html ? body.html : void 0;
909
+ if (body.text) return { text: body.text, html };
910
+ if (html) return { text: htmlToText(html), html };
911
+ return { text: "" };
912
+ }
913
+ function toAddress(contact) {
914
+ return contact.name ? { name: contact.name, address: contact.email } : contact.email;
915
+ }
916
+ function toAddressList(contacts) {
917
+ return contacts?.length ? contacts.map(toAddress) : void 0;
918
+ }
919
+ async function buildMimeMessage(from, params) {
920
+ const { text, html } = normalizeBody(params.body);
921
+ const mail = {
922
+ from,
923
+ to: toAddressList(params.to),
924
+ cc: toAddressList(params.cc),
925
+ bcc: toAddressList(params.bcc),
926
+ subject: params.subject,
927
+ text,
928
+ html,
929
+ inReplyTo: params.inReplyTo,
930
+ references: params.references?.length ? params.references : void 0,
931
+ attachments: params.attachments?.map((a) => ({
932
+ filename: a.filename,
933
+ content: a.content,
934
+ contentType: a.contentType
935
+ }))
936
+ };
937
+ const node = new MailComposer(mail).compile();
938
+ node.keepBcc = true;
939
+ return node.build();
940
+ }
941
+
895
942
  // src/providers/gmail/adapter.ts
896
943
  var GmailAdapter = class {
897
944
  providerType = ProviderType.Gmail;
@@ -1034,7 +1081,7 @@ var GmailAdapter = class {
1034
1081
  }
1035
1082
  async sendEmail(params) {
1036
1083
  const gmail = this.ensureConnected();
1037
- const raw = this.buildRfc2822(params);
1084
+ const raw = await this.buildRaw(params);
1038
1085
  const res = await gmail.users.messages.send({
1039
1086
  userId: "me",
1040
1087
  requestBody: { raw }
@@ -1046,7 +1093,7 @@ var GmailAdapter = class {
1046
1093
  }
1047
1094
  async createDraft(params) {
1048
1095
  const gmail = this.ensureConnected();
1049
- const raw = this.buildRfc2822(params);
1096
+ const raw = await this.buildRaw(params);
1050
1097
  const res = await gmail.users.drafts.create({
1051
1098
  userId: "me",
1052
1099
  requestBody: {
@@ -1057,7 +1104,7 @@ var GmailAdapter = class {
1057
1104
  }
1058
1105
  async updateDraft(draftId, params) {
1059
1106
  const gmail = this.ensureConnected();
1060
- const raw = this.buildRfc2822(params);
1107
+ const raw = await this.buildRaw(params);
1061
1108
  const res = await gmail.users.drafts.update({
1062
1109
  userId: "me",
1063
1110
  id: draftId,
@@ -1386,30 +1433,15 @@ var GmailAdapter = class {
1386
1433
  const gmail = this.ensureConnected();
1387
1434
  await gmail.users.settings.filters.delete({ userId: "me", id: ruleId });
1388
1435
  }
1389
- buildRfc2822(params) {
1390
- const lines = [];
1391
- lines.push(`From: ${this.email}`);
1392
- lines.push(
1393
- `To: ${params.to.map((c) => c.name ? `"${c.name}" <${c.email}>` : c.email).join(", ")}`
1394
- );
1395
- if (params.cc?.length) {
1396
- lines.push(`Cc: ${params.cc.map((c) => c.email).join(", ")}`);
1397
- }
1398
- if (params.bcc?.length) {
1399
- lines.push(`Bcc: ${params.bcc.map((c) => c.email).join(", ")}`);
1400
- }
1401
- lines.push(`Subject: ${params.subject}`);
1402
- if (params.inReplyTo) {
1403
- lines.push(`In-Reply-To: ${params.inReplyTo}`);
1404
- }
1405
- if (params.references?.length) {
1406
- lines.push(`References: ${params.references.join(" ")}`);
1407
- }
1408
- lines.push("MIME-Version: 1.0");
1409
- lines.push("Content-Type: text/plain; charset=UTF-8");
1410
- lines.push("");
1411
- lines.push(params.body.text || params.body.html || "");
1412
- return Buffer.from(lines.join("\r\n")).toString("base64url");
1436
+ /**
1437
+ * Builds the base64url `raw` payload Gmail expects for send, draft create and
1438
+ * draft update: a full MIME message with a text part, an HTML part when one
1439
+ * was given (or a text part derived from HTML-only input), encoded headers
1440
+ * and any attachments. See `buildMimeMessage`.
1441
+ */
1442
+ async buildRaw(params) {
1443
+ const message = await buildMimeMessage(this.email, params);
1444
+ return message.toString("base64url");
1413
1445
  }
1414
1446
  };
1415
1447
 
@@ -2094,14 +2126,15 @@ function createSmtpTransport(email, creds) {
2094
2126
  });
2095
2127
  }
2096
2128
  async function sendViaSmtp(transport, from, params) {
2129
+ const { text, html } = normalizeBody(params.body);
2097
2130
  const result = await transport.sendMail({
2098
2131
  from,
2099
- to: params.to.map((c) => c.name ? `"${c.name}" <${c.email}>` : c.email).join(", "),
2100
- cc: params.cc?.map((c) => c.email).join(", "),
2101
- bcc: params.bcc?.map((c) => c.email).join(", "),
2132
+ to: params.to.map(toAddress),
2133
+ cc: params.cc?.length ? params.cc.map(toAddress) : void 0,
2134
+ bcc: params.bcc?.length ? params.bcc.map(toAddress) : void 0,
2102
2135
  subject: params.subject,
2103
- text: params.body.text,
2104
- html: params.body.html,
2136
+ text,
2137
+ html,
2105
2138
  inReplyTo: params.inReplyTo,
2106
2139
  references: params.references?.join(" "),
2107
2140
  attachments: params.attachments?.map((a) => ({
@@ -2572,26 +2605,17 @@ var ImapAdapter = class {
2572
2605
  const messageId = await sendViaSmtp(transport, this.email, params);
2573
2606
  return { id: messageId };
2574
2607
  }
2575
- buildDraftRfc2822(params) {
2576
- const lines = [];
2577
- lines.push(`From: ${this.email}`);
2578
- lines.push(`To: ${params.to.map((c) => c.name ? `"${c.name}" <${c.email}>` : c.email).join(", ")}`);
2579
- if (params.cc?.length) {
2580
- lines.push(`Cc: ${params.cc.map((c) => c.email).join(", ")}`);
2581
- }
2582
- lines.push(`Subject: ${params.subject}`);
2583
- lines.push(`Date: ${(/* @__PURE__ */ new Date()).toUTCString()}`);
2584
- if (params.inReplyTo) lines.push(`In-Reply-To: ${params.inReplyTo}`);
2585
- if (params.references?.length) lines.push(`References: ${params.references.join(" ")}`);
2586
- lines.push("MIME-Version: 1.0");
2587
- lines.push("Content-Type: text/plain; charset=utf-8");
2588
- lines.push("");
2589
- lines.push(params.body.text || "");
2590
- return lines.join("\r\n");
2608
+ /**
2609
+ * Builds the full MIME message a draft is stored as: text and HTML parts,
2610
+ * Bcc kept so the draft remembers its recipients, encoded headers and any
2611
+ * attachments. See `buildMimeMessage`.
2612
+ */
2613
+ buildDraftMessage(params) {
2614
+ return buildMimeMessage(this.email, params);
2591
2615
  }
2592
2616
  async createDraft(params) {
2593
2617
  const client = await this.ensureConnected();
2594
- const rawMessage = this.buildDraftRfc2822(params);
2618
+ const rawMessage = await this.buildDraftMessage(params);
2595
2619
  const draftsFolder = await this.resolveFolder("Drafts");
2596
2620
  const result = await client.append(draftsFolder, rawMessage, ["\\Draft", "\\Seen"]);
2597
2621
  if (!result) throw new Error("Failed to append draft");
@@ -2606,6 +2630,7 @@ var ImapAdapter = class {
2606
2630
  async updateDraft(draftId, params, sourceFolder) {
2607
2631
  const client = await this.ensureConnected();
2608
2632
  const draftsFolder = sourceFolder ? await this.resolveFolder(sourceFolder) : await this.resolveFolder("Drafts");
2633
+ const rawMessage = await this.buildDraftMessage(params);
2609
2634
  let lock;
2610
2635
  try {
2611
2636
  lock = await client.getMailboxLock(draftsFolder);
@@ -2617,7 +2642,6 @@ var ImapAdapter = class {
2617
2642
  } finally {
2618
2643
  lock.release();
2619
2644
  }
2620
- const rawMessage = this.buildDraftRfc2822(params);
2621
2645
  const result = await client.append(draftsFolder, rawMessage, ["\\Draft", "\\Seen"]);
2622
2646
  if (!result) throw new Error("Failed to append updated draft");
2623
2647
  return { id: String(result.uid || result) };