list_senders
Group senders by message count, unread count, and last arrival time to identify bulk senders and reduce mailbox noise.
Instructions
Return a grouped count of senders in a mailbox — who sends how many messages, how many are unread, and when the last arrived. Ideal for identifying bulk senders and noise.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mailbox | No | Mailbox name or URL substring to restrict to (e.g. 'Amtrak', 'INBOX') | |
| account | No | Account host/name substring to restrict to | |
| limit | No | Max senders to return, ordered by message count desc |
Implementation Reference
- src/tools/list_senders.ts:44-66 (handler)The register function that registers the 'list_senders' tool with the MCP server and contains the full handler logic (lines 44-66). It receives {mailbox, account, limit}, builds a SQL query via buildSql, executes it against the Envelope Index SQLite database, and returns a JSON string of senders with address, name, message count, unread count, and last received timestamp.
export function register(server: McpServer): void { server.tool( "list_senders", "Return a grouped count of senders in a mailbox — who sends how many messages, how many are unread, and when the last arrived. Ideal for identifying bulk senders and noise.", schema, { title: "List Senders", readOnlyHint: true, destructiveHint: false }, async ({ mailbox, account, limit }) => { const dbPath = await locateEnvelopeIndex(); const sql = buildSql(mailbox, account, limit); const rows = await querySqlite<SenderRow>(dbPath, sql); const result = rows.map((r) => ({ address: r.sender_address, name: r.sender_name || null, count: Number(r.message_count), unread: Number(r.unread_count), lastReceived: r.last_received ? new Date(r.last_received * 1000).toISOString() : null, })); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; }, ); } - src/tools/list_senders.ts:5-9 (schema)Zod schema for the tool's input parameters: mailbox (optional string), account (optional string), limit (optional number, default 50, min 1, max 500).
const schema = { mailbox: z.string().optional().describe("Mailbox name or URL substring to restrict to (e.g. 'Amtrak', 'INBOX')"), account: z.string().optional().describe("Account host/name substring to restrict to"), limit: z.number().int().min(1).max(500).default(50).describe("Max senders to return, ordered by message count desc"), }; - src/server.ts:17-37 (registration)Import and registration of the list_senders tool on the McpServer instance (line 17 imports, line 37 registers).
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); registerSend(server); registerReply(server); registerFlags(server); registerMove(server); registerTrash(server); registerCreateMailbox(server); registerBulkMarkRead(server); registerGetUnsubscribeLink(server); registerListSenders(server); - src/tools/list_senders.ts:23-42 (helper)The buildSql function that constructs the SQL query to aggregate senders by address/comment with message count, unread count, and last received date, filtered by mailbox/account substring and deleted flag.
export function buildSql(mailbox?: string, account?: string, limit = 50): string { const where: string[] = ["m.deleted = 0"]; if (mailbox) where.push(`mb.url LIKE '%${sqlEscape(mailbox)}%'`); if (account) where.push(`mb.url LIKE '%${sqlEscape(account)}%'`); return ` SELECT a.address AS sender_address, a.comment AS sender_name, COUNT(*) AS message_count, SUM(CASE WHEN m.read = 0 THEN 1 ELSE 0 END) AS unread_count, MAX(m.date_received) AS last_received FROM messages m LEFT JOIN addresses a ON a.ROWID = m.sender LEFT JOIN mailboxes mb ON mb.ROWID = m.mailbox WHERE ${where.join(" AND ")} GROUP BY a.address, a.comment ORDER BY message_count DESC LIMIT ${Math.max(1, Math.floor(limit))}; `; } - src/tools/list_senders.ts:19-21 (helper)The sqlEscape utility function used to safely escape single quotes in SQL string literals.
function sqlEscape(v: string): string { return v.replace(/'/g, "''"); }