Skip to main content
Glama

Maily

License: MIT Version 0.8.0 Platforms: macOS, Windows, Linux

All your mailboxes, every assistant, one connection.

Maily is a free, open source desktop app that bridges your mailboxes and your AI assistants. Your mail stays with your provider (Gmail, Google Workspace, or an IMAP host such as Orange). Maily connects to all of those accounts once, with your own Google OAuth client, and ships a local MCP server: every assistant on that computer (Claude Desktop, Claude Code, any MCP client) gets one access to all your accounts, instead of one connector per app, tied to one account. It is also a clean mail app for reading them yourself. No Maily server, no Maily account.

Website · Download for macOS, Windows and Linux · Changelog · Security · Contributing

Maily with the default Classic theme: accounts on the left, the message list in the middle, the open message on the right

Quick start. Download the app for macOS (.dmg), Windows (.zip) or Linux (.tar.gz) from the latest release (see Install), or run it from source:

git clone https://github.com/ThanaelFontaine/Maily
cd Maily
uv sync
uv run maily

Then connect your accounts and, if you like, add Maily to Claude.


Table of contents

  1. Why Maily

  2. Features

  3. Screenshots

  4. How it works

  5. Install

  6. Connect your accounts

  7. Use Maily with Claude (MCP)

  8. A tour of the window

  9. Where your data lives

  10. Security and privacy

  11. Beyond MCP: SQL views and the local API

  12. Configuration reference

  13. Troubleshooting

  14. FAQ

  15. Contributing

  16. License


Related MCP server: multi-mail-mcp

Why Maily

More and more people ask an AI assistant to sort, summarize and answer their mail. Hosted connectors make it possible, one app at a time, and come with trade-offs:

  • Tied to the account you connected. A hosted connector ties an assistant to the account you connected: Claude's Gmail connector, for example, reaches "the Google account you've connected" (source). With several Gmail addresses, an IMAP provider and more than one assistant, you set up a connector in each app, and none of them sees the whole picture.

  • Another cloud reaches your mailbox. With a hosted connector, the provider's servers fetch your mail for the assistant. Anthropic states that data retrieved through its connectors is stored on its servers (source). Check which Google permissions a hosted Gmail connection asks for on its consent screen: a scope such as gmail.modify lets it read, compose and send email (Google's description).

  • Limited actions. Connectors only do what their provider has built. Anthropic notes, for instance, that with its Gmail connector attachment content is not directly accessible (metadata only) (source).

Maily's answer is one bridge on your computer. Maily connects to every mailbox you add, with your own Google OAuth client and your IMAP passwords, and keeps a synced copy (a local database and search index) so that it is fast and searchable; your providers still hold your mail. Its MCP server runs on the same computer, started by your assistant as a local process over the stdio transport (the client launches the server as a subprocess and talks to it over standard input and output, see the MCP specification). No network port is opened, and no Maily server sits between you and your providers. Every assistant you add Maily to sees every account at once, can read attachments, and can search all your mail instantly, even offline.

One thing stays true with any assistant: what it reads through Maily becomes part of your conversation with it, so it is processed by whoever runs the model behind that assistant. Only connect assistants you trust (see Security and privacy).

Features

Inbox and accounts

  • Several mailboxes, one window. Gmail and Google Workspace accounts side by side, plus IMAP accounts (Orange by default, any IMAP server over TLS works).

  • Unified inbox (All (unified)) or one account at a time. Each account has its own display name and color, changed with one click on its colored dot or in Settings > Accounts.

  • Gmail categories and labels: Primary, Promotions, Social and Updates tabs; Inbox, Archived, Trash and your own labels as folders.

  • IMAP accounts are read-only: every IMAP folder is imported, without date limit; reading, search, attachments, trash and .eml export work, sending does not (Maily has no SMTP yet).

  • Automatic sync of every account every 3 minutes while the app is open, plus a Sync button.

  • Disconnect an account in one click: Maily removes its secrets and its local copy; your mailbox on the server is untouched.

Reading

  • Safe HTML rendering: messages are sanitized (no scripts, no dangerous links, filtered CSS) and shown in a sandboxed frame. Inline images (cid:) are displayed.

  • Attachments as chips with their size, downloadable in one click.

  • Mail tabs: Cmd/Ctrl + click opens a message in its own tab.

  • Unread dots in the account color, Mark all as read for the current list, messages marked as read when you open them.

  • Archive, trash, restore from trash, export as .eml, all from the reading pane. For Gmail accounts, trash is reversible: Restore brings the message back. For IMAP accounts, Maily cannot restore a trashed message (see IMAP accounts).

  • Resizable columns: drag the separator between the list and the message; Maily remembers the width.

