// mailu's tools — moved here from the shared sdk (novox/hq ADR 0044), importing mailu's own client. // They return structured data; the mesh serves them through the sdk's tool harness. Deletions are // guarded by an explicit `confirm`, since removing a mailbox destroys its mail and cannot be undone. import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools"; import { MailuClient } from "../client.js"; export function getMailuTools(mailu: MailuClient): ToolDefinition[] { return [ { name: "mailu_list_users", description: "List all email accounts on the mail server.", input: {}, run: async () => { const users = await mailu.listUsers(); return { count: users.length, users }; }, }, { name: "mailu_create_user", description: "Create a new email account.", input: { email: { type: "string", description: "full address, e.g. user@example.com" }, password: { type: "string", description: "the account's initial password" }, }, run: async (args) => { const email = String(args.email); await mailu.createUser(email, String(args.password)); return { created: email }; }, }, { name: "mailu_change_password", description: "Change the password of an email account.", input: { email: { type: "string", description: "full address of the account" }, password: { type: "string", description: "the new password" }, }, run: async (args) => { const email = String(args.email); await mailu.changePassword(email, String(args.password)); return { changed: email }; }, }, { name: "mailu_delete_user", description: "Delete an email account. DESTRUCTIVE — removes the mailbox and all its mail.", input: { email: { type: "string", description: "full address of the account to delete" }, confirm: { type: "boolean", description: "must be true — deletion is irreversible" }, }, run: async (args) => { const email = String(args.email); if (args.confirm !== true) return { aborted: "confirm must be true to delete a user", email }; await mailu.deleteUser(email); return { deleted: email }; }, }, { name: "mailu_list_aliases", description: "List all email aliases and where they forward.", input: {}, run: async () => { const aliases = await mailu.listAliases(); return { count: aliases.length, aliases }; }, }, { name: "mailu_create_alias", description: "Create an email alias forwarding to one or more destinations.", input: { localpart: { type: "string", description: "the part before @, e.g. 'sales'" }, domain: { type: "string", description: "the domain, e.g. example.com" }, destination: { type: "string", description: "destination address(es), comma-separated" }, wildcard: { type: "boolean", description: "match any localpart under the domain (optional)" }, }, run: async (args) => { const email = `${String(args.localpart)}@${String(args.domain)}`; const destination = String(args.destination).split(",").map((d) => d.trim()).filter(Boolean); await mailu.createAlias(email, destination, args.wildcard === true); return { created: email, destination }; }, }, { name: "mailu_delete_alias", description: "Delete an email alias.", input: { email: { type: "string", description: "the alias address to delete" }, confirm: { type: "boolean", description: "must be true to confirm deletion" }, }, run: async (args) => { const email = String(args.email); if (args.confirm !== true) return { aborted: "confirm must be true to delete an alias", email }; await mailu.deleteAlias(email); return { deleted: email }; }, }, { name: "mailu_list_domains", description: "List the mail domains the server handles.", input: {}, run: async () => { const domains = await mailu.listDomains(); return { count: domains.length, domains }; }, }, { name: "mailu_read_mail", description: "Read recent messages in a user's mailbox — date, from, subject and a preview.", input: { user: { type: "string", description: "the mailbox owner's address" }, mailbox: { type: "string", description: "which mailbox (default INBOX)" }, limit: { type: "number", description: "how many recent messages (default 10)" }, }, run: async (args) => { const user = String(args.user); const mailbox = args.mailbox ? String(args.mailbox) : "INBOX"; const messages = await mailu.readMail(user, mailbox, args.limit ? Number(args.limit) : 10); return { user, mailbox, count: messages.length, messages }; }, }, { name: "mailu_search_mail", description: "Search a user's mailbox by subject, sender, and/or date.", input: { user: { type: "string", description: "the mailbox owner's address" }, subject: { type: "string", description: "substring to match in the subject (optional)" }, from: { type: "string", description: "sender address or name to match (optional)" }, since: { type: "string", description: "only messages since a date, e.g. 01-Jan-2026 (optional)" }, limit: { type: "number", description: "maximum results (default 10)" }, }, run: async (args) => { const user = String(args.user); const criteria = { subject: args.subject ? String(args.subject) : undefined, from: args.from ? String(args.from) : undefined, since: args.since ? String(args.since) : undefined, }; const messages = await mailu.searchMail(user, criteria, args.limit ? Number(args.limit) : 10); return { user, count: messages.length, messages }; }, }, ]; } // The tools exist only when the admin API is configured; without it, mailu contributes none rather // than failing the whole runtime. registerModuleTools("mailu", (env) => { try { return getMailuTools(MailuClient.fromEnv(env)); } catch { return []; } });