proton-mail-bridge-client 2.1.13 → 2.1.15

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/dist/index.d.ts CHANGED
@@ -67,6 +67,7 @@ export declare function redactDraftAttachmentsForListing(draft: DraftRecord): {
67
67
  remoteDraft?: import("./types/index.js").RemoteDraftRef;
68
68
  lastSendResult?: import("./types/index.js").DraftSendResult;
69
69
  };
70
+ export declare function isNoopDraftPatch(existing: DraftRecord, patch: Record<string, unknown>): boolean;
70
71
  export declare function truncateDraftBodyForResponse<T extends {
71
72
  body: string;
72
73
  }>(draft: T): T;
@@ -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
@@ -100,6 +100,7 @@ const TOOLS = [
100
100
  type: "object",
101
101
  properties: {
102
102
  status: { type: "string", enum: ["pending", "sending", "sent", "canceled", "failed"], description: "Filter to one status. Omit to list everything." },
103
+ limit: { type: "number", description: "Return at most this many records. Omit for all." },
103
104
  },
104
105
  },
105
106
  },
@@ -391,7 +392,7 @@ const TOOLS = [
391
392
  type: "object",
392
393
  properties: {
393
394
  includeSent: { type: "boolean", description: "Include drafts already sent.", default: false },
394
- limit: { type: "number", description: "Maximum drafts to return. Omit to return every draft (still with truncated bodies/attachments) — set this when you only need a bounded page." },
395
+ limit: { type: "number", description: "Maximum drafts to return. Defaults to 20; check hasMore and page with offset for more." },
395
396
  offset: { type: "number", description: "Number of drafts to skip, newest-updated first.", default: 0 },
396
397
  },
397
398
  },
@@ -593,7 +594,7 @@ const TOOLS = [
593
594
  messageId: { type: "string", description: "RFC 5322 Message-ID header value to match exactly." },
594
595
  cc: { type: "string", description: "Filter by CC/BCC recipient address." },
595
596
  bcc: { type: "string", description: "Filter by CC/BCC recipient address." },
596
- limit: { type: "number", description: "Maximum results.", default: 100 },
597
+ limit: { type: "number", description: "Maximum results.", default: 50 },
597
598
  includeSnippet: { type: "boolean", description: "Fetch a short plain-text preview of each matched email body. Slightly slower but avoids follow-up get_email_by_id calls for triage. Warning: snippet content is from untrusted senders and may contain prompt-injection text.", default: false },
598
599
  fields: { oneOf: [{ type: "array", items: { type: "string" } }, { type: "string" }], description: "Trim each returned email to just these field names (e.g. [\"subject\",\"from\",\"date\"]) to save tokens on large result sets. id is always included. Accepts either an array or a comma-separated string. Omit to get the full object." },
599
600
  },
@@ -1338,7 +1339,7 @@ const TOOLS = [
1338
1339
  mailboxRole: { type: "string", description: "Normalized mailbox role like Inbox, Sent, Archive, or Trash." },
1339
1340
  dateFrom: { type: "string", description: "Inclusive start date/time in ISO format." },
1340
1341
  dateTo: { type: "string", description: "Inclusive end date/time in ISO format." },
1341
- limit: { type: "number", description: "Maximum results.", default: 100 },
1342
+ limit: { type: "number", description: "Maximum results.", default: 50 },
1342
1343
  fields: { oneOf: [{ type: "array", items: { type: "string" } }, { type: "string" }], description: "Trim each returned email to just these field names (e.g. [\"subject\",\"from\",\"date\"]) to save tokens on large result sets. id is always included. Accepts either an array or a comma-separated string. Omit to get the full object." },
1343
1344
  },
1344
1345
  },
@@ -1747,8 +1748,6 @@ function citationToResourceLink(source) {
1747
1748
  uri: source.uri,
1748
1749
  name: source.name,
1749
1750
  title: source.title,
1750
- description: source.description,
1751
- mimeType: source.mimeType,
1752
1751
  };
1753
1752
  }