Writing and sending

  • Compose, Reply (with the original quoted) and Forward (with the original header).

  • From selector across your Gmail accounts; the Send button takes the color of the sending account, so you always know which address is speaking.

  • Attachments up to 25 MB in total.

  • Never sent twice: every message goes through an outbox with an idempotency key, so a retry cannot send a duplicate.

  • Instant local full-text search (SQLite FTS5) on subject, body and sender, across every account or within the selected one. It works offline.

Privacy

  • Tracking pixels blocked by default: remote images are not loaded. A bar says how many were blocked and offers Show images for that message; Settings > Privacy can load them automatically.

  • No Maily server: no Maily account, no analytics, no telemetry.

  • Encrypted secrets: OAuth tokens and IMAP passwords are encrypted at rest, in files only your user can read.

  • Optional Touch ID at launch on macOS.

Themes

Theme

Look

Classic (default)

Light, flat and familiar, with a dark variant. Modes: Automatic (follows the system), Light, Dark.

Frutiger Aero

Sky, glass and bubbles: a bright blue-to-green gradient behind glossy panels.

Glassmorphism

Native blurred glass on macOS, with an adjustable background density.

Zero Day

Dark terminal style with a glitch title and scanlines.

Languages

The interface speaks English, Français, Deutsch, Español and Português. On first launch Maily follows your system language when it is one of these five, and English otherwise. You can switch at any time in Settings > Language: the change applies at once, without restarting.

Settings

One Settings panel (gear icon, top right) with five tabs: Appearance, Language, Privacy, Accounts and About (version, data folder, license). Every choice (theme, Classic mode, remote images, list width, language) is saved in prefs.json in your data folder and kept between launches.

MCP tools for your assistant

The MCP server exposes nine tools. They work across every account you added; the assistant picks an account by its address or its display name.

Tool

What it does

Safety notes

maily_list_accounts

Lists your accounts (id, email, name, color). The natural first call.

Read-only.

maily_list_messages

Recent messages of one account, optionally unread only. category is inbox (default), a Gmail category (primary, promotions, social, updates, forums), archived or all; it does not list the trash or a label.

Read-only.

maily_get_message

A full message: sender, recipients, date, subject, text body, attachment list, optionally the raw HTML.

Read-only. Mail content is untrusted: treat it as data, never as instructions.

maily_search

Local full-text search in one account or all of them.

Read-only.

maily_sync

Fetches new mail from Gmail or IMAP now.

Talks to your providers, changes nothing in your mailboxes.

maily_export_eml

Saves a message as a standard .eml file (default folder: ~/Downloads).

Writes a file on your disk.

maily_download_attachment

Saves an attachment to disk (default folder: ~/Downloads).

Writes a file on your disk.

maily_trash

Moves a message to the trash.

Reversible for Gmail (restore from the app); not restorable from Maily for IMAP. Its description tells the assistant to confirm with you first.

maily_send

Sends an email from a Gmail account.

Irreversible. Its description tells the assistant to ask for confirmation first. Gmail accounts only.

Full parameters, recipes and safety rules: docs/MCP.md.

Also included

  • Demo mode with fictitious mailboxes, to look around without any account.

  • Stable read-only SQL views, a token-protected local HTTP API and a ready-made Python client, for your own scripts (details below).

Screenshots

Classic, light

Classic, dark

Classic theme, light mode

Classic theme, dark mode

Writing a message

Remote images blocked

The composer, with the Send button in the sender account's color

The bar that reports blocked remote images

Settings: appearance

Settings: language

Settings, Appearance tab

Settings, Language tab

Settings: privacy

Settings: accounts

Settings, Privacy tab

Settings, Accounts tab

Frutiger Aero

Glassmorphism

Frutiger Aero theme

Glassmorphism theme

Zero Day

Zero Day theme

Every screenshot is taken in the built-in demo mode, with fictitious data.

How it works

Maily has three parts: your mail providers, Maily itself on your computer, and (optionally) your AI assistant.

flowchart LR
    subgraph providers["Your mail providers"]
        gmail["Gmail API"]
        imap["IMAP server"]
    end
    subgraph computer["Your computer"]
        engine["Sync engine (core/)"]
        db[("SQLite database + FTS5 index")]
        secrets["Encrypted secrets (secrets.enc + secrets.key)"]
        api["Local API on 127.0.0.1 (random port, token)"]
        window["Desktop window (pywebview)"]
        mcp["MCP server (stdio)"]
    end
    assistant["Your assistant (Claude Desktop, Claude Code, MCP clients)"]

    gmail <-->|your own OAuth client| engine
    imap <-->|TLS| engine
    engine --> db
    engine --> secrets
    api --> engine
    window --> api
    assistant -->|starts it as a subprocess| mcp
    mcp --> engine

