@honest-magic/mail-mcp 1.0.1 → 1.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.
- package/README.md +2 -2
- package/dist/cli/accounts.js +3 -3
- package/dist/cli/accounts.js.map +1 -1
- package/dist/config.d.ts +26 -6
- package/dist/config.js +85 -24
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +36 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +202 -33
- package/dist/index.js.map +1 -1
- package/dist/protocol/imap.d.ts +4 -2
- package/dist/protocol/imap.js +50 -9
- package/dist/protocol/imap.js.map +1 -1
- package/dist/services/mail.d.ts +5 -4
- package/dist/services/mail.js +13 -5
- package/dist/services/mail.js.map +1 -1
- package/dist/types/index.d.ts +1 -11
- package/dist/utils/rate-limiter.d.ts +19 -0
- package/dist/utils/rate-limiter.js +43 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.js +32 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +20 -4
package/dist/services/mail.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MessageMetadata } from '../protocol/imap.js';
|
|
1
|
+
import { ImapClient, MessageMetadata } from '../protocol/imap.js';
|
|
2
2
|
import { EmailAccount } from '../types/index.js';
|
|
3
3
|
export declare class MailService {
|
|
4
4
|
private readonly readOnly;
|
|
@@ -7,22 +7,23 @@ export declare class MailService {
|
|
|
7
7
|
private account;
|
|
8
8
|
private smtpConnected;
|
|
9
9
|
constructor(account: EmailAccount, readOnly?: boolean);
|
|
10
|
+
get imap(): ImapClient;
|
|
10
11
|
connect(): Promise<void>;
|
|
11
12
|
private ensureSmtp;
|
|
12
13
|
disconnect(): Promise<void>;
|
|
13
|
-
listEmails(folder?: string, count?: number): Promise<MessageMetadata[]>;
|
|
14
|
+
listEmails(folder?: string, count?: number, offset?: number): Promise<MessageMetadata[]>;
|
|
14
15
|
searchEmails(query: {
|
|
15
16
|
from?: string;
|
|
16
17
|
subject?: string;
|
|
17
18
|
since?: string;
|
|
18
19
|
before?: string;
|
|
19
20
|
keywords?: string;
|
|
20
|
-
}, folder?: string, count?: number): Promise<MessageMetadata[]>;
|
|
21
|
+
}, folder?: string, count?: number, offset?: number): Promise<MessageMetadata[]>;
|
|
21
22
|
sendEmail(to: string, subject: string, body: string, isHtml?: boolean, cc?: string, bcc?: string): Promise<any>;
|
|
22
23
|
createDraft(to: string, subject: string, body: string, isHtml?: boolean, cc?: string, bcc?: string): Promise<void>;
|
|
23
24
|
readEmail(uid: string, folder?: string): Promise<string>;
|
|
24
25
|
getThread(threadId: string, folder?: string): Promise<MessageMetadata[]>;
|
|
25
|
-
downloadAttachment(uid: string, filename: string, folder?: string): Promise<{
|
|
26
|
+
downloadAttachment(uid: string, filename: string, folder?: string, maxBytes?: number): Promise<{
|
|
26
27
|
content: Buffer;
|
|
27
28
|
contentType: string;
|
|
28
29
|
}>;
|
package/dist/services/mail.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ImapClient } from '../protocol/imap.js';
|
|
2
2
|
import { SmtpClient } from '../protocol/smtp.js';
|
|
3
3
|
import { htmlToMarkdown } from '../utils/markdown.js';
|
|
4
|
+
import { ValidationError } from '../errors.js';
|
|
4
5
|
export class MailService {
|
|
5
6
|
readOnly;
|
|
6
7
|
imapClient;
|
|
@@ -13,6 +14,9 @@ export class MailService {
|
|
|
13
14
|
this.imapClient = new ImapClient(account);
|
|
14
15
|
this.smtpClient = new SmtpClient(account);
|
|
15
16
|
}
|
|
17
|
+
get imap() {
|
|
18
|
+
return this.imapClient;
|
|
19
|
+
}
|
|
16
20
|
async connect() {
|
|
17
21
|
await this.imapClient.connect();
|
|
18
22
|
}
|
|
@@ -26,10 +30,10 @@ export class MailService {
|
|
|
26
30
|
await this.imapClient.disconnect();
|
|
27
31
|
// nodemailer transporter doesn't strictly need closing, but good practice if pooling
|
|
28
32
|
}
|
|
29
|
-
async listEmails(folder = 'INBOX', count = 10) {
|
|
30
|
-
return this.imapClient.listMessages(folder, count);
|
|
33
|
+
async listEmails(folder = 'INBOX', count = 10, offset = 0) {
|
|
34
|
+
return this.imapClient.listMessages(folder, count, offset);
|
|
31
35
|
}
|
|
32
|
-
async searchEmails(query, folder = 'INBOX', count = 10) {
|
|
36
|
+
async searchEmails(query, folder = 'INBOX', count = 10, offset = 0) {
|
|
33
37
|
const criteria = {};
|
|
34
38
|
if (query.from)
|
|
35
39
|
criteria.from = query.from;
|
|
@@ -41,7 +45,7 @@ export class MailService {
|
|
|
41
45
|
criteria.before = query.before;
|
|
42
46
|
if (query.keywords)
|
|
43
47
|
criteria.body = query.keywords;
|
|
44
|
-
return this.imapClient.searchMessages(criteria, folder, count);
|
|
48
|
+
return this.imapClient.searchMessages(criteria, folder, count, offset);
|
|
45
49
|
}
|
|
46
50
|
async sendEmail(to, subject, body, isHtml = false, cc, bcc) {
|
|
47
51
|
// Build raw message before sending so we can append to Sent folder
|
|
@@ -136,7 +140,11 @@ export class MailService {
|
|
|
136
140
|
async getThread(threadId, folder = 'INBOX') {
|
|
137
141
|
return this.imapClient.fetchThreadMessages(threadId, folder);
|
|
138
142
|
}
|
|
139
|
-
async downloadAttachment(uid, filename, folder = 'INBOX') {
|
|
143
|
+
async downloadAttachment(uid, filename, folder = 'INBOX', maxBytes = 50 * 1024 * 1024) {
|
|
144
|
+
const size = await this.imapClient.fetchAttachmentSize(uid, filename, folder);
|
|
145
|
+
if (size != null && size > maxBytes) {
|
|
146
|
+
throw new ValidationError(`Attachment "${filename}" is ${Math.round(size / 1024 / 1024)} MB, which exceeds the ${Math.round(maxBytes / 1024 / 1024)} MB limit. Use an email client to download large attachments directly.`);
|
|
147
|
+
}
|
|
140
148
|
const parsed = await this.imapClient.fetchMessageBody(uid, folder);
|
|
141
149
|
if (!parsed.attachments || parsed.attachments.length === 0) {
|
|
142
150
|
throw new Error('No attachments found in this email');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../../src/services/mail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../../src/services/mail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,OAAO,WAAW;IAO8B;IAN5C,UAAU,CAAa;IACvB,UAAU,CAAa;IACvB,OAAO,CAAe;IAEtB,aAAa,GAAG,KAAK,CAAC;IAE9B,YAAY,OAAqB,EAAmB,WAAoB,KAAK;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;QAC3E,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACnC,qFAAqF;IACvF,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB,OAAO,EAAE,QAAgB,EAAE,EAAE,SAAiB,CAAC;QAC/E,OAAO,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAA8F,EAAE,SAAiB,OAAO,EAAE,QAAgB,EAAE,EAAE,SAAiB,CAAC;QACjL,MAAM,QAAQ,GAAQ,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,IAAI;YAAE,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3C,IAAI,KAAK,CAAC,OAAO;YAAE,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QACpD,IAAI,KAAK,CAAC,KAAK;YAAE,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC9C,IAAI,KAAK,CAAC,MAAM;YAAE,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QACjD,IAAI,KAAK,CAAC,QAAQ;YAAE,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC;QAEnD,OAAO,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU,EAAE,OAAe,EAAE,IAAY,EAAE,SAAkB,KAAK,EAAE,EAAW,EAAE,GAAY;QAC3G,mEAAmE;QACnE,MAAM,UAAU,GAAG;YACjB,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YAC5B,OAAO,EAAE,EAAE;YACX,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5B,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,YAAY,OAAO,EAAE;YACrB,mBAAmB;YACnB,iBAAiB,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,iBAAiB;YACrE,EAAE;YACF,IAAI;SACL,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEf,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QAC5E,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,CAAC,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,OAAe,EAAE,IAAY,EAAE,SAAkB,KAAK,EAAE,EAAW,EAAE,GAAY;QAC7G,MAAM,OAAO,GAAG;YACd,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YAC5B,OAAO,EAAE,EAAE;YACX,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5B,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,YAAY,OAAO,EAAE;YACrB,mBAAmB;YACnB,iBAAiB,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,iBAAiB;YACrE,EAAE;YACF,IAAI;SACL,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEf,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,SAAiB,OAAO;QACnD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAEnE,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YACvB,4CAA4C;YAC5C,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;gBACvB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;oBACrC,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;wBACzE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;wBAC9C,MAAM,OAAO,GAAG,QAAQ,GAAG,CAAC,WAAW,WAAW,MAAM,EAAE,CAAC;wBAC3D,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;wBAChG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBACzC,CAAC;gBACH,CAAC;YACH,CAAC;YACD,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YAC7B,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9C,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YACvB,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;QACxB,CAAC;QAED,IAAI,cAAc,GAAG,EAAE,CAAC;QACxB,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxD,cAAc,GAAG,wBAAwB,CAAC;YAC1C,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;gBAC/B,cAAc,IAAI,KAAK,GAAG,CAAC,QAAQ,IAAI,SAAS,KAAK,GAAG,CAAC,WAAW,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC/G,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,MAAM,GAAG,aAAa,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,SAAS,IAAI,CAAC;QAC7D,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC;QAClG,MAAM,IAAI,WAAW,MAAM,IAAI,SAAS,IAAI,CAAC;QAC7C,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC;YACjG,MAAM,IAAI,WAAW,MAAM,IAAI,CAAC;QAClC,CAAC;QACD,MAAM,IAAI,gBAAgB,MAAM,CAAC,OAAO,IAAI,YAAY,IAAI,CAAC;QAC7D,MAAM,IAAI,aAAa,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,SAAS,IAAI,CAAC;QAEnE,iCAAiC;QACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAClD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,kBAAkB,QAAQ,IAAI,CAAC;QAC3C,CAAC;QAED,wEAAwE;QACxE,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACvE,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,IAAI,mBAAmB,SAAS,IAAI,CAAC;QAC7C,CAAC;QAED,MAAM,IAAI,WAAW,CAAC;QAEtB,OAAO,MAAM,GAAG,OAAO,GAAG,cAAc,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB,EAAE,SAAiB,OAAO;QACxD,OAAO,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,GAAW,EAAE,QAAgB,EAAE,SAAiB,OAAO,EAAE,WAAmB,EAAE,GAAG,IAAI,GAAG,IAAI;QACnH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9E,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,eAAe,CACvB,eAAe,QAAQ,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,0BAA0B,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC,wEAAwE,CAClM,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,aAAa,CAAC,CAAC;QACxD,CAAC;QACD,OAAO;YACL,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,WAAW,EAAE,UAAU,CAAC,WAAW;SACpC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,GAAW,EAAE,QAAgB,EAAE,SAAiB,OAAO;QACjF,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACtF,IAAI,WAAW,KAAK,iBAAiB,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YACtC,gFAAgF;YAChF,MAAM,SAAS,GAAI,GAAW,CAAC,OAAO,IAAI,GAAG,CAAC;YAC9C,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;YACtC,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;aAAM,IAAI,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3C,OAAO,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,8CAA8C,WAAW,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,GAAW,EAAE,YAAoB,EAAE,YAAoB;QACvE,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,GAAW,EAAE,MAAc,EAAE,SAAmB,EAAE,YAAsB;QACzF,OAAO,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAC5E,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,IAAc,EACd,MAAc,EACd,SAGoE;QAEpE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QAChF,CAAC;aAAM,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;aAAM,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACtC,MAAM,IAAI,CAAC,UAAU,CAAC,iBAAiB,CACrC,IAAI,EACJ,MAAM,EACN,SAAS,CAAC,SAAS,IAAI,EAAE,EACzB,SAAS,CAAC,YAAY,IAAI,EAAE,CAC7B,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,iCAAkC,SAAiB,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9E,CAAC;QAED,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACpC,CAAC;CACF"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
+
export type { EmailAccount } from '../config.js';
|
|
1
2
|
export type AuthType = 'login' | 'oauth2';
|
|
2
|
-
export interface EmailAccount {
|
|
3
|
-
id: string;
|
|
4
|
-
name: string;
|
|
5
|
-
host: string;
|
|
6
|
-
port: number;
|
|
7
|
-
smtpHost?: string;
|
|
8
|
-
smtpPort?: number;
|
|
9
|
-
user: string;
|
|
10
|
-
authType: AuthType;
|
|
11
|
-
useTLS: boolean;
|
|
12
|
-
}
|
|
13
3
|
export interface Credentials {
|
|
14
4
|
password?: string;
|
|
15
5
|
accessToken?: string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const DEFAULT_RATE_LIMIT_POINTS = 100;
|
|
2
|
+
export declare const DEFAULT_RATE_LIMIT_DURATION = 60;
|
|
3
|
+
export interface AccountRateLimiterOptions {
|
|
4
|
+
points?: number;
|
|
5
|
+
duration?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Per-account rate limiter.
|
|
9
|
+
* Uses a separate RateLimiterMemory instance per accountId for true isolation.
|
|
10
|
+
* Instantiate per MailMCPServer — NOT as a module-level singleton.
|
|
11
|
+
*/
|
|
12
|
+
export declare class AccountRateLimiter {
|
|
13
|
+
private readonly limiters;
|
|
14
|
+
private readonly points;
|
|
15
|
+
private readonly duration;
|
|
16
|
+
constructor(opts?: AccountRateLimiterOptions);
|
|
17
|
+
private getLimiter;
|
|
18
|
+
consume(accountId: string): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { RateLimiterMemory, RateLimiterRes } from 'rate-limiter-flexible';
|
|
2
|
+
import { QuotaError } from '../errors.js';
|
|
3
|
+
export const DEFAULT_RATE_LIMIT_POINTS = 100;
|
|
4
|
+
export const DEFAULT_RATE_LIMIT_DURATION = 60;
|
|
5
|
+
/**
|
|
6
|
+
* Per-account rate limiter.
|
|
7
|
+
* Uses a separate RateLimiterMemory instance per accountId for true isolation.
|
|
8
|
+
* Instantiate per MailMCPServer — NOT as a module-level singleton.
|
|
9
|
+
*/
|
|
10
|
+
export class AccountRateLimiter {
|
|
11
|
+
limiters = new Map();
|
|
12
|
+
points;
|
|
13
|
+
duration;
|
|
14
|
+
constructor(opts = {}) {
|
|
15
|
+
this.points = opts.points ?? DEFAULT_RATE_LIMIT_POINTS;
|
|
16
|
+
this.duration = opts.duration ?? DEFAULT_RATE_LIMIT_DURATION;
|
|
17
|
+
}
|
|
18
|
+
getLimiter(accountId) {
|
|
19
|
+
let limiter = this.limiters.get(accountId);
|
|
20
|
+
if (!limiter) {
|
|
21
|
+
limiter = new RateLimiterMemory({
|
|
22
|
+
points: this.points,
|
|
23
|
+
duration: this.duration,
|
|
24
|
+
});
|
|
25
|
+
this.limiters.set(accountId, limiter);
|
|
26
|
+
}
|
|
27
|
+
return limiter;
|
|
28
|
+
}
|
|
29
|
+
async consume(accountId) {
|
|
30
|
+
try {
|
|
31
|
+
await this.getLimiter(accountId).consume(accountId);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err instanceof RateLimiterRes) {
|
|
35
|
+
const wait = Math.ceil(err.msBeforeNext / 1000);
|
|
36
|
+
throw new QuotaError(`Rate limit exceeded for account "${accountId}". Retry after ${wait} second(s).`);
|
|
37
|
+
}
|
|
38
|
+
// Re-throw unexpected errors
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAO9C;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IACZ,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;IAChD,MAAM,CAAS;IACf,QAAQ,CAAS;IAElC,YAAY,OAAkC,EAAE;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,yBAAyB,CAAC;QACvD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,2BAA2B,CAAC;IAC/D,CAAC;IAEO,UAAU,CAAC,SAAiB;QAClC,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,iBAAiB,CAAC;gBAC9B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,SAAiB;QAC7B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;gBAChD,MAAM,IAAI,UAAU,CAClB,oCAAoC,SAAS,kBAAkB,IAAI,aAAa,CACjF,CAAC;YACJ,CAAC;YACD,6BAA6B;YAC7B,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates one or more email address fields (to, cc, bcc).
|
|
3
|
+
* Each field may contain multiple comma-separated addresses or be undefined.
|
|
4
|
+
* Throws ValidationError listing all invalid addresses if any are found.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateEmailAddresses(...addrFields: Array<string | undefined>): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ValidationError } from '../errors.js';
|
|
2
|
+
// ReDoS-safe email regex — no nested quantifiers
|
|
3
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4
|
+
// Matches angle-bracket format: "Name <user@example.com>"
|
|
5
|
+
const ANGLE_BRACKET_RE = /<([^>]+)>/;
|
|
6
|
+
/**
|
|
7
|
+
* Validates one or more email address fields (to, cc, bcc).
|
|
8
|
+
* Each field may contain multiple comma-separated addresses or be undefined.
|
|
9
|
+
* Throws ValidationError listing all invalid addresses if any are found.
|
|
10
|
+
*/
|
|
11
|
+
export function validateEmailAddresses(...addrFields) {
|
|
12
|
+
const invalid = [];
|
|
13
|
+
for (const field of addrFields) {
|
|
14
|
+
if (field === undefined || field === '')
|
|
15
|
+
continue;
|
|
16
|
+
const entries = field.split(',').map((e) => e.trim());
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
if (entry === '')
|
|
19
|
+
continue;
|
|
20
|
+
// Extract address from angle-bracket format or use raw entry
|
|
21
|
+
const angleMatch = ANGLE_BRACKET_RE.exec(entry);
|
|
22
|
+
const address = angleMatch ? angleMatch[1].trim() : entry;
|
|
23
|
+
if (!EMAIL_RE.test(address)) {
|
|
24
|
+
invalid.push(entry);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (invalid.length > 0) {
|
|
29
|
+
throw new ValidationError('Invalid email address(es): ' + invalid.join(', '));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,iDAAiD;AACjD,MAAM,QAAQ,GAAG,4BAA4B,CAAC;AAE9C,0DAA0D;AAC1D,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAErC;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAG,UAAqC;IAC7E,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;YAAE,SAAS;QAElD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAEtD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,KAAK,EAAE;gBAAE,SAAS;YAE3B,6DAA6D;YAC7D,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChD,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;YAE1D,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,eAAe,CAAC,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAChF,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honest-magic/mail-mcp",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "MCP server for AI-powered email access via IMAP and SMTP",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"email",
|
|
8
|
+
"imap",
|
|
9
|
+
"smtp",
|
|
10
|
+
"ai",
|
|
11
|
+
"llm",
|
|
12
|
+
"model-context-protocol"
|
|
13
|
+
],
|
|
6
14
|
"author": "honest-magic",
|
|
7
15
|
"license": "MIT",
|
|
8
16
|
"homepage": "https://github.com/honest-magic/mail-mcp#readme",
|
|
@@ -20,14 +28,19 @@
|
|
|
20
28
|
"bin": {
|
|
21
29
|
"mail-mcp": "dist/index.js"
|
|
22
30
|
},
|
|
23
|
-
"files": [
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
24
36
|
"publishConfig": {
|
|
25
37
|
"access": "public"
|
|
26
38
|
},
|
|
27
39
|
"scripts": {
|
|
28
40
|
"build": "tsc",
|
|
29
41
|
"start": "node dist/index.js",
|
|
30
|
-
"test": "vitest run"
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
31
44
|
},
|
|
32
45
|
"dependencies": {
|
|
33
46
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -37,6 +50,7 @@
|
|
|
37
50
|
"mailparser": "^3.9.4",
|
|
38
51
|
"nodemailer": "^8.0.3",
|
|
39
52
|
"pdf-parse": "^2.4.5",
|
|
53
|
+
"rate-limiter-flexible": "^10.0.1",
|
|
40
54
|
"turndown": "^7.2.2",
|
|
41
55
|
"zod": "^4.3.6"
|
|
42
56
|
},
|
|
@@ -45,7 +59,9 @@
|
|
|
45
59
|
"@types/node": "^25.5.0",
|
|
46
60
|
"@types/nodemailer": "^7.0.11",
|
|
47
61
|
"@types/pdf-parse": "^1.1.5",
|
|
62
|
+
"@types/smtp-server": "^3.5.12",
|
|
48
63
|
"@types/turndown": "^5.0.6",
|
|
64
|
+
"smtp-server": "^3.18.1",
|
|
49
65
|
"ts-node": "^10.9.2",
|
|
50
66
|
"typescript": "^5.9.3",
|
|
51
67
|
"vitest": "^4.1.0"
|