Initial commit: Hermes MCP - Yahoo Mail server for Claude AI
- Multi-account email support (Yahoo + self-hosted IMAP/SMTP) - MCP tools: get_profile, search_messages, read_message, list_folders, create_draft, send_email - Streamable HTTP transport with session recovery - Docker + Kubernetes deployment configuration - Express server with health endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
97
src/smtp.ts
Normal file
97
src/smtp.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import nodemailer from 'nodemailer';
|
||||
import type { Account } from './imap.js';
|
||||
|
||||
function getSmtpTransport(account: Account = 'yahoo') {
|
||||
if (account === 'fetcherpay') {
|
||||
return nodemailer.createTransport({
|
||||
host: process.env['FETCHERPAY_SMTP_HOST'] ?? 'mail.fetcherpay.com',
|
||||
port: parseInt(process.env['FETCHERPAY_SMTP_PORT'] ?? '30587'),
|
||||
secure: false, // STARTTLS
|
||||
auth: {
|
||||
user: process.env['FETCHERPAY_EMAIL']!,
|
||||
pass: process.env['FETCHERPAY_PASSWORD']!,
|
||||
},
|
||||
tls: { rejectUnauthorized: false }, // self-signed cert
|
||||
});
|
||||
}
|
||||
return nodemailer.createTransport({
|
||||
host: 'smtp.mail.yahoo.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: process.env['YAHOO_EMAIL']!,
|
||||
pass: process.env['YAHOO_APP_PASSWORD']!,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getSenderEmail(account: Account = 'yahoo'): string {
|
||||
return account === 'fetcherpay'
|
||||
? process.env['FETCHERPAY_EMAIL']!
|
||||
: process.env['YAHOO_EMAIL']!;
|
||||
}
|
||||
|
||||
export async function sendEmail(
|
||||
to: string,
|
||||
subject: string,
|
||||
body: string,
|
||||
account: Account = 'yahoo',
|
||||
): Promise<string> {
|
||||
const transporter = getSmtpTransport(account);
|
||||
const info = await transporter.sendMail({
|
||||
from: getSenderEmail(account),
|
||||
to,
|
||||
subject,
|
||||
text: body,
|
||||
});
|
||||
return info.messageId;
|
||||
}
|
||||
|
||||
export async function createDraft(
|
||||
to: string,
|
||||
subject: string,
|
||||
body: string,
|
||||
account: Account = 'yahoo',
|
||||
): Promise<string> {
|
||||
const { ImapFlow } = await import('imapflow');
|
||||
|
||||
const imapConfig = account === 'fetcherpay'
|
||||
? {
|
||||
host: process.env['FETCHERPAY_IMAP_HOST'] ?? 'mail.fetcherpay.com',
|
||||
port: parseInt(process.env['FETCHERPAY_IMAP_PORT'] ?? '30993'),
|
||||
secure: true,
|
||||
auth: {
|
||||
user: process.env['FETCHERPAY_EMAIL']!,
|
||||
pass: process.env['FETCHERPAY_PASSWORD']!,
|
||||
},
|
||||
tls: { rejectUnauthorized: false },
|
||||
}
|
||||
: {
|
||||
host: 'imap.mail.yahoo.com',
|
||||
port: 993,
|
||||
secure: true,
|
||||
auth: {
|
||||
user: process.env['YAHOO_EMAIL']!,
|
||||
pass: process.env['YAHOO_APP_PASSWORD']!,
|
||||
},
|
||||
};
|
||||
|
||||
const client = new ImapFlow(imapConfig);
|
||||
await client.connect();
|
||||
|
||||
const from = getSenderEmail(account);
|
||||
const rawMessage = [
|
||||
`From: ${from}`,
|
||||
`To: ${to}`,
|
||||
`Subject: ${subject}`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: text/plain; charset=UTF-8`,
|
||||
``,
|
||||
body,
|
||||
].join('\r\n');
|
||||
|
||||
await client.append('Drafts', Buffer.from(rawMessage), ['\\Draft', '\\Seen']);
|
||||
await client.logout();
|
||||
|
||||
return `Draft created: "${subject}" to ${to}`;
|
||||
}
|
||||
Reference in New Issue
Block a user