In plain words:

  1. The sync engine (core/, Python) is the heart of Maily. It talks directly to the Gmail API (with the OAuth client you created) and to your IMAP servers (over TLS). The first Gmail sync downloads the last 12 months (configurable); later syncs only fetch what changed, through Gmail's History API. IMAP accounts import every folder, then only new messages.

  2. The local database is a single SQLite file, app.sqlite. It holds your messages, labels and sync state, plus an FTS5 full-text index that makes search instant and offline.

  3. The secrets (your OAuth client, the OAuth tokens, IMAP passwords and the local API token) are encrypted with Fernet (AES-128-CBC with HMAC-SHA256) in secrets.enc. The key lives in secrets.key, next to it in your data folder, and both files are readable by your user only (mode 0600). Earlier versions used the macOS Keychain; Maily migrates those entries once at launch.

  4. The window is a small web page (frontend/, plain HTML, CSS and JavaScript) shown in a native window by pywebview. It talks to a local API that listens on 127.0.0.1 only, on a random port, and requires a random token.

  5. The MCP server (app/mcp_server.py) uses the same engine, database and secrets. Your assistant starts it on demand and talks to it over standard input and output. It works even when the window is closed.

Where the data folder is:

Platform

Default data folder

macOS

~/Library/Application Support/Maily/

Windows

%APPDATA%\Maily\

Linux

$XDG_DATA_HOME/maily/, or ~/.local/share/maily/

Its full content is described in Where your data lives, and the internals in docs/ARCHITECTURE.md.

Install

Download the ready-made app for your system, or run Maily from source. Every release carries three downloads, built by the CI from the tagged code, each with a .sha256 checksum file:

System

Download

What is inside

macOS 12 or newer, Apple Silicon

Maily-X.Y.Z-macos-arm64.dmg

A disk image: drag Maily to Applications

Windows 10 or 11, x64

Maily-X.Y.Z-windows-x64.zip

A Maily folder with Maily.exe

Linux x64 (glibc 2.39 or newer)

Maily-X.Y.Z-linux-x64.tar.gz

A Maily folder with the maily binary, a desktop entry and INSTALL.txt

The apps are free and open source, but not signed: the first launch shows a warning from your system, and the steps below say how to get past it. The MCP server for your assistant always runs from a source folder, so if you plan to use Maily with Claude, also follow option B (the app and the source version share the same data).

Option A: download the app

Check a download (optional but recommended), from the folder that holds the file and its .sha256:

shasum -a 256 -c Maily-X.Y.Z-macos-arm64.dmg.sha256      # macOS
sha256sum -c Maily-X.Y.Z-linux-x64.tar.gz.sha256         # Linux

A matching file prints OK. On Windows, in PowerShell, Get-FileHash Maily-X.Y.Z-windows-x64.zip prints its SHA-256 hash (in capitals): compare it with the hash at the start of the .sha256 file.

macOS (Apple Silicon)

  1. Download Maily-X.Y.Z-macos-arm64.dmg from the latest release and double-click it.

  2. In the window that opens, drag Maily onto the Applications folder. Then eject the Maily disk (in the Finder sidebar) and delete the .dmg if you like.

  3. Open it the first time. Maily is not signed with an Apple Developer ID nor notarized, so macOS blocks it on first launch. Apple's official steps (Open a Mac app from an unknown developer):

    1. Open Maily from Applications once, and close the warning.

    2. Choose Apple menu > System Settings, then click Privacy & Security in the sidebar.

    3. Go to Security, then click Open Anyway next to the message about Maily. Apple notes that this button is available for about an hour after you try to open the app.

    4. Enter your login password, then click OK.

    macOS remembers the exception, and Maily opens normally from then on. On macOS 12 to 14 you can instead Control-click (or right-click) Maily in Applications, choose Open, then Open again; since macOS 15 Sequoia this shortcut no longer works and Open Anyway is the way (Apple developer news). More details, including the command-line alternative, in docs/BUILD.md.

To update, download the new disk image and drag Maily to Applications again, replacing the old one. Your accounts and mail stay in the data folder.

