@ni-c/imap-mcp 0.2.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 +308 -0
- package/dist/analyze.d.ts +129 -0
- package/dist/analyze.js +313 -0
- package/dist/analyze.js.map +1 -0
- package/dist/approval.d.ts +45 -0
- package/dist/approval.js +69 -0
- package/dist/approval.js.map +1 -0
- package/dist/attachments.d.ts +55 -0
- package/dist/attachments.js +270 -0
- package/dist/attachments.js.map +1 -0
- package/dist/audit.d.ts +17 -0
- package/dist/audit.js +33 -0
- package/dist/audit.js.map +1 -0
- package/dist/config.d.ts +75 -0
- package/dist/config.js +202 -0
- package/dist/config.js.map +1 -0
- package/dist/confirm.d.ts +59 -0
- package/dist/confirm.js +92 -0
- package/dist/confirm.js.map +1 -0
- package/dist/download.d.ts +23 -0
- package/dist/download.js +65 -0
- package/dist/download.js.map +1 -0
- package/dist/draft.d.ts +34 -0
- package/dist/draft.js +119 -0
- package/dist/draft.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/imap.d.ts +161 -0
- package/dist/imap.js +300 -0
- package/dist/imap.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/message.d.ts +51 -0
- package/dist/message.js +155 -0
- package/dist/message.js.map +1 -0
- package/dist/resources.d.ts +16 -0
- package/dist/resources.js +89 -0
- package/dist/resources.js.map +1 -0
- package/dist/result.d.ts +57 -0
- package/dist/result.js +193 -0
- package/dist/result.js.map +1 -0
- package/dist/schema.d.ts +42 -0
- package/dist/schema.js +99 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +64 -0
- package/dist/server.js.map +1 -0
- package/dist/stream.d.ts +9 -0
- package/dist/stream.js +25 -0
- package/dist/stream.js.map +1 -0
- package/dist/tool-filter.d.ts +45 -0
- package/dist/tool-filter.js +171 -0
- package/dist/tool-filter.js.map +1 -0
- package/dist/tools/catalogue.d.ts +46 -0
- package/dist/tools/catalogue.js +67 -0
- package/dist/tools/catalogue.js.map +1 -0
- package/dist/tools/read.d.ts +4 -0
- package/dist/tools/read.js +576 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +5 -0
- package/dist/tools/write.js +291 -0
- package/dist/tools/write.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { requestApproval } from '../approval.js';
|
|
3
|
+
import { audit } from '../audit.js';
|
|
4
|
+
import { confirmationPrompt, setResourceKey, } from '../confirm.js';
|
|
5
|
+
import { ToolInputError } from '../errors.js';
|
|
6
|
+
import { withTimeout } from '../imap.js';
|
|
7
|
+
import { buildDraft } from '../draft.js';
|
|
8
|
+
import { jsonResult, run, textResult } from '../result.js';
|
|
9
|
+
import { addressListParam, confirmTokenParam, flagListParam, mailboxParam, optionalMailboxParam, uidListParam, uidParam, } from '../schema.js';
|
|
10
|
+
export function registerWriteTools(server, client, config, confirmations) {
|
|
11
|
+
server.registerTool('set_message_flags', {
|
|
12
|
+
title: 'Add or remove message flags',
|
|
13
|
+
description: 'Adds or removes IMAP flags and keywords on one or more messages: ' +
|
|
14
|
+
'\\Seen to mark read or unread, \\Flagged to star, \\Answered, \\Draft, ' +
|
|
15
|
+
'or any custom keyword the server accepts. Reversible, so no ' +
|
|
16
|
+
'confirmation is required — which is why \\Deleted cannot be added ' +
|
|
17
|
+
'here; use delete_messages, which asks first. Removing \\Deleted is ' +
|
|
18
|
+
'allowed, since that undoes one. Removing the new-mail keyword makes ' +
|
|
19
|
+
'those messages show up in list_new_messages again.',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
uids: uidListParam,
|
|
22
|
+
mailbox: optionalMailboxParam,
|
|
23
|
+
add: flagListParam.optional().describe('Flags or keywords to set.'),
|
|
24
|
+
remove: flagListParam
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Flags or keywords to clear.'),
|
|
27
|
+
},
|
|
28
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
29
|
+
}, async ({ uids, mailbox, add, remove }) => run(async () => {
|
|
30
|
+
if (add === undefined && remove === undefined) {
|
|
31
|
+
throw new ToolInputError('imap-mcp: give at least one of "add" or "remove".');
|
|
32
|
+
}
|
|
33
|
+
// \Deleted is not a flag like the others: it is half of a deletion.
|
|
34
|
+
// This tool has no confirmation and is annotated destructiveHint:false
|
|
35
|
+
// on the grounds that everything it does can be undone — true of \Seen
|
|
36
|
+
// and \Flagged, not of a flag that the next client to close the folder,
|
|
37
|
+
// or any server with autoexpunge, turns into a permanent removal. Left
|
|
38
|
+
// open it is delete_messages without the dialog, reachable in one call
|
|
39
|
+
// and present in the `essential` preset.
|
|
40
|
+
if (add?.some((flag) => flag.toLowerCase() === '\\deleted')) {
|
|
41
|
+
throw new ToolInputError('imap-mcp: \\Deleted cannot be added here — it marks messages for ' +
|
|
42
|
+
'permanent removal, which the next client to close the mailbox ' +
|
|
43
|
+
'may carry out. Use delete_messages, which asks for confirmation. ' +
|
|
44
|
+
'Moving them to a Trash folder with move_messages is usually what ' +
|
|
45
|
+
'is meant instead.');
|
|
46
|
+
}
|
|
47
|
+
return client.withMailbox(mailbox, false, async (connection) => {
|
|
48
|
+
if (add !== undefined) {
|
|
49
|
+
await withTimeout(connection.messageFlagsAdd(uids, add, { uid: true }), 'STORE');
|
|
50
|
+
}
|
|
51
|
+
if (remove !== undefined) {
|
|
52
|
+
await withTimeout(connection.messageFlagsRemove(uids, remove, { uid: true }), 'STORE');
|
|
53
|
+
}
|
|
54
|
+
const source = mailbox ?? client.defaultMailbox;
|
|
55
|
+
audit('set_flags', {
|
|
56
|
+
mailbox: source,
|
|
57
|
+
uids,
|
|
58
|
+
added: add,
|
|
59
|
+
removed: remove,
|
|
60
|
+
});
|
|
61
|
+
return jsonResult({
|
|
62
|
+
mailbox: source,
|
|
63
|
+
uids,
|
|
64
|
+
added: add ?? [],
|
|
65
|
+
removed: remove ?? [],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}));
|
|
69
|
+
server.registerTool('move_messages', {
|
|
70
|
+
title: 'Move or copy messages',
|
|
71
|
+
description: 'Moves messages to another mailbox, or copies them when mode is ' +
|
|
72
|
+
'"copy". Both require confirmation: call once to receive a token, ' +
|
|
73
|
+
'then again with that token and the same UID list and destination.',
|
|
74
|
+
inputSchema: {
|
|
75
|
+
uids: uidListParam,
|
|
76
|
+
destination: mailboxParam.describe('Mailbox the messages end up in, exactly as listed by list_mailboxes.'),
|
|
77
|
+
mailbox: optionalMailboxParam,
|
|
78
|
+
mode: z
|
|
79
|
+
.enum(['move', 'copy'])
|
|
80
|
+
.optional()
|
|
81
|
+
.describe('"move" (default) removes the originals, "copy" keeps them.'),
|
|
82
|
+
confirm_token: confirmTokenParam,
|
|
83
|
+
},
|
|
84
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
85
|
+
}, async ({ uids, destination, mailbox, mode, confirm_token }) => run(async () => {
|
|
86
|
+
const copying = mode === 'copy';
|
|
87
|
+
const source = mailbox ?? client.defaultMailbox;
|
|
88
|
+
// Copying is confirmed as well. The old rule — a token for "move",
|
|
89
|
+
// nothing for "copy" — reasoned about deletion, and deletion is not the
|
|
90
|
+
// only thing that cannot be taken back. `destination` is a free-form
|
|
91
|
+
// mailbox name, so on a shared account or a public namespace one
|
|
92
|
+
// unconfirmed call hands every named message to everyone with access to
|
|
93
|
+
// that folder. Disclosure is the irreversible part, and copying is the
|
|
94
|
+
// mode that does it while leaving no trace in the source folder.
|
|
95
|
+
//
|
|
96
|
+
// The key covers the exact UID set and both mailboxes: a confirmation
|
|
97
|
+
// for [1] must not execute [1, 2], where the model picked the second
|
|
98
|
+
// list, and one for Archive must not execute against Public/Shared.
|
|
99
|
+
const key = setResourceKey(`${copying ? 'copy_messages' : 'move_messages'}:${source}:${destination}`, uids.map(String));
|
|
100
|
+
if (!confirmations.consume(key, confirm_token)) {
|
|
101
|
+
return textResult(confirmationPrompt(`${copying ? 'copy' : 'move'} ${uids.length} message(s) between mailboxes`, confirmations.issue(key), confirmations.ttlMinutes, copying
|
|
102
|
+
? 'Everyone with access to the destination can read the copies, and taking them back does not unsee them.'
|
|
103
|
+
: 'The messages keep their content but get new UIDs, so the current ones stop working.', [
|
|
104
|
+
{ label: 'From', value: source },
|
|
105
|
+
{ label: 'To', value: destination },
|
|
106
|
+
]));
|
|
107
|
+
}
|
|
108
|
+
return client.withMailbox(mailbox, false, async (connection) => {
|
|
109
|
+
if (copying) {
|
|
110
|
+
await withTimeout(connection.messageCopy(uids, destination, { uid: true }), 'COPY');
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await withTimeout(connection.messageMove(uids, destination, { uid: true }), 'MOVE');
|
|
114
|
+
}
|
|
115
|
+
audit(copying ? 'copy' : 'move', {
|
|
116
|
+
from: source,
|
|
117
|
+
to: destination,
|
|
118
|
+
uids,
|
|
119
|
+
});
|
|
120
|
+
return jsonResult({
|
|
121
|
+
action: copying ? 'copied' : 'moved',
|
|
122
|
+
source,
|
|
123
|
+
destination,
|
|
124
|
+
uids,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}));
|
|
128
|
+
server.registerTool('delete_messages', {
|
|
129
|
+
title: 'Delete messages',
|
|
130
|
+
description: 'Permanently deletes messages: sets \\Deleted and expunges them. On ' +
|
|
131
|
+
'most servers this does not go to Trash — move them there instead if ' +
|
|
132
|
+
'that is what is meant. Asks the user to confirm; where the client ' +
|
|
133
|
+
'cannot show a prompt, it falls back to a two-call token.',
|
|
134
|
+
inputSchema: {
|
|
135
|
+
uids: uidListParam,
|
|
136
|
+
mailbox: optionalMailboxParam,
|
|
137
|
+
confirm_token: confirmTokenParam,
|
|
138
|
+
},
|
|
139
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
140
|
+
}, async ({ uids, mailbox, confirm_token }) => run(async () => {
|
|
141
|
+
const source = mailbox ?? client.defaultMailbox;
|
|
142
|
+
const approval = await requestApproval(server, confirmations, {
|
|
143
|
+
what: `Permanently delete ${uids.length} message(s) (UIDs ${uids.slice(0, 20).join(', ')}${uids.length > 20 ? ', …' : ''})`,
|
|
144
|
+
consequence: 'The messages are expunged, not moved to Trash. They cannot be recovered from here.',
|
|
145
|
+
resourceKey: setResourceKey(`delete_messages:${source}`, uids.map(String)),
|
|
146
|
+
token: confirm_token,
|
|
147
|
+
details: [{ label: 'Mailbox', value: source }],
|
|
148
|
+
});
|
|
149
|
+
if (!approval.approved)
|
|
150
|
+
return approval.result;
|
|
151
|
+
return client.withMailbox(mailbox, false, async (connection) => {
|
|
152
|
+
await withTimeout(connection.messageDelete(uids, { uid: true }), 'EXPUNGE');
|
|
153
|
+
audit('delete', { mailbox: source, uids });
|
|
154
|
+
return jsonResult({ action: 'deleted', mailbox: source, uids });
|
|
155
|
+
});
|
|
156
|
+
}));
|
|
157
|
+
server.registerTool('manage_mailbox', {
|
|
158
|
+
title: 'Create, rename or delete a mailbox',
|
|
159
|
+
description: 'Creates a folder, renames one, or deletes one. Deleting asks the user ' +
|
|
160
|
+
'to confirm and takes every message in the folder with it; renaming ' +
|
|
161
|
+
'uses the two-call token because it is reversible.',
|
|
162
|
+
inputSchema: {
|
|
163
|
+
action: z
|
|
164
|
+
.enum(['create', 'rename', 'delete'])
|
|
165
|
+
.describe('What to do with the mailbox.'),
|
|
166
|
+
mailbox: mailboxParam.describe('The mailbox to create, rename or delete.'),
|
|
167
|
+
new_name: mailboxParam
|
|
168
|
+
.optional()
|
|
169
|
+
.describe('Required for "rename": the full new path.'),
|
|
170
|
+
confirm_token: confirmTokenParam,
|
|
171
|
+
},
|
|
172
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
173
|
+
}, async ({ action, mailbox, new_name, confirm_token }) => run(async () => {
|
|
174
|
+
if (action === 'rename' && new_name === undefined) {
|
|
175
|
+
throw new ToolInputError('imap-mcp: "rename" needs new_name — the full new mailbox path.');
|
|
176
|
+
}
|
|
177
|
+
if (action === 'delete') {
|
|
178
|
+
const approval = await requestApproval(server, confirmations, {
|
|
179
|
+
what: 'Delete a mailbox',
|
|
180
|
+
consequence: 'Every message in the folder is deleted with it, and it cannot be recovered from here.',
|
|
181
|
+
resourceKey: `manage_mailbox:delete:${mailbox}`,
|
|
182
|
+
token: confirm_token,
|
|
183
|
+
details: [{ label: 'Mailbox', value: mailbox }],
|
|
184
|
+
});
|
|
185
|
+
if (!approval.approved)
|
|
186
|
+
return approval.result;
|
|
187
|
+
}
|
|
188
|
+
else if (action === 'rename') {
|
|
189
|
+
const key = `manage_mailbox:rename:${mailbox}:${new_name ?? ''}`;
|
|
190
|
+
if (!confirmations.consume(key, confirm_token)) {
|
|
191
|
+
return textResult(confirmationPrompt('rename a mailbox', confirmations.issue(key), confirmations.ttlMinutes, 'Clients that cached the old path will have to resynchronise.', [
|
|
192
|
+
{ label: 'Mailbox', value: mailbox },
|
|
193
|
+
{ label: 'New name', value: new_name ?? '' },
|
|
194
|
+
]));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return client.withConnection(async (connection) => {
|
|
198
|
+
if (action === 'create') {
|
|
199
|
+
await withTimeout(connection.mailboxCreate(mailbox), 'CREATE');
|
|
200
|
+
}
|
|
201
|
+
else if (action === 'rename') {
|
|
202
|
+
await withTimeout(connection.mailboxRename(mailbox, new_name), 'RENAME');
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
await withTimeout(connection.mailboxDelete(mailbox), 'DELETE');
|
|
206
|
+
}
|
|
207
|
+
audit(`mailbox_${action}`, { mailbox, new_name });
|
|
208
|
+
return jsonResult({
|
|
209
|
+
action,
|
|
210
|
+
mailbox,
|
|
211
|
+
...(new_name === undefined ? {} : { new_name }),
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}));
|
|
215
|
+
server.registerTool('save_draft', {
|
|
216
|
+
title: 'Save a draft',
|
|
217
|
+
description: 'Composes a plain-text message and stores it in the Drafts folder. ' +
|
|
218
|
+
'This server cannot send mail — the draft waits in the mailbox until a ' +
|
|
219
|
+
'person opens it in their own mail client and sends it from there. ' +
|
|
220
|
+
'That is deliberate: it keeps the composing useful while leaving the ' +
|
|
221
|
+
'decision to send with a human.',
|
|
222
|
+
inputSchema: {
|
|
223
|
+
to: addressListParam,
|
|
224
|
+
cc: addressListParam.optional(),
|
|
225
|
+
bcc: addressListParam.optional(),
|
|
226
|
+
subject: z
|
|
227
|
+
.string()
|
|
228
|
+
.max(500)
|
|
229
|
+
.refine((v) => !/[\r\n]/.test(v), 'must not contain line breaks')
|
|
230
|
+
.describe('Subject line.'),
|
|
231
|
+
body: z
|
|
232
|
+
.string()
|
|
233
|
+
.min(1)
|
|
234
|
+
.max(100_000)
|
|
235
|
+
.describe('Plain-text body of the message.'),
|
|
236
|
+
reply_to_uid: uidParam
|
|
237
|
+
.optional()
|
|
238
|
+
.describe('UID of the message being answered; threads the draft through In-Reply-To and References.'),
|
|
239
|
+
mailbox: optionalMailboxParam.describe('Where to look for reply_to_uid. Defaults to the configured mailbox.'),
|
|
240
|
+
},
|
|
241
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
242
|
+
}, async ({ to, cc, bcc, subject, body, reply_to_uid, mailbox }) => run(async () => {
|
|
243
|
+
const thread = reply_to_uid === undefined
|
|
244
|
+
? undefined
|
|
245
|
+
: await client.threadHeaders(mailbox, reply_to_uid);
|
|
246
|
+
const draft = buildDraft({
|
|
247
|
+
from: client.user,
|
|
248
|
+
to,
|
|
249
|
+
...(cc === undefined ? {} : { cc }),
|
|
250
|
+
...(bcc === undefined ? {} : { bcc }),
|
|
251
|
+
subject,
|
|
252
|
+
body,
|
|
253
|
+
...(thread === undefined ? {} : { thread }),
|
|
254
|
+
});
|
|
255
|
+
const folder = await resolveDraftsMailbox(client, config);
|
|
256
|
+
await client.withConnection(async (connection) => {
|
|
257
|
+
await withTimeout(connection.append(folder, draft, ['\\Draft', '\\Seen']), 'APPEND');
|
|
258
|
+
});
|
|
259
|
+
audit('save_draft', {
|
|
260
|
+
mailbox: folder,
|
|
261
|
+
recipients: to.length + (cc?.length ?? 0) + (bcc?.length ?? 0),
|
|
262
|
+
in_reply_to: thread?.messageId,
|
|
263
|
+
});
|
|
264
|
+
return jsonResult({
|
|
265
|
+
action: 'draft_saved',
|
|
266
|
+
mailbox: folder,
|
|
267
|
+
recipients: [...to, ...(cc ?? []), ...(bcc ?? [])],
|
|
268
|
+
note: 'The draft is stored but not sent. This server has no way to send mail; open it in your mail client to send it.',
|
|
269
|
+
});
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Finds the Drafts folder.
|
|
274
|
+
*
|
|
275
|
+
* The `\Drafts` special-use flag is the right answer where the server reports
|
|
276
|
+
* it, because folder names are localised — "Entwürfe", "Brouillons" — and
|
|
277
|
+
* guessing wrong creates a stray folder rather than failing loudly.
|
|
278
|
+
*/
|
|
279
|
+
async function resolveDraftsMailbox(client, config) {
|
|
280
|
+
if (config.imap.draftsMailbox !== undefined) {
|
|
281
|
+
return config.imap.draftsMailbox;
|
|
282
|
+
}
|
|
283
|
+
const mailboxes = await client.listMailboxes();
|
|
284
|
+
const found = mailboxes.find((box) => box.specialUse === '\\Drafts');
|
|
285
|
+
if (found === undefined) {
|
|
286
|
+
throw new ToolInputError('imap-mcp: this account does not advertise a \\Drafts folder. Call ' +
|
|
287
|
+
'list_mailboxes to find the right one and set IMAP_DRAFTS_MAILBOX to it.');
|
|
288
|
+
}
|
|
289
|
+
return found.path;
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ni-c/imap-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"mcpName": "io.github.ni-c/imap-mcp",
|
|
5
|
+
"description": "MCP server for IMAP mailboxes: read, search, organise and draft mail — it cannot send",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"imap",
|
|
10
|
+
"email",
|
|
11
|
+
"mail",
|
|
12
|
+
"mailbox",
|
|
13
|
+
"read-only"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Willi Thiel",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/ni-c/imap-mcp.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/ni-c/imap-mcp/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://imap-mcp.ni-c.de",
|
|
25
|
+
"funding": "https://github.com/sponsors/ni-c",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"imap-mcp": "dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"main": "dist/index.js",
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"prepublishOnly": "npm run lint && npm run build && npm test",
|
|
42
|
+
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"watch": "tsc --watch",
|
|
45
|
+
"lint": "eslint . && prettier --check .",
|
|
46
|
+
"format": "prettier --write .",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:coverage": "vitest run --coverage",
|
|
49
|
+
"assets": "node scripts/gen-assets.mjs",
|
|
50
|
+
"assets:check": "node scripts/gen-assets.mjs --check"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
54
|
+
"imapflow": "^1.7.6",
|
|
55
|
+
"mailparser": "^3.9.17",
|
|
56
|
+
"zod": "^4.5.4"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@eslint/js": "^10.0.1",
|
|
60
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
61
|
+
"@types/mailparser": "^3.4.6",
|
|
62
|
+
"@types/node": "^26.4.0",
|
|
63
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
64
|
+
"eslint": "^10.9.1",
|
|
65
|
+
"prettier": "^3.9.6",
|
|
66
|
+
"typescript": "^6.0.3",
|
|
67
|
+
"typescript-eslint": "^8.68.0",
|
|
68
|
+
"vitest": "^4.1.11"
|
|
69
|
+
}
|
|
70
|
+
}
|