Skip to main content
Glama

Frieda – MCP connector for iCloud

Frieda (technical name friedafetch) is a Home Assistant integration (HACS) that connects Apple iCloud with an app-specific password and exposes calendars, contacts, e-mail and notes as tools for LLMs. The tools are registered as a Home Assistant LLM API, so they are available to every conversation agent that enables the API (Assist, Google Generative AI, OpenAI, Ollama …) and to any MCP client (Claude, ChatGPT connectors, local agents) through Home Assistant's MCP Server integration or the integration's own MCP endpoint. Inbox rules, push delivery and a daily overview come with the separate extension Frieda Postfach. The project is named after the maintainer's dog Frieda, who looks out of the cloud in the logo.

Features

Area

Protocol

Read

Write

Calendar

CalDAV

list calendars, search/read events (recurrences expanded)

create, update (incl. RRULE) and delete events

Contacts

CardDAV

search (name, mail, phone, company …) and read contacts

create, update and delete contacts

E-mail

IMAP + SMTP

mailboxes, search/filter, read mail (text, attachment list)

send/reply, mark read/flagged, move, trash – many uids at once

Notes

IMAP folder Notes

list/search/read notes

create, update and delete notes

Rules

–

with the extension Frieda Postfach

inbox rules, push, daily overview

Insights

all of the above

mailbox statistics, free time slots, birthdays, duplicate contacts

unsubscribe from newsletters, store attachments (receipts)

Important note on Notes

Apple offers no interface for modern iCloud Notes (CloudKit, the default since iOS 9 / "upgraded notes") that works with an app-specific password. The integration can therefore only read and write notes stored in the IMAP folder Notes of the iCloud mail account (classic IMAP notes). If that folder does not exist, the note tools say so. The other areas use official, stable protocols (CalDAV, CardDAV, IMAP, SMTP) and are fully usable.

Related MCP server: icloud-mcp

Requirements

  • Home Assistant 2026.10 or newer (the integration uses the current llm.Tool API with probatio schemas; older 2025 releases work through the built-in voluptuous fallback but are untested).

  • An Apple ID with two-factor authentication and an app-specific password (appleid.apple.com → Sign-In and Security → App-Specific Passwords).

  • iCloud Mail must be enabled for the Apple ID if you want e-mail and notes.

  • Optional: Home Assistant's Model Context Protocol Server integration (mcp_server) if you prefer it over the built-in endpoint.

Installation

  1. HACS → Integrations → ⋮ → Custom repositories → URL of this repository, category Integration.

  2. Install "Frieda" and restart Home Assistant.

Manual

Copy custom_components/friedafetch into the custom_components directory of your Home Assistant configuration and restart Home Assistant.

Upgrading to 2.0

Frieda 2.0 is a pure connector: inbox rules, push delivery, the daily overview and the birthday sensor moved to the separate integration Frieda Postfach. Install it via HACS and add it for your account; existing rules, hit counters, run history and automation settings are picked up automatically. Until then Frieda shows a repair notice for accounts with stored rules, and no rules run. The rule tools (icloud_*_rule, icloud_run_rules, icloud_daily_overview) appear again as soon as Frieda Postfach is set up.

Upgrading from “iCloud MCP” (versions up to 1.0.x)

The project was renamed to Frieda with version 1.1.0; the integration domain changed from icloud_mcp to friedafetch. Remove the old integration (Settings → Devices & services → iCloud MCP → Delete), update the HACS custom repository to https://github.com/nadeo360/friedafetch, install Frieda and add it again with the Apple ID and app-specific password. Rules and options are not migrated; MCP clients need the new endpoint /api/friedafetch. The tool names (icloud_*) are unchanged. If an old custom_components/icloud_mcp folder is left behind, Home Assistant may keep loading it instead of the current version (both declare the domain friedafetch); Frieda detects this at startup and shows a repair issue – delete the folder named there and restart Home Assistant.

Setup

  1. Settings → Devices & services → Add integration → "Frieda".

  2. Enter the Apple ID and the app-specific password. Credentials are validated against CalDAV (mandatory) and IMAP (optional – if IMAP fails, the mail and note tools start disabled).

  3. Connect an MCP client, either via the built-in endpoint (see below) or via the core Model Context Protocol Server integration: there, under Configure, enable "all LLM APIs" or select "iCloud (Calendar, Contacts, Mail, Notes)" in addition to Assist.

  4. Optional for voice assistants: enable the iCloud LLM API in the conversation integration (e.g. Google Generative AI) under "Control Home Assistant".

Note on mcp_server_http_transport (hass-mcp-server): that HACS integration ships its own fixed tool set and does not forward Home Assistant LLM APIs, so the iCloud tools do not appear there. Use the built-in endpoint or the core MCP Server integration instead; both can run side by side.

Options (Configure)

The options dialog has three sections: Tools and account, Security and guard rails and Approvals.

Option

Effect

Calendar / Contact / E-mail / Note tools

show or hide tool groups

Read-only mode

hides every create/update/delete/send tool

Allow sending e-mails

hides icloud_send_mail when disabled

Allow deleting

hides all icloud_delete_* tools (except rule deletion) when disabled

Default maximum number of results

default list limit (1–200)

Sender address

From: for outgoing mail (default: Apple ID; must be an iCloud alias)

Attachment folder

where icloud_save_attachments (and Frieda Postfach rules) store files (default <media>/frieda; must be an allowed path)

Account label

short name used as tool suffix when several accounts are configured

Security section

quotas, recipient policy, protected calendars/mailboxes, hidden mailboxes (see Security)

All tools carry MCP annotations (readOnlyHint, destructiveHint) so that clients in read-only mode can drop the writing tools automatically.

Capabilities you switch off are not hidden silently: the LLM prompt (and the instructions of the built-in MCP endpoint) lists what the administrator disabled, so the assistant can tell the user why it cannot send or delete and where to enable it (Settings → Devices & services → Frieda → Configure). Likewise, active approvals are announced so that an approval_required result is explained as "waiting for approval", not as an error.

Built-in MCP endpoint

The integration serves its tools directly at POST /api/friedafetch (MCP Streamable HTTP, stateless) and authenticates with Home Assistant access tokens – no additional integration required.

  1. In Home Assistant open your profile → Security → Long-lived access tokens → Create token.

  2. Configure the client with the URL https://<your-home-assistant>/api/friedafetch and the header Authorization: Bearer <token>.

For Claude Code (~/.claude.json) or Claude Desktop (claude_desktop_config.json) this looks like:

