list_recent
Retrieve recent emails from a specified mailbox in your Mail.app account. Filter by number of messages or show only unread ones.
Instructions
List recent messages in a mailbox of a specific account.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name as shown in Mail.app | |
| mailbox | No | INBOX | |
| limit | No | ||
| unread_only | No |
Implementation Reference
- src/tools/list_recent.ts:80-100 (handler)The register function that registers the 'list_recent' tool with the MCP server. The handler executes an AppleScript to list recent messages in a specified mailbox, parses the output, and returns it as JSON.
export function register(server: McpServer): void { server.tool( "list_recent", "List recent messages in a mailbox of a specific account.", schema, { title: "List Recent Emails", readOnlyHint: true, destructiveHint: false }, async ({ account, mailbox, limit, unread_only }) => { const raw = await runAppleScript({ script: SCRIPT, args: { acctName: account, mbName: mailbox, limStr: String(limit), unreadOnlyStr: unread_only ? "true" : "false", }, }); return { content: [{ type: "text", text: JSON.stringify(parse(raw), null, 2) }], }; }, ); - src/tools/list_recent.ts:5-10 (schema)Zod schema for the tool's input parameters: account (string), mailbox (string, default INBOX), limit (1-50, default 10), unread_only (boolean, default false).
const schema = { account: z.string().describe("Account name as shown in Mail.app"), mailbox: z.string().default("INBOX"), limit: z.number().int().min(1).max(50).default(10), unread_only: z.boolean().default(false), }; - src/server.ts:8-28 (registration)Import and registration of the list_recent tool in the main server file. Line 8 imports register, line 28 calls registerListRecent(server).
import { register as registerListRecent } from "./tools/list_recent.js"; import { register as registerSend } from "./tools/send.js"; import { register as registerReply } from "./tools/reply.js"; import { register as registerFlags } from "./tools/flags.js"; import { register as registerMove } from "./tools/move.js"; import { register as registerTrash } from "./tools/trash.js"; import { register as registerCreateMailbox } from "./tools/create_mailbox.js"; import { register as registerBulkMarkRead } from "./tools/bulk_mark_read.js"; import { register as registerGetUnsubscribeLink } from "./tools/get_unsubscribe_link.js"; import { register as registerListSenders } from "./tools/list_senders.js"; import { register as registerEmptyMailbox } from "./tools/empty_mailbox.js"; const server = new McpServer({ name: "mail-app-mcp", version: "1.0.0", }); registerSearch(server); registerRead(server); registerAccounts(server); registerListRecent(server); - src/lib/applescript.ts:22-46 (helper)The runAppleScript helper used by the handler to execute the AppleScript via osascript with safe argument passing.
export async function runAppleScript(opts: RunAppleScriptOptions): Promise<string> { const { script, args = {}, timeoutMs = 30_000 } = opts; const argNames = Object.keys(args); const argValues = argNames.map((n) => args[n]!); const bindings = argNames .map((name, i) => ` set ${name} to item ${i + 1} of argv`) .join("\n"); const wrapped = `on run argv\n${bindings}\n${script}\nend run\n`; const dir = await mkdtemp(path.join(tmpdir(), "mail-mcp-")); const scriptPath = path.join(dir, "script.applescript"); try { await writeFile(scriptPath, wrapped, "utf8"); const { stdout } = await execFileP("osascript", [scriptPath, ...argValues], { timeout: timeoutMs, maxBuffer: 10 * 1024 * 1024, }); return stdout.trimEnd(); } finally { await rm(dir, { recursive: true, force: true }).catch(() => {}); } } - src/tools/list_recent.ts:63-78 (helper)The parse function that transforms the raw AppleScript output (field-separated lines) into an array of RecentMsg objects with subject, from, date, unread, and messageId.
function parse(raw: string): RecentMsg[] { const out: RecentMsg[] = []; for (const line of raw.split("\n")) { if (!line) continue; const parts = line.split(FIELD_SEP); if (parts.length < 5) continue; out.push({ subject: parts[0] ?? "", from: parts[1] ?? "", date: parts[2] ?? "", unread: parts[3] === "true", messageId: parts[4] ?? "", }); } return out; }