kern.services
•
automation tools email calendar mcp ai productivity

When Your AI Bot Needs Real Mail and Calendar, Not Another Cloud Connector

How Ironbrain MailCal — a .NET 10 CLI and MCP server for IMAP, SMTP and CalDAV — gave AI bots real access to self-hosted mail and shared calendars, without leaning on Gmail or Microsoft 365 connectors.

The gap between “AI can use email” and my actual inbox

I run self-hosted mail. Mailcow with SOGo, a mox instance here and there — the usual mix when you care about owning the stack. For day-to-day work that is fine. For AI assistants and bots, it is suddenly a problem.

Most “connect your email” stories assume Gmail or Microsoft 365. They ship polished OAuth connectors, curated scopes, and demos that look great — until your bot needs to talk to a real IMAP mailbox and a real CalDAV calendar on infrastructure you control. Shared and delegated calendars. A dedicated bot account that may send mail. A personal account that must stay read-only. Multiple calendars under one login.

The assistants I want to build do not live in a single vendor silo. They need the boring protocols that self-hosted mail already speaks: IMAP, SMTP, CalDAV. And they need those protocols in a shape that tools like Cursor can call without a browser session.

That gap stayed open longer than I liked.

The realization

The missing piece was not another web UI. It was a small, honest layer that:

  1. Speaks IMAP / SMTP / CalDAV against real servers (Mailcow/SOGo, mox, and friends).
  2. Supports multi-account config — bot send vs personal read-only — without mixing identities by accident.
  3. Discovers multiple calendars, including shared and delegated ones.
  4. Exposes the same capabilities as a CLI and as an MCP server over stdio, so an AI agent can use it the same way I do in a terminal.

In other words: treat mail and calendar like any other integration surface for automation — not as a product login page.

The tool: Ironbrain MailCal

So I built Ironbrain MailCal as part of the Ironbrain toolbox — see also the MailCal product page .

Two .NET 10 global tools:

  • Ironbrain.MailCal.Cli → ironbrain-mailcal
  • Ironbrain.MailCal.Mcp → ironbrain-mailcal-mcp (stdio MCP for Cursor and other MCP clients)

Current nuget.org release is 0.1.1 (the CLI reports an informational version with the build commit, e.g. 0.1.1+76b2152…). Full setup and command reference live on the MailCal install page ( German ).

CLI vs MCP — complementary, not either/or

Both surfaces share the same config at ~/.config/ironbrain/mailcal.json: accounts, calendars, and the SMTP send gate. They are two transports on one stack, not competing products.

CLI (ironbrain-mailcal)MCP (ironbrain-mailcal-mcp)
ForScripts, cron/routines, CI, smoke tests, bot hosts that shell outCursor and other MCP clients in-chat
ShapeExit codes, stdout, something a cron job can callstdio tools the agent invokes
ConfigSame file, same --account / accounts mapSame file, same send gate

Ops bots call the CLI. Cursor talks MCP. I am not maintaining two stories of “how we talk to mail.”

What matters in practice

A few design choices that came straight from real bot setups:

  • Multi-account config at ~/.config/ironbrain/mailcal.json, with a defaultAccount and an accounts map. One tool install, several identities.
  • SMTP send gate: set Email.Smtp.Enabled to false on personal accounts so a bot (or a clumsy prompt) cannot accidentally send as you. The assistant account can send; the personal account stays read-only for mail.
  • CalDAV multi-calendar discovery, including shared calendars via cal calendars --include-shared / --exclude-shared.
  • MCP without a browser: ironbrain-mailcal-mcp is a stdio MCP server — same stack, no OAuth dance aimed at a consumer webmail product.

Install

Primary path: public nuget.org — no PAT. Requires the .NET 10 SDK:

dotnet tool install -g Ironbrain.MailCal.Cli
dotnet tool install -g Ironbrain.MailCal.Mcp

ironbrain-mailcal --version   # e.g. 0.1.1+76b2152…

Optional / advanced: GitHub Packages for the kern-services org feed (https://nuget.pkg.github.com/kern-services/index.json) with a PAT that has read:packages (and org SSO if required). Most setups only need nuget.org. If you use the private feed, add it as a NuGet source first — current .NET SDKs may not accept --username / --password on dotnet tool install itself:

# Optional: private GitHub Packages feed
export GITHUB_TOKEN=…   # read:packages (+ org SSO if required)

dotnet nuget add source "https://nuget.pkg.github.com/kern-services/index.json" \
  --name github-kern-services \
  --username kern-services \
  --password "$GITHUB_TOKEN" \
  --store-password-in-clear-text

dotnet tool install --global Ironbrain.MailCal.Cli --source github-kern-services
dotnet tool install --global Ironbrain.MailCal.Mcp --source github-kern-services

Smoke checks once config is in place (full schema on the install page — do not guess it from this post):

ironbrain-mailcal account list
ironbrain-mailcal --account personal mail list --limit 5
ironbrain-mailcal --account kernetics-cal cal calendars --include-shared

Wire ironbrain-mailcal-mcp into Cursor (or your MCP host) the same way you would any other stdio server. Details belong on the MailCal install page .

MailCal is a building block of ironbrain.de — the toolbox side of the work I do under kern.services .

How I’d actually use this

This is the part that matters day to day. Same config file; different accounts for different risk levels.

Morning inbox triage (read-only personal)

Start with the personal account — SMTP send disabled — and skim what landed overnight. List a batch, open the ones that look actionable, summarize unread, and flag what needs a human. Never auto-send as me.

ironbrain-mailcal --account personal mail list --limit 20
ironbrain-mailcal --account personal mail get <id>

A typical morning pass looks like: three newsletters to ignore, one invoice from a fictional vendor (“Nordlicht Bürobedarf”) that needs a payment bookmark, and a scheduling ping that belongs on today’s calendar briefing. The bot drafts a short triage note for me. It does not touch SMTP on this account — the send gate makes that a hard stop, not a prompt hope.

Reply via the assistant mailbox when it makes sense

Outbound mail goes through a dedicated assistant account with SMTP enabled. Personal stays read-only. That split is the whole point of the gate: a bot cannot accidentally send as me, even if a prompt asks it to.

I only send when it is clearly appropriate — bot-owned threads, or when I explicitly asked to send. Examples: confirm a booking for a workshop room, acknowledge a vendor quote request, or ship an approved draft I already reviewed.

# Read on personal (SMTP off) …
ironbrain-mailcal --account personal mail get <id>

# New outbound from assistant (SMTP on) — e.g. acknowledge a vendor request
ironbrain-mailcal --account assistant mail send \
  --to "quotes@nordlicht-buero.example" \
  --subject "Re: Quote request #4821" \
  --body "Thanks — we received the quote and will review it this week."

# Or reply in an assistant-owned thread (loads subject/from via IMAP, sends via SMTP)
ironbrain-mailcal --account assistant mail reply <id> \
  --body "Confirmed — workshop room booked for Thu 10:00–12:00."

If the thread should stay in my personal voice, the bot stops at a draft for me to send myself. If it is fine as the assistant identity (“Ironbrain scheduling for kern.services”), mail send / mail reply from --account assistant is the path.

Turn mail into todos (MailCal fetches; the bot writes tasks elsewhere)

MailCal is the mail/calendar side. It does not grow a fake todo subcommand. The pattern I actually want:

  1. Read a message with MailCal.
  2. Extract actionable items in the assistant.
  3. Write the todo into the task system I already use.

My glue story is Obsidian: the assistant reads mail via MailCal, then appends a checkbox line under Current Tasks in the vault (or creates a small daily note task). Linear/Jira/Apple Reminders would be the same idea with a different writer.

ironbrain-mailcal --account personal mail list --limit 20
ironbrain-mailcal --account personal mail get <id>
# then the assistant appends something like:
# - [ ] Follow up: Nordlicht quote #4821 — due Fri
# into Obsidian Current Tasks (vault write / other MCP — not MailCal)

Boundary check: MailCal fetches the mail; the bot writes the todo elsewhere. Keep that line sharp and the architecture stays honest.

Calendar briefing — including shared calendars

Before the day starts, discover calendars (including shared/delegated ones) and list what is on today:

ironbrain-mailcal --account kernetics-cal cal calendars --include-shared
ironbrain-mailcal --account kernetics-cal cal list --date today --range day

Shared calendars show up in that discovery pass when you ask for them — which is exactly when you care about not double-booking Friday evening.

After an external booking, write a CalDAV event

Optional but useful: once a booking is confirmed (say a client workshop slot booked through an external form), add the corresponding event on the work calendar with cal add. It never writes to “all calendars” — you target one collection via -c / --calendar, --url, or the configured DefaultWriteCalendar:

ironbrain-mailcal --account kernetics-cal cal add \
  --summary "Workshop: Acme onboarding" \
  --start "2026-10-02 10:00" \
  --end "2026-10-02 12:00" \
  --tz Europe/Berlin \
  --location "Meeting room A" \
  -c work

MCP / Cursor loop (HITL by default)

Same config in Cursor via ironbrain-mailcal-mcp. Default behavior I want from the agent:

  • Draft replies and proposed todos freely.
  • Send mail or create calendar events only when I ask (human-in-the-loop).

That matches how Ironbrain thinks about actions in general: prepare the work, then wait for approval before anything leaves the machine. The SMTP send gate on personal is the safety net; HITL in the MCP loop is the habit.

The bigger point

AI assistants are only as useful as the systems they can reach. A model that “knows how to use email” in a demo, but cannot talk to your Mailcow mailbox or see a shared CalDAV calendar, is still stuck behind the same integration wall as a junior intern without VPN access.

The interesting shift is not another chatbot feature. It is treating IMAP, SMTP and CalDAV as first-class automation surfaces — with explicit accounts, send gates, and discovery handled in one place — and then exposing that surface both to humans (CLI) and to agents (MCP).

What I keep coming back to is not tools that sit at the gap between AI and infrastructure, but tools that close it. MailCal binds the assistant to the same mail and calendar stack I already run — the pieces fit together, so the bot is not stuck behind a demo connector. That is the kind of binding I want more of.


If you are wiring AI into self-hosted mail, calendars, or other production systems — or you want help designing that kind of toolbox for your stack — let’s talk .