protonmail-mcp

by amotivv
Verified
  • src
#!/usr/bin/env node /** * Protonmail MCP Server * * This MCP server provides email sending functionality using Protonmail's SMTP service. * It implements a single tool for sending emails with various options. */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from "@modelcontextprotocol/sdk/types.js"; import { EmailService, EmailConfig } from "./email-service.js"; // Get environment variables for SMTP configuration const PROTONMAIL_USERNAME = process.env.PROTONMAIL_USERNAME; const PROTONMAIL_PASSWORD = process.env.PROTONMAIL_PASSWORD; const PROTONMAIL_HOST = process.env.PROTONMAIL_HOST || "smtp.protonmail.ch"; const PROTONMAIL_PORT = parseInt(process.env.PROTONMAIL_PORT || "587", 10); const PROTONMAIL_SECURE = process.env.PROTONMAIL_SECURE === "true"; const DEBUG = process.env.DEBUG === "true"; // Validate required environment variables if (!PROTONMAIL_USERNAME || !PROTONMAIL_PASSWORD) { console.error("[Error] Missing required environment variables: PROTONMAIL_USERNAME and PROTONMAIL_PASSWORD must be set"); process.exit(1); } // Helper function for debug logging function debugLog(message: string): void { if (DEBUG) { console.error(message); } } // Create email service configuration const emailConfig: EmailConfig = { host: PROTONMAIL_HOST, port: PROTONMAIL_PORT, secure: PROTONMAIL_SECURE, auth: { user: PROTONMAIL_USERNAME, pass: PROTONMAIL_PASSWORD, }, debug: DEBUG, }; // Initialize email service const emailService = new EmailService(emailConfig); /** * Create an MCP server with capabilities for tools (to send emails) */ const server = new Server( { name: "protonmail-mcp", version: "0.1.0", }, { capabilities: { tools: {}, }, } ); /** * Handler that lists available tools. * Exposes a single "send_email" tool that lets clients send emails. */ server.setRequestHandler(ListToolsRequestSchema, async () => { debugLog("[Setup] Listing available tools"); return { tools: [ { name: "send_email", description: "Send an email using Protonmail SMTP", inputSchema: { type: "object", properties: { to: { type: "string", description: "Recipient email address(es). Multiple addresses can be separated by commas." }, subject: { type: "string", description: "Email subject line" }, body: { type: "string", description: "Email body content (can be plain text or HTML)" }, isHtml: { type: "boolean", description: "Whether the body contains HTML content", default: false }, cc: { type: "string", description: "CC recipient(s), separated by commas" }, bcc: { type: "string", description: "BCC recipient(s), separated by commas" } }, required: ["to", "subject", "body"] } } ] }; }); /** * Handler for the send_email tool. * Sends an email with the provided details and returns success message. */ server.setRequestHandler(CallToolRequestSchema, async (request) => { debugLog(`[Tool] Executing tool: ${request.params.name}`); switch (request.params.name) { case "send_email": { const args = request.params.arguments; // Validate required arguments if (!args || typeof args !== "object") { throw new McpError(ErrorCode.InvalidParams, "Invalid arguments"); } const { to, subject, body, isHtml, cc, bcc } = args as any; if (!to || typeof to !== "string") { throw new McpError(ErrorCode.InvalidParams, "Missing or invalid 'to' parameter"); } if (!subject || typeof subject !== "string") { throw new McpError(ErrorCode.InvalidParams, "Missing or invalid 'subject' parameter"); } if (!body || typeof body !== "string") { throw new McpError(ErrorCode.InvalidParams, "Missing or invalid 'body' parameter"); } try { // Send the email const result = await emailService.sendEmail({ to, subject, body, isHtml: isHtml === true, cc: typeof cc === "string" ? cc : undefined, bcc: typeof bcc === "string" ? bcc : undefined, }); return { content: [{ type: "text", text: `Email sent successfully to ${to}${cc ? ` with CC to ${cc}` : ""}${bcc ? ` and BCC to ${bcc}` : ""}.` }] }; } catch (error) { // Always log errors, even in non-debug mode console.error(`[Error] Failed to send email: ${error instanceof Error ? error.message : String(error)}`); throw new McpError( ErrorCode.InternalError, `Failed to send email: ${error instanceof Error ? error.message : String(error)}` ); } } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } }); /** * Start the server using stdio transport. * This allows the server to communicate via standard input/output streams. */ async function main() { debugLog("[Setup] Starting Protonmail MCP server..."); try { // Verify SMTP connection on startup await emailService.verifyConnection(); const transport = new StdioServerTransport(); await server.connect(transport); debugLog("[Setup] Protonmail MCP server started successfully"); } catch (error) { console.error(`[Error] Server startup failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } } // Set up error handling process.on("uncaughtException", (error) => { console.error(`[Error] Uncaught exception: ${error.message}`); process.exit(1); }); process.on("unhandledRejection", (reason) => { console.error(`[Error] Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`); process.exit(1); }); // Start the server main().catch((error) => { console.error(`[Error] Server error: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); });