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:
+57
-1
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user