feat: MCP server for AI agent email access [WIP]#89
Conversation
- StreamableHTTP MCP endpoint at /api/mcp/stream (Bearer token auth) - API key management routes (generate/revoke via session auth) - Migration 0008: add mcp_api_key column to users table - 16 tools: list_accounts, list_folders, list_messages, get_message, get_thread, search_messages, mark_read, mark_starred, move_message, send_email, reply_to_message, forward_email, sync, get_unread_counts, delete_message, archive_message - All tools delegate to shared service layer (no SQL in MCP layer) - forward/reply preserve original HTML body via quotedBodyHtml - Admin panel: AI Agents tab (robot-head icon, id: mcp) - Restore CONCURRENTLY in 0006/0007 migrations to origin versions - 215 vitest tests covering messageQueries, mailActions, emailSend, mcp/server
…t, drop Claude mention
Current Blockers1. Migration number conflict The PR adds 2. Unique constraint vulnerability in
const newUid = await imapManager.moveMessage(...);
if (newUid != null) {
await query('UPDATE messages SET folder = $1, uid = $2 WHERE id = $3', [...]);
}There is a unique constraint on 3.
Should be addressed before merge4. MCP API key stored in plaintext All other sensitive values in the 5. No rate limiting on Every other route in the backend has rate limiting. The MCP stream endpoint has none. An API key holder can call it in a tight loop and hammer the IMAP manager directly. Please add a rate limit consistent with the rest of the API. 6. The existing Fix the three blockers and the above items and I'll get this merged. Happy to discuss any of this, let me know. |
Summary
Adds an MCP (Model Context Protocol) server to mailflow, allowing any MCP-compatible AI assistant to read and manage a user's inbox through a secure Bearer-token-authenticated endpoint. This is an exploratory implementation to see what an MCP integration could look like for mailflow — it needs more testing and review before it would be production-ready.
Changes
backend/src/mcp/server.js— MCP server with 16 tools:list_accounts,list_folders,list_messages,get_message,get_thread,search_messages,mark_read,mark_starred,move_message,send_email,reply_to_message,forward_email,sync,get_unread_counts,delete_message,archive_messagebackend/src/routes/mcp.js— StreamableHTTP endpoint at/api/mcp/stream; API key management routes (generate/revoke, session-authenticated)backend/migrations/0008_mcp_api_key.sql— addsmcp_api_keycolumn touserstableservices/messageQueries.js,services/mailActions.js,services/emailSend.jsso both HTTP routes and MCP tools use the same functions with no duplicated SQLgetMessagenow fetchesbody_html; forwarded and replied messages include the original HTML body viaquotedBodyHtmlmcptab label translated for all 6 supported localesmessageQueries,mailActions,emailSend, andmcp/serverNew dependency:
@modelcontextprotocol/sdk@^1.29.0— the official MCP reference implementation. This is the only viable way to implement the StreamableHTTP transport and JSON-RPC protocol correctly.zod(already in the dependency tree as a peer dep of the SDK) is also used directly and should be declared explicitly — happy to add it if the approach is approved.Testing
Manually tested against a live mailflow instance via MCP curl calls and a connected AI client:
npm test)More testing needed: edge cases around OAuth accounts, attachment forwarding, concurrent flag operations.
Contributor License Agreement
By submitting this pull request I confirm that: