Das Problem

Setup: thomas.mueller@egonetix.de ist ein MCP-gesteuerter Assistent-Account, der den Kalender seines Chefs robert.wiegand@egonetix.de pflegen soll. Konkret: 2-Wochen-Rhythmus-Serie (“Kind bei Papa Mi+Do”, “Do-So”) in Roberts Kalender anlegen.

Klingt trivial. Ist es nicht. grommunio (bzw. dessen SabreDAV-basierter DAV-Layer) wirft dir vier verschiedene Steine in den Weg.

TL;DR

  • METHOD-Property im VCALENDAR-Block → SabreDAV antwortet 415 Unsupported Media Type. Weglassen.
  • ATTENDEE-Property beim CalDAV-PUT → grommunio verschluckt sie kommentarlos, keine Einladungsmail geht raus.
  • Cross-Principal-Zugriff via CalDAV (/dav/calendars/<anderer-user>/Calendar/) → 404 Not Found, auch wenn Kalender in grommunio-Web geteilt wurde.
  • ICS als Mail-Attachment → grommunio-Web zeigt nur “Download-Link”, keinen Annehmen-Button.

Funktioniert: Direktimport per gromox-eml2mt --ical | gromox-mt2exm -u <email> -B CALENDAR auf dem Mailserver selbst. Bypass über MAPI, Events landen sauber im Zielkalender.

Stein 1: METHOD-Property

Das iCal-Template vieler Libraries setzt bei Einladungen automatisch:

BEGIN:VCALENDAR
METHOD:REQUEST
...

Ist RFC 5545-konform. SabreDAV (grommunios DAV-Stack) lehnt aber ab:

415 Unsupported Media Type
Validation error in iCalendar: A calendar object on a CalDAV server MUST NOT have a METHOD property.

Spec-Zitat: RFC 4791 §4.1 sagt tatsächlich, dass gespeicherte CalDAV-Ressourcen keine METHOD-Property haben dürfen. METHOD ist nur für iCalendar-Streams (Einladungsmails) relevant.

Fix: In deinem iCal-Generator den METHOD-Part raus, wenn du ein Event via CalDAV-PUT speicherst.

Stein 2: ATTENDEE wird verschluckt

Naiver Gedanke: “Wenn ich ein Event mit ATTENDEE:mailto:robert@... via CalDAV schreibe, schickt grommunio automatisch eine Einladungsmail. Macht Outlook ja auch so.”

Falsch. grommunio speichert das Event lokal, aber die Einladung geht nicht raus. Schlimmer: Die ATTENDEE-Property wird von gromox-oxcical bei der MAPI-Konvertierung rausgekürzt. Später ist sie weg.

Verifikation auf dem Server:

ssh root@srvmail01
# Roh-ICS aus dem CalDAV-Store lesen
python3 -c "
import caldav
c = caldav.DAVClient(url='https://srvmail01/dav', username='thomas.mueller@egonetix.de', password='...', ssl_verify_cert=False)
cal = c.principal().calendars()[0]
for ev in cal.events():
    if 'junior da' in ev.data:
        print(ev.data); break
"

Ausgabe: Keine ATTENDEE-Zeile mehr.

Hintergrund: Einladungen werden in grommunio vom Scheduling-Layer (IPM.Schedule.Meeting.Request) behandelt, den CalDAV-Clients nicht direkt ansprechen. Outlook/grommunio-Web gehen einen anderen Weg.

Stein 3: Cross-Principal-Zugriff

Plan B: Wenn Auto-Invitations nicht gehen, dann soll der Assistent halt direkt in Roberts Kalender schreiben. Kalender wurde in grommunio-Web geteilt (Thomas bekommt “Autor”-Rechte). Also:

# Als Thomas auf Roberts Kalender zugreifen
client = caldav.DAVClient(
    url='https://srvmail01/dav/calendars/robert.wiegand@egonetix.de/Calendar/',
    username='thomas.mueller@egonetix.de',
    password='...'
)
cal = caldav.Calendar(client=client, url='...')
cal.get_properties([...])

Antwort:

404 Not Found
Sabre\DAV\Exception\NotFound
Node with name 'Calendar' could not be found

grommunio/SabreDAV exposed das Sharing nicht über CalDAV. Die in grommunio-Web konfigurierte Berechtigung wirkt in Outlook/Web, aber über den DAV-Endpunkt siehst du nur deinen eigenen Principal. Known Limitation.

