Add create_draft tool to Mail server

Creates email drafts via IMAP APPEND to the Drafts folder.
Supports Stefan (1blu, Drafts folder) and Kati (Gmail, Entwürfe).
Draft can be reviewed and sent from Roundcube or phone.

IMAP credentials externalized to config.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Stefan Lohmaier
2026-06-12 08:16:53 +02:00
parent 5190e8c849
commit 1f98695821
+57 -1
View File
@@ -3,8 +3,11 @@
import os import os
import sys import sys
import contextlib import contextlib
import imaplib
import mailbox import mailbox
from email.header import decode_header from email.header import decode_header
from email.mime.text import MIMEText
from email.utils import formatdate
from pathlib import Path from pathlib import Path
from typing import Annotated from typing import Annotated
@@ -17,7 +20,25 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from common import get_current_user, OAUTH_ROUTES, BearerAuthMiddleware from common import get_current_user, OAUTH_ROUTES, BearerAuthMiddleware
from common import load_config as _lc from common import load_config as _lc
MAIL_ROOTS = _lc()["mail_roots"] _cfg = _lc()
MAIL_ROOTS = _cfg["mail_roots"]
IMAP_ACCOUNTS = {
"stefan": {
"host": "imap.1blu.de",
"user": "d370128_0-slohmaier",
"password": _cfg["imap_passwords"]["stefan"],
"drafts_folder": "Drafts",
"from_addr": "stefan@slohmaier.de",
},
"kati": {
"host": "imap.gmail.com",
"user": "akpolke@gmail.com",
"password": _cfg["imap_passwords"]["kati"],
"drafts_folder": "[Google Mail]/Entw&APw-rfe",
"from_addr": "akpolke@gmail.com",
},
}
mcp = FastMCP("Mail", stateless_http=True, mcp = FastMCP("Mail", stateless_http=True,
transport_security={"enable_dns_rebinding_protection": False}) transport_security={"enable_dns_rebinding_protection": False})
@@ -171,6 +192,41 @@ def read_mail(
) )
@mcp.tool()
def create_draft(
to: Annotated[str, Field(description="Recipient email address, e.g. 'max@example.com'")],
subject: Annotated[str, Field(description="Email subject line")],
body: Annotated[str, Field(description="Email body text (plain text)")],
cc: Annotated[str, Field(description="CC recipients, comma-separated")] = "",
account: Annotated[str, Field(description="Which account to create the draft in. Leave empty for default account.")] = "",
) -> str:
"""Create an email draft in the Drafts folder. The draft can then be reviewed and sent from Roundcube or phone. Does NOT send the email."""
user = get_current_user()
if not user: return "Error: not authenticated"
acct = IMAP_ACCOUNTS.get(user)
if not acct: return f"Kein IMAP-Account fuer {user} konfiguriert"
msg = MIMEText(body, "plain", "utf-8")
msg["From"] = acct["from_addr"]
msg["To"] = to
if cc:
msg["Cc"] = cc
msg["Subject"] = subject
msg["Date"] = formatdate(localtime=True)
msg["X-Mailer"] = "MCP Mail Server"
try:
imap = imaplib.IMAP4_SSL(acct["host"])
imap.login(acct["user"], acct["password"])
status, _ = imap.append(acct["drafts_folder"], "\\Draft", None, msg.as_bytes())
imap.logout()
if status == "OK":
return f"Entwurf erstellt: An {to}, Betreff \"{subject}\". Oeffne Roundcube oder die Mail-App um ihn zu senden."
return f"Fehler beim Erstellen des Entwurfs: {status}"
except Exception as e:
return f"IMAP-Fehler: {e}"
def create_app(): def create_app():
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
mcp_app = mcp.streamable_http_app() mcp_app = mcp.streamable_http_app()