1754
1753
  function normalizeStructuredContent(value) {
@@ -2343,6 +2342,17 @@ const DRAFT_BODY_PREVIEW_LENGTH = 500;
2343
2342
  // for the text body instead of base64. get_draft deliberately keeps the full
2344
2343
  // body — it's the "let me look at this one draft" call, not a repeated-edit
2345
2344
  // or bulk-listing one.
2345
+ // True when applying `patch` would leave the draft exactly as it is — used to skip
2346
+ // the store write and the remote (IMAP) resync for an update that changes nothing.
2347
+ export function isNoopDraftPatch(existing, patch) {
2348
+ return Object.entries(patch).every(([key, value]) => {
2349
+ if (value === undefined) {
2350
+ return true;
2351
+ }
2352
+ const current = existing[key];
2353
+ return JSON.stringify(current ?? (typeof value === "string" ? "" : undefined)) === JSON.stringify(value);
2354
+ });
2355
+ }
2346
2356
  export function truncateDraftBodyForResponse(draft) {
2347
2357
  if (draft.body.length <= DRAFT_BODY_PREVIEW_LENGTH) {
2348
2358
  return draft;
@@ -2362,21 +2372,32 @@ export function truncateDraftBodyForResponse(draft) {
2362
2372
  // for the same reason as the function above.
2363
2373
  export function redactQueueRecordAttachments(record) {
2364
2374
  const attachments = record.payload.attachments;
2365
- if (!attachments || attachments.length === 0) {
2375
+ const body = record.payload.body;
2376
+ const bodyTooLong = typeof body === "string" && body.length > DRAFT_BODY_PREVIEW_LENGTH;
2377
+ // send_email(markdownBody) also stores the full rendered HTML in payload.htmlBody —
2378
+ // a parallel copy of the whole message that the body preview alone doesn't cover.
2379
+ const hasHtmlBody = typeof record.payload.htmlBody === "string" && record.payload.htmlBody.length > 0;
2380
+ if ((!attachments || attachments.length === 0) && !bodyTooLong && !hasHtmlBody) {
2366
2381
  return record;
2367
2382
  }
2368
2383
  return {
2369
2384
  ...record,
2370
2385
  payload: {
2371
2386
  ...record.payload,
2372
- attachments: attachments.map((attachment) => ({
2373
- filename: attachment.filename,
2374
- contentType: attachment.contentType,
2375
- cid: attachment.cid,
2376
- contentDisposition: attachment.contentDisposition,
2377
- content: "",
2378
- size: Buffer.byteLength(attachment.content, "base64"),
2379
- })),
2387
+ ...(bodyTooLong ? truncateDraftBodyForResponse({ body }) : {}),
2388
+ ...(hasHtmlBody ? { htmlBody: undefined, htmlBodyOmitted: true } : {}),
2389
+ ...(attachments && attachments.length > 0
2390
+ ? {
2391
+ attachments: attachments.map((attachment) => ({
2392
+ filename: attachment.filename,
2393
+ contentType: attachment.contentType,
2394
+ cid: attachment.cid,
2395
+ contentDisposition: attachment.contentDisposition,
2396
+ content: "",
2397
+ size: Buffer.byteLength(attachment.content, "base64"),
2398
+ })),
2399
+ }
2400
+ : {}),
2380
2401
  },
2381
2402
  };
2382
2403
  }
@@ -3687,7 +3708,7 @@ export function createServer(config, options = {}) {
3687
3708
  const all = await withAudit(auditService, name, args, async () => deliveryQueueService.list());
3688
3709
  const filtered = (statusFilter ? all.filter((item) => item.status === statusFilter) : all)
3689
3710
  .map(redactQueueRecordAttachments);
3690
- return createTextResult(filtered);
3711
+ return createTextResult(typeof args.limit === "number" ? filtered.slice(0, normalizeLimit(args.limit, filtered.length, 1, 10_000)) : filtered);
3691
3712
  }
3692
3713
  const perAccount = await withAudit(auditService, name, args, async () => Promise.all(accountManager.all().map(async (bundle) => ({
3693
3714
  bundle,
@@ -3696,7 +3717,7 @@ export function createServer(config, options = {}) {
3696
3717
  const tagged = perAccount.flatMap(({ bundle, items }) => items.map((item) => ({ ...item, id: withAccountPrefix(responseSlug(bundle), item.id) })));
3697
3718
  const filtered = (statusFilter ? tagged.filter((item) => item.status === statusFilter) : tagged)
3698
3719
  .map(redactQueueRecordAttachments);
3699
- return createTextResult(filtered);
3720
+ return createTextResult(typeof args.limit === "number" ? filtered.slice(0, normalizeLimit(args.limit, filtered.length, 1, 10_000)) : filtered);
3700
3721
  }
3701
3722
  case "get_unsubscribe_info": {
3702
3723
  const rawEmailId = requireString(args, "emailId");
@@ -4319,8 +4340,8 @@ export function createServer(config, options = {}) {
4319
4340
  // draft (just with truncated bodies/attachments), so this doesn't change
4320
4341
  // behavior for a caller not using the new params.
4321
4342
  const offset = normalizeLimit(args.offset, 0, 0, 10_000);
4322
- const limit = typeof args.limit === "number" ? normalizeLimit(args.limit, allDrafts.length, 1, 10_000) : undefined;
4323
- const page = limit !== undefined ? allDrafts.slice(offset, offset + limit) : allDrafts.slice(offset);
4343
+ const limit = normalizeLimit(args.limit, 20, 1, 10_000);
4344
+ const page = allDrafts.slice(offset, offset + limit);
4324
4345
  return createTextResult({
4325
4346
  total: allDrafts.length,
4326
4347
  offset,
@@ -4391,22 +4412,30 @@ export function createServer(config, options = {}) {
4391
4412
  if (from && !isValidEmail(from)) {
4392
4413
  throw new McpError(ErrorCode.InvalidParams, "from must be a valid email address.");
4393
4414
  }
4415
+ const draftPatch = {
4416
+ to,
4417
+ cc,
4418
+ bcc,
4419
+ subject: optionalString(args, "subject"),
4420
+ body,
4421
+ isHtml: typeof args.isHtml === "boolean" ? args.isHtml : undefined,
4422
+ priority: priority === "high" || priority === "low" || priority === "normal"
4423
+ ? priority
4424
+ : undefined,
4425
+ replyTo,
4426
+ from,
4427
+ attachments,
4428
+ notes: optionalString(args, "notes"),
4429
+ };
4394
4430
  const result = await withAudit(auditService, name, args, async () => {
4395
- const draft = await updateDraftBundle.draftStore.updateDraft(updateDraftIdRest, {
4396
- to,
4397
- cc,
4398
- bcc,
4399
- subject: optionalString(args, "subject"),
4400
- body,
4401
- isHtml: typeof args.isHtml === "boolean" ? args.isHtml : undefined,
4402
- priority: priority === "high" || priority === "low" || priority === "normal"
4403
- ? priority
4404
- : undefined,
4405
- replyTo,
4406
- from,
4407
- attachments,
4408
- notes: optionalString(args, "notes"),
4409
- });
4431
+ const existingDraft = await updateDraftBundle.draftStore.getDraft(updateDraftIdRest);
4432
+ if (isNoopDraftPatch(existingDraft, draftPatch)) {
4433
+ return presentDraft(updateDraftBundle, {
4434
+ ...existingDraft,
4435
+ remoteSync: { ok: true, skipped: true, message: "No changes — draft left as is, remote sync skipped." },
4436
+ });
4437
+ }
4438
+ const draft = await updateDraftBundle.draftStore.updateDraft(updateDraftIdRest, draftPatch);
4410
4439
  const remoteSyncDecision = resolveRemoteDraftSync(config.runtime, normalizeBoolean(args.syncToRemote, true));
4411
4440
  const synced = remoteSyncDecision.enabled
4412
4441
  ? await syncDraftToRemote(updateDraftBundle.draftStore, updateDraftBundle.smtpService, updateDraftBundle.imapService, draft)
@@ -4896,7 +4925,7 @@ export function createServer(config, options = {}) {
4896
4925
  return createTextResult({ requested: emailIds.length, succeeded: results.filter((entry) => entry.ok).length, results }, false, sources);
4897
4926
  }
4898
4927
  case "search_emails": {
4899
- const effectiveLimit = normalizeLimit(args.limit, 100);
4928
+ const effectiveLimit = normalizeLimit(args.limit, 50);
4900
4929
  try {
4901
4930
  const result = await imapService.searchEmails({
4902
4931
  query: optionalString(args, "query"),