apple-mail-mcp 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/build/index.js +25 -4
- package/build/services/imapClient.d.ts +33 -0
- package/build/services/imapClient.d.ts.map +1 -1
- package/build/services/imapClient.js +83 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -279,9 +279,15 @@ an account is configured for IMAP, `search-messages` and `list-messages` instead
|
|
|
279
279
|
run a **server-side IMAP search** ([#43](https://github.com/sweetrb/apple-mail-mcp/issues/43)) —
|
|
280
280
|
typically sub-second and correct on the same mailbox where AppleScript times out.
|
|
281
281
|
This is **opt-in and additive**: any account without IMAP configured behaves
|
|
282
|
-
exactly as before (AppleScript).
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
exactly as before (AppleScript). When an account is IMAP-configured,
|
|
283
|
+
`search-messages`/`list-messages` (read) and `create-mailbox`/`rename-mailbox`/
|
|
284
|
+
`delete-mailbox` (folder ops) route to IMAP. The folder ops are the key win for
|
|
285
|
+
server accounts: IMAP's `CREATE`/`RENAME`/`DELETE` succeed on exactly the
|
|
286
|
+
iCloud/Gmail/Workspace/Exchange mailboxes where Mail.app's AppleScript bridge
|
|
287
|
+
can't (#42). `get-message` and message-level mutations (mark/flag/move/delete-
|
|
288
|
+
message) stay on AppleScript for now — they key off a message id, and the IMAP
|
|
289
|
+
read rows report **UIDs** (a different, per-mailbox namespace), so routing them
|
|
290
|
+
safely needs a UID-aware design (tracked on #43).
|
|
285
291
|
|
|
286
292
|
Routing is conservative: only a call whose explicit `account` matches the
|
|
287
293
|
configured IMAP account goes to IMAP; everything else falls through to
|
|
@@ -303,9 +309,15 @@ config. Gmail label semantics: common names (`All Mail`, `Sent`, `Trash`,
|
|
|
303
309
|
`Spam`, `Important`, …) map to their `[Gmail]/…` IMAP paths automatically.
|
|
304
310
|
|
|
305
311
|
> Note: each call currently opens its own IMAP connection (no pooling yet), so
|
|
306
|
-
> expect a few seconds of connection overhead per call. Phase 2
|
|
307
|
-
>
|
|
308
|
-
> [#42](https://github.com/sweetrb/apple-mail-mcp/issues/42)
|
|
312
|
+
> expect a few seconds of connection overhead per call. Phase 2 added the folder
|
|
313
|
+
> ops (create/rename/delete-mailbox) — resolving the IMAP slice of
|
|
314
|
+
> [#42](https://github.com/sweetrb/apple-mail-mcp/issues/42). IMAP-backed
|
|
315
|
+
> message-level mutations are still future work (see #43).
|
|
316
|
+
>
|
|
317
|
+
> **iCloud:** set `APPLE_MAIL_MCP_IMAP_HOST=imap.mail.me.com`, `APPLE_MAIL_MCP_IMAP_USER`
|
|
318
|
+
> to your iCloud address, `APPLE_MAIL_MCP_IMAP_ACCOUNT` to the Mail account name
|
|
319
|
+
> (e.g. `iCloud`), and use an **app-specific password** (from appleid.apple.com)
|
|
320
|
+
> stored in the Keychain.
|
|
309
321
|
|
|
310
322
|
---
|
|
311
323
|
|
package/build/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
25
25
|
import { z } from "zod";
|
|
26
26
|
import { AppleMailManager } from "./services/appleMailManager.js";
|
|
27
27
|
import { sendViaSmtp } from "./services/smtpMailer.js";
|
|
28
|
-
import { isImapAccount, imapSearchMessages, imapListMessages } from "./services/imapClient.js";
|
|
28
|
+
import { isImapAccount, imapSearchMessages, imapListMessages, imapCreateMailbox, imapDeleteMailbox, imapRenameMailbox, } from "./services/imapClient.js";
|
|
29
29
|
import { createSerialGate } from "./utils/serialize.js";
|
|
30
30
|
// =============================================================================
|
|
31
31
|
// Shared Validation Schemas
|
|
@@ -582,7 +582,15 @@ server.tool("get-unread-count", {
|
|
|
582
582
|
server.tool("create-mailbox", {
|
|
583
583
|
name: z.string().min(1, "Mailbox name is required"),
|
|
584
584
|
account: z.string().optional().describe("Account to create the mailbox in"),
|
|
585
|
-
}, withErrorHandling(({ name, account }) => {
|
|
585
|
+
}, withErrorHandling(async ({ name, account }) => {
|
|
586
|
+
// IMAP backend (issue #43, Phase 2): server-side folder op when this account
|
|
587
|
+
// is IMAP-configured; otherwise AppleScript.
|
|
588
|
+
if (isImapAccount(account)) {
|
|
589
|
+
const r = await imapCreateMailbox(name);
|
|
590
|
+
if (!r.success)
|
|
591
|
+
return errorResponse(r.error || `Failed to create mailbox "${name}"`);
|
|
592
|
+
return successResponse(r.info || `Mailbox "${name}" created`);
|
|
593
|
+
}
|
|
586
594
|
const success = mailManager.createMailbox(name, account);
|
|
587
595
|
if (!success) {
|
|
588
596
|
return errorResponse(`Failed to create mailbox "${name}"`);
|
|
@@ -593,7 +601,13 @@ server.tool("create-mailbox", {
|
|
|
593
601
|
server.tool("delete-mailbox", {
|
|
594
602
|
name: z.string().min(1, "Mailbox name is required"),
|
|
595
603
|
account: z.string().optional().describe("Account containing the mailbox"),
|
|
596
|
-
}, withErrorHandling(({ name, account }) => {
|
|
604
|
+
}, withErrorHandling(async ({ name, account }) => {
|
|
605
|
+
if (isImapAccount(account)) {
|
|
606
|
+
const r = await imapDeleteMailbox(name);
|
|
607
|
+
if (!r.success)
|
|
608
|
+
return errorResponse(r.error || `Failed to delete mailbox "${name}"`);
|
|
609
|
+
return successResponse(r.info || `Mailbox "${name}" deleted`);
|
|
610
|
+
}
|
|
597
611
|
const { success, error } = mailManager.deleteMailbox(name, account);
|
|
598
612
|
if (!success) {
|
|
599
613
|
return errorResponse(error || `Failed to delete mailbox "${name}"`);
|
|
@@ -605,7 +619,14 @@ server.tool("rename-mailbox", {
|
|
|
605
619
|
oldName: z.string().min(1, "Current mailbox name is required"),
|
|
606
620
|
newName: z.string().min(1, "New mailbox name is required"),
|
|
607
621
|
account: z.string().optional().describe("Account containing the mailbox"),
|
|
608
|
-
}, withErrorHandling(({ oldName, newName, account }) => {
|
|
622
|
+
}, withErrorHandling(async ({ oldName, newName, account }) => {
|
|
623
|
+
if (isImapAccount(account)) {
|
|
624
|
+
const r = await imapRenameMailbox(oldName, newName);
|
|
625
|
+
if (!r.success) {
|
|
626
|
+
return errorResponse(r.error || `Failed to rename mailbox "${oldName}" to "${newName}"`);
|
|
627
|
+
}
|
|
628
|
+
return successResponse(r.info || `Mailbox renamed from "${oldName}" to "${newName}"`);
|
|
629
|
+
}
|
|
609
630
|
const { success, error } = mailManager.renameMailbox(oldName, newName, account);
|
|
610
631
|
if (!success) {
|
|
611
632
|
return errorResponse(error || `Failed to rename mailbox "${oldName}" to "${newName}"`);
|
|
@@ -46,6 +46,10 @@ interface ImapMessage {
|
|
|
46
46
|
interface MailboxLock {
|
|
47
47
|
release: () => void;
|
|
48
48
|
}
|
|
49
|
+
interface ImapMailboxListing {
|
|
50
|
+
path: string;
|
|
51
|
+
name: string;
|
|
52
|
+
}
|
|
49
53
|
export interface ImapClientLike {
|
|
50
54
|
connect(): Promise<void>;
|
|
51
55
|
getMailboxLock(path: string): Promise<MailboxLock>;
|
|
@@ -55,6 +59,18 @@ export interface ImapClientLike {
|
|
|
55
59
|
fetch(range: string, query: Record<string, unknown>, opts: {
|
|
56
60
|
uid: true;
|
|
57
61
|
}): AsyncIterable<ImapMessage>;
|
|
62
|
+
list(): Promise<ImapMailboxListing[]>;
|
|
63
|
+
mailboxCreate(path: string): Promise<{
|
|
64
|
+
path: string;
|
|
65
|
+
created: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
mailboxRename(path: string, newPath: string): Promise<{
|
|
68
|
+
path: string;
|
|
69
|
+
newPath: string;
|
|
70
|
+
}>;
|
|
71
|
+
mailboxDelete(path: string): Promise<{
|
|
72
|
+
path: string;
|
|
73
|
+
}>;
|
|
58
74
|
logout(): Promise<void>;
|
|
59
75
|
}
|
|
60
76
|
export type ImapConnect = (cfg: ImapConfig) => Promise<ImapClientLike>;
|
|
@@ -71,5 +87,22 @@ export declare function imapListMessages(args: ImapSearchArgs, deps?: {
|
|
|
71
87
|
connect?: ImapConnect;
|
|
72
88
|
config?: ImapConfig;
|
|
73
89
|
}): Promise<string>;
|
|
90
|
+
export interface ImapOpResult {
|
|
91
|
+
success: boolean;
|
|
92
|
+
error?: string;
|
|
93
|
+
info?: string;
|
|
94
|
+
}
|
|
95
|
+
export declare function imapCreateMailbox(name: string, deps?: {
|
|
96
|
+
connect?: ImapConnect;
|
|
97
|
+
config?: ImapConfig;
|
|
98
|
+
}): Promise<ImapOpResult>;
|
|
99
|
+
export declare function imapDeleteMailbox(name: string, deps?: {
|
|
100
|
+
connect?: ImapConnect;
|
|
101
|
+
config?: ImapConfig;
|
|
102
|
+
}): Promise<ImapOpResult>;
|
|
103
|
+
export declare function imapRenameMailbox(oldName: string, newName: string, deps?: {
|
|
104
|
+
connect?: ImapConnect;
|
|
105
|
+
config?: ImapConfig;
|
|
106
|
+
}): Promise<ImapOpResult>;
|
|
74
107
|
export {};
|
|
75
108
|
//# sourceMappingURL=imapClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imapClient.d.ts","sourceRoot":"","sources":["../../src/services/imapClient.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"imapClient.d.ts","sourceRoot":"","sources":["../../src/services/imapClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -175,3 +175,86 @@ export function imapSearchMessages(args, deps = {}) {
|
|
|
175
175
|
export function imapListMessages(args, deps = {}) {
|
|
176
176
|
return run(args, true, deps);
|
|
177
177
|
}
|
|
178
|
+
function errText(e) {
|
|
179
|
+
return e instanceof Error ? e.message : String(e);
|
|
180
|
+
}
|
|
181
|
+
/** Connect, run `fn`, always log out. */
|
|
182
|
+
async function withClient(deps, fn) {
|
|
183
|
+
const cfg = deps.config ?? resolveImapConfig();
|
|
184
|
+
const client = await (deps.connect ?? defaultConnect)(cfg);
|
|
185
|
+
try {
|
|
186
|
+
return await fn(client, cfg);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
await client.logout().catch(() => undefined);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Resolve a user-supplied mailbox name to an actual server path by listing the
|
|
194
|
+
* mailboxes and matching on full path, then leaf name (case-insensitive).
|
|
195
|
+
* Returns null when no such mailbox exists.
|
|
196
|
+
*/
|
|
197
|
+
async function findMailboxPath(client, name) {
|
|
198
|
+
const wanted = name.trim().toLowerCase();
|
|
199
|
+
const boxes = await client.list();
|
|
200
|
+
const byPath = boxes.find((b) => b.path.toLowerCase() === wanted);
|
|
201
|
+
if (byPath)
|
|
202
|
+
return byPath.path;
|
|
203
|
+
const byName = boxes.find((b) => b.name.toLowerCase() === wanted);
|
|
204
|
+
return byName ? byName.path : null;
|
|
205
|
+
}
|
|
206
|
+
export function imapCreateMailbox(name, deps = {}) {
|
|
207
|
+
return withClient(deps, async (client) => {
|
|
208
|
+
try {
|
|
209
|
+
const res = await client.mailboxCreate(name);
|
|
210
|
+
return res.created
|
|
211
|
+
? { success: true, info: `Created mailbox "${res.path}".` }
|
|
212
|
+
: { success: true, info: `Mailbox "${res.path}" already existed.` };
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
return { success: false, error: `IMAP create failed for "${name}": ${errText(e)}` };
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
export function imapDeleteMailbox(name, deps = {}) {
|
|
220
|
+
return withClient(deps, async (client, cfg) => {
|
|
221
|
+
const path = await findMailboxPath(client, name);
|
|
222
|
+
if (!path) {
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
error: `Mailbox "${name}" not found on IMAP account ${cfg.accountLabel}.`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
await client.mailboxDelete(path);
|
|
230
|
+
return {
|
|
231
|
+
success: true,
|
|
232
|
+
info: `Deleted mailbox "${path}" via IMAP (account ${cfg.accountLabel}).`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
return { success: false, error: `IMAP delete failed for "${path}": ${errText(e)}` };
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
export function imapRenameMailbox(oldName, newName, deps = {}) {
|
|
241
|
+
return withClient(deps, async (client, cfg) => {
|
|
242
|
+
const path = await findMailboxPath(client, oldName);
|
|
243
|
+
if (!path) {
|
|
244
|
+
return {
|
|
245
|
+
success: false,
|
|
246
|
+
error: `Mailbox "${oldName}" not found on IMAP account ${cfg.accountLabel}.`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const res = await client.mailboxRename(path, newName);
|
|
251
|
+
return { success: true, info: `Renamed "${res.path}" to "${res.newPath}" via IMAP.` };
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: `IMAP rename failed for "${path}" -> "${newName}": ${errText(e)}`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|