Thunderbird MCP gives an AI agent real control of the Thunderbird already running on your machine — your mail, your folders, your contacts, your calendar, your filters, and your actual settings.
概览
Thunderbird MCP gives an AI agent real control of the Thunderbird already running on your machine — your mail, your folders, your contacts, your calendar, your filters, and your actual settings.
README
Thunderbird MCP
Thunderbird MCP gives an AI agent real control of the Thunderbird already running on your machine — your mail, your folders, your contacts, your calendar, your filters, and your actual settings. Not a copy, not an IMAP re-implementation: the same Thunderbird you have open, driven through its own internals.
Written in Python, built for Claude Code and Codex CLI, and able to drive one Thunderbird from both at the same time.
What you can ask for
- Find things you half-remember. “What did the accountant say about VAT in June?” runs a ranked search over the whole indexed corpus and can reconstruct a thread that spans Inbox, Sent and an archive folder in one call.
- Triage a mailbox. Mark, tag, move, archive and file in bulk — with the source folders reported back so a wrong move is reversible.
- Write mail you get to read first.
mail_sendproduces a reviewable draft by default; sending is a separate, confirmed step, and needs no compose window. - Change settings, properly. Server ports and connection security, identities and signatures, SMTP servers, junk handling, archive layout, message-pane layout, ~5,700 preferences — read the current value, write the new one, and get the old one back so you can undo it.
- Automate the boring rules. Create and reorder message filters, then run them over an existing folder to check they do what you meant.
- Keep a calendar honest. Events and tasks, with recurring items addressed as a series unless you name one occurrence.
[!NOTE] Everything documented here was verified against a live Thunderbird 153 on Windows 11, not inferred from documentation. The measurements, and the traps found the hard way, are in docs/VERIFIED-FINDINGS.md.
Quick start
There is no PyPI package to install from yet — clone the repo and let it build its own environment:
git clone https://github.com/U-C4N/Thunderbird-MCP
cd Thunderbird-MCP
python bootstrap.py
One command: it picks an interpreter that works, builds the environment, installs
the add-on, and verifies the whole chain before it returns. Re-running it is safe —
healthy steps are no-ops. Add --clients claude-code,codex to also register those
clients in the same run, or do it afterward from “Install into a client” below.
Installing the add-on closes Thunderbird, installs through Thunderbird’s own
automation channel, and starts it again — no clicking through the Add-ons UI;
bootstrap does this for you as its addon step. Prefer to do it by hand, or on its
own? tbmcp install-addon --manual builds the package and prints the three clicks.
Already installed? tbmcp bootstrap does the same thing.
For AI agents
python bootstrap.py --json
Emits one object: ok, version, launcher, steps[] (each with name,
status, seconds, detail), and next_command — null on success, otherwise the
single command that addresses the failure. status is one of ok, repaired,
skipped, failed. Parse this instead of the human output; the columns are not a
stable interface and the JSON is.
A healthy doctor looks like this:
thunderbird-mcp doctor
Python
version 3.14.6
interpreter C:\Users\VECTOR\Documents\GitHub\Thunderbird-MCP\.venv\Scripts\python.exe
Thunderbird
executable C:\Program Files\Mozilla Thunderbird\thunderbird.exe
running True
add-on version (source) 1.2.0
profile C:\Users\VECTOR\AppData\Roaming\Thunderbird\Profiles\81l4u5ba.default-release
accounts (from prefs.js) 2
outgoing servers 1
global index db True
add-on startup report 2026-08-11T06:41:40.527Z
privileged modules 12 loaded
bridge methods 128
Bridge
daemon pid 31120
connected True
add-on version (live) 1.2.0
privileged half True
app Thunderbird 153.0.2
tb_status tool call connected
Tools
toolsets mail,folders,compose,search,admin
read-only False
send mode draft
Install into a client
Toolsets
Tools are grouped so you only pay context for what you use. The default set is lean;
add the rest with --toolsets.
tbmcp serve --toolsets all # everything
tbmcp serve --toolsets mail,settings # exactly these
tbmcp serve --toolsets +calendar # the default set plus one
tbmcp tools --toolsets all # list what would be registered
112 tools across 10 toolsets; 48 of them read-only. Full signatures in docs/TOOL-REFERENCE.md.
Safety
Reads are unrestricted. Anything that sends, deletes, or changes configuration is gated four ways, because no single mechanism exists on every client:
| Layer | Effect | Present on |
|---|---|---|
readOnlyHint / destructiveHint annotations |
lets the host decide when to ask | hosts that read annotations |
anthropic/requiresUserInteraction |
prompts even under bypassPermissions |
Claude Code |
an explicit confirm=true argument |
the call is refused without it | everything, including Codex |
| an approval prompt via elicitation | a real question, and invisible in the tool schema so a model cannot fabricate the answer | clients with elicitation |
Beyond the gate:
mail_senddrafts by default.--sendormode="send"changes that.dry_run_only=truepreviews a write — including what it would replace — without asking for approval and without touching anything.- Every write reports the previous value, which is what makes an undo possible without a transaction log.
- Preference writes are allowlisted.
--unsafe-prefswidens the allowlist, but credentials,network.proxy.*,security.*and the add-on trust model are refused outright — at the Python layer and again in the privileged module, which is the only layer with real privilege. - Private keys never move. OpenPGP keys can be listed and public keys exported; asking for secret key material is refused by design.
--read-onlyregisters no mutating tools at all, which makes a safe second registration easy.--yoloremoves every gate. It exists for scripted use. Do not leave it on.
How it works
Thunderbird has no external API, so anything that drives it has to run inside it.
The official MailExtension API is large — 250 functions on 153 — but it cannot touch
preferences, account or server configuration, message filters, junk training, virtual
folders, or the calendar. So the add-on pairs that API with a WebExtension
Experiment API, which runs with the system principal and therefore has full XPCOM
access. Release Thunderbird builds ship MOZ_REQUIRE_SIGNING=false and default
extensions.experiments.enabled=true, so the unsigned bridge installs and gets those
privileges on a stock install.
Python listens and the add-on dials out, rather than embedding an HTTP server in Thunderbird. That needs no port bound inside Thunderbird and no firewall exception, survives Thunderbird restarts, works unchanged under Snap and Flatpak, and vendors no MPL-licensed Mozilla code. A small broker daemon owns the single connection, which is what lets two clients share one Thunderbird.
Claude Code ──stdio──▶ tbmcp serve ─┐
├─local RPC─▶ tbmcp daemon ◀══WebSocket══ add-on
Codex CLI ──stdio──▶ tbmcp serve ─┘ owns the socket, inside
multiplexes clients Thunderbird
The daemon picks a free port and writes /tbmcp-bridge.json with a token;
the add-on reads it with privileged file I/O and authenticates on connect. Nothing is
ever bound to a non-loopback interface.
Full detail in docs/ARCHITECTURE.md and docs/PROTOCOL.md.
Requirements
- Thunderbird 128 or newer — developed and verified against 153
- Python 3.11+
- Windows, macOS or Linux, including Snap and Flatpak Thunderbird
Troubleshooting
tbmcp doctor checks each link in the chain and names the one that is broken. The
add-on also writes /tbmcp-addon-status.json at startup — which privileged
modules loaded, how many bridge methods exist, which capabilities Thunderbird actually
granted — and doctor reads it. Its absence, on an add-on that is installed and
active, is itself the diagnosis.
| Symptom | Cause |
|---|---|
| “Thunderbird is not connected” | Thunderbird is closed, or the add-on is not installed |
| “the add-on never wrote its startup report” | the privileged half did not load → tbmcp install-addon again |
| settings tools fail but mail tools work | same cause; check privileged modules in doctor |
| full-text search finds nothing | Thunderbird’s global indexer is off (Settings → General) |
| raw message source unavailable on IMAP | the message is not stored offline → folder_sync_offline |
| the first tool call after a killed daemon fails | the add-on takes ~40-60 s to reattach after an abnormal daemon exit; retry, or tbmcp doctor --wait 60. A clean exit reattaches in about a second. Details |
| Codex reports a startup timeout | raise startup_timeout_sec; Codex defaults to 10 s |
| Claude Code truncates a large result | raise MAX_MCP_OUTPUT_TOKENS (default 25,000) |
| A dependency fails with “DLL load failed” or “cannot open shared object file” | A binary your OS will not load — Windows Application Control blocks unsigned, low-reputation wheels. bootstrap detects this and downgrades the offending package automatically; run python bootstrap.py and read the binaries step. |
TBMCP_DEBUG=1 turns on verbose logging to stderr. The add-on logs to Thunderbird’s
error console with a [tbmcp] prefix, and tb_console returns those lines as a tool.
Development
uv venv && uv pip install -e ".[dev]"
pytest # 181 tests, no Thunderbird needed
ruff check . && ruff format --check .
python tools/check_consistency.py # do all three layers still agree?
python tools/build_xpi.py build # build the add-on package
python tools/gen_tool_reference.py # regenerate the tool docs from the code
python tools/smoke_live.py # read-only checks against a live Thunderbird
python tools/smoke_write.py # gated-write checks; leaves the profile unchanged
Three layers have to agree on method names — the Python tools, the add-on handlers,
and the privileged forwarding list — and nothing notices when they stop agreeing until
runtime. check_consistency.py compares them, validates every JavaScript file, and
checks the manifest lists exactly the scripts that exist. Run it before you commit.
| Document | |
|---|---|
| ARCHITECTURE.md | why it is built this way, and what was rejected |
| PROTOCOL.md | the wire protocol between Python and the add-on |
| TOOLS.md | the contract: tool → bridge method → implementation |
| TOOL-REFERENCE.md | every tool and parameter, generated from the code |
| VERIFIED-FINDINGS.md | measurements against a live Thunderbird, and the traps |
Author
GitHub @U-C4N · X @UEdizaslan
Built from actually living in Thunderbird all day, then made model-agnostic through MCP. Every capability was measured against a real install before it was documented — see VERIFIED-FINDINGS.md for what that turned up.
Related work: Autocad-MCP · U-Pool · Deuz-SDK
Contributing
Issues and pull requests are welcome. Before opening one:
pytest && ruff check . && python tools/check_consistency.py
check_consistency.py is the important one — it catches the mismatches between the
three layers that nothing else notices until runtime. If you add a tool, run
python tools/gen_tool_reference.py so the docs follow the code.
Report a security problem through GitHub rather than a public issue.
Licence
MIT — see LICENSE. The add-on contains no Mozilla-licensed code: the reverse-WebSocket design was chosen partly so that no MPL-2.0 HTTP server needed to be vendored.
安装
This server does not publish a one-line install command.
Open the repository installation guide