@n24q02m/better-email-mcp 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.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/bin/cli.mjs +13 -0
- package/build/scripts/start-server.d.ts +6 -0
- package/build/scripts/start-server.d.ts.map +1 -0
- package/build/scripts/start-server.js +21 -0
- package/build/scripts/start-server.js.map +1 -0
- package/build/src/docs/attachments.md +48 -0
- package/build/src/docs/folders.md +45 -0
- package/build/src/docs/messages.md +92 -0
- package/build/src/docs/send.md +52 -0
- package/build/src/init-server.d.ts +40 -0
- package/build/src/init-server.d.ts.map +1 -0
- package/build/src/init-server.js +57 -0
- package/build/src/init-server.js.map +1 -0
- package/build/src/tools/composite/attachments.d.ts +17 -0
- package/build/src/tools/composite/attachments.d.ts.map +1 -0
- package/build/src/tools/composite/attachments.js +77 -0
- package/build/src/tools/composite/attachments.js.map +1 -0
- package/build/src/tools/composite/attachments.test.d.ts +2 -0
- package/build/src/tools/composite/attachments.test.d.ts.map +1 -0
- package/build/src/tools/composite/attachments.test.js +138 -0
- package/build/src/tools/composite/attachments.test.js.map +1 -0
- package/build/src/tools/composite/folders.d.ts +14 -0
- package/build/src/tools/composite/folders.d.ts.map +1 -0
- package/build/src/tools/composite/folders.js +57 -0
- package/build/src/tools/composite/folders.js.map +1 -0
- package/build/src/tools/composite/folders.test.d.ts +2 -0
- package/build/src/tools/composite/folders.test.d.ts.map +1 -0
- package/build/src/tools/composite/folders.test.js +61 -0
- package/build/src/tools/composite/folders.test.js.map +1 -0
- package/build/src/tools/composite/messages.d.ts +20 -0
- package/build/src/tools/composite/messages.d.ts.map +1 -0
- package/build/src/tools/composite/messages.js +244 -0
- package/build/src/tools/composite/messages.js.map +1 -0
- package/build/src/tools/composite/messages.test.d.ts +2 -0
- package/build/src/tools/composite/messages.test.d.ts.map +1 -0
- package/build/src/tools/composite/messages.test.js +217 -0
- package/build/src/tools/composite/messages.test.js.map +1 -0
- package/build/src/tools/composite/send.d.ts +21 -0
- package/build/src/tools/composite/send.d.ts.map +1 -0
- package/build/src/tools/composite/send.js +127 -0
- package/build/src/tools/composite/send.js.map +1 -0
- package/build/src/tools/composite/send.test.d.ts +2 -0
- package/build/src/tools/composite/send.test.d.ts.map +1 -0
- package/build/src/tools/composite/send.test.js +232 -0
- package/build/src/tools/composite/send.test.js.map +1 -0
- package/build/src/tools/helpers/config.d.ts +33 -0
- package/build/src/tools/helpers/config.d.ts.map +1 -0
- package/build/src/tools/helpers/config.js +167 -0
- package/build/src/tools/helpers/config.js.map +1 -0
- package/build/src/tools/helpers/config.test.d.ts +2 -0
- package/build/src/tools/helpers/config.test.d.ts.map +1 -0
- package/build/src/tools/helpers/config.test.js +141 -0
- package/build/src/tools/helpers/config.test.js.map +1 -0
- package/build/src/tools/helpers/errors.d.ts +34 -0
- package/build/src/tools/helpers/errors.d.ts.map +1 -0
- package/build/src/tools/helpers/errors.js +152 -0
- package/build/src/tools/helpers/errors.js.map +1 -0
- package/build/src/tools/helpers/errors.test.d.ts +2 -0
- package/build/src/tools/helpers/errors.test.d.ts.map +1 -0
- package/build/src/tools/helpers/errors.test.js +203 -0
- package/build/src/tools/helpers/errors.test.js.map +1 -0
- package/build/src/tools/helpers/html-utils.d.ts +10 -0
- package/build/src/tools/helpers/html-utils.d.ts.map +1 -0
- package/build/src/tools/helpers/html-utils.js +29 -0
- package/build/src/tools/helpers/html-utils.js.map +1 -0
- package/build/src/tools/helpers/html-utils.test.d.ts +2 -0
- package/build/src/tools/helpers/html-utils.test.d.ts.map +1 -0
- package/build/src/tools/helpers/html-utils.test.js +103 -0
- package/build/src/tools/helpers/html-utils.test.js.map +1 -0
- package/build/src/tools/helpers/imap-client.d.ts +90 -0
- package/build/src/tools/helpers/imap-client.d.ts.map +1 -0
- package/build/src/tools/helpers/imap-client.js +321 -0
- package/build/src/tools/helpers/imap-client.js.map +1 -0
- package/build/src/tools/helpers/imap-client.test.d.ts +2 -0
- package/build/src/tools/helpers/imap-client.test.d.ts.map +1 -0
- package/build/src/tools/helpers/imap-client.test.js +401 -0
- package/build/src/tools/helpers/imap-client.test.js.map +1 -0
- package/build/src/tools/helpers/smtp-client.d.ts +38 -0
- package/build/src/tools/helpers/smtp-client.d.ts.map +1 -0
- package/build/src/tools/helpers/smtp-client.js +124 -0
- package/build/src/tools/helpers/smtp-client.js.map +1 -0
- package/build/src/tools/helpers/smtp-client.test.d.ts +2 -0
- package/build/src/tools/helpers/smtp-client.test.d.ts.map +1 -0
- package/build/src/tools/helpers/smtp-client.test.js +210 -0
- package/build/src/tools/helpers/smtp-client.test.js.map +1 -0
- package/build/src/tools/registry.d.ts +11 -0
- package/build/src/tools/registry.d.ts.map +1 -0
- package/build/src/tools/registry.js +262 -0
- package/build/src/tools/registry.js.map +1 -0
- package/build/src/tools/registry.test.d.ts +2 -0
- package/build/src/tools/registry.test.d.ts.map +1 -0
- package/build/src/tools/registry.test.js +135 -0
- package/build/src/tools/registry.test.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Messages Mega Tool
|
|
3
|
+
* All email message operations in one unified interface
|
|
4
|
+
*/
|
|
5
|
+
import { EmailMCPError, withErrorHandling } from '../helpers/errors.js';
|
|
6
|
+
import { listFolders, modifyFlags, moveEmails, readEmail, searchEmails, trashEmails } from '../helpers/imap-client.js';
|
|
7
|
+
/**
|
|
8
|
+
* Resolve target accounts from input
|
|
9
|
+
*/
|
|
10
|
+
function resolveAccounts(accounts, accountFilter) {
|
|
11
|
+
if (!accountFilter)
|
|
12
|
+
return accounts;
|
|
13
|
+
const lower = accountFilter.toLowerCase();
|
|
14
|
+
const matched = accounts.filter((a) => a.email.toLowerCase() === lower || a.id === lower || a.email.toLowerCase().includes(lower));
|
|
15
|
+
if (matched.length === 0) {
|
|
16
|
+
throw new EmailMCPError(`Account not found: ${accountFilter}`, 'ACCOUNT_NOT_FOUND', `Available accounts: ${accounts.map((a) => a.email).join(', ')}`);
|
|
17
|
+
}
|
|
18
|
+
return matched;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a single account (for operations that require exactly one)
|
|
22
|
+
*/
|
|
23
|
+
function resolveSingleAccount(accounts, accountFilter) {
|
|
24
|
+
const resolved = resolveAccounts(accounts, accountFilter);
|
|
25
|
+
if (resolved.length > 1) {
|
|
26
|
+
throw new EmailMCPError('Multiple accounts matched. Specify the exact account email.', 'AMBIGUOUS_ACCOUNT', `Matched: ${resolved.map((a) => a.email).join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
return resolved[0];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Unified messages tool - handles all message operations
|
|
32
|
+
*/
|
|
33
|
+
export async function messages(accounts, input) {
|
|
34
|
+
return withErrorHandling(async () => {
|
|
35
|
+
switch (input.action) {
|
|
36
|
+
case 'search':
|
|
37
|
+
return await handleSearch(accounts, input);
|
|
38
|
+
case 'read':
|
|
39
|
+
return await handleRead(accounts, input);
|
|
40
|
+
case 'mark_read':
|
|
41
|
+
return await handleMarkRead(accounts, input);
|
|
42
|
+
case 'mark_unread':
|
|
43
|
+
return await handleMarkUnread(accounts, input);
|
|
44
|
+
case 'flag':
|
|
45
|
+
return await handleFlag(accounts, input);
|
|
46
|
+
case 'unflag':
|
|
47
|
+
return await handleUnflag(accounts, input);
|
|
48
|
+
case 'move':
|
|
49
|
+
return await handleMove(accounts, input);
|
|
50
|
+
case 'archive':
|
|
51
|
+
return await handleArchive(accounts, input);
|
|
52
|
+
case 'trash':
|
|
53
|
+
return await handleTrash(accounts, input);
|
|
54
|
+
default:
|
|
55
|
+
throw new EmailMCPError(`Unknown action: ${input.action}`, 'VALIDATION_ERROR', 'Supported actions: search, read, mark_read, mark_unread, flag, unflag, move, archive, trash');
|
|
56
|
+
}
|
|
57
|
+
})();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Search emails across accounts
|
|
61
|
+
*/
|
|
62
|
+
async function handleSearch(accounts, input) {
|
|
63
|
+
const targetAccounts = resolveAccounts(accounts, input.account);
|
|
64
|
+
const query = input.query || 'UNSEEN';
|
|
65
|
+
const folder = input.folder || 'INBOX';
|
|
66
|
+
const limit = input.limit || 20;
|
|
67
|
+
const results = await searchEmails(targetAccounts, query, folder, limit);
|
|
68
|
+
return {
|
|
69
|
+
action: 'search',
|
|
70
|
+
query,
|
|
71
|
+
folder,
|
|
72
|
+
total: results.length,
|
|
73
|
+
accounts_searched: targetAccounts.map((a) => a.email),
|
|
74
|
+
messages: results
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Read a single email by UID
|
|
79
|
+
*/
|
|
80
|
+
async function handleRead(accounts, input) {
|
|
81
|
+
if (!input.uid) {
|
|
82
|
+
throw new EmailMCPError('uid is required for read action', 'VALIDATION_ERROR', 'Provide the email UID from search');
|
|
83
|
+
}
|
|
84
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
85
|
+
const folder = input.folder || 'INBOX';
|
|
86
|
+
const email = await readEmail(account, input.uid, folder);
|
|
87
|
+
return {
|
|
88
|
+
action: 'read',
|
|
89
|
+
...email
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Mark emails as read
|
|
94
|
+
*/
|
|
95
|
+
async function handleMarkRead(accounts, input) {
|
|
96
|
+
const uids = input.uids || (input.uid ? [input.uid] : []);
|
|
97
|
+
if (uids.length === 0) {
|
|
98
|
+
throw new EmailMCPError('uid or uids required', 'VALIDATION_ERROR', 'Provide at least one email UID');
|
|
99
|
+
}
|
|
100
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
101
|
+
const folder = input.folder || 'INBOX';
|
|
102
|
+
const result = await modifyFlags(account, uids, folder, ['\\Seen'], 'add');
|
|
103
|
+
return {
|
|
104
|
+
action: 'mark_read',
|
|
105
|
+
account: account.email,
|
|
106
|
+
folder,
|
|
107
|
+
...result
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Mark emails as unread
|
|
112
|
+
*/
|
|
113
|
+
async function handleMarkUnread(accounts, input) {
|
|
114
|
+
const uids = input.uids || (input.uid ? [input.uid] : []);
|
|
115
|
+
if (uids.length === 0) {
|
|
116
|
+
throw new EmailMCPError('uid or uids required', 'VALIDATION_ERROR', 'Provide at least one email UID');
|
|
117
|
+
}
|
|
118
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
119
|
+
const folder = input.folder || 'INBOX';
|
|
120
|
+
const result = await modifyFlags(account, uids, folder, ['\\Seen'], 'remove');
|
|
121
|
+
return {
|
|
122
|
+
action: 'mark_unread',
|
|
123
|
+
account: account.email,
|
|
124
|
+
folder,
|
|
125
|
+
...result
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Flag (star) emails
|
|
130
|
+
*/
|
|
131
|
+
async function handleFlag(accounts, input) {
|
|
132
|
+
const uids = input.uids || (input.uid ? [input.uid] : []);
|
|
133
|
+
if (uids.length === 0) {
|
|
134
|
+
throw new EmailMCPError('uid or uids required', 'VALIDATION_ERROR', 'Provide at least one email UID');
|
|
135
|
+
}
|
|
136
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
137
|
+
const folder = input.folder || 'INBOX';
|
|
138
|
+
const result = await modifyFlags(account, uids, folder, ['\\Flagged'], 'add');
|
|
139
|
+
return {
|
|
140
|
+
action: 'flag',
|
|
141
|
+
account: account.email,
|
|
142
|
+
folder,
|
|
143
|
+
...result
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Unflag (unstar) emails
|
|
148
|
+
*/
|
|
149
|
+
async function handleUnflag(accounts, input) {
|
|
150
|
+
const uids = input.uids || (input.uid ? [input.uid] : []);
|
|
151
|
+
if (uids.length === 0) {
|
|
152
|
+
throw new EmailMCPError('uid or uids required', 'VALIDATION_ERROR', 'Provide at least one email UID');
|
|
153
|
+
}
|
|
154
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
155
|
+
const folder = input.folder || 'INBOX';
|
|
156
|
+
const result = await modifyFlags(account, uids, folder, ['\\Flagged'], 'remove');
|
|
157
|
+
return {
|
|
158
|
+
action: 'unflag',
|
|
159
|
+
account: account.email,
|
|
160
|
+
folder,
|
|
161
|
+
...result
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Move emails to another folder
|
|
166
|
+
*/
|
|
167
|
+
async function handleMove(accounts, input) {
|
|
168
|
+
const uids = input.uids || (input.uid ? [input.uid] : []);
|
|
169
|
+
if (uids.length === 0) {
|
|
170
|
+
throw new EmailMCPError('uid or uids required', 'VALIDATION_ERROR', 'Provide at least one email UID');
|
|
171
|
+
}
|
|
172
|
+
if (!input.destination) {
|
|
173
|
+
throw new EmailMCPError('destination is required for move action', 'VALIDATION_ERROR', 'Provide the target folder name. Use folders tool to list available folders.');
|
|
174
|
+
}
|
|
175
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
176
|
+
const folder = input.folder || 'INBOX';
|
|
177
|
+
const result = await moveEmails(account, uids, folder, input.destination);
|
|
178
|
+
return {
|
|
179
|
+
action: 'move',
|
|
180
|
+
account: account.email,
|
|
181
|
+
from_folder: folder,
|
|
182
|
+
to_folder: input.destination,
|
|
183
|
+
...result
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Archive emails (move to archive folder)
|
|
188
|
+
*/
|
|
189
|
+
async function handleArchive(accounts, input) {
|
|
190
|
+
const uids = input.uids || (input.uid ? [input.uid] : []);
|
|
191
|
+
if (uids.length === 0) {
|
|
192
|
+
throw new EmailMCPError('uid or uids required', 'VALIDATION_ERROR', 'Provide at least one email UID');
|
|
193
|
+
}
|
|
194
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
195
|
+
const folder = input.folder || 'INBOX';
|
|
196
|
+
// Detect archive folder based on provider
|
|
197
|
+
let archiveFolder = '[Gmail]/All Mail';
|
|
198
|
+
if (account.imap.host.includes('office365') || account.imap.host.includes('outlook')) {
|
|
199
|
+
archiveFolder = 'Archive';
|
|
200
|
+
}
|
|
201
|
+
else if (account.imap.host.includes('yahoo')) {
|
|
202
|
+
archiveFolder = 'Archive';
|
|
203
|
+
}
|
|
204
|
+
// Try to find actual archive folder
|
|
205
|
+
try {
|
|
206
|
+
const folders = await listFolders(account);
|
|
207
|
+
const found = folders.find((f) => f.path.toLowerCase().includes('archive') ||
|
|
208
|
+
f.path.toLowerCase().includes('all mail') ||
|
|
209
|
+
f.flags.some((flag) => flag.toLowerCase().includes('archive') || flag.toLowerCase().includes('all')));
|
|
210
|
+
if (found) {
|
|
211
|
+
archiveFolder = found.path;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Use default if folder listing fails
|
|
216
|
+
}
|
|
217
|
+
const result = await moveEmails(account, uids, folder, archiveFolder);
|
|
218
|
+
return {
|
|
219
|
+
action: 'archive',
|
|
220
|
+
account: account.email,
|
|
221
|
+
from_folder: folder,
|
|
222
|
+
archive_folder: archiveFolder,
|
|
223
|
+
...result
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Trash emails
|
|
228
|
+
*/
|
|
229
|
+
async function handleTrash(accounts, input) {
|
|
230
|
+
const uids = input.uids || (input.uid ? [input.uid] : []);
|
|
231
|
+
if (uids.length === 0) {
|
|
232
|
+
throw new EmailMCPError('uid or uids required', 'VALIDATION_ERROR', 'Provide at least one email UID');
|
|
233
|
+
}
|
|
234
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
235
|
+
const folder = input.folder || 'INBOX';
|
|
236
|
+
const result = await trashEmails(account, uids, folder);
|
|
237
|
+
return {
|
|
238
|
+
action: 'trash',
|
|
239
|
+
account: account.email,
|
|
240
|
+
folder,
|
|
241
|
+
...result
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../../src/tools/composite/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.test.d.ts","sourceRoot":"","sources":["../../../../src/tools/composite/messages.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
// --- Mocks ---
|
|
3
|
+
vi.mock('../helpers/imap-client.js', () => ({
|
|
4
|
+
searchEmails: vi.fn(),
|
|
5
|
+
readEmail: vi.fn(),
|
|
6
|
+
modifyFlags: vi.fn(),
|
|
7
|
+
moveEmails: vi.fn(),
|
|
8
|
+
trashEmails: vi.fn(),
|
|
9
|
+
listFolders: vi.fn()
|
|
10
|
+
}));
|
|
11
|
+
import { listFolders, modifyFlags, moveEmails, readEmail, searchEmails, trashEmails } from '../helpers/imap-client.js';
|
|
12
|
+
import { messages } from './messages.js';
|
|
13
|
+
const mockSearchEmails = vi.mocked(searchEmails);
|
|
14
|
+
const mockReadEmail = vi.mocked(readEmail);
|
|
15
|
+
const mockModifyFlags = vi.mocked(modifyFlags);
|
|
16
|
+
const mockMoveEmails = vi.mocked(moveEmails);
|
|
17
|
+
const mockTrashEmails = vi.mocked(trashEmails);
|
|
18
|
+
const mockListFolders = vi.mocked(listFolders);
|
|
19
|
+
const accounts = [
|
|
20
|
+
{
|
|
21
|
+
id: 'user1_gmail_com',
|
|
22
|
+
email: 'user1@gmail.com',
|
|
23
|
+
password: 'pass1',
|
|
24
|
+
imap: { host: 'imap.gmail.com', port: 993, secure: true },
|
|
25
|
+
smtp: { host: 'smtp.gmail.com', port: 465, secure: true }
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'user2_outlook_com',
|
|
29
|
+
email: 'user2@outlook.com',
|
|
30
|
+
password: 'pass2',
|
|
31
|
+
imap: { host: 'outlook.office365.com', port: 993, secure: true },
|
|
32
|
+
smtp: { host: 'smtp.office365.com', port: 587, secure: false }
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
vi.clearAllMocks();
|
|
37
|
+
});
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// search
|
|
40
|
+
// ============================================================================
|
|
41
|
+
describe('messages - search', () => {
|
|
42
|
+
it('searches all accounts by default', async () => {
|
|
43
|
+
mockSearchEmails.mockResolvedValue([]);
|
|
44
|
+
const result = await messages(accounts, { action: 'search' });
|
|
45
|
+
expect(result.action).toBe('search');
|
|
46
|
+
expect(result.query).toBe('UNSEEN');
|
|
47
|
+
expect(result.folder).toBe('INBOX');
|
|
48
|
+
expect(result.accounts_searched).toHaveLength(2);
|
|
49
|
+
expect(mockSearchEmails).toHaveBeenCalledWith(accounts, 'UNSEEN', 'INBOX', 20);
|
|
50
|
+
});
|
|
51
|
+
it('filters by account when specified', async () => {
|
|
52
|
+
mockSearchEmails.mockResolvedValue([]);
|
|
53
|
+
await messages(accounts, { action: 'search', account: 'user1@gmail.com' });
|
|
54
|
+
expect(mockSearchEmails).toHaveBeenCalledWith([accounts[0]], expect.any(String), expect.any(String), expect.any(Number));
|
|
55
|
+
});
|
|
56
|
+
it('uses custom query, folder, and limit', async () => {
|
|
57
|
+
mockSearchEmails.mockResolvedValue([]);
|
|
58
|
+
await messages(accounts, { action: 'search', query: 'FLAGGED', folder: 'Sent', limit: 5 });
|
|
59
|
+
expect(mockSearchEmails).toHaveBeenCalledWith(accounts, 'FLAGGED', 'Sent', 5);
|
|
60
|
+
});
|
|
61
|
+
it('throws when account not found', async () => {
|
|
62
|
+
await expect(messages(accounts, { action: 'search', account: 'nonexistent@test.com' })).rejects.toThrow('Account not found');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// read
|
|
67
|
+
// ============================================================================
|
|
68
|
+
describe('messages - read', () => {
|
|
69
|
+
it('reads email by UID', async () => {
|
|
70
|
+
mockReadEmail.mockResolvedValue({
|
|
71
|
+
account_id: 'user1_gmail_com',
|
|
72
|
+
account_email: 'user1@gmail.com',
|
|
73
|
+
uid: 42,
|
|
74
|
+
subject: 'Test',
|
|
75
|
+
from: 'sender@test.com',
|
|
76
|
+
to: 'user1@gmail.com',
|
|
77
|
+
date: '2025-01-01',
|
|
78
|
+
flags: ['\\Seen'],
|
|
79
|
+
body_text: 'Hello',
|
|
80
|
+
attachments: []
|
|
81
|
+
});
|
|
82
|
+
const result = await messages(accounts, { action: 'read', uid: 42, account: 'user1@gmail.com' });
|
|
83
|
+
expect(result.action).toBe('read');
|
|
84
|
+
expect(result.uid).toBe(42);
|
|
85
|
+
expect(result.subject).toBe('Test');
|
|
86
|
+
});
|
|
87
|
+
it('throws when uid is missing', async () => {
|
|
88
|
+
await expect(messages(accounts, { action: 'read', account: 'user1@gmail.com' })).rejects.toThrow('uid is required');
|
|
89
|
+
});
|
|
90
|
+
it('throws when multiple accounts match', async () => {
|
|
91
|
+
await expect(messages(accounts, { action: 'read', uid: 1, account: '.com' })).rejects.toThrow('Multiple accounts matched');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// mark_read / mark_unread
|
|
96
|
+
// ============================================================================
|
|
97
|
+
describe('messages - mark_read', () => {
|
|
98
|
+
it('adds \\Seen flag', async () => {
|
|
99
|
+
mockModifyFlags.mockResolvedValue({ success: true, modified: 1 });
|
|
100
|
+
const result = await messages(accounts, { action: 'mark_read', uid: 5, account: 'user1@gmail.com' });
|
|
101
|
+
expect(result.action).toBe('mark_read');
|
|
102
|
+
expect(mockModifyFlags).toHaveBeenCalledWith(accounts[0], [5], 'INBOX', ['\\Seen'], 'add');
|
|
103
|
+
});
|
|
104
|
+
it('supports batch uids', async () => {
|
|
105
|
+
mockModifyFlags.mockResolvedValue({ success: true, modified: 3 });
|
|
106
|
+
await messages(accounts, { action: 'mark_read', uids: [1, 2, 3], account: 'user1@gmail.com' });
|
|
107
|
+
expect(mockModifyFlags).toHaveBeenCalledWith(accounts[0], [1, 2, 3], 'INBOX', ['\\Seen'], 'add');
|
|
108
|
+
});
|
|
109
|
+
it('throws when no uids provided', async () => {
|
|
110
|
+
await expect(messages(accounts, { action: 'mark_read', account: 'user1@gmail.com' })).rejects.toThrow('uid or uids required');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('messages - mark_unread', () => {
|
|
114
|
+
it('removes \\Seen flag', async () => {
|
|
115
|
+
mockModifyFlags.mockResolvedValue({ success: true, modified: 1 });
|
|
116
|
+
await messages(accounts, { action: 'mark_unread', uid: 5, account: 'user1@gmail.com' });
|
|
117
|
+
expect(mockModifyFlags).toHaveBeenCalledWith(accounts[0], [5], 'INBOX', ['\\Seen'], 'remove');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
// ============================================================================
|
|
121
|
+
// flag / unflag
|
|
122
|
+
// ============================================================================
|
|
123
|
+
describe('messages - flag', () => {
|
|
124
|
+
it('adds \\Flagged flag', async () => {
|
|
125
|
+
mockModifyFlags.mockResolvedValue({ success: true, modified: 1 });
|
|
126
|
+
await messages(accounts, { action: 'flag', uid: 10, account: 'user1@gmail.com' });
|
|
127
|
+
expect(mockModifyFlags).toHaveBeenCalledWith(accounts[0], [10], 'INBOX', ['\\Flagged'], 'add');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe('messages - unflag', () => {
|
|
131
|
+
it('removes \\Flagged flag', async () => {
|
|
132
|
+
mockModifyFlags.mockResolvedValue({ success: true, modified: 1 });
|
|
133
|
+
await messages(accounts, { action: 'unflag', uid: 10, account: 'user1@gmail.com' });
|
|
134
|
+
expect(mockModifyFlags).toHaveBeenCalledWith(accounts[0], [10], 'INBOX', ['\\Flagged'], 'remove');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// move
|
|
139
|
+
// ============================================================================
|
|
140
|
+
describe('messages - move', () => {
|
|
141
|
+
it('moves emails to destination folder', async () => {
|
|
142
|
+
mockMoveEmails.mockResolvedValue({ success: true, moved: 2 });
|
|
143
|
+
const result = await messages(accounts, {
|
|
144
|
+
action: 'move',
|
|
145
|
+
uids: [1, 2],
|
|
146
|
+
destination: 'Archive',
|
|
147
|
+
account: 'user1@gmail.com'
|
|
148
|
+
});
|
|
149
|
+
expect(result.action).toBe('move');
|
|
150
|
+
expect(result.to_folder).toBe('Archive');
|
|
151
|
+
expect(mockMoveEmails).toHaveBeenCalledWith(accounts[0], [1, 2], 'INBOX', 'Archive');
|
|
152
|
+
});
|
|
153
|
+
it('throws when destination is missing', async () => {
|
|
154
|
+
await expect(messages(accounts, { action: 'move', uid: 1, account: 'user1@gmail.com' })).rejects.toThrow('destination is required');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// archive
|
|
159
|
+
// ============================================================================
|
|
160
|
+
describe('messages - archive', () => {
|
|
161
|
+
it('moves to Gmail archive folder', async () => {
|
|
162
|
+
mockListFolders.mockResolvedValue([
|
|
163
|
+
{ name: 'All Mail', path: '[Gmail]/All Mail', flags: ['\\All'], delimiter: '/' }
|
|
164
|
+
]);
|
|
165
|
+
mockMoveEmails.mockResolvedValue({ success: true, moved: 1 });
|
|
166
|
+
const result = await messages(accounts, { action: 'archive', uid: 1, account: 'user1@gmail.com' });
|
|
167
|
+
expect(result.action).toBe('archive');
|
|
168
|
+
expect(result.archive_folder).toBe('[Gmail]/All Mail');
|
|
169
|
+
});
|
|
170
|
+
it('falls back to default archive folder if listing fails', async () => {
|
|
171
|
+
mockListFolders.mockRejectedValue(new Error('fail'));
|
|
172
|
+
mockMoveEmails.mockResolvedValue({ success: true, moved: 1 });
|
|
173
|
+
const result = await messages(accounts, { action: 'archive', uid: 1, account: 'user1@gmail.com' });
|
|
174
|
+
expect(result.archive_folder).toBe('[Gmail]/All Mail');
|
|
175
|
+
});
|
|
176
|
+
it('uses Archive for Outlook accounts', async () => {
|
|
177
|
+
mockListFolders.mockResolvedValue([{ name: 'Archive', path: 'Archive', flags: ['\\Archive'], delimiter: '/' }]);
|
|
178
|
+
mockMoveEmails.mockResolvedValue({ success: true, moved: 1 });
|
|
179
|
+
const result = await messages(accounts, { action: 'archive', uid: 1, account: 'user2@outlook.com' });
|
|
180
|
+
expect(result.archive_folder).toBe('Archive');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// trash
|
|
185
|
+
// ============================================================================
|
|
186
|
+
describe('messages - trash', () => {
|
|
187
|
+
it('trashes emails', async () => {
|
|
188
|
+
mockTrashEmails.mockResolvedValue({ success: true, trashed: 1 });
|
|
189
|
+
const result = await messages(accounts, { action: 'trash', uid: 99, account: 'user1@gmail.com' });
|
|
190
|
+
expect(result.action).toBe('trash');
|
|
191
|
+
expect(mockTrashEmails).toHaveBeenCalledWith(accounts[0], [99], 'INBOX');
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// unknown action
|
|
196
|
+
// ============================================================================
|
|
197
|
+
describe('messages - unknown action', () => {
|
|
198
|
+
it('throws for unknown action', async () => {
|
|
199
|
+
await expect(messages(accounts, { action: 'unknown_action' })).rejects.toThrow();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// account resolution
|
|
204
|
+
// ============================================================================
|
|
205
|
+
describe('account resolution', () => {
|
|
206
|
+
it('matches by partial email', async () => {
|
|
207
|
+
mockSearchEmails.mockResolvedValue([]);
|
|
208
|
+
await messages(accounts, { action: 'search', account: 'gmail' });
|
|
209
|
+
expect(mockSearchEmails).toHaveBeenCalledWith([accounts[0]], expect.any(String), expect.any(String), expect.any(Number));
|
|
210
|
+
});
|
|
211
|
+
it('matches by account id', async () => {
|
|
212
|
+
mockSearchEmails.mockResolvedValue([]);
|
|
213
|
+
await messages(accounts, { action: 'search', account: 'user1_gmail_com' });
|
|
214
|
+
expect(mockSearchEmails).toHaveBeenCalledWith([accounts[0]], expect.any(String), expect.any(String), expect.any(Number));
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
//# sourceMappingURL=messages.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.test.js","sourceRoot":"","sources":["../../../../src/tools/composite/messages.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send Mega Tool
|
|
3
|
+
* Send new emails, reply, and forward via SMTP
|
|
4
|
+
*/
|
|
5
|
+
import type { AccountConfig } from '../helpers/config.js';
|
|
6
|
+
export interface SendInput {
|
|
7
|
+
action: 'new' | 'reply' | 'forward';
|
|
8
|
+
account: string;
|
|
9
|
+
to: string;
|
|
10
|
+
subject: string;
|
|
11
|
+
body: string;
|
|
12
|
+
cc?: string;
|
|
13
|
+
bcc?: string;
|
|
14
|
+
uid?: number;
|
|
15
|
+
folder?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Unified send tool - handles all outbound email operations
|
|
19
|
+
*/
|
|
20
|
+
export declare function send(accounts: AccountConfig[], input: SendInput): Promise<any>;
|
|
21
|
+
//# sourceMappingURL=send.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../../../src/tools/composite/send.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAKzD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,SAAS,CAAA;IAGnC,OAAO,EAAE,MAAM,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IAGZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IAGZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA8BD;;GAEG;AACH,wBAAsB,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAoCpF"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send Mega Tool
|
|
3
|
+
* Send new emails, reply, and forward via SMTP
|
|
4
|
+
*/
|
|
5
|
+
import { EmailMCPError, withErrorHandling } from '../helpers/errors.js';
|
|
6
|
+
import { readEmail } from '../helpers/imap-client.js';
|
|
7
|
+
import { forwardEmail, replyToEmail, sendNewEmail } from '../helpers/smtp-client.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a single account by filter
|
|
10
|
+
*/
|
|
11
|
+
function resolveSingleAccount(accounts, accountFilter) {
|
|
12
|
+
const lower = accountFilter.toLowerCase();
|
|
13
|
+
const matched = accounts.filter((a) => a.email.toLowerCase() === lower || a.id === lower || a.email.toLowerCase().includes(lower));
|
|
14
|
+
if (matched.length === 0) {
|
|
15
|
+
throw new EmailMCPError(`Account not found: ${accountFilter}`, 'ACCOUNT_NOT_FOUND', `Available accounts: ${accounts.map((a) => a.email).join(', ')}`);
|
|
16
|
+
}
|
|
17
|
+
if (matched.length > 1) {
|
|
18
|
+
throw new EmailMCPError('Multiple accounts matched. Specify the exact account email.', 'AMBIGUOUS_ACCOUNT', `Matched: ${matched.map((a) => a.email).join(', ')}`);
|
|
19
|
+
}
|
|
20
|
+
return matched[0];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Unified send tool - handles all outbound email operations
|
|
24
|
+
*/
|
|
25
|
+
export async function send(accounts, input) {
|
|
26
|
+
return withErrorHandling(async () => {
|
|
27
|
+
if (!input.account) {
|
|
28
|
+
throw new EmailMCPError('account is required for send operations', 'VALIDATION_ERROR', 'Provide the sender account email address');
|
|
29
|
+
}
|
|
30
|
+
if (!input.to) {
|
|
31
|
+
throw new EmailMCPError('to is required', 'VALIDATION_ERROR', 'Provide recipient email address');
|
|
32
|
+
}
|
|
33
|
+
if (!input.body) {
|
|
34
|
+
throw new EmailMCPError('body is required', 'VALIDATION_ERROR', 'Provide the email body text');
|
|
35
|
+
}
|
|
36
|
+
switch (input.action) {
|
|
37
|
+
case 'new':
|
|
38
|
+
return await handleNew(accounts, input);
|
|
39
|
+
case 'reply':
|
|
40
|
+
return await handleReply(accounts, input);
|
|
41
|
+
case 'forward':
|
|
42
|
+
return await handleForward(accounts, input);
|
|
43
|
+
default:
|
|
44
|
+
throw new EmailMCPError(`Unknown action: ${input.action}`, 'VALIDATION_ERROR', 'Supported actions: new, reply, forward');
|
|
45
|
+
}
|
|
46
|
+
})();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Send a new email
|
|
50
|
+
*/
|
|
51
|
+
async function handleNew(accounts, input) {
|
|
52
|
+
if (!input.subject) {
|
|
53
|
+
throw new EmailMCPError('subject is required for new email', 'VALIDATION_ERROR', 'Provide the email subject');
|
|
54
|
+
}
|
|
55
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
56
|
+
const result = await sendNewEmail(account, {
|
|
57
|
+
to: input.to,
|
|
58
|
+
subject: input.subject,
|
|
59
|
+
body: input.body,
|
|
60
|
+
cc: input.cc,
|
|
61
|
+
bcc: input.bcc
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
action: 'new',
|
|
65
|
+
from: account.email,
|
|
66
|
+
to: input.to,
|
|
67
|
+
subject: input.subject,
|
|
68
|
+
...result
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Reply to an email (maintains thread headers)
|
|
73
|
+
*/
|
|
74
|
+
async function handleReply(accounts, input) {
|
|
75
|
+
if (!input.uid) {
|
|
76
|
+
throw new EmailMCPError('uid is required for reply action', 'VALIDATION_ERROR', 'Provide the UID of the email to reply to (from search/read)');
|
|
77
|
+
}
|
|
78
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
79
|
+
const folder = input.folder || 'INBOX';
|
|
80
|
+
// Read original email to get threading headers
|
|
81
|
+
const original = await readEmail(account, input.uid, folder);
|
|
82
|
+
const result = await replyToEmail(account, {
|
|
83
|
+
to: input.to,
|
|
84
|
+
subject: input.subject || original.subject,
|
|
85
|
+
body: input.body,
|
|
86
|
+
cc: input.cc,
|
|
87
|
+
bcc: input.bcc,
|
|
88
|
+
in_reply_to: original.message_id,
|
|
89
|
+
references: original.references || original.message_id
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
action: 'reply',
|
|
93
|
+
from: account.email,
|
|
94
|
+
to: input.to,
|
|
95
|
+
subject: input.subject || `Re: ${original.subject}`,
|
|
96
|
+
in_reply_to: original.message_id,
|
|
97
|
+
...result
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Forward an email
|
|
102
|
+
*/
|
|
103
|
+
async function handleForward(accounts, input) {
|
|
104
|
+
if (!input.uid) {
|
|
105
|
+
throw new EmailMCPError('uid is required for forward action', 'VALIDATION_ERROR', 'Provide the UID of the email to forward (from search/read)');
|
|
106
|
+
}
|
|
107
|
+
const account = resolveSingleAccount(accounts, input.account);
|
|
108
|
+
const folder = input.folder || 'INBOX';
|
|
109
|
+
// Read original email to include in forward
|
|
110
|
+
const original = await readEmail(account, input.uid, folder);
|
|
111
|
+
const result = await forwardEmail(account, {
|
|
112
|
+
to: input.to,
|
|
113
|
+
subject: input.subject || original.subject,
|
|
114
|
+
body: input.body,
|
|
115
|
+
cc: input.cc,
|
|
116
|
+
bcc: input.bcc,
|
|
117
|
+
original_body: original.body_text
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
action: 'forward',
|
|
121
|
+
from: account.email,
|
|
122
|
+
to: input.to,
|
|
123
|
+
subject: input.subject || `Fwd: ${original.subject}`,
|
|
124
|
+
...result
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=send.js.map
|