@mindstone-engineering/mcp-server-email-imap 0.1.0

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.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Email IMAP tool registration — aggregates all tool modules.
3
+ */
4
+ import { getPreset } from '../presets.js';
5
+ import { initImap, cleanup as cleanupImap } from '../imap-client.js';
6
+ import { initSmtp, cleanup as cleanupSmtp } from '../smtp-client.js';
7
+ import { setClientConfig } from './shared.js';
8
+ export { registerMailboxTools } from './mailbox.js';
9
+ export { registerMessageTools } from './messages.js';
10
+ export { registerSendTools } from './send.js';
11
+ export { registerConfigureTools, getCredentials, setCredentials } from './configure.js';
12
+ /**
13
+ * Initialize IMAP and SMTP clients from provider settings or explicit config.
14
+ */
15
+ export async function initClients(options) {
16
+ const normalizedProvider = options.provider.trim().toLowerCase();
17
+ let config;
18
+ if (normalizedProvider === 'custom') {
19
+ const imapHost = options.imapHost ?? process.env.EMAIL_IMAP_IMAP_HOST?.trim();
20
+ const smtpHost = options.smtpHost ?? process.env.EMAIL_IMAP_SMTP_HOST?.trim();
21
+ if (!imapHost || !smtpHost) {
22
+ throw new Error('Custom email requires IMAP and SMTP server addresses. Provide imapHost and smtpHost.');
23
+ }
24
+ const imapPort = options.imapPort ?? parseInt(process.env.EMAIL_IMAP_IMAP_PORT || '993', 10);
25
+ const smtpPort = options.smtpPort ?? parseInt(process.env.EMAIL_IMAP_SMTP_PORT || '587', 10);
26
+ config = {
27
+ imapHost,
28
+ imapPort,
29
+ imapTls: options.imapTls ?? imapPort === 993,
30
+ smtpHost,
31
+ smtpPort,
32
+ smtpSecure: options.smtpSecure ?? smtpPort === 465,
33
+ email: options.email.trim(),
34
+ password: options.password.trim(),
35
+ };
36
+ }
37
+ else {
38
+ const preset = getPreset(normalizedProvider);
39
+ if (!preset) {
40
+ throw new Error(`Unsupported provider "${normalizedProvider}". Supported providers: icloud, yahoo, custom.`);
41
+ }
42
+ config = {
43
+ imapHost: preset.imapHost,
44
+ imapPort: preset.imapPort,
45
+ imapTls: preset.imapTls,
46
+ smtpHost: preset.smtpHost,
47
+ smtpPort: preset.smtpPort,
48
+ smtpSecure: preset.smtpSecure,
49
+ email: options.email.trim(),
50
+ password: options.password.trim(),
51
+ };
52
+ }
53
+ setClientConfig(config);
54
+ await initImap({
55
+ host: config.imapHost,
56
+ port: config.imapPort,
57
+ tls: config.imapTls,
58
+ user: config.email,
59
+ pass: config.password,
60
+ });
61
+ await initSmtp({
62
+ host: config.smtpHost,
63
+ port: config.smtpPort,
64
+ secure: config.smtpSecure,
65
+ user: config.email,
66
+ pass: config.password,
67
+ });
68
+ }
69
+ /**
70
+ * Clean up all email clients.
71
+ */
72
+ export async function cleanupClients() {
73
+ await Promise.allSettled([cleanupImap(), cleanupSmtp()]);
74
+ setClientConfig(null);
75
+ }
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Mailbox tools — listing mailboxes and getting mailbox status.
3
+ */
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ export declare function registerMailboxTools(server: McpServer): void;
6
+ //# sourceMappingURL=mailbox.d.ts.map
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Mailbox tools — listing mailboxes and getting mailbox status.
3
+ */
4
+ import { z } from 'zod';
5
+ import { withErrorHandling } from '../utils.js';
6
+ import { getConnection } from '../imap-client.js';
7
+ import { getMailboxLock } from '../imap-client.js';
8
+ import { ensureInitialized, formatAddresses, formatDate } from './shared.js';
9
+ export function registerMailboxTools(server) {
10
+ // ── email_list_mailboxes ────────────────────────────────────────
11
+ server.registerTool('email_list_mailboxes', {
12
+ description: 'List all email folders/mailboxes with message counts.',
13
+ inputSchema: z.object({}),
14
+ annotations: { readOnlyHint: true },
15
+ }, withErrorHandling(async () => {
16
+ ensureInitialized();
17
+ const client = await getConnection();
18
+ const listedMailboxes = await client.list({
19
+ statusQuery: {
20
+ messages: true,
21
+ unseen: true,
22
+ },
23
+ });
24
+ const mailboxes = await Promise.all(listedMailboxes.map(async (mailbox) => {
25
+ let messages = mailbox.status?.messages;
26
+ let unseen = mailbox.status?.unseen;
27
+ if (messages === undefined || unseen === undefined) {
28
+ const status = await client.status(mailbox.path, {
29
+ messages: true,
30
+ unseen: true,
31
+ });
32
+ messages = status.messages ?? 0;
33
+ unseen = status.unseen ?? 0;
34
+ }
35
+ return {
36
+ name: mailbox.path,
37
+ specialUse: mailbox.specialUse ??
38
+ (mailbox.path.toUpperCase() === 'INBOX' ? '\\Inbox' : undefined),
39
+ messages,
40
+ unseen,
41
+ };
42
+ }));
43
+ return JSON.stringify({ ok: true, mailboxes });
44
+ }));
45
+ // ── email_get_mailbox_status ────────────────────────────────────
46
+ server.registerTool('email_get_mailbox_status', {
47
+ description: 'Get mailbox status: total count, unread count, and optionally the latest unread message subjects.',
48
+ inputSchema: z.object({
49
+ mailbox: z.string().optional().describe('Mailbox/folder name (defaults to INBOX)'),
50
+ includeLatest: z
51
+ .boolean()
52
+ .optional()
53
+ .describe('Include latest unread message summaries'),
54
+ }),
55
+ annotations: { readOnlyHint: true },
56
+ }, withErrorHandling(async (args) => {
57
+ ensureInitialized();
58
+ const mailbox = args.mailbox?.trim() || 'INBOX';
59
+ const includeLatest = args.includeLatest ?? false;
60
+ const client = await getConnection();
61
+ const status = await client.status(mailbox, {
62
+ messages: true,
63
+ unseen: true,
64
+ });
65
+ let latestUnread;
66
+ if (includeLatest) {
67
+ const lock = await getMailboxLock(mailbox);
68
+ try {
69
+ const unreadUidsResult = await client.search({ seen: false }, { uid: true });
70
+ const unreadUids = Array.isArray(unreadUidsResult) ? unreadUidsResult : [];
71
+ const latestUids = [...unreadUids].sort((a, b) => b - a).slice(0, 5);
72
+ latestUnread = [];
73
+ if (latestUids.length > 0) {
74
+ for await (const message of client.fetch(latestUids, {
75
+ uid: true,
76
+ envelope: true,
77
+ }, { uid: true })) {
78
+ latestUnread.push({
79
+ uid: message.uid,
80
+ subject: message.envelope?.subject ?? '',
81
+ from: formatAddresses(message.envelope?.from),
82
+ date: formatDate(message.envelope?.date),
83
+ });
84
+ }
85
+ }
86
+ latestUnread.sort((a, b) => b.uid - a.uid);
87
+ }
88
+ finally {
89
+ lock.release();
90
+ }
91
+ }
92
+ return JSON.stringify({
93
+ ok: true,
94
+ mailbox,
95
+ total: status.messages ?? 0,
96
+ unread: status.unseen ?? 0,
97
+ ...(latestUnread ? { latestUnread } : {}),
98
+ });
99
+ }));
100
+ }
101
+ //# sourceMappingURL=mailbox.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Message tools — search, get, move, and flag management.
3
+ */
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ export declare function registerMessageTools(server: McpServer): void;
6
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Message tools — search, get, move, and flag management.
3
+ */
4
+ import { z } from 'zod';
5
+ import { withErrorHandling } from '../utils.js';
6
+ import { getConnection, getMailboxLock } from '../imap-client.js';
7
+ import { ensureInitialized, formatAddresses, formatDate, downloadPartAsText, collectMessageParts, ensureMailboxExists, } from './shared.js';
8
+ export function registerMessageTools(server) {
9
+ // ── email_search_messages ───────────────────────────────────────
10
+ server.registerTool('email_search_messages', {
11
+ description: 'Search for emails in a mailbox. Returns summaries with UIDs for use with email_get_message.',
12
+ inputSchema: z.object({
13
+ mailbox: z.string().min(1).describe('Mailbox/folder name to search (e.g. INBOX)'),
14
+ from: z.string().optional().describe('Filter by sender email or name'),
15
+ subject: z.string().optional().describe('Filter by subject text'),
16
+ unread: z.boolean().optional().describe('If true, return only unread messages'),
17
+ limit: z.number().positive().optional().describe('Maximum number of messages to return'),
18
+ }),
19
+ annotations: { readOnlyHint: true },
20
+ }, withErrorHandling(async (args) => {
21
+ ensureInitialized();
22
+ const mailbox = args.mailbox;
23
+ const from = args.from?.trim() || undefined;
24
+ const subject = args.subject?.trim() || undefined;
25
+ const unread = args.unread ?? false;
26
+ const limit = args.limit !== undefined ? Math.trunc(args.limit) : undefined;
27
+ const lock = await getMailboxLock(mailbox);
28
+ try {
29
+ const client = await getConnection();
30
+ const criteria = { all: true };
31
+ if (from) {
32
+ criteria.from = from;
33
+ }
34
+ if (subject) {
35
+ criteria.subject = subject;
36
+ }
37
+ if (unread) {
38
+ criteria.seen = false;
39
+ }
40
+ const uidSearchResult = await client.search(criteria, { uid: true });
41
+ const allUids = Array.isArray(uidSearchResult) ? uidSearchResult : [];
42
+ const sortedUids = [...allUids].sort((a, b) => b - a);
43
+ const targetUids = limit ? sortedUids.slice(0, limit) : sortedUids;
44
+ const messages = [];
45
+ if (targetUids.length > 0) {
46
+ for await (const message of client.fetch(targetUids, {
47
+ uid: true,
48
+ envelope: true,
49
+ flags: true,
50
+ }, { uid: true })) {
51
+ messages.push({
52
+ uid: message.uid,
53
+ subject: message.envelope?.subject ?? '',
54
+ from: formatAddresses(message.envelope?.from),
55
+ date: formatDate(message.envelope?.date),
56
+ flags: message.flags ? [...message.flags] : [],
57
+ });
58
+ }
59
+ }
60
+ messages.sort((a, b) => b.uid - a.uid);
61
+ return JSON.stringify({
62
+ ok: true,
63
+ messages,
64
+ ...(limit && sortedUids.length > limit ? { hasMore: true } : {}),
65
+ });
66
+ }
67
+ finally {
68
+ lock.release();
69
+ }
70
+ }));
71
+ // ── email_get_message ───────────────────────────────────────────
72
+ server.registerTool('email_get_message', {
73
+ description: 'Get full email content by UID. Returns headers, text/HTML body, and attachment metadata.',
74
+ inputSchema: z.object({
75
+ mailbox: z.string().min(1).describe('Mailbox/folder name that contains the message'),
76
+ uid: z.number().int().positive().describe('Message UID from email_search_messages'),
77
+ }),
78
+ annotations: { readOnlyHint: true },
79
+ }, withErrorHandling(async (args) => {
80
+ ensureInitialized();
81
+ const { mailbox, uid } = args;
82
+ const lock = await getMailboxLock(mailbox);
83
+ try {
84
+ const client = await getConnection();
85
+ const fetchedMessage = await client.fetchOne(uid, {
86
+ bodyStructure: true,
87
+ envelope: true,
88
+ flags: true,
89
+ uid: true,
90
+ }, { uid: true });
91
+ if (!fetchedMessage) {
92
+ throw new Error('Message not found for the specified UID');
93
+ }
94
+ const parts = { attachments: [] };
95
+ collectMessageParts(fetchedMessage.bodyStructure, parts);
96
+ const textBody = parts.textPart
97
+ ? await downloadPartAsText(uid, parts.textPart)
98
+ : '';
99
+ const htmlBody = parts.htmlPart
100
+ ? await downloadPartAsText(uid, parts.htmlPart)
101
+ : undefined;
102
+ const fallbackTextBody = !textBody && htmlBody
103
+ ? htmlBody.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim()
104
+ : textBody;
105
+ return JSON.stringify({
106
+ ok: true,
107
+ message: {
108
+ uid: fetchedMessage.uid,
109
+ subject: fetchedMessage.envelope?.subject ?? '',
110
+ from: formatAddresses(fetchedMessage.envelope?.from),
111
+ to: formatAddresses(fetchedMessage.envelope?.to),
112
+ date: formatDate(fetchedMessage.envelope?.date),
113
+ messageId: fetchedMessage.envelope?.messageId ?? null,
114
+ textBody: fallbackTextBody,
115
+ ...(htmlBody ? { htmlBody } : {}),
116
+ attachments: parts.attachments,
117
+ },
118
+ });
119
+ }
120
+ finally {
121
+ lock.release();
122
+ }
123
+ }));
124
+ // ── email_move_messages ─────────────────────────────────────────
125
+ server.registerTool('email_move_messages', {
126
+ description: 'Move emails between folders by UID.',
127
+ inputSchema: z.object({
128
+ uids: z
129
+ .array(z.number().int().positive())
130
+ .min(1)
131
+ .describe('Array of message UIDs to move'),
132
+ mailbox: z.string().min(1).describe('Source mailbox/folder name'),
133
+ destination: z.string().min(1).describe('Destination mailbox/folder name'),
134
+ }),
135
+ annotations: { readOnlyHint: false, destructiveHint: false },
136
+ }, withErrorHandling(async (args) => {
137
+ ensureInitialized();
138
+ const { uids, mailbox, destination } = args;
139
+ await ensureMailboxExists(destination);
140
+ const lock = await getMailboxLock(mailbox);
141
+ try {
142
+ const client = await getConnection();
143
+ try {
144
+ const moveResult = await client.messageMove(uids, destination, {
145
+ uid: true,
146
+ });
147
+ if (!moveResult) {
148
+ throw new Error('MOVE command failed');
149
+ }
150
+ return JSON.stringify({
151
+ ok: true,
152
+ moved: moveResult.uidMap ? moveResult.uidMap.size : uids.length,
153
+ });
154
+ }
155
+ catch {
156
+ const copyResult = await client.messageCopy(uids, destination, {
157
+ uid: true,
158
+ });
159
+ if (!copyResult) {
160
+ throw new Error('Unable to copy messages to destination mailbox');
161
+ }
162
+ await client.messageFlagsAdd(uids, ['\\Deleted'], { uid: true });
163
+ await client.messageDelete(uids, { uid: true });
164
+ return JSON.stringify({
165
+ ok: true,
166
+ moved: uids.length,
167
+ });
168
+ }
169
+ }
170
+ finally {
171
+ lock.release();
172
+ }
173
+ }));
174
+ // ── email_set_flags ─────────────────────────────────────────────
175
+ server.registerTool('email_set_flags', {
176
+ description: 'Set or remove flags on messages. Common flags: \\Seen (read), \\Flagged (starred).',
177
+ inputSchema: z.object({
178
+ uids: z
179
+ .array(z.number().int().positive())
180
+ .min(1)
181
+ .describe('Array of message UIDs to update'),
182
+ mailbox: z.string().min(1).describe('Mailbox/folder containing the messages'),
183
+ action: z.enum(['add', 'remove']).describe('Whether to add or remove flags'),
184
+ flags: z
185
+ .array(z.string().min(1))
186
+ .min(1)
187
+ .describe('Flags to update (e.g. \\Seen, \\Flagged)'),
188
+ }),
189
+ annotations: { readOnlyHint: false, destructiveHint: false },
190
+ }, withErrorHandling(async (args) => {
191
+ ensureInitialized();
192
+ const { uids, mailbox, action, flags } = args;
193
+ const lock = await getMailboxLock(mailbox);
194
+ try {
195
+ const client = await getConnection();
196
+ const success = action === 'add'
197
+ ? await client.messageFlagsAdd(uids, flags, { uid: true })
198
+ : await client.messageFlagsRemove(uids, flags, { uid: true });
199
+ if (!success) {
200
+ throw new Error('Unable to update message flags');
201
+ }
202
+ return JSON.stringify({
203
+ ok: true,
204
+ updated: uids.length,
205
+ });
206
+ }
207
+ finally {
208
+ lock.release();
209
+ }
210
+ }));
211
+ }
212
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Send/draft tools — sending emails and saving drafts.
3
+ */
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ export declare function registerSendTools(server: McpServer): void;
6
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Send/draft tools — sending emails and saving drafts.
3
+ */
4
+ import { z } from 'zod';
5
+ import nodemailer from 'nodemailer';
6
+ import { withErrorHandling } from '../utils.js';
7
+ import { getConnection } from '../imap-client.js';
8
+ import { getTransport } from '../smtp-client.js';
9
+ import { ensureInitialized, generateMessageId, resolveDraftsMailbox, } from './shared.js';
10
+ export function registerSendTools(server) {
11
+ // ── email_send ──────────────────────────────────────────────────
12
+ server.registerTool('email_send', {
13
+ description: 'Send an email. For replies, provide reply_to_message_id from the original message.',
14
+ inputSchema: z.object({
15
+ to: z
16
+ .union([z.string().min(1), z.array(z.string().min(1)).min(1)])
17
+ .describe('Recipient email address or array of addresses'),
18
+ subject: z.string().optional().describe('Email subject line'),
19
+ text: z.string().optional().describe('Plain-text email body'),
20
+ html: z.string().optional().describe('HTML email body'),
21
+ cc: z
22
+ .union([z.string().min(1), z.array(z.string().min(1)).min(1)])
23
+ .optional()
24
+ .describe('CC recipient(s)'),
25
+ bcc: z
26
+ .union([z.string().min(1), z.array(z.string().min(1)).min(1)])
27
+ .optional()
28
+ .describe('BCC recipient(s)'),
29
+ reply_to_message_id: z
30
+ .string()
31
+ .optional()
32
+ .describe('Message-ID of the original email when replying'),
33
+ }),
34
+ annotations: { readOnlyHint: false, destructiveHint: false },
35
+ }, withErrorHandling(async (args) => {
36
+ const config = ensureInitialized();
37
+ const { to, subject, text, html, cc, bcc, reply_to_message_id } = args;
38
+ const messageId = generateMessageId(config.email);
39
+ const transport = await getTransport();
40
+ await transport.sendMail({
41
+ from: config.email,
42
+ to,
43
+ cc,
44
+ bcc,
45
+ subject,
46
+ text,
47
+ html,
48
+ messageId,
49
+ ...(reply_to_message_id
50
+ ? {
51
+ inReplyTo: reply_to_message_id,
52
+ references: reply_to_message_id,
53
+ }
54
+ : {}),
55
+ });
56
+ return JSON.stringify({ ok: true, messageId });
57
+ }));
58
+ // ── email_save_draft ────────────────────────────────────────────
59
+ server.registerTool('email_save_draft', {
60
+ description: 'Save a draft email to the Drafts folder.',
61
+ inputSchema: z.object({
62
+ to: z
63
+ .union([z.string().min(1), z.array(z.string().min(1)).min(1)])
64
+ .optional()
65
+ .describe('Recipient email address or array of addresses'),
66
+ subject: z.string().optional().describe('Draft subject line'),
67
+ text: z.string().optional().describe('Plain-text draft body'),
68
+ html: z.string().optional().describe('HTML draft body'),
69
+ reply_to_message_id: z
70
+ .string()
71
+ .optional()
72
+ .describe('Message-ID of the original email when drafting a reply'),
73
+ }),
74
+ annotations: { readOnlyHint: false, destructiveHint: false },
75
+ }, withErrorHandling(async (args) => {
76
+ const config = ensureInitialized();
77
+ const { to, subject, text, html, reply_to_message_id } = args;
78
+ const hasSubject = Boolean(subject && subject.trim().length > 0);
79
+ const hasBody = Boolean(text && text.trim().length > 0) ||
80
+ Boolean(html && html.trim().length > 0);
81
+ if (!hasSubject && !hasBody) {
82
+ throw new Error('Provide at least a subject or a text/html body');
83
+ }
84
+ const messageId = generateMessageId(config.email);
85
+ const draftTransport = nodemailer.createTransport({
86
+ streamTransport: true,
87
+ buffer: true,
88
+ newline: 'unix',
89
+ });
90
+ const draftResult = await draftTransport.sendMail({
91
+ from: config.email,
92
+ to,
93
+ subject,
94
+ text,
95
+ html,
96
+ messageId,
97
+ ...(reply_to_message_id
98
+ ? {
99
+ inReplyTo: reply_to_message_id,
100
+ references: reply_to_message_id,
101
+ }
102
+ : {}),
103
+ });
104
+ draftTransport.close();
105
+ const rawMessageValue = draftResult.message;
106
+ const rawMessage = Buffer.isBuffer(rawMessageValue)
107
+ ? rawMessageValue
108
+ : typeof rawMessageValue === 'string'
109
+ ? Buffer.from(rawMessageValue)
110
+ : null;
111
+ if (!rawMessage) {
112
+ throw new Error('Unable to construct raw draft message');
113
+ }
114
+ const draftsMailbox = await resolveDraftsMailbox();
115
+ const client = await getConnection();
116
+ const appendResult = await client.append(draftsMailbox, rawMessage, [
117
+ '\\Draft',
118
+ '\\Seen',
119
+ ]);
120
+ if (!appendResult) {
121
+ throw new Error('Unable to append draft message');
122
+ }
123
+ return JSON.stringify({
124
+ ok: true,
125
+ messageId,
126
+ mailbox: draftsMailbox,
127
+ ...(appendResult.uid !== undefined ? { uid: appendResult.uid } : {}),
128
+ });
129
+ }));
130
+ }
131
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Shared utilities for email-imap tool handlers.
3
+ */
4
+ import type { Readable } from 'node:stream';
5
+ import type { MessageAddressObject, MessageStructureObject } from 'imapflow';
6
+ import type { ClientConfig } from '../types.js';
7
+ export declare function setClientConfig(config: ClientConfig | null): void;
8
+ export declare function getClientConfig(): ClientConfig | null;
9
+ export declare function ensureInitialized(): ClientConfig;
10
+ /**
11
+ * Attachment metadata from a parsed message.
12
+ */
13
+ export interface AttachmentMetadata {
14
+ filename: string | null;
15
+ contentType: string;
16
+ size: number;
17
+ }
18
+ /**
19
+ * Parsed message parts (text, html, attachments).
20
+ */
21
+ export interface MessageParts {
22
+ textPart?: string;
23
+ htmlPart?: string;
24
+ attachments: AttachmentMetadata[];
25
+ }
26
+ /**
27
+ * Format email addresses for display.
28
+ */
29
+ export declare function formatAddresses(addresses?: MessageAddressObject[]): string;
30
+ /**
31
+ * Format a date value to ISO string.
32
+ */
33
+ export declare function formatDate(date: Date | string | undefined): string | null;
34
+ /**
35
+ * Generate a unique Message-ID for an email.
36
+ */
37
+ export declare function generateMessageId(email: string): string;
38
+ /**
39
+ * Read a stream into a Buffer.
40
+ */
41
+ export declare function streamToBuffer(stream: Readable): Promise<Buffer>;
42
+ /**
43
+ * Download a message part as text by UID and part number.
44
+ */
45
+ export declare function downloadPartAsText(uid: number, part: string): Promise<string>;
46
+ /**
47
+ * Recursively collect text/html parts and attachments from a message structure.
48
+ */
49
+ export declare function collectMessageParts(node: MessageStructureObject | undefined, parts: MessageParts): void;
50
+ /**
51
+ * Remove duplicates from an array, preserving order (case-insensitive).
52
+ */
53
+ export declare function uniquePreserveOrder(values: string[]): string[];
54
+ /**
55
+ * Resolve the drafts mailbox name for the current account.
56
+ */
57
+ export declare function resolveDraftsMailbox(): Promise<string>;
58
+ /**
59
+ * Ensure a mailbox exists, creating it if necessary.
60
+ */
61
+ export declare function ensureMailboxExists(mailbox: string): Promise<void>;
62
+ //# sourceMappingURL=shared.d.ts.map