{
  "mcpServers": {
    "icloud": {
      "type": "http",
      "url": "https://<your-home-assistant>/api/friedafetch",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

GET /api/friedafetch answers with a short status (name, version, tool count). Server-initiated streams and sessions are not needed; every POST is handled on its own.

Several iCloud accounts

Add the integration once per Apple ID. The first account keeps the plain tool names (icloud_list_events); every further account gets a suffix derived from its account label (option) or the local part of its Apple ID, for example icloud_list_events_work. The LLM APIs are registered the same way (friedafetch, friedafetch_work), and the panel offers an account selector. Each account has its own options.

Tools

Tool

Description

icloud_list_calendars

calendars with ids

icloud_list_events

events in a time range (recurrences expanded), optional text filter

icloud_get_event

one event with details

icloud_create_event

create an event (all-day or timed, optional RRULE)

icloud_update_event

change fields of an event (duration kept when only start changes)

icloud_delete_event

delete an event

icloud_search_contacts

search contacts (empty query = alphabetical list)

icloud_get_contact

contact by uid

icloud_create_contact

create a contact

icloud_update_contact

change a contact (list fields replace the existing list)

icloud_delete_contact

delete a contact

icloud_list_mailboxes

mailboxes/folders

icloud_search_mail

filter mail (sender, subject, full text, date range, unseen, flagged)

icloud_get_mail

read a mail (text, optional HTML, attachment list, optional raw headers)

icloud_send_mail

send or reply (quote, In-Reply-To, copy in the sent folder)

icloud_mark_mail

read/unread, flagged/unflagged – one or many uids (uids) at once

icloud_move_mail

move to another folder – one or many uids with a single IMAP command

icloud_delete_mail

move to trash (or delete permanently when already in trash) – one or many uids

icloud_approval_status

outcome of an action that required human approval

icloud_mailbox_stats

top senders/domains, unread and newsletter share, rule coverage and rule suggestions

icloud_unsubscribe

unsubscribe from a newsletter (one-click https or unsubscribe e-mail)

icloud_save_attachments

store the attachments of a mail in <folder>/<subfolder>/<YYYY-MM>/

icloud_find_free_time

free slots of a given length within working hours

icloud_upcoming_birthdays

birthdays of the contacts in the coming days (with age, if known)

icloud_find_duplicate_contacts

probable duplicates (same e-mail, phone or name)

icloud_list_notes

list/search notes

icloud_get_note

read a note

icloud_create_note

create a note

icloud_update_note

replace a note (it gets a new uid)

icloud_delete_note

delete a note

Times are ISO 8601; naive times are interpreted in the Home Assistant time zone. All-day events use dates (YYYY-MM-DD) with an inclusive end date. The mail tools icloud_mark_mail, icloud_move_mail and icloud_delete_mail accept a uids list (up to 500) and process it with a single IMAP command, so cleaning up spam is one call instead of one per message.

Panel

The integration adds "Frieda" to the Home Assistant sidebar (admins only, German/English following the HA language):

  • Overview: whether the MCP endpoint is active (both URLs to copy – for the local address and, if set up, the external address and Home Assistant Cloud –, number of reading and writing tools, token rule), the MCP clients that connected recently (name and version from the MCP handshake), whether the LLM API is exposed by Home Assistant's own MCP server, and the state of Frieda Postfach (running, installed but not linked, not installed).

  • Usage: tool calls today, in 7 and 30 days and in total, split into reading and writing, a 14-day chart, the most used tools, where the calls came from (Frieda endpoint, HA MCP server, Assist agents) and the hourly quotas. Only counters are stored, never arguments or results.

  • Approvals: pending and decided requests (approve or deny with one tap, also from the companion app; the request shows "Sending …" until the result is in).

  • Change log: everything Frieda changed in iCloud as a table grouped by day (e.g. "Mail moved · 3 messages · → Archive"), with search, filters for area and trigger, a result switch and selectable columns; a click on a row shows the error and the raw parameters, and for runs of Frieda Postfach the messages with a link to their timeline.

Tabs and widgets: three tabs keep the panel tidy – Overview (with the usage), Approvals (shown when approvals are on or there are entries; the tab counts the pending ones, so they stand out from the other tabs too) and Change log. On a keyboard 1–3 switch the tabs, / searches the change log and ? shows the keys; another tab glides in briefly (not with “reduce motion”). On the overview, Arrange in the title bar lets you drag the cards by the handle (or move them with the arrows), switch them between full and half width and hide them; tables offer a Columns menu. Rare actions such as clearing the log sit in a small ⋮ menu and always ask for confirmation. The layout and the last tab are stored per Home Assistant user in Home Assistant itself, so they survive restarts and follow you to other devices.

Live: the tables follow changes right away (new change-log entries, approvals, runs), without reloading the page; new rows light up briefly. After an update the panel offers a reload when it still runs the old code.

Pages: long tables show 5, 10, 25, 50 or all entries per page (remembered per table); search and filters always cover all entries.

Frieda UI: both panels use one shared design library (/friedafetch_static/ui.js): colours from the Home Assistant theme, buttons, chips, tables, menus, dialogs and widgets. Frieda Postfach loads it from Frieda, so every module looks and behaves the same. It also covers accessibility: the focus stays in place when a panel redraws, dialogs keep the focus inside, notices and search hits are read out, symbols have names for screen readers, and coloured text keeps WCAG AA contrast in light and dark themes.

The panel has no external dependencies and talks to the integration via WebSocket commands (friedafetch/accounts, friedafetch/overview, friedafetch/usage/reset, friedafetch/approvals/*, friedafetch/audit/*).

Attachments and unsubscribing

  • Attachments: icloud_save_attachments stores the attachments of a mail below the attachment folder, sorted by month and named <date>_<sender>_<file>. Identical files are not stored twice, small images (logos, signatures) are skipped. Home Assistant must allow the folder (media folders are allowed by default, others go into allowlist_external_dirs).

  • Unsubscribe: icloud_unsubscribe uses the List-Unsubscribe header: one-click https (RFC 8058) first, then an unsubscribe e-mail (only when sending is allowed; it counts against the sending quota and needs approval if sending does). Links that require a web page are returned for you to open.

Extensions

Frieda only acts when an AI client or you call it. Everything that runs on its own lives in extensions that attach to a Frieda account, reuse its connection and guard rails, and add their tools to it:

  • Frieda Postfach (friedafetch_mail): inbox rules with push delivery (IMAP IDLE), receipts via rules, the daily overview, a birthday sensor and the rule tools.

Security

  • Only an app-specific password is used; it can be revoked at Apple any time and is redacted in diagnostics. Connections go exclusively over TLS to Apple endpoints; there is no telemetry.

  • The MCP endpoint and the panel are protected by Home Assistant tokens and admin rights; tool groups, read-only mode, sending and deleting can be switched off individually.

  • Guard rails (options → Security): hourly quotas per account for mail moves/deletions/flag changes, sent mails and deleted events/contacts/notes; a recipient policy for sending (any address, known contacts, or an allowlist of addresses/domains) with a recipient cap and optional BCC ban; protected calendars and mailboxes that AI clients may only read, and hidden mailboxes that are not exposed at all. Blocked calls return code: "blocked".

  • Change log: every writing tool call and every rule run with hits or errors (marked manual or automatic) is recorded (time, client, Home Assistant user, tool, shortened parameters without message bodies, result) in the "Change log" card of the panel and fired as the event friedafetch_action, so an automation can notify you, e.g. when more than ten mails are deleted at once.

  • Approvals (human in the loop, options → Approvals): sending, deleting and mass mail actions above a threshold can be parked instead of executed. Home Assistant shows a notification (optionally an actionable push on your phone with Approve/Deny) and the "Approvals" card in the panel lists the request; after approval Home Assistant runs the action and the AI reads the outcome with icloud_approval_status. Requests expire after a configurable time.

  • Delayed sending (options → Approvals, default off): e-mails from assistants wait a few seconds (e.g. 60) before they go out. Until then the "Approvals" card shows them with "Send now" and "Cancel", and the notification has a Cancel button. Recipients and quotas are checked at once and again when the mail is sent. Without effect when sending needs approval anyway.

  • Persistent state: the hourly quotas, active injection locks and the list of approvals survive restarts and reloads, so a restart neither refills a quota nor lifts a lock. Requests that were still open at a restart cannot be executed afterwards: they are closed as expired (a delayed mail as cancelled, i.e. not sent).

  • Endpoint profiles: besides /api/friedafetch there is /api/friedafetch/readonly, which serves only tools that cannot change anything – the right endpoint for experiments or less trusted agents. The option "MCP endpoint only for administrator tokens" refuses non-admin tokens; request bodies are capped at 1 MiB.

  • Prompt-injection defence: subjects, bodies, notes and event texts are stripped of invisible/bidi characters, marked as third-party content in the tool results, and scanned for typical injection patterns; hits are flagged with suspicious: true and warnings. The LLM prompt instructs the model never to follow instructions found in such content.

  • Injection lock (options → Security, default on): once a tool returned such flagged content, the same caller (platform and Home Assistant user) may not send, delete, unsubscribe or create deleting/moving rules for a while (default 15 minutes): the call is parked for approval, or blocked if approvals are off. The panel overview shows active locks. This stops the classic attack where a mail says “forward everything to …”.

  • Rules are mass actions too: with Frieda Postfach, rules that delete or move and real runs over the whole mailbox follow the approvals (“approve deleting”, mass threshold). Unattended runs (timer, push) and runs requested by an AI change at most 100 messages (50 deletions) per run and count against the hourly quota for mail changes; the rest follows in the next run. AI sorting counts against the same quota.

  • Supply chain: every commit runs tests, Ruff with the Bandit rule set, Bandit, CodeQL, pip-audit, hassfest and HACS validation; GitHub Actions are pinned to commit SHAs, the main branch is protected, OpenSSF Scorecard rates the repository continuously, Dependabot and GitHub security alerts are enabled. Releases are built by GitHub Actions and published with signed build provenance (Sigstore) and a CycloneDX SBOM; HACS installs exactly this signed package (gh attestation verify friedafetch.zip --repo nadeo360/friedafetch). Details and the disclosure process: SECURITY.md.

Example automation on the audit event:

automation:
  - alias: Warn on mass deletion by AI
    triggers:
      - trigger: event
        event_type: friedafetch_action
    conditions:
      - "{{ trigger.event.data.action == 'delete' and (trigger.event.data.details.count | default(1)) > 10 }}"
    actions:
      - action: notify.mobile_app_iphone
        data:
          message: "{{ trigger.event.data.client }} deleted {{ trigger.event.data.details.count }} mails"

Technical notes

  • Credentials live only in the config entry; the password is redacted in diagnostics.

  • CalDAV/CardDAV are implemented directly on aiohttp (principal discovery, calendar-query, addressbook-multiget, ETag-protected updates). Contacts are cached locally and synchronised incrementally via CTag/ETag.

  • Recurring events are expanded with python-dateutil (RRULE, EXDATE, RDATE, moved occurrences).

  • vCards are handled by a small lossless parser: unknown fields (photo, Apple extensions) survive updates.

  • IMAP via aioimaplib (one serialised connection with automatic reconnect), SMTP via aiosmtplib (STARTTLS, 587).

  • The own MCP endpoint (mcp_http.py) speaks the small stateless part of the protocol itself (initialize, ping, tools/list, tools/call) and needs no mcp package, so a new major version of that package in Home Assistant cannot stop Frieda from loading (as mcp 2.0 did with Home Assistant 2026.11 and Frieda up to 2.0.24).

  • Packages that Home Assistant ships itself (icalendar, aioimaplib, aiosmtplib) are declared as minimum versions and follow the HA release, as hassfest requires.

Development

How to report bugs, suggest ideas and send pull requests: CONTRIBUTING.md.

The architecture of Frieda and Frieda Postfach is described in the arc42 document docs/architecture.md.

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements_dev.txt
pytest
ruff check . && ruff format .

Home Assistant 2026.10 requires Python ≥ 3.14.2.

The type check is basedpyright (requirements/types.txt); findings from before it came are listed in .basedpyright/baseline.json and only new ones fail.

Releases

scripts/release.py VERSION --notes NOTES.md releases from a clean dev: it stops if origin/main has commits dev lacks, sets the version in the manifest, runs ruff and the tests, moves main on by fast-forward and pushes it, waits until every CI run of that commit has passed, creates the GitHub release with the notes (German, ---, English), and then checks the zip the release workflow attached – checksum, version and every file against the tagged source. --dry-run only checks and tests; --draft prints a notes skeleton from the commits since the last release. A CI run that GitHub still shows as running five minutes after its last job finished counts by its jobs (GitHub sometimes never closes a run). If the script stops while waiting anyway, --resume continues the same version later: it checks that the tag does not exist yet and main is the pushed commit, waits for CI again, creates the release and checks the zip.

Artwork

assets/*.svg are the sources; scripts/build_icons.py renders every PNG variant (icon, @2x, light/dark logo) into assets/ and into custom_components/friedafetch/brand/, from where Home Assistant (2026.3+) and HACS load the integration's icon directly. --check verifies in CI that the files are current.

About the use of AI

This integration was developed with substantial help from an AI coding assistant (Anthropic's Claude), working from the maintainer's requirements and decisions. Every change is covered by the automated test suite and the security checks described above, and all releases were reviewed and tested by the maintainer against a real iCloud account. Please report bugs and ideas via GitHub issues – they are read by a human.

Apple, iCloud, iPhone, iPad, Mac and macOS are trademarks of Apple Inc.; Home Assistant is a trademark of the Open Home Foundation; "Model Context Protocol" and "Claude" are trademarks of Anthropic, PBC. This is an independent community project and is not affiliated with, endorsed or supported by Apple, the Open Home Foundation, Nabu Casa, Anthropic or HACS. Using iCloud through this integration is subject to Apple's iCloud terms; you are responsible for complying with them. The software comes without warranty, reads and changes personal data on your behalf and hands it to the AI clients you connect. Full notices, third-party licenses (including the GPL-3.0 licensed aioimaplib) and the liability disclaimer: NOTICE.md.

License

MIT – see LICENSE and NOTICE.md.

Related MCP Connectors

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    An MCP server for iCloud Calendar, Contacts, and Mail, usable from any MCP-capable AI client.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables local AI agents to search and read iCloud Mail, draft and send reviewed replies, and manage calendar events via a local MCP connection.
    19
    1
    MIT