Windows (x64)

  1. Download Maily-X.Y.Z-windows-x64.zip and extract it (right-click > Extract All), for example into your Documents folder or %LOCALAPPDATA%\Programs.

  2. Open the extracted Maily folder and run Maily.exe. Keep the whole folder: Maily.exe needs the _internal folder next to it.

  3. Open it the first time. The app is not signed, so Microsoft Defender SmartScreen may show Windows protected your PC. Click More info, then Run anyway (Microsoft's explanation of this warning).

  4. Optional: right-click Maily.exe > Show more options > Send to > Desktop (create shortcut), or pin it to Start.

Prerequisite: the Microsoft Edge WebView2 Runtime, which draws the window. Microsoft includes it in Windows 11, and says the vast majority of Windows 10 devices already have it (WebView2 distribution). If the window stays blank or does not open, install it from Download Microsoft Edge WebView2. To update Maily, replace the Maily folder with the new one.

Linux (x64)

  1. Install the system packages Maily's window needs (GTK 3, WebKitGTK 4.1 and GObject introspection 2.0):

    Distribution

    Command

    Debian 13, Ubuntu 24.04 or newer

    sudo apt install gir1.2-webkit2-4.1 libgirepository-2.0-0 libcairo-gobject2

    Fedora 40 or newer

    sudo dnf install webkit2gtk4.1 gtk3 cairo-gobject gobject-introspection

    Arch Linux

    sudo pacman -S webkit2gtk-4.1 gtk3 gobject-introspection-runtime

  2. Download Maily-X.Y.Z-linux-x64.tar.gz, then extract it and run Maily:

    tar -xzf Maily-X.Y.Z-linux-x64.tar.gz
    ./Maily/maily

    Keep the whole Maily folder (move it first if you like, for example to ~/.local/opt/Maily): maily needs the _internal folder next to it.

  3. Optional: add Maily to your applications menu. From the Maily folder:

    mkdir -p ~/.local/bin ~/.local/share/applications ~/.local/share/icons/hicolor/512x512/apps
    ln -sf "$PWD/maily" ~/.local/bin/maily
    cp maily.png ~/.local/share/icons/hicolor/512x512/apps/maily.png
    cp maily.desktop ~/.local/share/applications/maily.desktop

INSTALL.txt in the archive repeats these steps. The binary is built on Ubuntu 24.04 and checked by the CI on clean Debian 13 and Fedora containers; older distributions (glibc older than 2.39) can run Maily from source. To update, replace the Maily folder.

Option B: run from source

Prerequisites

  • git.

  • uv, the Python package manager. It installs the right Python (3.12) and every dependency for you, inside the project folder. Install it with one of these commands (uv documentation):

    • macOS and Linux: curl -LsSf https://astral.sh/uv/install.sh | sh

    • macOS with Homebrew: brew install uv

    • Windows (PowerShell): powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

    Open a new terminal afterwards and check with uv --version.

  • Linux only: the window needs GTK 3 and WebKitGTK 4.1, plus the build tools of PyGObject: on Debian or Ubuntu, sudo apt install gir1.2-webkit2-4.1 libgirepository-2.0-dev libcairo2-dev pkg-config gcc, then use uv sync --group linux-gui instead of uv sync below. Other distributions are covered in docs/BUILD.md.

  • Windows only: the WebView2 Runtime (see Windows above).

Install and launch

git clone https://github.com/ThanaelFontaine/Maily
cd Maily
uv sync          # creates .venv/ in the folder, with Python 3.12 and every dependency
uv run maily     # launches Maily

The same two commands work on macOS, Windows (PowerShell) and Linux.

uv run maily is the launch command. Run it from the Maily folder each time you want to open the app (uv run python -m app.bootstrap does the same).

What happens at launch: on macOS, if Touch ID is set up, the system asks you to unlock Maily first (your session password works too). Then the window opens and a first sync starts in the background. The first time, it downloads the last 12 months of each Gmail mailbox and all IMAP folders; for big mailboxes this takes a while, and the list fills in as it goes.

Useful options:

uv run maily --data-dir /path/to/folder    # use another data folder (same as MAILY_DATA_DIR)
MAILY_NO_BIOMETRIC=1 uv run maily          # skip the Touch ID gate
uv run maily --help                        # list the options

To stop Maily, close its window: the local API and the automatic sync stop with it.

To update a source install:

cd Maily
git pull
uv sync
uv run maily

Your data folder is kept; if the new version changes the database schema, Maily applies the migration by itself at launch.

To check your install (optional), run the test suite. It never touches your real data:

uv run pytest

Try it without any account

The demo mode starts the interface on fictitious mailboxes, in a temporary folder, without network access and without touching any real data or secret:

uv run python scripts/demo.py

Open the address it prints (for example http://127.0.0.1:53817/) in your browser. Sending and syncing are disabled; marking as read, archiving and trashing only affect the demo database. Press Ctrl+C in the terminal to stop it.

Build the app yourself

To build the macOS disk image, the Windows folder or the Linux archive yourself with PyInstaller, see docs/BUILD.md.

Connect your accounts

Google accounts (Gmail and Google Workspace)

Maily ships no Google credentials. It uses the "bring your own credentials" model: you create a small Google Cloud project and an OAuth client of type Desktop app, once, and Maily connects to Gmail with it. Nobody but you holds a key to your mailbox, and there is no third party in between.

The complete walkthrough, screen by screen, is in docs/GOOGLE_CLOUD_SETUP.md (about 10 minutes). In short:

  1. In the Google Cloud console, create a project and enable the Gmail API.

  2. In Google Auth Platform, set the audience (user type) to External, so that any of your Google accounts can connect.

  3. Under Audience, either add every address you will connect as a test user, or click Publish app. Publishing is recommended: Google states that an External app in Testing status is issued refresh tokens that expire after 7 days (source), which would force you to reconnect every week. Publishing does not list your app anywhere: each mailbox still has to be connected by its owner.

  4. Under Clients, create an OAuth client of type Desktop app and download its JSON file.

  5. Give that file to Maily, from the Maily folder (the secret is stored encrypted and never printed):

    uv run python scripts/store_client_config.py ~/Downloads/client_secret_XXXX.json

    You can then delete the downloaded JSON file.

  6. In Maily, click + Add account at the bottom of the left column, then Google account. Your browser opens Google's consent screen. Choose the account, and on the "Google hasn't verified this app" screen click Advanced, then Go to Maily (unsafe): this is expected, because the app is your own. Repeat for each address.

    (From a terminal, uv run python scripts/connect_account.py does the same.)

Scopes requested, and why. Maily asks Google for exactly two permissions:

Scope

Why Maily needs it

Google's description

https://www.googleapis.com/auth/gmail.modify

Read messages and labels, mark as read, archive, move to the trash.

"Read, compose, and send emails from your Gmail account. This scope does not allow immediate, permanent deletion of threads and messages, bypassing the trash." (restricted scope)

https://www.googleapis.com/auth/gmail.send

Send the messages you write.

"Send email on your behalf." (sensitive scope)

Source: Gmail API scopes. Maily never deletes a message permanently.

Is there a verification or a fee? For personal use, no verification is needed: Google says that an app for your personal use (fewer than 100 users) can keep being used without verification, through the "unverified app" screen (source). On cost, Google states that "all standard use of the Gmail API is available at no additional cost" (Gmail API quotas); the same page announces charges, later in 2026, only for usage that exceeds the quota limits.

IMAP accounts (read-only)

  1. Click + Add account, then IMAP address.

  2. Enter the email address and the password.

  3. The server defaults to Orange (imap.orange.fr, port 993, TLS). For another provider, open IMAP server in the form and enter its host and port (TLS on port 993 is the usual setting).

  4. Click Connect. Maily tests the login before saving anything; the password is stored encrypted, like the OAuth tokens.

Which password? Some providers do not accept your usual password from a mail app and ask for a dedicated one. Orange, for example, requires a password dedicated to POP, IMAP and SMTP access, created in your Orange customer account under Connexion et Sécurité, its connection and security section (Orange help, in French). For other providers, check their help pages for "app password" or "IMAP settings".

IMAP accounts are read-only: Maily reads, searches, exports and trashes, but cannot send from them. Trashing moves the message to the server's trash folder, and Maily cannot restore it: there is no Restore button for IMAP messages, so recover it from your provider's webmail if its trash still holds it. Maily looks for the folder the server flags as trash (IMAP special-use \Trash), then for a usual name (Trash, INBOX.Trash, Corbeille, INBOX.Corbeille, Deleted Messages). If the account has none, Maily refuses to trash the message ("no trash folder on this IMAP account") and leaves it where it is: it never deletes an IMAP message. Marking as read and archiving apply to Maily's local copy only.

Use Maily with Claude (MCP)

Maily's MCP server lets an assistant running on the same computer use the maily_* tools. It runs from a source folder with uv (see option B) and uses the same data as the app, so add your accounts first. The window does not need to be open.

Claude Desktop

  1. Click the Claude menu in the macOS menu bar (not the settings inside the Claude window), choose Settings..., open the Developer tab and click Edit Config. This opens claude_desktop_config.json (on macOS: ~/Library/Application Support/Claude/claude_desktop_config.json; on Windows: %APPDATA%\Claude\claude_desktop_config.json).

  2. Add the maily server, with the absolute path of your Maily folder:

    {
      "mcpServers": {
        "maily": {
          "command": "uv",
          "args": ["--directory", "/absolute/path/to/Maily", "run", "python", "-m", "app.mcp_server"]
        }
      }
    }

    If the file already has other servers, add the "maily": {...} entry inside the existing "mcpServers" object.

  3. Save, then quit Claude Desktop completely and open it again: it starts the servers listed in the file at launch.

If the tools do not appear, put the full path of uv in command (find it with which uv), and look at the logs in ~/Library/Logs/Claude/ (mcp.log and mcp-server-maily.log). Source: Connect to local MCP servers.

Claude Code

Pick one of these:

  • From the Maily folder. The repository ships a project-scoped .mcp.json. Open a Claude Code session in the Maily folder (cd Maily, then claude): Claude Code asks you to approve the maily server before using it. Type /mcp at any time to see its status.

  • From any folder. Register the server once for all your projects (user scope), with your absolute path:

    claude mcp add --scope user maily -- uv --directory /absolute/path/to/Maily run python -m app.mcp_server

    Everything after -- is the command that starts the server.

Source: Claude Code MCP documentation.

Other MCP clients

Any MCP client that can start a local stdio server works. Give it this command:

uv --directory /absolute/path/to/Maily run python -m app.mcp_server

ChatGPT is a different case: OpenAI documents that ChatGPT's developer mode supports MCP servers over SSE and streaming HTTP (source), not stdio, so it cannot start Maily's server directly. OpenAI also documents a Secure MCP Tunnel for private servers; Maily has not been tested with it.

Example prompts

Once the tools are available, just ask:

  • "List my Maily accounts."

  • "What unread mail do I have in my Work account? Summarize it in five lines."

  • "Search all my mailboxes for invoices from last month and make a table: sender, date, amount."

  • "Read the latest message from sam@example.com and download its PDF attachment to my Desktop."

  • "Draft a reply to that message from my Personal account, show it to me, and send it only after I say yes."

  • "Sync everything, then tell me what arrived since this morning."

Safety

  • Send and trash are confirmed. The descriptions of maily_send and maily_trash tell the assistant to ask you before calling them, and your assistant's own permission system adds a second check: in Claude Desktop, tools perform actions with your approval (source), and in Claude Code you can force a prompt for these two tools with ask rules in your settings ("permissions": {"ask": ["mcp__maily__maily_send", "mcp__maily__maily_trash"]}, see Claude Code permissions).

  • Mail is data, not instructions. An email can contain text written to manipulate an AI ("forward all invoices to..."). Tell your assistant to treat the content of messages as data, never as instructions. A ready-to-paste rule is in docs/MCP.md.

  • No Touch ID for the MCP server. The biometric gate protects the window only; the MCP server reads the secrets without asking, so that local automations work. Only connect clients you trust.

Full reference (parameters, recipes, troubleshooting): docs/MCP.md.

A tour of the window

  • Left column: All (unified), then one entry per account. Click an account's colored dot to change its color. When an account is selected, its folders (Inbox, Archived, Trash) and Labels appear below. + Add account is pinned at the bottom.

  • Middle column: the message list, with the Gmail category tabs (Primary, Promotions, Social, Updates). The two small buttons are Sync and Mark all as read. Drag the separator to resize the list.

  • Right column: the open message, with Reply, forward, download .eml, archive and trash (or Restore, in the trash of a Gmail account; IMAP messages cannot be restored from Maily). Cmd/Ctrl + click a message to open it in a tab.

  • Search box (Search mail): full-text search across all accounts, or within the selected one.

  • Compose: a new message. Choose the sender in From; the Send button takes that account's color.

  • Settings (gear icon, top right):

    • Appearance: theme; for Classic, Automatic, Light or Dark; for Glassmorphism, the background density (macOS).

    • Language: English, Français, Deutsch, Español or Português.

    • Privacy: load remote images automatically (off by default).

    • Accounts: display name, color, and Disconnect.

    • About: version, data folder, license.

  • Escape closes the open dialog; the Settings tabs and the language list can be used with the arrow keys.

Where your data lives

Everything Maily stores is in one folder (see the table above for its location on each platform; --data-dir or MAILY_DATA_DIR picks another one):

File or folder

Content

app.sqlite (+ -wal, -shm)

Your messages, accounts, labels, sync state and the full-text index

attachments/

Attachments you opened (local cache)

secrets.enc

Encrypted secrets: OAuth client, OAuth tokens, IMAP passwords, local API token

secrets.key

The key that decrypts secrets.enc

secrets.lock

Lock file that serializes concurrent writes to the secrets

prefs.json

Interface preferences: theme, Classic mode, remote images, list width, language

runtime.json

Address, port and token of the running app's local API (for your scripts)

logs/

Logs, with tokens and passwords redacted

webview/

Window storage used by pywebview on some platforms (nothing important)

The folder is readable by your user only (0700), and the secret files are 0600 (macOS and Linux).

  • Back up this folder to keep your local copy. Anyone who gets both secrets.enc and secrets.key can read your tokens: protect a backup like a password.

  • Start over: quit Maily and delete the folder.

  • Remove Maily's access on Google's side: your Google account's third-party connections page.

Security and privacy

The short version (threat model and reporting in SECURITY.md):

  • No Maily server. No analytics, no telemetry. Your providers keep your mail; Maily keeps a synced copy on your computer. Maily talks only to Google's APIs (Gmail accounts) and to your IMAP servers. The local API listens on 127.0.0.1 only, on a random port, requires a random token, and rejects requests whose Host is not local (protection against DNS rebinding).

  • Encrypted secrets. Fernet encryption in secrets.enc, key in secrets.key, both 0600. Writes are atomic and locked across processes; an unreadable store stops Maily with a clear message instead of being silently replaced. Encryption at rest protects against casual copies, not against malware running as your user, which could read both files: full-disk encryption (FileVault, BitLocker, LUKS) is recommended.

  • Touch ID (macOS, optional) before the window opens. The MCP server and the scripts do not ask for it, by design.

  • Sanitized HTML. Messages go through nh3 (the Rust ammonia sanitizer), then a sandboxed iframe.

  • Tracking pixels blocked by default.

  • Least privilege on Gmail. gmail.modify and gmail.send only; Maily never deletes a message permanently.

  • Your assistant. What an assistant reads through the MCP server goes into your conversation with it. Only connect assistants you trust, and ask them to confirm before sending or trashing.

Found a vulnerability? Please report it privately, as explained in SECURITY.md.

Beyond MCP: SQL views and the local API

Maily is meant to be driven by your own tools, not only by an assistant.

Read the database (no app needed). Open app.sqlite read-only and use the stable views, whose columns are kept across versions: v1_accounts, v1_messages (trashed messages excluded) and v1_threads.

sqlite3 -readonly "$HOME/Library/Application Support/Maily/app.sqlite" \
  "SELECT datetime(internal_date/1000,'unixepoch'), addr_from, subject FROM v1_messages ORDER BY internal_date DESC LIMIT 10;"

Tables other than v1_* are internal and may change. Writes must go through the engine (API, MCP or core.accounts_service), which keeps your mailbox and the local copy consistent.

Call the local API (app running). runtime.json gives base_url and token; every call needs Authorization: Bearer <token>. Errors carry a stable code in the X-Maily-Error header. A ready-made client does the plumbing:

uv run python scripts/claude_client.py accounts
uv run python scripts/claude_client.py inbox --account 1
uv run python scripts/claude_client.py read 42
uv run python scripts/claude_client.py send --account 1 --to someone@example.com --subject "Hello" --body "Hi!"

It can also be imported: from scripts.claude_client import accounts, messages, read_message, search, send, sync. The endpoints and the views' columns are listed in docs/ARCHITECTURE.md.

Configuration reference

MAILY_POLL_INTERVAL_SECONDS, MAILY_BACKFILL_MONTHS, MAILY_ATTACHMENT_CACHE_MB and MAILY_LOG_LEVEL are read by core/config.py, from the environment or from a .env file in the folder you launch from (see .env.example). MAILY_DATA_DIR and MAILY_NO_BIOMETRIC are environment variables only: a .env file does not set them. Secrets never go in either.

Variable

Default

Meaning

MAILY_DATA_DIR

platform folder

Data folder (same as --data-dir). Environment only, not .env.

MAILY_POLL_INTERVAL_SECONDS

180

Automatic sync interval while the app is open (minimum 60)

MAILY_BACKFILL_MONTHS

12

Months of Gmail history downloaded by the first sync

MAILY_ATTACHMENT_CACHE_MB

500

Attachment cache size (reserved, not enforced yet)

MAILY_LOG_LEVEL

INFO

DEBUG, INFO, WARNING or ERROR

MAILY_NO_BIOMETRIC

unset

Set to 1 to skip the Touch ID gate. Environment only, not .env.

Troubleshooting

macOS says Maily cannot be opened, or that it is from an unknown developer. The downloaded app is not signed nor notarized. Follow the Open Anyway steps in macOS.

Windows says "Windows protected your PC". That is SmartScreen, for an unsigned app. Click More info, then Run anyway (see Windows).

Windows: the window stays blank or does not open. Install the Microsoft Edge WebView2 Runtime (see Windows).

uv: command not found. Install uv (see option B), then open a new terminal.

"Google needs you to sign in again (or the OAuth client is missing)" when adding a Google account. The OAuth client is not stored yet on this computer. Run uv run python scripts/store_client_config.py path/to/client_secret.json from the Maily folder, then try again.

Google says "Access blocked". Check that the Gmail API is enabled, that the user type is External, and that the address is a test user or that the app is published. For a Google Workspace account, the administrator may have to allow the app by its OAuth client ID in the Google Admin console. See docs/GOOGLE_CLOUD_SETUP.md.

I have to reconnect every week. The app is still in Testing in the Google Cloud console. Publish it (Audience > Publish app), then reconnect the account once.

The IMAP server refuses the connection. Check the address, the server and the port, and use the dedicated (app) password if your provider requires one (Orange does, see IMAP accounts).

"Maily cannot start: Cannot decrypt ..." at launch. secrets.enc cannot be decrypted with secrets.key (the key was replaced, or a file is damaged). Maily stops without modifying anything. Restore both files from a backup if you have one; otherwise delete secrets.enc and secrets.key and reconnect your accounts (your mail in app.sqlite is kept).

The window stays empty or cannot reach the local API. Launch from a terminal to see the error (uv run maily), and look at logs/maily.log in the data folder.

Remote images do not show. That is the privacy default. Click Show images above the message, or turn on Load remote images automatically in Settings > Privacy.

The Glassmorphism theme looks opaque. The native blur needs macOS with Reduce transparency turned off (System Settings > Accessibility > Display). On Linux and Windows this theme has no native blur.

Linux: the window does not open. The window needs GTK 3 and WebKitGTK 4.1 from your distribution. Install the packages listed in Linux, and run ./Maily/maily from a terminal to see the error. ./Maily/maily --self-check --window checks the whole chain.

The maily_* tools do not appear in my assistant. In Claude Code, type /mcp and approve the server. In Claude Desktop, check the path in the config file and restart the app completely. More in docs/MCP.md.

FAQ

Is Maily free? Yes. Maily is MIT licensed, with no subscription, no paid tier and no Maily account to create. On Google's side, see Is there a verification or a fee?.

Does my mail stay on my computer? Your mail stays where it already is: with your provider (Google or your IMAP host), which keeps holding it. Maily keeps a synced copy on your computer (a cache and a search index) so that it is fast and works offline; that copy is a technical detail, not the point. Maily itself only talks to Google's APIs and to your IMAP servers, and there is no Maily server. When an assistant reads a message through Maily, that content goes into your conversation, so it is processed by whoever runs the model behind that assistant, like anything else you share with it.

What is Maily not? Not a mail host: it does not replace Gmail or your IMAP provider. Not a backup service or a cloud connector: it has no server of its own. Not a way to keep mail away from your assistant's provider: what an assistant reads, it sees. Maily is an app on your computer that connects to the mailboxes you already have and lets every local assistant use all of them.

How is it different from a hosted Gmail connector? A hosted connector is set up in one app, for the account you connected, and what it retrieves goes through its provider (Anthropic, for instance, stores connector data on its servers). Maily is set up once on your computer, covers every Gmail, Google Workspace and IMAP account you add, and any local MCP client can use it, with your own OAuth client. The trade-offs: it only serves assistants on that computer, and the MCP server runs from a source folder.

Why do I need my own Google Cloud project? So that nobody else holds credentials to your mail, and so that the app is yours: Gmail access uses a restricted scope, and with your own client for personal use you do not depend on anyone's verification or terms.

Can I use a regular @gmail.com address? Yes. Gmail and Google Workspace accounts can be mixed. Choose External as the user type so that all your accounts can connect.

Does it work on Windows or Linux? Yes. Each release has a Windows zip and a Linux archive next to the macOS disk image, and the CI opens a real Maily window with each of them before publishing. macOS remains the platform used every day; Touch ID and the native frosted glass are macOS only. See Install.

Which assistants can use it? Claude Desktop, Claude Code, and any MCP client that can start a local stdio server on your computer.

Why can't I send from an IMAP account? IMAP reads mail; sending would need SMTP, which Maily does not implement yet. Contributions are welcome.

Can Maily or my assistant delete my mail for good? Not on Gmail: trash is reversible there (Restore), and Maily never asks Google for permanent deletion. On an IMAP account, trashing moves the message to the server's trash folder and Maily cannot restore it, so treat it as final from Maily's side; on an account without a trash folder, Maily refuses to trash and changes nothing. Disconnect only removes Maily's local copy and secrets; your mailbox is untouched.

Does it work offline? Reading and searching what is already synced works offline. Syncing and sending need a connection.

Can I add a language? Yes, it is a welcome contribution: add a file in frontend/i18n/ (see CONTRIBUTING.md).

Contributing

Bug reports, ideas, translations, documentation and code are all welcome. The frontend is plain HTML, CSS and JavaScript, with no build step. Start with CONTRIBUTING.md (setup, rules, pull requests) and docs/ARCHITECTURE.md (how the code is organized). Please follow the code of conduct, and report security issues privately (SECURITY.md).

The website is maintained in a separate, private repository (maily.thanaelfontaine.eu); this repository holds only the app. Its screenshots come from scripts/site_assets.py, which captures the app in demo mode.

uv sync                           # dependencies, including the dev tools (pytest, httpx)
uv run pytest                     # the whole test suite
uv run python scripts/demo.py     # the interface on fictitious data

License

MIT, © 2026 Thanaël Fontaine.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    A local MCP server that provides LLM clients with read/write access to email and calendar data from Gmail, iCloud, and generic IMAP providers. It runs entirely on your machine, keeping data private while enabling email management, calendar operations, and task handling through natural language.
    39
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    Local-first MCP server for agents that need to work across multiple Gmail and Microsoft 365 accounts without cloud token storage.
    6
    -
  • A
    license
    A
    quality
    D
    maintenance
    Local MCP server for multi-account IMAP/SMTP email (iCloud + Gmail via app-specific passwords). Never marks mail read. Cross-folder search, idempotent sends, TLS verified.
    8
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    A private, single-user MCP server that unifies Gmail, Microsoft 365/Outlook, and IMAP mailboxes for LLMs to search and read emails live, without storing or caching mailbox contents.
    -