@mcp-monorepo/mail 1.0.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.
Files changed (41) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +13 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/lib/accounts.d.ts +17 -0
  6. package/dist/lib/accounts.d.ts.map +1 -0
  7. package/dist/lib/accounts.js +48 -0
  8. package/dist/lib/accounts.js.map +1 -0
  9. package/dist/lib/imap.d.ts +4 -0
  10. package/dist/lib/imap.d.ts.map +1 -0
  11. package/dist/lib/imap.js +21 -0
  12. package/dist/lib/imap.js.map +1 -0
  13. package/dist/lib/mail.service.d.ts +10 -0
  14. package/dist/lib/mail.service.d.ts.map +1 -0
  15. package/dist/lib/mail.service.js +94 -0
  16. package/dist/lib/mail.service.js.map +1 -0
  17. package/dist/lib/types.d.ts +36 -0
  18. package/dist/lib/types.d.ts.map +1 -0
  19. package/dist/lib/types.js +2 -0
  20. package/dist/lib/types.js.map +1 -0
  21. package/dist/lib/utils.d.ts +10 -0
  22. package/dist/lib/utils.d.ts.map +1 -0
  23. package/dist/lib/utils.js +40 -0
  24. package/dist/lib/utils.js.map +1 -0
  25. package/dist/tools/fetch-latest-mails.d.ts +3 -0
  26. package/dist/tools/fetch-latest-mails.d.ts.map +1 -0
  27. package/dist/tools/fetch-latest-mails.js +57 -0
  28. package/dist/tools/fetch-latest-mails.js.map +1 -0
  29. package/dist/tools/mark-mails-as-seen.d.ts +3 -0
  30. package/dist/tools/mark-mails-as-seen.d.ts.map +1 -0
  31. package/dist/tools/mark-mails-as-seen.js +46 -0
  32. package/dist/tools/mark-mails-as-seen.js.map +1 -0
  33. package/dist/tools/read-mail.d.ts +3 -0
  34. package/dist/tools/read-mail.d.ts.map +1 -0
  35. package/dist/tools/read-mail.js +36 -0
  36. package/dist/tools/read-mail.js.map +1 -0
  37. package/dist/tools/search-mails.d.ts +3 -0
  38. package/dist/tools/search-mails.d.ts.map +1 -0
  39. package/dist/tools/search-mails.js +61 -0
  40. package/dist/tools/search-mails.js.map +1 -0
  41. package/package.json +42 -0
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { createMcpServer, logger } from '@mcp-monorepo/shared';
3
+ import { registerFetchLatestMailsTool } from './tools/fetch-latest-mails.js';
4
+ import { registerMarkMailsAsSeenTool } from './tools/mark-mails-as-seen.js';
5
+ import { registerReadMailTool } from './tools/read-mail.js';
6
+ import { registerSearchMailsTool } from './tools/search-mails.js';
7
+ createMcpServer({
8
+ name: 'mail',
9
+ importMetaPath: import.meta.filename,
10
+ title: 'Mail MCP Server',
11
+ tools: [registerFetchLatestMailsTool, registerSearchMailsTool, registerReadMailTool, registerMarkMailsAsSeenTool],
12
+ }).catch((e) => logger.error('Failed to start mail server', e));
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAE9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAC5E,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAA;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,eAAe,CAAC;IACd,IAAI,EAAE,MAAM;IACZ,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ;IACpC,KAAK,EAAE,iBAAiB;IACxB,KAAK,EAAE,CAAC,4BAA4B,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,2BAA2B,CAAC;CAClH,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,CAAC,CAAC,CAAC,CAAA"}
@@ -0,0 +1,17 @@
1
+ import type { AccountCredentials } from './types.js';
2
+ /**
3
+ * Parses the MAIL_ACCOUNTS env variable into an array of AccountCredentials.
4
+ * Format: user:pass@host:port (space-separated for multiple accounts)
5
+ * Handles special characters in user/pass robustly.
6
+ * Throws on invalid/missing format.
7
+ */
8
+ export declare function parseMailAccounts(): AccountCredentials[];
9
+ /**
10
+ * Finds a specific mail account from the configured environment variables.
11
+ * @throws An error if the account is not found.
12
+ */
13
+ export declare function findMailAccount({ username, host }: {
14
+ username: string;
15
+ host: string;
16
+ }): AccountCredentials;
17
+ //# sourceMappingURL=accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../src/lib/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,kBAAkB,EAAE,CAyBxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,kBAAkB,CAO1G"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Parses the MAIL_ACCOUNTS env variable into an array of AccountCredentials.
3
+ * Format: user:pass@host:port (space-separated for multiple accounts)
4
+ * Handles special characters in user/pass robustly.
5
+ * Throws on invalid/missing format.
6
+ */
7
+ export function parseMailAccounts() {
8
+ const env = process.env.MAIL_ACCOUNTS;
9
+ if (!env)
10
+ throw new Error('MAIL_ACCOUNTS env variable is not set');
11
+ return env
12
+ .split(' ')
13
+ .filter(Boolean)
14
+ .map((entry) => {
15
+ const atIdx = entry.lastIndexOf('@');
16
+ if (atIdx === -1)
17
+ throw new Error('Invalid MAIL_ACCOUNTS entry: Could not split at LAST @ for host/port, FIRST for user/pass');
18
+ const cred = entry.slice(0, atIdx);
19
+ const hostPort = entry.slice(atIdx + 1);
20
+ const colonIdx = cred.indexOf(':');
21
+ if (colonIdx === -1)
22
+ throw new Error('Invalid MAIL_ACCOUNTS entry (missing colon in user:pass)');
23
+ const user = cred.slice(0, colonIdx);
24
+ const pass = cred.slice(colonIdx + 1);
25
+ const hostPortMatch = hostPort.match(/^(.*?):(\d+)$/);
26
+ if (!hostPortMatch)
27
+ throw new Error(`Invalid MAIL_ACCOUNTS entry (host:port): ${entry}`);
28
+ return {
29
+ user,
30
+ pass,
31
+ host: hostPortMatch[1],
32
+ port: Number(hostPortMatch[2]),
33
+ };
34
+ });
35
+ }
36
+ /**
37
+ * Finds a specific mail account from the configured environment variables.
38
+ * @throws An error if the account is not found.
39
+ */
40
+ export function findMailAccount({ username, host }) {
41
+ const accounts = parseMailAccounts();
42
+ const account = accounts.find((a) => a.user === username && a.host === host);
43
+ if (!account) {
44
+ throw new Error(`Account for ${username}@${host} not found in configuration.`);
45
+ }
46
+ return account;
47
+ }
48
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/lib/accounts.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;IACrC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;IAClE,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;QACpC,IAAI,KAAK,KAAK,CAAC,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAA;QAC9G,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;QAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;QAChG,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAA;QACrC,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;QACrD,IAAI,CAAC,aAAa;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,KAAK,EAAE,CAAC,CAAA;QACxF,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;YACtB,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SAC/B,CAAA;IACH,CAAC,CAAC,CAAA;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAsC;IACpF,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAA;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,IAAI,IAAI,8BAA8B,CAAC,CAAA;IAChF,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { ImapFlow, type MailboxLockObject } from 'imapflow';
2
+ import type { AccountCredentials } from './types.js';
3
+ export declare function withImapClient<T>(account: AccountCredentials, action: (client: ImapFlow, lock: MailboxLockObject) => Promise<T>): Promise<T>;
4
+ //# sourceMappingURL=imap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imap.d.ts","sourceRoot":"","sources":["../../src/lib/imap.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD,wBAAsB,cAAc,CAAC,CAAC,EACpC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,GAChE,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
@@ -0,0 +1,21 @@
1
+ import { logger } from '@mcp-monorepo/shared';
2
+ import { ImapFlow } from 'imapflow';
3
+ export async function withImapClient(account, action) {
4
+ const client = new ImapFlow({
5
+ host: account.host,
6
+ port: account.port,
7
+ secure: true,
8
+ auth: { user: account.user, pass: account.pass },
9
+ logger: false, // Set to true for verbose IMAP logging
10
+ });
11
+ await client.connect();
12
+ const lock = await client.getMailboxLock('INBOX');
13
+ try {
14
+ return await action(client, lock);
15
+ }
16
+ finally {
17
+ lock.release();
18
+ await client.logout().catch((e) => logger.warn(`Failed to logout from ${account.host}`, e));
19
+ }
20
+ }
21
+ //# sourceMappingURL=imap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imap.js","sourceRoot":"","sources":["../../src/lib/imap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAA0B,MAAM,UAAU,CAAA;AAI3D,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAA2B,EAC3B,MAAiE;IAEjE,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,IAAI;QACZ,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;QAChD,MAAM,EAAE,KAAK,EAAE,uCAAuC;KACvD,CAAC,CAAA;IAEF,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;IACtB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;IACjD,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnC,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,OAAO,EAAE,CAAA;QACd,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IAC7F,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { AccountCredentials, Mail, MailMarkResult, ReadMailResult } from './types.js';
2
+ export declare function searchMails(account: AccountCredentials, { searchString, searchBody, fromContains }: {
3
+ searchString?: string;
4
+ searchBody?: boolean;
5
+ fromContains?: string;
6
+ }): Promise<Mail[]>;
7
+ export declare function fetchLatestMails(account: AccountCredentials, days?: number): Promise<Mail[]>;
8
+ export declare function readMailContent(account: AccountCredentials, mailId: string): Promise<ReadMailResult>;
9
+ export declare function markMailAsSeen(account: AccountCredentials, mailId: string): Promise<MailMarkResult>;
10
+ //# sourceMappingURL=mail.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.service.d.ts","sourceRoot":"","sources":["../../src/lib/mail.service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAe1F,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,EAC3B,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACjH,OAAO,CAAC,IAAI,EAAE,CAAC,CAmBjB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,EAAE,IAAI,SAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAM7F;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmC1G;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBzG"}
@@ -0,0 +1,94 @@
1
+ import { withImapClient } from './imap.js';
2
+ import { convertHtmlToText, findTextPart, getStartOfPeriodUTC, mapImapMessageToMail, streamToString } from './utils.js';
3
+ async function fetchAndMapMails(client, uids, accountIdentifier) {
4
+ if (!uids || uids.length === 0) {
5
+ return [];
6
+ }
7
+ const fetchedMails = [];
8
+ for await (const msg of client.fetch(uids, { envelope: true, flags: true, uid: true }, { uid: true })) {
9
+ fetchedMails.push(mapImapMessageToMail(msg, accountIdentifier));
10
+ }
11
+ return fetchedMails.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
12
+ }
13
+ export async function searchMails(account, { searchString, searchBody, fromContains }) {
14
+ const accountIdentifier = `${account.user}@${account.host}`;
15
+ return withImapClient(account, async (client) => {
16
+ const orConditions = [];
17
+ if (searchString) {
18
+ orConditions.push({ subject: searchString });
19
+ if (searchBody) {
20
+ orConditions.push({ body: searchString });
21
+ }
22
+ }
23
+ if (fromContains) {
24
+ orConditions.push({ from: fromContains });
25
+ }
26
+ const searchQuery = orConditions.length > 1 ? { or: orConditions } : orConditions[0];
27
+ const uids = (await client.search(searchQuery, { uid: true })) || [];
28
+ return fetchAndMapMails(client, uids, accountIdentifier);
29
+ });
30
+ }
31
+ export async function fetchLatestMails(account, days = 2) {
32
+ const accountIdentifier = `${account.user}@${account.host}`;
33
+ return withImapClient(account, async (client) => {
34
+ const uids = (await client.search({ since: getStartOfPeriodUTC(days) }, { uid: true })) || [];
35
+ return fetchAndMapMails(client, uids, accountIdentifier);
36
+ });
37
+ }
38
+ export async function readMailContent(account, mailId) {
39
+ try {
40
+ return await withImapClient(account, async (client) => {
41
+ const msg = await client.fetchOne(mailId, { envelope: true, bodyStructure: true }, { uid: true });
42
+ if (!msg)
43
+ throw new Error(`Mail with ID ${mailId} not found.`);
44
+ const textPart = msg.bodyStructure && findTextPart(msg.bodyStructure, 'plain');
45
+ const htmlPart = msg.bodyStructure && findTextPart(msg.bodyStructure, 'html');
46
+ let content = '';
47
+ if (textPart?.part) {
48
+ const { content: stream } = await client.download(mailId, textPart.part, { uid: true });
49
+ content = await streamToString(stream);
50
+ }
51
+ else if (htmlPart?.part) {
52
+ const { content: stream } = await client.download(mailId, htmlPart.part, { uid: true });
53
+ const htmlRaw = await streamToString(stream);
54
+ content = convertHtmlToText(htmlRaw);
55
+ }
56
+ else {
57
+ throw new Error('No viewable part (text or html) in mail');
58
+ }
59
+ return {
60
+ id: mailId,
61
+ title: msg.envelope?.subject || '(no subject)',
62
+ content,
63
+ };
64
+ });
65
+ }
66
+ catch (err) {
67
+ return {
68
+ id: mailId,
69
+ title: '',
70
+ content: '',
71
+ error: err instanceof Error ? err.message : String(err),
72
+ };
73
+ }
74
+ }
75
+ export async function markMailAsSeen(account, mailId) {
76
+ let title = '(unknown title)';
77
+ try {
78
+ await withImapClient(account, async (client) => {
79
+ const msg = await client.fetchOne(mailId, { envelope: true }, { uid: true });
80
+ title = (msg && msg.envelope?.subject) || '(no subject)';
81
+ await client.messageFlagsAdd(mailId, ['\\Seen'], { uid: true });
82
+ });
83
+ return { id: mailId, title, success: true };
84
+ }
85
+ catch (error) {
86
+ return {
87
+ id: mailId,
88
+ title,
89
+ success: false,
90
+ error: error instanceof Error ? error.message : String(error),
91
+ };
92
+ }
93
+ }
94
+ //# sourceMappingURL=mail.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.service.js","sourceRoot":"","sources":["../../src/lib/mail.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ export interface AccountCredentials {
2
+ user: string;
3
+ pass: string;
4
+ host: string;
5
+ port: number;
6
+ }
7
+ export interface MailAddress {
8
+ address?: string;
9
+ name?: string;
10
+ }
11
+ export interface Mail {
12
+ uid: string;
13
+ account: string;
14
+ title: string;
15
+ read: boolean;
16
+ from: MailAddress;
17
+ date: string;
18
+ }
19
+ export interface MailAccountResult {
20
+ account: string;
21
+ mails: Mail[];
22
+ error?: string;
23
+ }
24
+ export interface ReadMailResult {
25
+ id: string;
26
+ title: string;
27
+ content: string;
28
+ error?: string;
29
+ }
30
+ export interface MailMarkResult {
31
+ id: string;
32
+ title: string;
33
+ success: boolean;
34
+ error?: string;
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { Mail } from './types.js';
2
+ import type { FetchMessageObject, MessageStructureObject } from 'imapflow';
3
+ export declare function streamToString(stream: NodeJS.ReadableStream): Promise<string>;
4
+ export declare function findTextPart(struct: MessageStructureObject, subtype: 'plain' | 'html'): {
5
+ part: string;
6
+ } | undefined;
7
+ export declare function convertHtmlToText(html: string): string;
8
+ export declare function mapImapMessageToMail(msg: FetchMessageObject, accountIdentifier: string): Mail;
9
+ export declare const getStartOfPeriodUTC: (daysAgo: number) => Date;
10
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAE1E,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAInF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAWpH;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAY7F;AAED,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,IAA4D,CAAA"}
@@ -0,0 +1,40 @@
1
+ import { htmlToText } from 'html-to-text';
2
+ export async function streamToString(stream) {
3
+ const chunks = [];
4
+ for await (const chunk of stream)
5
+ chunks.push(Buffer.from(chunk));
6
+ return Buffer.concat(chunks).toString('utf8');
7
+ }
8
+ export function findTextPart(struct, subtype) {
9
+ if (struct.type?.toLowerCase() === `text/${subtype}` && struct.part) {
10
+ return { part: struct.part };
11
+ }
12
+ if (struct.childNodes) {
13
+ for (const child of struct.childNodes) {
14
+ const found = findTextPart(child, subtype);
15
+ if (found)
16
+ return found;
17
+ }
18
+ }
19
+ return undefined;
20
+ }
21
+ export function convertHtmlToText(html) {
22
+ return htmlToText(html, {
23
+ wordwrap: 130,
24
+ selectors: [{ selector: 'h1', options: { uppercase: false } }],
25
+ });
26
+ }
27
+ export function mapImapMessageToMail(msg, accountIdentifier) {
28
+ const from = msg.envelope?.from?.[0];
29
+ const date = msg.envelope?.date ?? new Date();
30
+ return {
31
+ uid: String(msg.uid),
32
+ account: accountIdentifier,
33
+ title: msg.envelope?.subject || '(no subject)',
34
+ read: msg.flags?.has('\\Seen') ?? false,
35
+ date: date.toISOString().slice(0, 16).replace('T', ' '),
36
+ from: { address: from?.address, name: from?.name },
37
+ };
38
+ }
39
+ export const getStartOfPeriodUTC = (daysAgo) => new Date(Date.now() - daysAgo * 24 * 60 * 60 * 1000);
40
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAKzC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAA6B;IAChE,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM;QAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACjE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAA8B,EAAE,OAAyB;IACpF,IAAI,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,QAAQ,OAAO,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACpE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAA;IAC9B,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;YAC1C,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAA;QACzB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,UAAU,CAAC,IAAI,EAAE;QACtB,QAAQ,EAAE,GAAG;QACb,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;KAC/D,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAuB,EAAE,iBAAyB;IACrF,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IACpC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI,IAAI,EAAE,CAAA;IAE7C,OAAO;QACL,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QACpB,OAAO,EAAE,iBAAiB;QAC1B,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,IAAI,cAAc;QAC9C,IAAI,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK;QACvC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;QACvD,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;KACnD,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAQ,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerFetchLatestMailsTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=fetch-latest-mails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-latest-mails.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-latest-mails.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAcxE,eAAO,MAAM,4BAA4B,GAAI,QAAQ,SAAS,SA4C1D,CAAA"}
@@ -0,0 +1,57 @@
1
+ import { registerTool } from '@mcp-monorepo/shared';
2
+ import { z } from 'zod';
3
+ import { parseMailAccounts } from '../lib/accounts.js';
4
+ import { fetchLatestMails } from '../lib/mail.service.js';
5
+ const mailSchema = z.object({
6
+ uid: z.string(),
7
+ account: z.string(),
8
+ title: z.string(),
9
+ read: z.boolean(),
10
+ from: z.object({
11
+ address: z.string().optional(),
12
+ name: z.string().optional(),
13
+ }),
14
+ date: z.string(),
15
+ });
16
+ export const registerFetchLatestMailsTool = (server) => registerTool(server, {
17
+ name: 'fetch-latest-mails',
18
+ title: 'Fetch Latest Mails',
19
+ description: 'Query all mails from the past two days for all configured accounts. No parameters.',
20
+ inputSchema: {},
21
+ outputSchema: {
22
+ accounts: z.array(z.object({
23
+ account: z.string(),
24
+ mails: z.array(mailSchema),
25
+ error: z.string().optional(),
26
+ })),
27
+ },
28
+ isReadOnly: true,
29
+ async fetcher() {
30
+ const accounts = parseMailAccounts();
31
+ if (!accounts.length) {
32
+ throw new Error('MAIL_ACCOUNTS env variable is not configured correctly');
33
+ }
34
+ const results = [];
35
+ for (const account of accounts) {
36
+ const accountIdentifier = `${account.user}@${account.host}`;
37
+ try {
38
+ const mails = await fetchLatestMails(account);
39
+ results.push({ account: accountIdentifier, mails });
40
+ }
41
+ catch (error) {
42
+ results.push({
43
+ account: accountIdentifier,
44
+ mails: [],
45
+ error: error instanceof Error ? error.message : String(error),
46
+ });
47
+ }
48
+ }
49
+ return results;
50
+ },
51
+ formatter(data) {
52
+ return {
53
+ accounts: data,
54
+ };
55
+ },
56
+ });
57
+ //# sourceMappingURL=fetch-latest-mails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-latest-mails.js","sourceRoot":"","sources":["../../src/tools/fetch-latest-mails.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAKzD,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC5B,CAAC;IACF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,MAAiB,EAAE,EAAE,CAChE,YAAY,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,oBAAoB;IAC1B,KAAK,EAAE,oBAAoB;IAC3B,WAAW,EAAE,oFAAoF;IACjG,WAAW,EAAE,EAAE;IACf,YAAY,EAAE;QACZ,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;YACnB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;YAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC7B,CAAC,CACH;KACF;IACD,UAAU,EAAE,IAAI;IAChB,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAA;QACpC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;QAC3E,CAAC;QAED,MAAM,OAAO,GAAwB,EAAE,CAAA;QAEvC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,iBAAiB,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAA;YAC3D,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAA;gBAC7C,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAA;YACrD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC;oBACX,OAAO,EAAE,iBAAiB;oBAC1B,KAAK,EAAE,EAAE;oBACT,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAC9D,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IACD,SAAS,CAAC,IAAI;QACZ,OAAO;YACL,QAAQ,EAAE,IAAI;SACf,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerMarkMailsAsSeenTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=mark-mails-as-seen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mark-mails-as-seen.d.ts","sourceRoot":"","sources":["../../src/tools/mark-mails-as-seen.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,2BAA2B,GAAI,QAAQ,SAAS,SA8CzD,CAAA"}
@@ -0,0 +1,46 @@
1
+ import { registerTool } from '@mcp-monorepo/shared';
2
+ import { z } from 'zod';
3
+ import { findMailAccount } from '../lib/accounts.js';
4
+ import { markMailAsSeen } from '../lib/mail.service.js';
5
+ export const registerMarkMailsAsSeenTool = (server) => registerTool(server, {
6
+ name: 'mark-mails-as-seen',
7
+ title: 'Mark Mails as Seen',
8
+ description: 'Marks specified emails as seen/read in the INBOX. Requires username, IMAP server, and a list of mail UIDs.',
9
+ inputSchema: {
10
+ username: z.string().describe('Username for IMAP authentication'),
11
+ imapServer: z.string().describe('IMAP server hostname'),
12
+ mailIds: z.array(z.string()).min(1).describe('List of mail UIDs to mark as seen'),
13
+ },
14
+ outputSchema: {
15
+ account: z.string(),
16
+ totalProcessed: z.number(),
17
+ successCount: z.number(),
18
+ failureCount: z.number(),
19
+ results: z.array(z.object({
20
+ id: z.string(),
21
+ title: z.string(),
22
+ success: z.boolean(),
23
+ error: z.string().optional(),
24
+ })),
25
+ },
26
+ isDestructive: true,
27
+ async fetcher({ username, imapServer, mailIds }) {
28
+ const account = findMailAccount({ username, host: imapServer });
29
+ const results = [];
30
+ for (const mailId of mailIds) {
31
+ // Await each call individually to handle per-mail errors
32
+ results.push(await markMailAsSeen(account, mailId));
33
+ }
34
+ return {
35
+ account: `${account.user}@${account.host}`,
36
+ totalProcessed: mailIds.length,
37
+ successCount: results.filter((r) => r.success).length,
38
+ failureCount: results.filter((r) => !r.success).length,
39
+ results,
40
+ };
41
+ },
42
+ formatter(data) {
43
+ return data;
44
+ },
45
+ });
46
+ //# sourceMappingURL=mark-mails-as-seen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mark-mails-as-seen.js","sourceRoot":"","sources":["../../src/tools/mark-mails-as-seen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAKvD,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,MAAiB,EAAE,EAAE,CAC/D,YAAY,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,oBAAoB;IAC1B,KAAK,EAAE,oBAAoB;IAC3B,WAAW,EACT,4GAA4G;IAC9G,WAAW,EAAE;QACX,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QACjE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QACvD,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mCAAmC,CAAC;KAClF;IACD,YAAY,EAAE;QACZ,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;QAC1B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,OAAO,EAAE,CAAC,CAAC,KAAK,CACd,CAAC,CAAC,MAAM,CAAC;YACP,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;YACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;YACjB,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;YACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC7B,CAAC,CACH;KACF;IACD,aAAa,EAAE,IAAI;IACnB,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE;QAC7C,MAAM,OAAO,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAA;QAE/D,MAAM,OAAO,GAAqB,EAAE,CAAA;QACpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,yDAAyD;YACzD,OAAO,CAAC,IAAI,CAAC,MAAM,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAA;QACrD,CAAC;QAED,OAAO;YACL,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE;YAC1C,cAAc,EAAE,OAAO,CAAC,MAAM;YAC9B,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM;YACrD,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM;YACtD,OAAO;SACR,CAAA;IACH,CAAC;IACD,SAAS,CAAC,IAAI;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerReadMailTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=read-mail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-mail.d.ts","sourceRoot":"","sources":["../../src/tools/read-mail.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,oBAAoB,GAAI,QAAQ,SAAS,SAmClD,CAAA"}
@@ -0,0 +1,36 @@
1
+ import { registerTool } from '@mcp-monorepo/shared';
2
+ import { z } from 'zod';
3
+ import { findMailAccount } from '../lib/accounts.js';
4
+ import { readMailContent } from '../lib/mail.service.js';
5
+ export const registerReadMailTool = (server) => registerTool(server, {
6
+ name: 'read-mail',
7
+ title: 'Read Mail Content',
8
+ description: 'Reads the content and subject/title of specified mails. If only HTML is present, converts it to plain text.',
9
+ inputSchema: {
10
+ username: z.string().min(1, 'username required'),
11
+ imapServer: z.string().min(1, 'imapServer required'),
12
+ mailIds: z.array(z.string().min(1)).min(1, 'Provide at least one mail ID'),
13
+ },
14
+ outputSchema: {
15
+ mails: z.array(z.object({
16
+ id: z.string(),
17
+ title: z.string(),
18
+ content: z.string(),
19
+ error: z.string().optional(),
20
+ })),
21
+ },
22
+ isReadOnly: true,
23
+ async fetcher({ username, imapServer, mailIds }) {
24
+ const account = findMailAccount({ username, host: imapServer });
25
+ const results = [];
26
+ for (const mailId of mailIds) {
27
+ // Await each call individually to handle per-mail errors
28
+ results.push(await readMailContent(account, mailId));
29
+ }
30
+ return results;
31
+ },
32
+ formatter(data) {
33
+ return { mails: data };
34
+ },
35
+ });
36
+ //# sourceMappingURL=read-mail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-mail.js","sourceRoot":"","sources":["../../src/tools/read-mail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAKxD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAiB,EAAE,EAAE,CACxD,YAAY,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EACT,6GAA6G;IAC/G,WAAW,EAAE;QACX,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,mBAAmB,CAAC;QAChD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,qBAAqB,CAAC;QACpD,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,8BAA8B,CAAC;KAC3E;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,CAAC,CAAC,KAAK,CACZ,CAAC,CAAC,MAAM,CAAC;YACP,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;YACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;YACjB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;YACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC7B,CAAC,CACH;KACF;IACD,UAAU,EAAE,IAAI;IAChB,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE;QAC7C,MAAM,OAAO,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAA;QAE/D,MAAM,OAAO,GAAqB,EAAE,CAAA;QACpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,yDAAyD;YACzD,OAAO,CAAC,IAAI,CAAC,MAAM,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAA;QACtD,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IACD,SAAS,CAAC,IAAI;QACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACxB,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerSearchMailsTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=search-mails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-mails.d.ts","sourceRoot":"","sources":["../../src/tools/search-mails.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAcxE,eAAO,MAAM,uBAAuB,GAAI,QAAQ,SAAS,SAiDrD,CAAA"}
@@ -0,0 +1,61 @@
1
+ import { registerTool } from '@mcp-monorepo/shared';
2
+ import { z } from 'zod';
3
+ import { parseMailAccounts } from '../lib/accounts.js';
4
+ import { searchMails } from '../lib/mail.service.js';
5
+ const mailSchema = z.object({
6
+ uid: z.string(),
7
+ account: z.string(),
8
+ title: z.string(),
9
+ read: z.boolean(),
10
+ from: z.object({
11
+ address: z.string().optional(),
12
+ name: z.string().optional(),
13
+ }),
14
+ date: z.string(),
15
+ });
16
+ export const registerSearchMailsTool = (server) => registerTool(server, {
17
+ name: 'search-mails',
18
+ title: 'Search Mails',
19
+ description: 'Find mails by subject, body, or sender address across all configured accounts. At least one of searchString or fromContains must be set.',
20
+ inputSchema: {
21
+ searchString: z.string().min(1).optional().describe('Substring to search in subject or body.'),
22
+ searchBody: z.boolean().optional().describe('If true, also search in mail body.'),
23
+ fromContains: z.string().min(1).optional().describe('Substring to search in sender address.'),
24
+ },
25
+ outputSchema: {
26
+ accounts: z.array(z.object({
27
+ account: z.string(),
28
+ mails: z.array(mailSchema),
29
+ error: z.string().optional(),
30
+ })),
31
+ },
32
+ isReadOnly: true,
33
+ async fetcher({ searchString, searchBody, fromContains }) {
34
+ if (!searchString && !fromContains) {
35
+ throw new Error('At least one of searchString or fromContains must be provided.');
36
+ }
37
+ const accounts = parseMailAccounts();
38
+ const results = [];
39
+ for (const account of accounts) {
40
+ const accountIdentifier = `${account.user}@${account.host}`;
41
+ try {
42
+ const mails = await searchMails(account, { searchString, searchBody, fromContains });
43
+ results.push({ account: accountIdentifier, mails });
44
+ }
45
+ catch (error) {
46
+ results.push({
47
+ account: accountIdentifier,
48
+ mails: [],
49
+ error: error instanceof Error ? error.message : String(error),
50
+ });
51
+ }
52
+ }
53
+ return results;
54
+ },
55
+ formatter(data) {
56
+ return {
57
+ accounts: data,
58
+ };
59
+ },
60
+ });
61
+ //# sourceMappingURL=search-mails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-mails.js","sourceRoot":"","sources":["../../src/tools/search-mails.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAKpD,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC5B,CAAC;IACF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,MAAiB,EAAE,EAAE,CAC3D,YAAY,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,cAAc;IACrB,WAAW,EACT,0IAA0I;IAC5I,WAAW,EAAE;QACX,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;QAC9F,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;QACjF,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;KAC9F;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;YACnB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;YAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC7B,CAAC,CACH;KACF;IACD,UAAU,EAAE,IAAI;IAChB,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE;QACtD,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;QACnF,CAAC;QAED,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAA;QACpC,MAAM,OAAO,GAAwB,EAAE,CAAA;QAEvC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,iBAAiB,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAA;YAC3D,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;gBACpF,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAA;YACrD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC;oBACX,OAAO,EAAE,iBAAiB;oBAC1B,KAAK,EAAE,EAAE;oBACT,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAC9D,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IACD,SAAS,CAAC,IAAI;QACZ,OAAO;YACL,QAAQ,EAAE,IAAI;SACf,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@mcp-monorepo/mail",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "MCP server for mail tools (fetch, read, search, mark as seen).",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "mcp-npm-server": "./dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc -b",
19
+ "dev": "tsc --watch",
20
+ "start": "node dist/index.js",
21
+ "test": "vitest run --passWithNoTests",
22
+ "test:watch": "vitest",
23
+ "test:coverage": "vitest run --coverage",
24
+ "typecheck": "tsc --noEmit",
25
+ "clean": "rimraf dist tsconfig.tsbuildinfo"
26
+ },
27
+ "dependencies": {
28
+ "@mcp-monorepo/shared": "*",
29
+ "@modelcontextprotocol/sdk": "^1.0.0",
30
+ "zod": "^3.25.76",
31
+ "imapflow": "^1.0.191",
32
+ "html-to-text": "^9.0.5"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.14.1",
36
+ "typescript": "^5.8.3",
37
+ "vitest": "^3.2.4",
38
+ "rimraf": "^6.0.1",
39
+ "@types/imapflow": "^1.0.23",
40
+ "@types/html-to-text": "^9.0.4"
41
+ }
42
+ }