Stein 4: ICS-Einladung per Mail

Nächster Versuch: E-Mail an Robert mit text/calendar; method=REQUEST als Inhalt. grommunio-Web sollte das als Meeting-Request erkennen und einen Annehmen-Button zeigen.

Erster Versuch: Als Attachment (Content-Disposition: attachment; filename=invite.ics). Ergebnis: grommunio-Web zeigt nur invite.ics (1.1 KB) mit Download-Link, kein Annehmen-Button.

Zweiter Versuch: Als multipart/alternative mit dem Calendar-Part inline (ohne Disposition). Ergebnis: grommunio-Web zeigt den Termin als .vcs-Attachment. Immer noch kein Annehmen-Button.

Grund: grommunio-Web hat strikte Anforderungen an das MIME-Layout von Meeting-Requests. Bei Mail-Delivery über SMTP → PMG (Proxmox Mail Gateway) → gromox-delivery läuft die Mail durch mehrere Filter, und je nach Layout bleibt nur noch ein Attachment übrig. Die native Exchange/Outlook-Invitations-Semantik (PR_MESSAGE_CLASS = IPM.Schedule.Meeting.Request) wird nur zuverlässig gesetzt, wenn die Mail aus einem MAPI-Client kommt — nicht aus einem generischen Python-smtplib-Skript.

Man kann das vermutlich hinbekommen (richtige Content-Type-Parameter, TNEF, etc.), aber der Aufwand lohnt sich nicht, weil es eine saubere Lösung gibt:

Die Lösung: MAPI-Direktimport via gromox

gromox (das Backend von grommunio) bringt CLI-Tools mit, die iCal direkt als MAPI-Message in einer beliebigen Mailbox anlegen — ohne SMTP, ohne CalDAV, ohne Einladungs-Roundtrip.

ssh root@srvmail01

# ICS-Datei schreiben (mit Serie)
cat > /tmp/event.ics <<'EOF'
BEGIN:VCALENDAR
PRODID:-//example//DE
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:12345@example.de
DTSTAMP:20260419T140000Z
DTSTART;TZID=Europe/Berlin:20260422T000000
DTEND;TZID=Europe/Berlin:20260424T000000
SUMMARY:Kind bei Papa
RRULE:FREQ=WEEKLY;INTERVAL=2
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
EOF

# Import in Roberts Kalender
gromox-eml2mt --ical /tmp/event.ics | \
  gromox-mt2exm -u robert.wiegand@egonetix.de -B CALENDAR

gromox-eml2mt --ical liest iCal-Input und produziert einen MAPI-Message-Stream (MT-Format). gromox-mt2exm schiebt diesen Stream in die angegebene Mailbox (-u) in den Kalender-Ordner (-B CALENDAR).

Verifikation

gromox-mbop -u robert.wiegand@egonetix.de get-freebusy \
  -a 2026-04-20T00:00:00 -b 2026-05-20T00:00:00 | grep "Kind bei Papa"

Output sollte die expandierten Serien-Instanzen zeigen. Robert sieht die Termine nach dem nächsten Sync in grommunio-Web / Outlook.

Haken: Löschen muss der Empfänger selbst

Die umgekehrte Operation (delete in fremder Mailbox via CalDAV) hat denselben Cross-Principal-Block wie das Schreiben. gromox-mbop delmsg -f <folder-id> funktioniert zwar, braucht aber Folder-ID + Message-ID-Mapping. Umständlich.

Pragmatisch: Wenn der importierte Event falsch ist, lässt du den Mailbox-Besitzer ihn in grommunio-Web per Rechtsklick löschen (drei Sekunden) und importierst korrigiert neu.

Fazit

grommunio/gromox ist ein ausgezeichneter MAPI-Server mit solidem Exchange-Replacement-Anspruch — aber der CalDAV-Layer (SabreDAV) ist eine dünne Kompatibilitätsschicht, die nicht alle Features abbildet. Wenn du Kalender-Automatisierung für mehrere User machst, vergiss CalDAV als Cross-User-Protokoll und gehe direkt über die gromox-CLI-Tools.

Das heißt auch: dein Automatisierungs-Tooling muss SSH-Zugriff auf den Mailserver haben. Für Homelab und kleine Teams ist das kein Problem; für Multi-Tenant-SaaS eher schon.

Referenzen