hypermail-mcp 0.7.11 → 0.7.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 CHANGED
@@ -3,6 +3,11 @@
3
3
  A **Model Context Protocol** server that lets an agent operate any of the user's
4
4
  inboxes through a single, unified tool surface.
5
5
 
6
+ > **v0.7.12** — Hardened Outlook reply/forward draft formatting when
7
+ > Microsoft Graph labels generated thread history as HTML but returns
8
+ > plain/unstructured text. Such histories are now defensively normalized with
9
+ > escaped content and line breaks so quoted messages remain readable.
10
+ >
6
11
  > **v0.7.11** — Fixed Outlook reply/forward drafts whose Graph-generated
7
12
  > quoted bodies are plain text by escaping them and patching the draft as HTML,
8
13
  > so newly composed HTML replies no longer render as raw text.
package/dist/cli.js CHANGED
@@ -14365,11 +14365,32 @@ function isTextBody(contentType) {
14365
14365
  function textToHtml(text) {
14366
14366
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/\r\n|\r|\n/g, "<br>");
14367
14367
  }
14368
+ function hasMeaningfulHtmlTag(html) {
14369
+ return /<\/?(?:p|div|br|blockquote|table|thead|tbody|tfoot|tr|td|th|ul|ol|li|a|span|font|b|strong|em|i|u|img|hr|pre)\b/i.test(html);
14370
+ }
14371
+ function wrapQuotedHistory(content) {
14372
+ return `<blockquote style="margin:0 0 0 .8ex;border-left:1px solid #ccc;padding-left:1ex">${content}</blockquote>`;
14373
+ }
14374
+ function normalizeDraftBody(content, contentType) {
14375
+ if (isTextBody(contentType)) return textToHtml(content);
14376
+ if (contentType?.toLowerCase() !== "html") return content;
14377
+ const bodyMatch = /(<body\b[^>]*>)([\s\S]*?)(<\/body>)/i.exec(content);
14378
+ const inner = bodyMatch ? bodyMatch[2] ?? "" : content;
14379
+ if (inner.trim() === "" || hasMeaningfulHtmlTag(inner)) return content;
14380
+ const normalized = wrapQuotedHistory(textToHtml(inner));
14381
+ if (!bodyMatch) return normalized;
14382
+ const bodyOpen = bodyMatch[1] ?? "";
14383
+ const bodyClose = bodyMatch[3] ?? "";
14384
+ return content.slice(0, bodyMatch.index) + bodyOpen + normalized + bodyClose + content.slice(bodyMatch.index + bodyMatch[0].length);
14385
+ }
14368
14386
  async function buildDraftFromReference(client, createEndpoint, createPayload, converted) {
14369
14387
  const draft = await client.api(createEndpoint).post(createPayload);
14370
14388
  const draftMsg = await client.api(`/me/messages/${draft.id}`).select("body").get();
14371
14389
  const rawDraftBody = draftMsg.body?.content ?? "";
14372
- const draftBody = isTextBody(draftMsg.body?.contentType) ? textToHtml(rawDraftBody) : rawDraftBody;
14390
+ const draftBody = normalizeDraftBody(
14391
+ rawDraftBody,
14392
+ draftMsg.body?.contentType
14393
+ );
14373
14394
  const spacer = '<div style="line-height:12px"><br></div>';
14374
14395
  const prepend = converted.body + spacer + THREAD_MARKER;
14375
14396
  const finalBody = draftBody.includes("<body") ? draftBody.replace(/(<body[^>]*>)/i, `$1${prepend}`) : prepend + draftBody;
@@ -18168,7 +18189,7 @@ function registerTools(server, opts) {
18168
18189
  // package.json
18169
18190
  var package_default = {
18170
18191
  name: "hypermail-mcp",
18171
- version: "0.7.11",
18192
+ version: "0.7.12",
18172
18193
  description: "Unified email MCP server \u2014 operate any inbox (Outlook now, IMAP/Gmail later) by passing an email address.",
18173
18194
  type: "module",
18174
18195
  bin: {