mail-notify
E-mail notifications for Claude Code, with a local settings web app. A small daemon on your own computer mails you when Claude finishes a long task, needs a permission, hits an error or a usage limit, or when your background processes finish. You can answer from your phone by replying to the mail.
Naming: the package and the command are mail-notify; the daemon and the Python module inside are called ccmail
(ccmail also works as a command).
- Few mails, on purpose. Every mail passes several gates (turn length, cooldown, hourly cap, event types). Turning it on or off sends nothing.
- Private by design. Standard library only, no telemetry. The only thing that leaves your machine is the mail itself, sent through your own mail account. The password lives in your system's credential store.
- Reply to act. Reply
OFF,ONorPAUSE 60to a notification or, if you enable it, write a sentence that is delivered to the Claude chat that sent the mail. - Understands your chats. Mails name the project, the chat and your Claude account, and carry a short summary of what Claude said (about 300 characters, at most 10 lines; the complete answer follows in a scrollable box).
- Works while you are away. While background tasks run, Claude is woken at a period you choose, only when tasks exist, to report their status.
Tested today: Windows with Gmail. macOS, Linux and mail providers other than Gmail (the Outlook, iCloud and Yahoo presets) are implemented and covered by the automated tests in CI, but are not yet tested on a real system: that is planned for a later release. Please report what you find.
Install step by step
You need your mail account's app password and about fifteen minutes. Run every command in your own terminal. ccmail never asks Claude for your password.
Requirements
- Python 3.10 or newer.
- Claude Code, as the CLI or the desktop app.
- A mail account that allows SMTP with an app password. Presets exist for Gmail, Outlook, iCloud and Yahoo, and you can enter any SMTP server.
- Optional, for replies by mail: IMAP access to the same mailbox.
Step 1: install the package
Install with pipx, which keeps the program in its own environment. Once mail-notify is on PyPI:
pipx install mail-notify
To install the current version from the repository instead:
pipx install git+https://github.com/lobascioemanuele-droid/mail-notify
With Microsoft Store Python the mail-notify command can be "not found", because its Scripts folder is not on
PATH. In that case use python -m ccmail instead of mail-notify in every step below, or run
python -m ccmail install-cli once: it creates a short mail-notify command in a folder that is already on PATH.
Step 2: set up the sender account
mail-notify init
The prompts ask for the sender address, the provider (the right app-password link is shown for it), the app password, the recipient (it defaults to the sender) and an optional device name. The app password is typed in a hidden prompt and stored only in the system credential store (Windows Credential Manager, macOS Keychain or Linux Secret Service). Never paste it into a chat, a file or an issue. This step sends no mail.
Where to create an app password:
- Gmail: https://myaccount.google.com/apppasswords (needs 2-step verification)
- Outlook: https://account.live.com/proofs/AppPassword
- iCloud: https://account.apple.com/account/manage
- Yahoo: https://login.yahoo.com/account/security
Step 3: install the hooks, the skill and the short command
mail-notify setup --autostart
One command does all of this, and sends nothing:
- adds ccmail's hooks to Claude Code's settings (a backup of the file is made first);
- copies the
/mail-notifyskill to~/.claude/skills/mail-notify/; - creates the short
mail-notifycommand if it is missing; - with
--autostart, starts the background daemon at logon.
It never asks for the password (that is init, step 2) and can be run again at any time. The three parts are also
available one by one: install-hooks, install-skill and install-cli.
Step 4: restart Claude Code
Close and reopen Claude Code: hooks and skills are read at startup. After that /mail-notify appears in the list of
skills.
Step 5: turn it on and test it
mail-notify on
Notifications are off after installation, so nothing is sent until you run this. on sends no mail.
mail-notify test
This sends one test mail. It also works while notifications are off.
mail-notify open
This opens the settings web app in your browser. It listens on 127.0.0.1 only and uses a one-time sign-in link.
Optionally, in its Rules tab, turn on:
- Control by replying to the e-mail (reply
OFF/ON/PAUSE; needs IMAP, see above). - Replies reach Claude (free-text replies are delivered to the chat that sent the mail).
- E-mail with buttons (an HTML version with tappable quick-reply cards).
Step 6: use it
In Claude Code, the /mail-notify skill accepts on, off, status, test and open:
/mail-notify on
/mail-notify status
Each project also has its own switch in the web app's Activity tab.
When reply control is on, each mail tells you how to reply. Write one of these on the first line of your reply:
OFF: turns notifications off for that project.ON: turns them back on for that project.PAUSE 60(orSLEEP, orPAUSEalone for 60 minutes): silence on all projects for that many minutes.
Updating
Update the package (use the command that matches how you installed it):
pipx upgrade mail-notify
pipx install --force git+https://github.com/lobascioemanuele-droid/mail-notify
Then refresh the hooks and the skill, and restart Claude Code:
mail-notify setup
Uninstalling
Stop the daemon, then remove the pieces in this order:
mail-notify stop
mail-notify uninstall-hooks --autostart
mail-notify uninstall-skill
mail-notify uninstall-cli
pipx uninstall mail-notify
Uninstalling does not delete the data folder or the stored password. Delete the data folder yourself (on Windows
%APPDATA%\ccmail), remove the ccmail entry from your credential store (on Windows it is ccmail:smtp in Credential
Manager), and revoke the app password at your mail provider.
What a mail looks like
- Header:
Project:,Account:andChat:(the chat's name in the desktop app, orSession:with a short id when the name is unknown).Device:appears when you set a device name. Turn duration and type follow for a finished turn. - OUTCOME (
ESITOin Italian): a short summary of the whole answer, about 300 characters (thesummary_charssetting) and at most 10 lines: a lead, one line per section, and the closing question when the answer ends with one. With HTML mail on, the complete answer is in a scrollable box below it. - Background processes: how many are still running, and their ids, when there are any.
- Suggested commands: a numbered list tailored to the message, to paste into the chat. They are tappable boxes when "Replies reach Claude" is on.
- Quick-reply cards (HTML mail with reply control on):
OFF,ONandSLEEP(a 1-hour pause on all projects), other pauses (15 minutes, 3 hours, 8 hours) and, if "Replies reach Claude" is on, "Write to Claude". Each card opens a pre-filled reply; you still press Send. - Reply instructions in plain text, when reply control is on.
What you get
| When | |
|---|---|
| Task finished | A turn longer than 2 minutes (setting) ended |
| Needs your input | A permission prompt or a question is waiting |
| Error / usage limit | An API error, or the account limit was reached (with the reset time) |
| Background processes | Periodic status while they run; one mail when the last one ends |
| Chat stalled | Optional watchdog: no events for N minutes with tasks declared |
HTML mail (optional)
Turn on E-mail with buttons in the web app's Rules tab and every mail also carries an HTML version next to the unchanged plain text: a coloured header by event type, the data in a grid, quick-reply cards, the suggested commands as tappable boxes (when "Replies reach Claude" is on), and the whole message in a box with its own scroll bar (at most 340 px tall). The same layout is used for the confirmation, "command executed" and "reply discarded" mails.
- The cards are pre-filled replies (
mailto:): you still press Send. One-click actions inside Gmail need registration with Google and a public HTTPS endpoint, which a daemon listening on loopback cannot offer. - A mail app that does not show HTML shows the plain text, without cards or the scroll box.
- Nothing extra leaves your machine: the box follows the content mode (
titleandminimaladd nothing), and text goes through the same secret redaction. - A verified reply that cannot be delivered (no chat tied to the mail, unknown or expired reference, delivery to Claude switched off) is answered with a mail whose outcome says "reply discarded" and why. At most 5 an hour.
Settings web app
mail-notify open shows: an Overview with health checks and the main on/off switch; Rules
(what to report, how often, how much text, reply control); Activity with per-project switches and the latest chats;
and Connection with provider presets, the device name and a test button. It listens on 127.0.0.1 only, with a
one-time sign-in link, CSRF protection and a strict content security policy.
The page, the mails and the command line follow the language setting: auto (the default), it or en. auto
uses the language set in Claude Code's own language setting when it names Italian or English, and otherwise the
system language. The page is translated from static catalogs in the package. Nothing is sent to a translation
service, and data from the daemon (project names, chat titles, mail subjects) is never translated.
Reply by mail
Off by default (web app → Rules → Control by replying to the e-mail). When on, ccmail reads your mailbox over IMAP
and accepts only replies that come from you, are verified by the provider, quote a valid reference from a recent
ccmail mail, and start with OFF, ON or PAUSE [minutes] (the Italian words SPEGNI, ACCENDI, PAUSA, and a few
others are accepted too). With Replies reach Claude on, any other reply text is delivered to the chat the mail came
from. The answer to it is mailed back to you when that turn ends, even if it was short, with the subject
"answer to your e-mail" (Italian: "risposta alla tua email").
See SECURITY.md for the exact rules and docs/HOW-IT-WORKS.md for the delivery paths.
The /mail-notify skill
mail-notify install-skill copies a small skill to ~/.claude/skills/mail-notify/. It lets you type
/mail-notify on, off, status, test or open in Claude Code, and tells Claude how to behave during long
processes (a concise status while they run, a short final summary). The skill is instructed never to read credential
files or edit Claude's settings.
Several computers
Every computer has its own daemon, data folder and credentials, and its own copy of the settings. Give each one a name
(mail-notify init, or web app → Connection → Name of this device) and it appears as Device: in the mails. A reply
is applied by the daemon that sent the mail it refers to; daemons ignore references they did not issue.
Platforms
| Windows | macOS | Linux | |
|---|---|---|---|
| Data folder | %APPDATA%\ccmail |
~/Library/Application Support/ccmail |
~/.config/ccmail |
| Password store | Credential Manager (fallback: DPAPI file) | Keychain (fallback: 0600 file) | Secret Service via secret-tool (fallback: 0600 file) |
| Start at logon | Task Scheduler | LaunchAgent | systemd --user (fallback: XDG autostart) |
| Status | tested | to be tested (later release) | to be tested (later release) |
The fallback files are announced loudly by init, status, doctor and the web app. On Linux install
libsecret-tools to use the Secret Service. Claude Code's folder honours CLAUDE_CONFIG_DIR.
Privacy and security
- ccmail reads only what it needs: what Claude Code passes to its hooks; the desktop app's chat titles and folders; the
latest chat title recorded at the end of a session transcript, when the app's metadata is missing; the account's
display name from
~/.claude.json(nothing else in that file is used); and thelanguagekey of Claude Code'ssettings.json. Installing the hooks edits that file, after making a backup. - Message text is redacted for common secrets before it is put in a mail.
titleandminimalcontent modes send no model-written text at all. - The password is never written to logs, the database, the repository or the web app; the API never returns it.
- The daemon binds to
127.0.0.1, authenticates hooks with a file-based token and checksHostandOrigin.
Details and the threat model: SECURITY.md. Report vulnerabilities privately (see that file).
Troubleshooting
| Symptom | Try |
|---|---|
| No mail at all | mail-notify status: notifications start OFF, so run mail-notify on. Then mail-notify doctor --network and mail-notify test; check spam and the app password |
| Hooks do nothing | mail-notify install-hooks, then restart Claude Code; mail-notify doctor flags a moved Python |
| No buttons or cards in a mail | Cards need HTML mail and reply control on; plain-text mail apps show no buttons |
| Chat names missing | mail-notify diagnose shows where the desktop app's metadata is looked for |
| Port busy | The daemon picks a free port by itself; hooks and CLI read it from daemon.json |
| Reply by mail is slow | Most of the delay is the provider delivering mail to yourself (seconds to about 25 s) |
| Linux: password in a file | Install libsecret-tools and run mail-notify init again |
Commands
init · setup [--autostart] · on · off · status [--cwd DIR] · test · open · stop · doctor [--network] · install-hooks [--autostart] · uninstall-hooks [--autostart] · install-skill · uninstall-skill · diagnose · trace on|off|show
· migrate · daemon [--foreground] · install-cli · uninstall-cli.
The command is installed as both mail-notify and ccmail. install-hooks and install-skill also create the short
mail-notify command. If neither name is found, run python -m ccmail install-cli once (see Step 1).
Resource use
No model calls at all in normal operation. The daemon uses about 35 MB and about 0.05–0.25 % of one core, and each hook call adds about 0.12 s. Tokens are spent only on the skill text, on periodic wake-ups while background tasks run and on delivered replies. The HTML mail costs no tokens (about 0.1–0.6 ms of CPU and 6–9 KB more per mail). Measurements, formulas and limits: docs/RESOURCES.md.
How it works
Hooks run a tiny client (python -m ccmail hook) that forwards the event to the local daemon and always exits 0. The
daemon applies the rules, writes to SQLite, and an outbox worker sends mail with retries. More in
docs/HOW-IT-WORKS.md.
Development
git clone https://github.com/lobascioemanuele-droid/mail-notify && cd mail-notify
python -m venv .venv
. .venv/bin/activate
On Windows, activate with .venv\Scripts\activate instead.
pip install -e .
python -W ignore -m unittest discover -s tests
Set PYTHONPATH=src:tests first if the package is not installed.
See CONTRIBUTING.md. Changes are listed in CHANGELOG.md; what 1.x promises to keep stable is in docs/STABILITY.md.
License
MIT.
Metadata
Release files for mail-notify 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mail_notify-1.0.0.tar.gz | 232.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mail_notify-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 409.7 kB
Release files / mail_notify-1.0.0.tar.gz
| Download URL | mail_notify-1.0.0.tar.gz |
|---|---|
| Size | 232.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9055bb36b5ebe0a304b8d6fb50fca2b43437888e290321229bf6e208b135b808
|
|
BLAKE2b-256 checksum How to use checksums |
c45dc29408fd1215cba9fd78a61b66b91ae7bb59d7f820bc5f39883e1659330b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / mail_notify-1.0.0-py3-none-any.whl
| Download URL | mail_notify-1.0.0-py3-none-any.whl |
|---|---|
| Size | 177.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4b9be36315dcc7a310d35956f48cfa153ff1ff0d86b6b98189fb559054894b47
|
|
BLAKE2b-256 checksum How to use checksums |
57b2c575ef8752a7a24ea9f89752606e04f2fb8d01a2bd8717b25792b90713dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency log