apple-mail-mcp 2.8.2 → 2.8.3
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 +5 -5
- package/build/cli.js +12083 -138
- package/build/index.js +82684 -1687
- package/package.json +3 -4
- package/build/cli.d.ts +0 -24
- package/build/cli.d.ts.map +0 -1
- package/build/index.d.ts +0 -23
- package/build/index.d.ts.map +0 -1
- package/build/services/appleMailManager.d.ts +0 -680
- package/build/services/appleMailManager.d.ts.map +0 -1
- package/build/services/appleMailManager.js +0 -3143
- package/build/services/fileConfig.d.ts +0 -7
- package/build/services/fileConfig.d.ts.map +0 -1
- package/build/services/fileConfig.js +0 -52
- package/build/services/imapClient.d.ts +0 -312
- package/build/services/imapClient.d.ts.map +0 -1
- package/build/services/imapClient.js +0 -1023
- package/build/services/imapIdle.d.ts +0 -58
- package/build/services/imapIdle.d.ts.map +0 -1
- package/build/services/imapIdle.js +0 -151
- package/build/services/imapMultiAccount.d.ts +0 -124
- package/build/services/imapMultiAccount.d.ts.map +0 -1
- package/build/services/imapMultiAccount.js +0 -253
- package/build/services/messageRouter.d.ts +0 -24
- package/build/services/messageRouter.d.ts.map +0 -1
- package/build/services/messageRouter.js +0 -31
- package/build/services/replyForward.d.ts +0 -87
- package/build/services/replyForward.d.ts.map +0 -1
- package/build/services/replyForward.js +0 -150
- package/build/services/smtpMailer.d.ts +0 -160
- package/build/services/smtpMailer.d.ts.map +0 -1
- package/build/services/smtpMailer.js +0 -268
- package/build/services/templateStore.d.ts +0 -18
- package/build/services/templateStore.d.ts.map +0 -1
- package/build/services/templateStore.js +0 -91
- package/build/tools/doctor.d.ts +0 -23
- package/build/tools/doctor.d.ts.map +0 -1
- package/build/tools/doctor.js +0 -74
- package/build/tools/resourcesAndPrompts.d.ts +0 -14
- package/build/tools/resourcesAndPrompts.d.ts.map +0 -1
- package/build/tools/resourcesAndPrompts.js +0 -109
- package/build/tools/respond.d.ts +0 -48
- package/build/tools/respond.d.ts.map +0 -1
- package/build/tools/respond.js +0 -95
- package/build/tools/thread.d.ts +0 -19
- package/build/tools/thread.d.ts.map +0 -1
- package/build/tools/thread.js +0 -32
- package/build/types.d.ts +0 -434
- package/build/types.d.ts.map +0 -1
- package/build/types.js +0 -13
- package/build/utils/applescript.d.ts +0 -45
- package/build/utils/applescript.d.ts.map +0 -1
- package/build/utils/applescript.js +0 -446
- package/build/utils/attachmentMaterialize.d.ts +0 -9
- package/build/utils/attachmentMaterialize.d.ts.map +0 -1
- package/build/utils/attachmentMaterialize.js +0 -38
- package/build/utils/mimeParse.d.ts +0 -62
- package/build/utils/mimeParse.d.ts.map +0 -1
- package/build/utils/mimeParse.js +0 -317
- package/build/utils/orphan.d.ts +0 -25
- package/build/utils/orphan.d.ts.map +0 -1
- package/build/utils/orphan.js +0 -26
- package/build/utils/serialize.d.ts +0 -30
- package/build/utils/serialize.d.ts.map +0 -1
- package/build/utils/serialize.js +0 -41
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { EmailTemplate } from "../types.js";
|
|
2
|
-
export declare class TemplateStore {
|
|
3
|
-
private readonly file;
|
|
4
|
-
private templates;
|
|
5
|
-
private nextId;
|
|
6
|
-
private loaded;
|
|
7
|
-
constructor(file?: string);
|
|
8
|
-
/** Lazily load from disk on first access; tolerates a missing/corrupt file. */
|
|
9
|
-
private ensureLoaded;
|
|
10
|
-
private persist;
|
|
11
|
-
list(): EmailTemplate[];
|
|
12
|
-
get(id: string): EmailTemplate | null;
|
|
13
|
-
/** Create or update a template, then persist. */
|
|
14
|
-
save(name: string, subject: string, body: string, to?: string[], cc?: string[], id?: string): EmailTemplate;
|
|
15
|
-
/** Delete a template, persisting the change. Returns false if absent. */
|
|
16
|
-
delete(id: string): boolean;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=templateStore.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"templateStore.d.ts","sourceRoot":"","sources":["../../src/services/templateStore.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAchD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAS;gBAEX,IAAI,CAAC,EAAE,MAAM;IAIzB,+EAA+E;IAC/E,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,OAAO;IAef,IAAI,IAAI,aAAa,EAAE;IAKvB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAKrC,iDAAiD;IACjD,IAAI,CACF,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,EAAE,CAAC,EAAE,MAAM,EAAE,EACb,EAAE,CAAC,EAAE,MAAM,EAAE,EACb,EAAE,CAAC,EAAE,MAAM,GACV,aAAa;IAShB,yEAAyE;IACzE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAM5B"}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persistent email-template store (B3 / audit #14).
|
|
3
|
-
*
|
|
4
|
-
* Templates used to live in an in-memory Map that was lost on every server
|
|
5
|
-
* restart (and `nextTemplateId` reset to 1, colliding ids across restarts).
|
|
6
|
-
* This persists them to a JSON file so they survive restarts. The file path is
|
|
7
|
-
* `APPLE_MAIL_MCP_TEMPLATES_FILE` or, by default,
|
|
8
|
-
* `~/Library/Application Support/apple-mail-mcp/templates.json`.
|
|
9
|
-
*
|
|
10
|
-
* @module services/templateStore
|
|
11
|
-
*/
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
13
|
-
import { dirname, join } from "path";
|
|
14
|
-
import { homedir } from "os";
|
|
15
|
-
function defaultTemplatesFile() {
|
|
16
|
-
const env = process.env.APPLE_MAIL_MCP_TEMPLATES_FILE;
|
|
17
|
-
if (env && env.trim())
|
|
18
|
-
return env.trim();
|
|
19
|
-
return join(homedir(), "Library", "Application Support", "apple-mail-mcp", "templates.json");
|
|
20
|
-
}
|
|
21
|
-
export class TemplateStore {
|
|
22
|
-
file;
|
|
23
|
-
templates = new Map();
|
|
24
|
-
nextId = 1;
|
|
25
|
-
loaded = false;
|
|
26
|
-
constructor(file) {
|
|
27
|
-
this.file = file ?? defaultTemplatesFile();
|
|
28
|
-
}
|
|
29
|
-
/** Lazily load from disk on first access; tolerates a missing/corrupt file. */
|
|
30
|
-
ensureLoaded() {
|
|
31
|
-
if (this.loaded)
|
|
32
|
-
return;
|
|
33
|
-
this.loaded = true;
|
|
34
|
-
try {
|
|
35
|
-
if (!existsSync(this.file))
|
|
36
|
-
return;
|
|
37
|
-
const parsed = JSON.parse(readFileSync(this.file, "utf8"));
|
|
38
|
-
if (Array.isArray(parsed.templates)) {
|
|
39
|
-
for (const t of parsed.templates) {
|
|
40
|
-
if (t && typeof t.id === "string")
|
|
41
|
-
this.templates.set(t.id, t);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (typeof parsed.nextId === "number" && parsed.nextId > 0)
|
|
45
|
-
this.nextId = parsed.nextId;
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
console.error(`Failed to load templates from ${this.file}: ${String(err)}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
persist() {
|
|
52
|
-
const data = {
|
|
53
|
-
version: 1,
|
|
54
|
-
nextId: this.nextId,
|
|
55
|
-
templates: Array.from(this.templates.values()),
|
|
56
|
-
};
|
|
57
|
-
try {
|
|
58
|
-
mkdirSync(dirname(this.file), { recursive: true });
|
|
59
|
-
writeFileSync(this.file, JSON.stringify(data, null, 2) + "\n");
|
|
60
|
-
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
// Don't fail the operation; the in-memory copy is still usable this session.
|
|
63
|
-
console.error(`Failed to persist templates to ${this.file}: ${String(err)}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
list() {
|
|
67
|
-
this.ensureLoaded();
|
|
68
|
-
return Array.from(this.templates.values());
|
|
69
|
-
}
|
|
70
|
-
get(id) {
|
|
71
|
-
this.ensureLoaded();
|
|
72
|
-
return this.templates.get(id) ?? null;
|
|
73
|
-
}
|
|
74
|
-
/** Create or update a template, then persist. */
|
|
75
|
-
save(name, subject, body, to, cc, id) {
|
|
76
|
-
this.ensureLoaded();
|
|
77
|
-
const templateId = id || `tmpl_${this.nextId++}`;
|
|
78
|
-
const template = { id: templateId, name, subject, body, to, cc };
|
|
79
|
-
this.templates.set(templateId, template);
|
|
80
|
-
this.persist();
|
|
81
|
-
return template;
|
|
82
|
-
}
|
|
83
|
-
/** Delete a template, persisting the change. Returns false if absent. */
|
|
84
|
-
delete(id) {
|
|
85
|
-
this.ensureLoaded();
|
|
86
|
-
const existed = this.templates.delete(id);
|
|
87
|
-
if (existed)
|
|
88
|
-
this.persist();
|
|
89
|
-
return existed;
|
|
90
|
-
}
|
|
91
|
-
}
|
package/build/tools/doctor.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Setup "doctor" (C3): one diagnostic that checks the things that actually break
|
|
3
|
-
* an apple-mail-mcp setup — Mail.app automation permission, account state, and
|
|
4
|
-
* the optional IMAP/SMTP backends — and reports each as ok / warn / fail with an
|
|
5
|
-
* actionable message, instead of failing opaquely deep in a tool call.
|
|
6
|
-
*
|
|
7
|
-
* @module tools/doctor
|
|
8
|
-
*/
|
|
9
|
-
import type { AppleMailManager } from "../services/appleMailManager.js";
|
|
10
|
-
export type CheckStatus = "ok" | "warn" | "fail";
|
|
11
|
-
export interface DoctorCheck {
|
|
12
|
-
name: string;
|
|
13
|
-
status: CheckStatus;
|
|
14
|
-
detail: string;
|
|
15
|
-
}
|
|
16
|
-
export interface DoctorReport {
|
|
17
|
-
healthy: boolean;
|
|
18
|
-
checks: DoctorCheck[];
|
|
19
|
-
}
|
|
20
|
-
export declare function runDoctor(mailManager: AppleMailManager): Promise<DoctorReport>;
|
|
21
|
-
/** Render a DoctorReport as readable text. */
|
|
22
|
-
export declare function formatDoctorReport(r: DoctorReport): string;
|
|
23
|
-
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/tools/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAIvE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AACjD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAmEpF;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAK1D"}
|
package/build/tools/doctor.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { imapHealthCheck, IMAP_ENV, listImapAccountLabels } from "../services/imapClient.js";
|
|
2
|
-
import { SMTP_ENV, isSmtpConfigured } from "../services/smtpMailer.js";
|
|
3
|
-
export async function runDoctor(mailManager) {
|
|
4
|
-
const checks = [];
|
|
5
|
-
// 1. Mail.app reachability + Automation permission (existing health check).
|
|
6
|
-
const hc = mailManager.healthCheck();
|
|
7
|
-
for (const c of hc.checks) {
|
|
8
|
-
checks.push({
|
|
9
|
-
name: `Mail.app: ${c.name}`,
|
|
10
|
-
status: c.passed ? "ok" : "fail",
|
|
11
|
-
detail: c.message,
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
// 2. Accounts — at least one enabled; name any disabled (they're invisible in
|
|
15
|
-
// the UI but still addressable, a common source of confusion — see #47).
|
|
16
|
-
try {
|
|
17
|
-
const accounts = mailManager.listAccounts();
|
|
18
|
-
const enabled = accounts.filter((a) => a.enabled);
|
|
19
|
-
const disabled = accounts.filter((a) => !a.enabled).map((a) => a.name);
|
|
20
|
-
checks.push({
|
|
21
|
-
name: "Accounts",
|
|
22
|
-
status: enabled.length > 0 ? "ok" : "warn",
|
|
23
|
-
detail: `${accounts.length} configured, ${enabled.length} enabled` +
|
|
24
|
-
(disabled.length ? ` (disabled: ${disabled.join(", ")})` : ""),
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
catch (e) {
|
|
28
|
-
checks.push({
|
|
29
|
-
name: "Accounts",
|
|
30
|
-
status: "fail",
|
|
31
|
-
detail: `could not list accounts: ${String(e)}`,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
// 3. IMAP backend (optional) — check every configured account (C2).
|
|
35
|
-
const imapAccounts = listImapAccountLabels();
|
|
36
|
-
if (imapAccounts.length === 0) {
|
|
37
|
-
checks.push({
|
|
38
|
-
name: "IMAP backend",
|
|
39
|
-
status: "warn",
|
|
40
|
-
detail: `not configured — AppleScript is used for all accounts. Set ${IMAP_ENV.user} (+ Keychain/password), or ${IMAP_ENV.accounts} for multiple accounts, to enable server-side search and server-mailbox ops.`,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
for (const label of imapAccounts) {
|
|
45
|
-
const h = await imapHealthCheck({ account: label });
|
|
46
|
-
checks.push({
|
|
47
|
-
name: `IMAP: ${label}`,
|
|
48
|
-
status: h.ok ? "ok" : "fail",
|
|
49
|
-
detail: h.ok
|
|
50
|
-
? `connected to ${h.host}`
|
|
51
|
-
: `connection failed: ${h.error}. Check the Keychain password and host/port.`,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 4. SMTP transport (optional) — configured?
|
|
56
|
-
const smtpHost = process.env[SMTP_ENV.host]?.trim();
|
|
57
|
-
checks.push({
|
|
58
|
-
name: "SMTP transport",
|
|
59
|
-
status: isSmtpConfigured() ? "ok" : "warn",
|
|
60
|
-
detail: isSmtpConfigured()
|
|
61
|
-
? `configured (${smtpHost}); send-email auto-prefers clean SMTP (no Mail.app Sent-folder copy; a non-email "account" label still routes to AppleScript). Pass transport:"applescript" to force Mail.app. The apple-mail-send CLI is also available.`
|
|
62
|
-
: `not configured — send-email uses AppleScript (subject to macOS 15+ blockquote wrapping). Set ${SMTP_ENV.host} and ${SMTP_ENV.user} (+ password via Keychain) to enable.`,
|
|
63
|
-
});
|
|
64
|
-
const healthy = !checks.some((c) => c.status === "fail");
|
|
65
|
-
return { healthy, checks };
|
|
66
|
-
}
|
|
67
|
-
/** Render a DoctorReport as readable text. */
|
|
68
|
-
export function formatDoctorReport(r) {
|
|
69
|
-
const icon = (s) => (s === "ok" ? "✅" : s === "warn" ? "⚠️ " : "❌");
|
|
70
|
-
const lines = [`🩺 apple-mail-mcp doctor — ${r.healthy ? "healthy" : "ISSUES FOUND"}`, ""];
|
|
71
|
-
for (const c of r.checks)
|
|
72
|
-
lines.push(`${icon(c.status)} ${c.name}: ${c.detail}`);
|
|
73
|
-
return lines.join("\n");
|
|
74
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP resources & prompts (D2).
|
|
3
|
-
*
|
|
4
|
-
* Resources expose read-only mail context the client can attach without a tool
|
|
5
|
-
* round-trip (accounts, templates, per-account mailboxes). Prompts package
|
|
6
|
-
* common multi-step workflows (triage, reply, weekly summary) so users can
|
|
7
|
-
* invoke them by name. Both are additive and complement the existing tools.
|
|
8
|
-
*
|
|
9
|
-
* @module tools/resourcesAndPrompts
|
|
10
|
-
*/
|
|
11
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
|
-
import type { AppleMailManager } from "../services/appleMailManager.js";
|
|
13
|
-
export declare function registerResourcesAndPrompts(server: McpServer, mailManager: AppleMailManager): void;
|
|
14
|
-
//# sourceMappingURL=resourcesAndPrompts.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resourcesAndPrompts.d.ts","sourceRoot":"","sources":["../../src/tools/resourcesAndPrompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAEtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAQvE,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,gBAAgB,GAC5B,IAAI,CAgIN"}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP resources & prompts (D2).
|
|
3
|
-
*
|
|
4
|
-
* Resources expose read-only mail context the client can attach without a tool
|
|
5
|
-
* round-trip (accounts, templates, per-account mailboxes). Prompts package
|
|
6
|
-
* common multi-step workflows (triage, reply, weekly summary) so users can
|
|
7
|
-
* invoke them by name. Both are additive and complement the existing tools.
|
|
8
|
-
*
|
|
9
|
-
* @module tools/resourcesAndPrompts
|
|
10
|
-
*/
|
|
11
|
-
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
|
-
import { z } from "zod";
|
|
13
|
-
function jsonResource(uri, data) {
|
|
14
|
-
return {
|
|
15
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }],
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
export function registerResourcesAndPrompts(server, mailManager) {
|
|
19
|
-
// --- Resources ---
|
|
20
|
-
server.registerResource("accounts", "mail://accounts", {
|
|
21
|
-
title: "Mail accounts",
|
|
22
|
-
description: "All configured Mail accounts (name, email, enabled state).",
|
|
23
|
-
mimeType: "application/json",
|
|
24
|
-
}, (uri) => jsonResource(uri.href, mailManager.listAccounts()));
|
|
25
|
-
server.registerResource("templates", "mail://templates", {
|
|
26
|
-
title: "Email templates",
|
|
27
|
-
description: "Saved, reusable email templates.",
|
|
28
|
-
mimeType: "application/json",
|
|
29
|
-
}, (uri) => jsonResource(uri.href, mailManager.listTemplates()));
|
|
30
|
-
server.registerResource("mailboxes", new ResourceTemplate("mail://mailboxes/{account}", {
|
|
31
|
-
list: () => ({
|
|
32
|
-
resources: mailManager.listAccounts().map((a) => ({
|
|
33
|
-
name: `Mailboxes — ${a.name}`,
|
|
34
|
-
uri: `mail://mailboxes/${encodeURIComponent(a.name)}`,
|
|
35
|
-
mimeType: "application/json",
|
|
36
|
-
})),
|
|
37
|
-
}),
|
|
38
|
-
}), {
|
|
39
|
-
title: "Mailboxes by account",
|
|
40
|
-
description: "Mailboxes/folders for a given account.",
|
|
41
|
-
mimeType: "application/json",
|
|
42
|
-
}, (uri, variables) => {
|
|
43
|
-
const account = decodeURIComponent(String(variables.account));
|
|
44
|
-
return jsonResource(uri.href, mailManager.listMailboxes(account));
|
|
45
|
-
});
|
|
46
|
-
// --- Prompts ---
|
|
47
|
-
server.registerPrompt("triage-inbox", {
|
|
48
|
-
title: "Triage inbox",
|
|
49
|
-
description: "Review unread mail and propose actions (archive, reply, flag, delete).",
|
|
50
|
-
argsSchema: {
|
|
51
|
-
account: z.string().optional().describe("Account to triage (default: all)"),
|
|
52
|
-
limit: z.string().optional().describe("How many unread to review (default 20)"),
|
|
53
|
-
},
|
|
54
|
-
}, ({ account, limit }) => ({
|
|
55
|
-
messages: [
|
|
56
|
-
{
|
|
57
|
-
role: "user",
|
|
58
|
-
content: {
|
|
59
|
-
type: "text",
|
|
60
|
-
text: `Triage my unread email${account ? ` in the "${account}" account` : ""}. ` +
|
|
61
|
-
`Use list-messages with unreadOnly=true${account ? ` and account="${account}"` : ""}` +
|
|
62
|
-
` and limit=${limit || "20"}. For each message, give a one-line summary and a ` +
|
|
63
|
-
`recommended action (reply / archive / flag / delete / ignore). Group by priority ` +
|
|
64
|
-
`and call out anything time-sensitive. Do not take any action without my confirmation.`,
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
}));
|
|
69
|
-
server.registerPrompt("compose-reply", {
|
|
70
|
-
title: "Compose reply",
|
|
71
|
-
description: "Draft a reply to a specific message in a chosen tone.",
|
|
72
|
-
argsSchema: {
|
|
73
|
-
messageId: z.string().describe("ID of the message to reply to"),
|
|
74
|
-
tone: z.string().optional().describe("Tone, e.g. friendly, formal, brief"),
|
|
75
|
-
intent: z.string().optional().describe("What the reply should accomplish"),
|
|
76
|
-
},
|
|
77
|
-
}, ({ messageId, tone, intent }) => ({
|
|
78
|
-
messages: [
|
|
79
|
-
{
|
|
80
|
-
role: "user",
|
|
81
|
-
content: {
|
|
82
|
-
type: "text",
|
|
83
|
-
text: `Read message ${messageId} with get-message, then draft a ${tone || "professional"} reply` +
|
|
84
|
-
`${intent ? ` that ${intent}` : ""}. Show me the draft first; only send after I approve, ` +
|
|
85
|
-
`using reply-to-message.`,
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
}));
|
|
90
|
-
server.registerPrompt("weekly-summary", {
|
|
91
|
-
title: "Weekly mail summary",
|
|
92
|
-
description: "Summarize the week's mail activity into a short briefing.",
|
|
93
|
-
argsSchema: {
|
|
94
|
-
account: z.string().optional().describe("Account to summarize (default: all)"),
|
|
95
|
-
},
|
|
96
|
-
}, ({ account }) => ({
|
|
97
|
-
messages: [
|
|
98
|
-
{
|
|
99
|
-
role: "user",
|
|
100
|
-
content: {
|
|
101
|
-
type: "text",
|
|
102
|
-
text: `Summarize my mail activity over the last 7 days${account ? ` for "${account}"` : ""}. ` +
|
|
103
|
-
`Use get-mail-stats and list-messages (with a 7-day window) to identify the busiest ` +
|
|
104
|
-
`threads, who I owe replies to, and any unresolved items. Produce a concise briefing.`,
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
}));
|
|
109
|
-
}
|
package/build/tools/respond.d.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { SearchDiagnostics, Message } from "../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Plain, JSON-friendly summary of a Message for `structuredContent` (A1) — dates
|
|
4
|
-
* as ISO strings, only the fields an agent needs to act (id is the key it passes
|
|
5
|
-
* back to get-message / mutations).
|
|
6
|
-
*/
|
|
7
|
-
export declare function messageSummary(m: Message): Record<string, unknown>;
|
|
8
|
-
/**
|
|
9
|
-
* Creates a successful MCP tool response. When `structured` is provided it is
|
|
10
|
-
* returned as `structuredContent` so callers can consume typed data instead of
|
|
11
|
-
* re-parsing the human text (A1).
|
|
12
|
-
*/
|
|
13
|
-
export declare function successResponse(message: string, structured?: Record<string, unknown>): {
|
|
14
|
-
content: {
|
|
15
|
-
type: "text";
|
|
16
|
-
text: string;
|
|
17
|
-
}[];
|
|
18
|
-
structuredContent?: Record<string, unknown>;
|
|
19
|
-
};
|
|
20
|
-
/** Creates an error MCP tool response. */
|
|
21
|
-
export declare function errorResponse(message: string, structured?: Record<string, unknown>): {
|
|
22
|
-
content: {
|
|
23
|
-
type: "text";
|
|
24
|
-
text: string;
|
|
25
|
-
}[];
|
|
26
|
-
isError: true;
|
|
27
|
-
structuredContent?: Record<string, unknown>;
|
|
28
|
-
};
|
|
29
|
-
export type ToolResponse = ReturnType<typeof successResponse> | ReturnType<typeof errorResponse>;
|
|
30
|
-
/**
|
|
31
|
-
* Render a partial-coverage warning from search/list diagnostics, so a caller
|
|
32
|
-
* never mistakes an incomplete scan for a confirmed "no matches" (#24/#29).
|
|
33
|
-
* Returns "" when coverage was complete.
|
|
34
|
-
*/
|
|
35
|
-
export declare function partialCoverageBlock(diagnostics: SearchDiagnostics): string;
|
|
36
|
-
/**
|
|
37
|
-
* Serial execution gate for AppleScript-backed tool calls (issue #11): only one
|
|
38
|
-
* osascript invocation hits Mail.app's single-threaded AppleScript dispatch at a
|
|
39
|
-
* time, with a short settle delay so the queue drains.
|
|
40
|
-
*/
|
|
41
|
-
export declare const serializeAppleScript: import("../utils/serialize.js").SerialGate;
|
|
42
|
-
/**
|
|
43
|
-
* Wraps a tool handler with consistent error handling, serialized through the
|
|
44
|
-
* AppleScript gate so concurrent MCP tool calls don't race into Mail.app (#11).
|
|
45
|
-
* Handlers may be sync or async; the result is awaited inside the gate.
|
|
46
|
-
*/
|
|
47
|
-
export declare function withErrorHandling<T extends Record<string, unknown>>(handler: (params: T) => ToolResponse | Promise<ToolResponse>, errorPrefix: string): (params: T) => Promise<ToolResponse>;
|
|
48
|
-
//# sourceMappingURL=respond.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"respond.d.ts","sourceRoot":"","sources":["../../src/tools/respond.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYlE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;aAExE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;wBACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAM9C;AAED,0CAA0C;AAC1C,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;aAEtE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;aAChC,IAAI;wBACO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAO9C;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AAEjG;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,iBAAiB,GAAG,MAAM,CAmB3E;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,2CAAqB,CAAC;AAEvD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,EAC5D,WAAW,EAAE,MAAM,IAEL,QAAQ,CAAC,2BAUxB"}
|
package/build/tools/respond.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared MCP tool-response helpers, the AppleScript serial gate, and the
|
|
3
|
-
* error-handling wrapper. Extracted from index.ts (D1) so every tool module
|
|
4
|
-
* shares one consistent response/serialization path.
|
|
5
|
-
*
|
|
6
|
-
* @module tools/respond
|
|
7
|
-
*/
|
|
8
|
-
import { createSerialGate } from "../utils/serialize.js";
|
|
9
|
-
/**
|
|
10
|
-
* Plain, JSON-friendly summary of a Message for `structuredContent` (A1) — dates
|
|
11
|
-
* as ISO strings, only the fields an agent needs to act (id is the key it passes
|
|
12
|
-
* back to get-message / mutations).
|
|
13
|
-
*/
|
|
14
|
-
export function messageSummary(m) {
|
|
15
|
-
return {
|
|
16
|
-
id: m.id,
|
|
17
|
-
subject: m.subject,
|
|
18
|
-
sender: m.sender,
|
|
19
|
-
dateReceived: m.dateReceived instanceof Date ? m.dateReceived.toISOString() : m.dateReceived,
|
|
20
|
-
isRead: m.isRead,
|
|
21
|
-
isFlagged: m.isFlagged,
|
|
22
|
-
mailbox: m.mailbox,
|
|
23
|
-
account: m.account,
|
|
24
|
-
hasAttachments: m.hasAttachments,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Creates a successful MCP tool response. When `structured` is provided it is
|
|
29
|
-
* returned as `structuredContent` so callers can consume typed data instead of
|
|
30
|
-
* re-parsing the human text (A1).
|
|
31
|
-
*/
|
|
32
|
-
export function successResponse(message, structured) {
|
|
33
|
-
const res = {
|
|
34
|
-
content: [{ type: "text", text: message }],
|
|
35
|
-
};
|
|
36
|
-
if (structured !== undefined)
|
|
37
|
-
res.structuredContent = structured;
|
|
38
|
-
return res;
|
|
39
|
-
}
|
|
40
|
-
/** Creates an error MCP tool response. */
|
|
41
|
-
export function errorResponse(message, structured) {
|
|
42
|
-
const res = {
|
|
43
|
-
content: [{ type: "text", text: message }],
|
|
44
|
-
isError: true,
|
|
45
|
-
};
|
|
46
|
-
if (structured !== undefined)
|
|
47
|
-
res.structuredContent = structured;
|
|
48
|
-
return res;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Render a partial-coverage warning from search/list diagnostics, so a caller
|
|
52
|
-
* never mistakes an incomplete scan for a confirmed "no matches" (#24/#29).
|
|
53
|
-
* Returns "" when coverage was complete.
|
|
54
|
-
*/
|
|
55
|
-
export function partialCoverageBlock(diagnostics) {
|
|
56
|
-
const notes = [];
|
|
57
|
-
if (diagnostics.timedOutAccounts.length > 0) {
|
|
58
|
-
notes.push(`timed out (no results) for account(s): ${diagnostics.timedOutAccounts.join(", ")}`);
|
|
59
|
-
}
|
|
60
|
-
if (diagnostics.skippedLargeMailboxes.length > 0) {
|
|
61
|
-
notes.push(`skipped mailbox(es) too large to scan via AppleScript: ${diagnostics.skippedLargeMailboxes.join(", ")} — scope with \`mailbox\` (+ a \`dateFrom\`/\`dateTo\` window for search) to reach them`);
|
|
62
|
-
}
|
|
63
|
-
if (diagnostics.notSearchedMailboxes.length > 0) {
|
|
64
|
-
notes.push(`could not finish scanning mailbox(es): ${diagnostics.notSearchedMailboxes.join(", ")}`);
|
|
65
|
-
}
|
|
66
|
-
if (notes.length === 0)
|
|
67
|
-
return "";
|
|
68
|
-
return `\n\n⚠️ Partial results — this is NOT a confirmed "no such mail":\n${notes
|
|
69
|
-
.map((n) => ` - ${n}`)
|
|
70
|
-
.join("\n")}`;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Serial execution gate for AppleScript-backed tool calls (issue #11): only one
|
|
74
|
-
* osascript invocation hits Mail.app's single-threaded AppleScript dispatch at a
|
|
75
|
-
* time, with a short settle delay so the queue drains.
|
|
76
|
-
*/
|
|
77
|
-
export const serializeAppleScript = createSerialGate();
|
|
78
|
-
/**
|
|
79
|
-
* Wraps a tool handler with consistent error handling, serialized through the
|
|
80
|
-
* AppleScript gate so concurrent MCP tool calls don't race into Mail.app (#11).
|
|
81
|
-
* Handlers may be sync or async; the result is awaited inside the gate.
|
|
82
|
-
*/
|
|
83
|
-
export function withErrorHandling(handler, errorPrefix) {
|
|
84
|
-
return async (params) => {
|
|
85
|
-
return serializeAppleScript(async () => {
|
|
86
|
-
try {
|
|
87
|
-
return await handler(params);
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
91
|
-
return errorResponse(`${errorPrefix}: ${message}`);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
};
|
|
95
|
-
}
|
package/build/tools/thread.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thread / conversation grouping (B1).
|
|
3
|
-
*
|
|
4
|
-
* Mail.app's AppleScript bridge has no thread object and IMAP threading
|
|
5
|
-
* (THREAD/REFERENCES) isn't uniformly available, so get-thread groups a
|
|
6
|
-
* conversation by its normalized subject: strip the reply/forward prefixes and
|
|
7
|
-
* gather every message sharing that base subject. Pragmatic and consistent
|
|
8
|
-
* across both the AppleScript and IMAP backends.
|
|
9
|
-
*
|
|
10
|
-
* @module tools/thread
|
|
11
|
-
*/
|
|
12
|
-
/**
|
|
13
|
-
* Strip leading reply/forward prefixes ("Re:", "Fwd:", "RE[2]:", localized
|
|
14
|
-
* variants, repeated/stacked) to get the conversation's base subject.
|
|
15
|
-
*/
|
|
16
|
-
export declare function normalizeSubject(subject: string): string;
|
|
17
|
-
/** Pull the subject line out of an imapGetMessage "Subject: …\n\n<body>" result. */
|
|
18
|
-
export declare function subjectFromGetMessage(info: string): string | null;
|
|
19
|
-
//# sourceMappingURL=thread.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/tools/thread.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWxD;AAED,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE"}
|
package/build/tools/thread.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thread / conversation grouping (B1).
|
|
3
|
-
*
|
|
4
|
-
* Mail.app's AppleScript bridge has no thread object and IMAP threading
|
|
5
|
-
* (THREAD/REFERENCES) isn't uniformly available, so get-thread groups a
|
|
6
|
-
* conversation by its normalized subject: strip the reply/forward prefixes and
|
|
7
|
-
* gather every message sharing that base subject. Pragmatic and consistent
|
|
8
|
-
* across both the AppleScript and IMAP backends.
|
|
9
|
-
*
|
|
10
|
-
* @module tools/thread
|
|
11
|
-
*/
|
|
12
|
-
/**
|
|
13
|
-
* Strip leading reply/forward prefixes ("Re:", "Fwd:", "RE[2]:", localized
|
|
14
|
-
* variants, repeated/stacked) to get the conversation's base subject.
|
|
15
|
-
*/
|
|
16
|
-
export function normalizeSubject(subject) {
|
|
17
|
-
const prefix = /^\s*(?:(?:re|fwd?|fw|aw|wg|sv|vs|antw|antwort|enc|rif)\s*(?:\[\d+\])?\s*:\s*)+/i;
|
|
18
|
-
let s = (subject ?? "").trim();
|
|
19
|
-
// Apply repeatedly in case of mixed-language stacked prefixes the single pass
|
|
20
|
-
// doesn't fully collapse.
|
|
21
|
-
let prev;
|
|
22
|
-
do {
|
|
23
|
-
prev = s;
|
|
24
|
-
s = s.replace(prefix, "").trim();
|
|
25
|
-
} while (s !== prev && s.length > 0);
|
|
26
|
-
return s || subject.trim();
|
|
27
|
-
}
|
|
28
|
-
/** Pull the subject line out of an imapGetMessage "Subject: …\n\n<body>" result. */
|
|
29
|
-
export function subjectFromGetMessage(info) {
|
|
30
|
-
const m = info.match(/^Subject:\s*(.*)$/m);
|
|
31
|
-
return m ? m[1].trim() : null;
|
|
32
|
-
}
|