proton-mail-bridge-client 2.1.42 → 2.1.43

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
@@ -26,6 +26,8 @@
26
26
 
27
27
  Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more — 96 MCP tools in total. Most of the same capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
28
28
 
29
+ > **Using Proton Drive too?** See [**proton-drive-mcp**](https://github.com/googlarz/proton-drive-mcp), the companion MCP server and CLI for Proton Drive (upload, download, share and manage your encrypted files from Claude). Run both side by side to handle mail and files in one conversation, for example save an attachment from here, then upload it to Drive. More in [Related projects](#related-projects).
30
+
29
31
  > **Battle-tested at scale (v2.0.0):** full mailbox backfill validated end-to-end against a real account with 57,000+ indexed messages across 62 folders/labels — including a 22,800-message Archive folder backfilled from scratch, UID-window by UID-window, with zero data loss across restarts, transient IMAP disconnects, and connection timeouts.
30
32
 
31
33
  ## What you get
package/dist/index.d.ts CHANGED
@@ -22,6 +22,8 @@ export declare function extractUnsubscribeInfo(detail: EmailDetail): {
22
22
  mailto?: string;
23
23
  url?: string;
24
24
  };
25
+ export declare function formatQuoteDate(value: string): string;
26
+ export declare function stripUnshippableImages(html: string): string;
25
27
  export declare function buildReplyHtml(detail: EmailDetail, htmlBody: string): string;
26
28
  export declare function buildForwardHtml(detail: EmailDetail, htmlBody?: string): string;
27
29
  export declare function getReplyRecipients(detail: EmailDetail, ownerEmail: string, replyAll: boolean, otherSelfAddresses?: string[]): {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1434,6 +1434,7 @@ const TOOLS = [
1434
1434
  type: "object",
1435
1435
  properties: {
1436
1436
  limit: { type: "number", description: "Maximum threads per digest section.", default: 10 },
1437
+ offset: { type: "number", description: "Skip this many threads in each section (use paging.<section>.nextOffset from the previous response to continue).", default: 0 },
1437
1438
  minAgeHours: {
1438
1439
  type: "number",
1439
1440
  description: "How old a thread must be before it is considered stale waiting on you.",
@@ -2164,10 +2165,34 @@ function quotePlainText(value) {
2164
2165
  .map((line) => `> ${line}`)
2165
2166
  .join("\n");
2166
2167
  }
2168
+ // "2026-09-30T08:11:15.000Z" is not how anyone writes "On <date>, X wrote:". Falls back to the
2169
+ // raw value when it is not a parseable date (an "unknown date" placeholder, a header string).
2170
+ export function formatQuoteDate(value) {
2171
+ const time = Date.parse(value);
2172
+ if (Number.isNaN(time))
2173
+ return value;
2174
+ return new Date(time).toLocaleString("en-GB", {
2175
+ weekday: "short", day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit",
2176
+ }).replace(",", "");
2177
+ }
2178
+ // The quoted original's images cannot travel with a reply: a data: image is dropped by Gmail/Outlook
2179
+ // (a broken-image box) and copies its base64 into every reply, and a cid: image refers to a part
2180
+ // that is not attached here. Replace each with its alt text, or nothing.
2181
+ export function stripUnshippableImages(html) {
2182
+ return html.replace(/<img\b[^>]*>/gi, (tag) => {
2183
+ const src = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tag);
2184
+ const value = (src?.[1] ?? src?.[2] ?? src?.[3] ?? "").trim().toLowerCase();
2185
+ if (!value.startsWith("data:") && !value.startsWith("cid:"))
2186
+ return tag;
2187
+ const alt = /\balt\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(tag);
2188
+ const text = (alt?.[1] ?? alt?.[2] ?? "").trim();
2189
+ return text ? `[${text}]` : "";
2190
+ });
2191
+ }
2167
2192
  function buildReplyText(detail, body) {
2168
2193
  const originalText = detail.text || detail.preview || "";
2169
2194
  const fromText = formatAddressList(detail.from);
2170
- const dateText = detail.date || detail.internalDate || "an unknown date";
2195
+ const dateText = formatQuoteDate(detail.date || detail.internalDate || "an unknown date");
2171
2196
  return [
2172
2197
  body.trim(),
2173
2198
  "",
@@ -2205,10 +2230,10 @@ function buildForwardText(detail, body) {
2205
2230
  // alternative (see smtp-service.ts's plainTextToHtml comment).
2206
2231
  export function buildReplyHtml(detail, htmlBody) {
2207
2232
  const originalHtml = typeof detail.html === "string" && detail.html
2208
- ? detail.html
2233
+ ? stripUnshippableImages(detail.html)
2209
2234
  : plainTextToHtml(detail.text || detail.preview || "");
2210
2235
  const fromText = formatAddressList(detail.from);
2211
- const dateText = detail.date || detail.internalDate || "an unknown date";
2236
+ const dateText = formatQuoteDate(detail.date || detail.internalDate || "an unknown date");
2212
2237
  return [
2213
2238
  htmlBody,
2214
2239
  `<p>On ${escapeHtmlForQuote(dateText)}, ${escapeHtmlForQuote(fromText || "the sender")} wrote:</p>`,
@@ -2217,7 +2242,7 @@ export function buildReplyHtml(detail, htmlBody) {
2217
2242
  }
2218
2243
  export function buildForwardHtml(detail, htmlBody) {
2219
2244
  const originalHtml = typeof detail.html === "string" && detail.html
2220
- ? detail.html
2245
+ ? stripUnshippableImages(detail.html)
2221
2246
  : plainTextToHtml(detail.text || detail.preview || "");
2222
2247
  return [
2223
2248
  htmlBody?.trim() || "",
@@ -6854,6 +6879,10 @@ export function createServer(config, options = {}) {
6854
6879
  case "get_inbox_digest":
6855
6880
  {
6856
6881
  const limit = typeof args.limit === "number" ? args.limit : 10;
6882
+ const offset = typeof args.offset === "number" ? Math.max(0, Math.floor(args.offset)) : 0;
6883
+ // Each account is asked for offset+limit+1 rows per section: enough to slice the requested
6884
+ // page from the merged list and to know whether another page exists.
6885
+ const perAccountWindow = offset + limit + 1;
6857
6886
  // Fan-out merge: get_inbox_digest already internally aggregates two lists
6858
6887
  // (topThreads by score/recency, staleAwaitingYou by score/recency) plus a
6859
6888
  // `counts` summary — mirror that same internal merge style across accounts.
@@ -6870,7 +6899,7 @@ export function createServer(config, options = {}) {
6870
6899
  limitPerFolder: 100,
6871
6900
  });
6872
6901
  const result = await bundle.localIndexService.getInboxDigest({
6873
- limit,
6902
+ limit: perAccountWindow,
6874
6903
  minAgeHours: typeof args.minAgeHours === "number" ? args.minAgeHours : undefined,
6875
6904
  });
6876
6905
  const slug = slugForBundle(bundle);
@@ -6886,8 +6915,10 @@ export function createServer(config, options = {}) {
6886
6915
  return right.score - left.score;
6887
6916
  return new Date(right.latestDate || 0).getTime() - new Date(left.latestDate || 0).getTime();
6888
6917
  };
6889
- const topBudget = trimThreadsToBudget(perAccount.flatMap((entry) => entry.topThreads).sort(byScoreThenDate).slice(0, limit), THREAD_LIST_RESPONSE_BUDGET_CHARS / 2);
6890
- const staleBudget = trimThreadsToBudget(perAccount.flatMap((entry) => entry.staleAwaitingYou).sort(byScoreThenDate).slice(0, limit), THREAD_LIST_RESPONSE_BUDGET_CHARS / 2);
6918
+ const mergedTop = perAccount.flatMap((entry) => entry.topThreads).sort(byScoreThenDate);
6919
+ const mergedStale = perAccount.flatMap((entry) => entry.staleAwaitingYou).sort(byScoreThenDate);
6920
+ const topBudget = trimThreadsToBudget(mergedTop.slice(offset, offset + limit), THREAD_LIST_RESPONSE_BUDGET_CHARS / 2);
6921
+ const staleBudget = trimThreadsToBudget(mergedStale.slice(offset, offset + limit), THREAD_LIST_RESPONSE_BUDGET_CHARS / 2);
6891
6922
  const topThreads = topBudget.threads;
6892
6923
  const staleAwaitingYou = staleBudget.threads;
6893
6924
  const countKeys = new Set(perAccount.flatMap((entry) => Object.keys(entry.counts)));
@@ -6901,6 +6932,12 @@ export function createServer(config, options = {}) {
6901
6932
  counts,
6902
6933
  topThreads,
6903
6934
  staleAwaitingYou,
6935
+ // Each section pages independently with the same offset/limit: pass nextOffset back as
6936
+ // `offset` to continue (a section with hasMore:false is exhausted).
6937
+ paging: {
6938
+ topThreads: paginationFields(mergedTop.length, offset, topThreads.length, topBudget.trimmed),
6939
+ staleAwaitingYou: paginationFields(mergedStale.length, offset, staleAwaitingYou.length, staleBudget.trimmed),
6940
+ },
6904
6941
  ...(topBudget.trimmed > 0 || staleBudget.trimmed > 0 ? { truncatedForSize: true } : {}),
6905
6942
  };
6906
6943
  return createTextResult(result, false, [...topThreads, ...staleAwaitingYou].map(threadSource));