An MCP server that lets an AI agent drive the Thunderbird already running on your machine — mail, folders, contacts, calendar, filters and settings — through Thunderbird's own internals. Not a copy of your mailbox and not an IMAP client: the same Thunderbird you have open.
Built for Claude Code and Codex CLI, and able to serve both at once.
- Search works. Every search tool in 1.2.0 was broken by six separate defects
(reported in #3).
mail_search,search_globalandsearch_conversationnow return results, dates, scores and participants, and folder scoping actually scopes. - Paging keeps every message. A cursor no longer skips the rest of the page it stopped in.
- Errors mean something. No more "An unexpected error occurred": a failure says
what to change, on every
mcpSDK version, and a large answer no longer drops the connection. - The bridge explains itself.
tb_statusandtbmcp doctorreport both sides of the connection, name the remedy, warn when the installed add-on is older than the package, and point at the daemon's log. - Tests that see the add-on. 111 node tests run the real add-on scripts; 288 Python tests; CI on Windows, macOS and Linux with Python 3.11–3.14.
Details in CHANGELOG.md.
git clone https://github.com/U-C4N/Thunderbird-MCP
cd Thunderbird-MCP
python bootstrap.py --clients claude-code,codexOne command builds the environment, installs the add-on (Thunderbird restarts
once), registers the clients you name and verifies the chain. Re-running it is safe.
Agents can parse python bootstrap.py --json instead of the human output.
Then ask your agent something — "what did the accountant say about VAT in June?", "archive everything from newsletters older than a month", "draft a reply to the last mail from Ali and let me read it first".
Prefer to register a client by hand? tbmcp setup --print-config all prints the
exact block for Claude Code, Codex, Claude Desktop, Cursor, VS Code, Gemini CLI and
Zed. Use python -m tbmcp serve as the command, with an absolute interpreter path.
| Find | ranked full-text search across every account, threads rebuilt across Inbox, Sent and archives, substring filters by folder, sender, date, tag |
| Triage | mark, tag, move, archive, file in bulk — with the source folder reported so a wrong move is reversible |
| Write | mail_send produces a reviewable draft by default; sending is a separate, confirmed step |
| Configure | server ports and security, identities and signatures, SMTP servers, junk handling, ~5,700 preferences — every write reports the old value so it can be undone |
| Automate | create, reorder and run message filters; events and tasks on the calendar |
| Diagnose | tb_status, tb_diagnostics, tb_console and tbmcp doctor say what is wrong and what to do |
Reads are unrestricted. Anything that sends, deletes or changes configuration needs
confirm=true, carries destructiveHint, and prompts for approval on clients that
support elicitation. mail_send drafts unless told mode="send"; dry_run_only
previews a write; preference writes are allowlisted and credentials, proxy and
security prefs are refused at both layers. --read-only registers no mutating tool
at all.
Tools are grouped so an agent only pays context for what it uses. The default set is
mail, folders, compose, search and admin; add the rest with --toolsets.
tbmcp serve --toolsets all # everything
tbmcp serve --toolsets +calendar # the default set plus one
tbmcp tools --toolsets all # list what would be registered112 tools across 10 toolsets; 48 of them read-only. Full signatures in docs/TOOL-REFERENCE.md.
mail — 15 tools · on by default — Search, read, triage and file messages, plus attachments and tags.
| Tool | What it does | |
|---|---|---|
mail_search |
read | Search the user's mail. Combine full_text with any filters below |
mail_list |
read | List messages in one folder, newest first by default |
mail_get |
read | Read one message. text gives headers plus the plain-text body |
mail_get_many |
read | Read up to 50 messages in one round trip — for triaging a search result |
mail_get_source |
read | Fetch a message's raw RFC 5322 source, for header forensics |
mail_attachments |
read | List a message's attachments with part names, sizes and content types |
mail_save_attachment |
write | Write one attachment to a directory on this machine |
mail_mark |
write | Set read/flagged/junk state or adjust tags on one or more messages |
mail_move |
write | Move messages into another folder |
mail_copy |
write | Copy messages into another folder, leaving the originals in place |
mail_archive |
write | Archive messages using each account's configured archive layout |
mail_delete |
destructive | Delete messages. Moves to Trash unless permanent=true |
mail_tags |
read | List the tags defined in Thunderbird, with keys, labels and colours |
mail_tag_upsert |
write | Create a tag, or recolour/rename an existing one |
mail_tag_delete |
destructive | Remove a tag definition. Messages keep the raw keyword but lose the label |
folders — 15 tools · on by default — The folder tree, its counts, and creating/renaming/emptying folders.
| Tool | What it does | |
|---|---|---|
folder_list |
read | List mail folders with their ids and message counts |
folder_get |
read | Get one folder: counts, special use, flags and IMAP quota |
folder_capabilities |
read | Report what may be done to a folder before attempting it |
folder_get_unified |
read | Get the unified folder that spans every account, e.g. all inboxes at once |
folder_create |
write | Create a folder inside another folder, or at the top of an account |
folder_rename |
write | Rename a folder, keeping its messages and subfolders |
folder_move |
write | Move a folder under a different parent, with its subfolders |
folder_copy |
write | Copy a folder and its contents under another parent, leaving the original |
folder_delete |
destructive | Delete a folder, its subfolders and every message in them |
folder_mark_read |
write | Mark every message in a folder as read |
folder_set_favorite |
write | Add or remove a folder from the user's favourites |
folder_empty_trash |
destructive | Permanently delete everything in one account's Trash |
folder_empty_junk |
destructive | Permanently delete everything in one account's Junk folder |
folder_sync_offline |
write | Fetch an IMAP folder's message bodies so they are available offline |
folder_compact |
write | Reclaim the disk space left behind by deleted messages |
compose — 6 tools · on by default — Sending, replying, forwarding, drafts and templates.
| Tool | What it does | |
|---|---|---|
mail_send |
destructive | Write a message. Saves a reviewable draft unless mode="send" |
mail_reply |
destructive | Reply to a message. Saves a reviewable draft unless mode="send" |
mail_forward |
destructive | Forward a message. Saves a reviewable draft unless mode="send" |
mail_draft_save |
write | Save a message without sending it, as a draft or a template |
mail_compose_open |
write | Open a populated compose window for the user to finish by hand |
mail_send_status |
read | List messages sitting in the Outbox, unsent |
search — 7 tools · on by default — Ranked whole-corpus search, conversations, and saved searches.
| Tool | What it does | |
|---|---|---|
search_global |
read | Ranked full-corpus search across every indexed folder and account |
search_conversation |
read | Every message in one thread, oldest first, across folders and accounts |
search_index_status |
read | Whether Thunderbird's global index is enabled, and how far along it is |
search_saved_list |
read | List the saved searches (virtual folders) and what each one matches |
search_saved_create |
write | Create a saved search that appears in the folder pane |
search_saved_update |
write | Redefine an existing saved search, by name or uri |
search_saved_delete |
destructive | Remove a saved search. The messages it listed are not touched |
contacts — 13 tools — Address books, contacts and mailing lists.
| Tool | What it does | |
|---|---|---|
contact_search |
read | Look someone up in the address book |
contact_list |
read | List contacts, across every address book unless one is named |
contact_get |
read | Read one contact in full, including its raw vCard |
contact_create |
write | Add a contact to an address book |
contact_update |
write | Change fields on an existing contact |
contact_delete |
destructive | Delete a contact. There is no Trash for contacts, so this cannot be undone |
addressbook_list |
read | List the address books, with how many contacts and lists each holds |
addressbook_create |
write | Create an empty local address book |
addressbook_delete |
destructive | Delete an address book together with all its contacts and mailing lists |
mailinglist_list |
read | List address book mailing lists, with member counts |
mailinglist_create |
write | Create an empty mailing list in an address book |
mailinglist_add_member |
write | Add an existing contact to a mailing list |
mailinglist_remove_member |
write | Take a contact off a mailing list. The contact itself is left alone |
calendar — 12 tools — Calendars, events and tasks, including recurring series.
| Tool | What it does | |
|---|---|---|
calendar_list |
read | List the user's calendars, with ids, types and whether each is writable |
calendar_create |
write | Create a calendar and register it with Thunderbird |
calendar_update |
write | Rename or recolour a calendar, or toggle read-only and disabled |
calendar_delete |
destructive | Remove a calendar. Deletes its events and tasks with it |
event_list |
read | List events in a time window, soonest first |
event_get |
read | Read one event or task in full, including attendees and recurrence |
event_create |
write | Create an event. Omit end for a one-hour meeting |
event_update |
write | Change an event. Only the fields you pass are touched |
event_delete |
destructive | Delete an event or a task. Calendars have no trash, so this is final |
task_list |
read | List tasks, soonest due first. Completed ones are hidden by default |
task_create |
write | Create a task. Everything but the title is optional |
task_update |
write | Change a task, or tick it off with completed=true |
filters — 8 tools — Thunderbird's message filters, including running them on demand.
| Tool | What it does | |
|---|---|---|
filter_list |
read | List filters in execution order, with their conditions and actions |
filter_get |
read | Read one filter in full, by account and index |
filter_create |
write | Create a filter. It is appended, so existing rules keep their order |
filter_update |
write | Change a filter in place. Only what you pass is touched |
filter_set_enabled |
write | Turn one filter on or off without changing its definition |
filter_reorder |
write | Move a filter to a different position in the execution order |
filter_delete |
destructive | Delete a filter. Thunderbird keeps no history, so the rule is gone |
filter_run |
write | Apply filters to folders on demand, as "Run Filters on Folder" does |
accounts — 18 tools — Incoming servers, identities, signatures, SMTP servers, per-account junk.
| Tool | What it does | |
|---|---|---|
account_list |
read | List the mail accounts and how each one is configured |
account_get_server |
read | Read one account's incoming server settings |
account_set_server |
write | Change one incoming server setting. Getting the connection wrong stops mail |
account_get_junk |
read | Read one account's junk-mail handling: level, whitelist, move and purge rules |
account_set_junk |
write | Change one junk-mail setting for an account |
account_get_folders |
read | Read where an identity files sent mail, drafts, templates and archives |
account_set_folders |
write | Change where an identity files sent mail, drafts, templates or archives |
account_get_sync |
read | Read an account's offline and synchronisation settings |
account_set_sync |
write | Change one offline or synchronisation setting for an account |
identity_list |
read | List the sending identities, across every account or just one |
identity_get |
read | Read one identity in full: addresses, signature, outgoing server, filing folders |
identity_set |
write | Change an identity's addresses and composition defaults |
identity_set_signature |
write | Replace an identity's signature text, or point it at a file |
smtp_list |
read | List the SMTP servers, and which one is the default |
smtp_create |
write | Add an SMTP server. Nothing sends through it until an identity points at it |
smtp_update |
write | Change an existing SMTP server. Only the fields you pass are touched |
smtp_delete |
destructive | Remove an SMTP server. Identities using it will be left unable to send |
smtp_set_default |
write | Make one SMTP server the default for identities that have none of their own |
settings — 11 tools — Preferences, junk training and OpenPGP key listing.
| Tool | What it does | |
|---|---|---|
pref_get |
read | Read one Thunderbird preference |
pref_get_many |
read | Read up to 100 preferences in one round trip |
pref_list |
read | List preferences under a branch, e.g. prefix="mail.biff." |
pref_user_set |
read | Everything the user has changed from the shipped defaults |
settings_describe |
read | Map a human request onto the preference that controls it |
pref_set |
write | Change one Thunderbird preference |
pref_reset |
write | Clear a user-set preference so the shipped default applies again |
junk_get |
read | Read the global junk (bayesian) filter settings |
junk_set |
write | Change the global junk filter settings. Only the fields you pass are touched |
junk_train |
write | Teach the junk filter that these messages are junk, or are not |
openpgp_list_keys |
read | List the OpenPGP keys in Thunderbird's keyring |
admin — 7 tools · on by default — Connection status, events, diagnostics and the error console.
| Tool | What it does | |
|---|---|---|
tb_status |
read | Whether Thunderbird is attached, and which halves of the add-on loaded |
tb_wait |
read | Block until Thunderbird attaches to the bridge, then report status |
tb_events |
read | Read buffered Thunderbird notifications: new mail, folder and account changes |
tb_diagnostics |
read | One report: versions, profile, which capabilities loaded, accounts, indexing |
tb_console |
read | Recent lines from Thunderbird's error console, newest last |
tb_addons |
read | List installed add-ons with their enabled and signature state |
tb_restart |
destructive | Restart Thunderbird. Every call in flight fails, including other clients' |
Claude Code ──stdio──▶ tbmcp serve ─┐
├─local RPC─▶ tbmcp daemon ◀══WebSocket══ add-on
Codex CLI ──stdio──▶ tbmcp serve ─┘ owns the socket, inside
multiplexes clients Thunderbird
Thunderbird has no external API, so an add-on runs inside it: the official
MailExtension API for mail and folders, plus a WebExtension Experiment for what that
API cannot reach (preferences, accounts, filters, calendar). Python listens on
loopback and the add-on dials out with a token from <profile>/tbmcp-bridge.json, so
nothing is bound inside Thunderbird and nothing leaves the machine. A small daemon
owns the one connection and shares it between clients.
More in docs/ARCHITECTURE.md and docs/PROTOCOL.md.
Run tbmcp doctor. It checks every link — Python, the profile, the add-on that is
installed versus the one in this package, the daemon, the handshake from both sides —
and names the broken one with the command that fixes it.
| Symptom | What to do |
|---|---|
| "Thunderbird is not connected" | start Thunderbird; if it is running, tbmcp doctor says why the add-on has not attached |
| the add-on connects but never completes the handshake | restart Thunderbird; doctor shows both views and the daemon log's path |
doctor warns the installed add-on is older than the package |
tbmcp install-addon |
| settings tools fail, mail tools work | the privileged half did not load → tbmcp install-addon |
| full-text search finds nothing | the global indexer is off (Settings → General), or search_global names the words the index cannot match |
| a dependency fails to load on Windows | Windows Application Control blocked a wheel; python bootstrap.py repairs it |
TBMCP_DEBUG=1 logs verbosely to stderr; TBMCP_STATE_DIR moves the daemon's
files; tb_console returns the add-on's [tbmcp] lines as a tool.
Thunderbird 128 or newer (verified on 155), Python 3.11–3.14, Windows, macOS or Linux including Snap and Flatpak Thunderbird.
uv venv && uv pip install -e ".[dev]"
pytest # 288 tests, no Thunderbird needed
node --test "tests/js/*.test.mjs" # 111 add-on tests under node:vm
ruff check . && ruff format --check .
python tools/check_consistency.py # the three layers still agree
python tools/smoke_search.py # live acceptance, against a running ThunderbirdThe add-on's real scripts run under node:vm against fakes of the WebExtension and
XPCOM surfaces (tests/js), which is how the sandbox rules that broke 1.2.0's search
are now caught before a release. Everything documented here was verified against a
live Thunderbird; the measurements are in
docs/VERIFIED-FINDINGS.md.
GitHub @U-C4N · X @UEdizaslan
Related: Autocad-MCP · U-Pool · Deuz-SDK
Issues and pull requests are welcome. Report a security problem through GitHub rather than a public issue.
MIT — see LICENSE. The add-on contains no Mozilla-licensed code.