import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { searchMessages, readMessage, getProfile, listFolders, type Account } from './imap.js'; import { sendEmail, createDraft } from './smtp.js'; const ACCOUNT_PARAM = { account: { type: 'string', enum: ['yahoo', 'fetcherpay', 'garfield', 'sales', 'leads', 'founder'], description: 'Which mailbox to use: "yahoo" (gheron01@yahoo.com), "fetcherpay" (garfield.heron@fetcherpay.com), "garfield" (garfield@fetcherpay.com), "sales" (sales@fetcherpay.com), "leads" (leads@fetcherpay.com), or "founder" (founder@fetcherpay.com). Defaults to "yahoo".', }, }; export const tools: Tool[] = [ { name: 'get_profile', description: 'Get the email account profile (email address and name)', inputSchema: { type: 'object', properties: { ...ACCOUNT_PARAM }, }, }, { name: 'search_messages', description: 'Search email messages by keyword, sender, or subject', inputSchema: { type: 'object', properties: { q: { type: 'string', description: 'Search query (keyword, from:email, subject:text)' }, maxResults: { type: 'number', description: 'Max messages to return (default 20)' }, ...ACCOUNT_PARAM, }, required: ['q'], }, }, { name: 'read_message', description: 'Read a full email message by UID', inputSchema: { type: 'object', properties: { uid: { type: 'number', description: 'Message UID from search results' }, ...ACCOUNT_PARAM, }, required: ['uid'], }, }, { name: 'list_folders', description: 'List all email folders/mailboxes', inputSchema: { type: 'object', properties: { ...ACCOUNT_PARAM }, }, }, { name: 'create_draft', description: 'Create a draft email', inputSchema: { type: 'object', properties: { to: { type: 'string', description: 'Recipient email address' }, subject: { type: 'string', description: 'Email subject' }, body: { type: 'string', description: 'Email body (plain text)' }, ...ACCOUNT_PARAM, }, required: ['to', 'subject', 'body'], }, }, { name: 'send_email', description: 'Send an email', inputSchema: { type: 'object', properties: { to: { type: 'string', description: 'Recipient email address' }, subject: { type: 'string', description: 'Email subject' }, body: { type: 'string', description: 'Email body (plain text)' }, ...ACCOUNT_PARAM, }, required: ['to', 'subject', 'body'], }, }, ]; function acct(args: Record): Account { return (args.account as Account) ?? 'yahoo'; } export async function handleToolCall( name: string, args: Record ): Promise<{ content: Array<{ type: string; text: string }> }> { console.log(`[tool] ${name}`, JSON.stringify(args)); const t0 = Date.now(); try { let result: unknown; switch (name) { case 'get_profile': result = await getProfile(acct(args)); break; case 'search_messages': result = await searchMessages(args.q as string, (args.maxResults as number) ?? 20, acct(args)); break; case 'read_message': result = await readMessage(args.uid as number, acct(args)); break; case 'list_folders': result = await listFolders(acct(args)); break; case 'create_draft': result = await createDraft(args.to as string, args.subject as string, args.body as string, acct(args)); break; case 'send_email': result = await sendEmail(args.to as string, args.subject as string, args.body as string, acct(args)); break; // Legacy Yahoo-prefixed names — keep working for any cached Claude sessions case 'yahoo_get_profile': result = await getProfile('yahoo'); break; case 'yahoo_search_messages': result = await searchMessages(args.q as string, (args.maxResults as number) ?? 20, 'yahoo'); break; case 'yahoo_read_message': result = await readMessage(args.uid as number, 'yahoo'); break; case 'yahoo_list_folders': result = await listFolders('yahoo'); break; case 'yahoo_create_draft': result = await createDraft(args.to as string, args.subject as string, args.body as string, 'yahoo'); break; case 'yahoo_send_email': result = await sendEmail(args.to as string, args.subject as string, args.body as string, 'yahoo'); break; default: throw new Error(`Unknown tool: ${name}`); } console.log(`[tool] ${name} OK (${Date.now() - t0}ms)`); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } catch (error) { const msg = (error as Error).message; const stack = (error as Error).stack ?? ''; console.error(`[tool] ${name} ERROR (${Date.now() - t0}ms):`, msg); console.error(stack); return { content: [{ type: 'text', text: `Error: ${msg}` }], }; } }