Thunderbird MCP
Provides tools to control the Thunderbird email client, including mail search, folder management, compose, settings, calendar, and message filters.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Thunderbird MCPdraft an email to Sarah about the quarterly report"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
Everything documented here was verified against a liveThunderbird 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
uv tool install thunderbird-mcp # or: pipx install thunderbird-mcp
tbmcp install-addon # builds the add-on and installs it into Thunderbird
tbmcp setup claude-code codex # registers the server with your clients
tbmcp doctor # confirms every link in the chaininstall-addon closes Thunderbird, installs through Thunderbird's own automation
channel, and starts it again — no clicking through the Add-ons UI. Prefer to do it by
hand? tbmcp install-addon --manual builds the package and prints the three clicks.
A healthy doctor looks like this:
Thunderbird
executable C:\Program Files\Mozilla Thunderbird\thunderbird.exe
add-on version (source) 0.1.4
profile …\Profiles\fw0oundg.default-release
accounts (from prefs.js) 3
add-on startup report 2026-07-29T11:37:46.643Z
privileged modules 12 loaded
bridge methods 128
Bridge
daemon pid 94108
connected True
privileged half True
app Thunderbird 153.0Related MCP server: Thunderbird MCP
Install into a client
tbmcp setup claude-code --toolsets allOr by hand — note the absolute path, because Claude Code spawns without a shell and
has no cwd setting:
claude mcp add-json thunderbird '{
"type": "stdio",
"command": "C:\\Users\\you\\.local\\bin\\thunderbird-mcp.exe",
"args": ["serve", "--toolsets", "all"],
"env": { "PYTHONUTF8": "1", "PYTHONUNBUFFERED": "1" }
}' --scope userVerify with claude mcp get thunderbird, or /mcp inside a session.
Runclaude mcp add from PowerShell or CMD. Git Bash rewrites /c into C:/ and
corrupts the written config; if you must use it, prefix with MSYS_NO_PATHCONV=1.
tbmcp setup codex --toolsets allOr by hand in ~/.codex/config.toml:
[mcp_servers.thunderbird]
command = 'C:\Users\you\.local\bin\thunderbird-mcp.exe'
args = ["serve", "--toolsets", "all"]
env = { PYTHONUTF8 = "1", PYTHONUNBUFFERED = "1" }
startup_timeout_sec = 60
tool_timeout_sec = 120
# Codex ignores tool annotations, so safety has to be stated here.
default_tools_approval_mode = "writes"
[mcp_servers.thunderbird.tools.pref_set]
approval_mode = "approve"
[mcp_servers.thunderbird.tools.mail_send]
approval_mode = "approve"Windows paths must be single-quoted TOML literals — "C:\Users\…" is an invalid
escape sequence. Codex also builds the child environment from scratch, so anything
your server needs has to be in env. Verify with codex mcp get thunderbird --json.
tbmcp setup claude-desktop cursor vscode gemini zed
tbmcp setup --print-config all # or just show the blocks and change nothingsetup prefers each client's own CLI when it is on PATH, falls back to editing the
config file, backs it up first, and reports added / updated / unchanged per client.
It never hand-edits ~/.claude.json, which holds OAuth state and project trust
decisions.
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 registered112 tools across 10 toolsets; 48 of them read-only. Full signatures in docs/TOOL-REFERENCE.md.
Tool | What it does | |
| read | Search the user's mail. Combine |
| read | List messages in one folder, newest first by default |
| read | Read one message. |
| read | Read up to 50 messages in one round trip — for triaging a search result |
| read | Fetch a message's raw RFC 5322 source, for header forensics |
| read | List a message's attachments with part names, sizes and content types |
| write | Write one attachment to a directory on this machine |
| write | Set read/flagged/junk state or adjust tags on one or more messages |
| write | Move messages into another folder |
| write | Copy messages into another folder, leaving the originals in place |
| write | Archive messages using each account's configured archive layout |
| destructive | Delete messages. Moves to Trash unless |
| read | List the tags defined in Thunderbird, with keys, labels and colours |
| write | Create a tag, or recolour/rename an existing one |
| destructive | Remove a tag definition. Messages keep the raw keyword but lose the label |
Tool | What it does | |
| read | List mail folders with their ids and message counts |
| read | Get one folder: counts, special use, flags and IMAP quota |
| read | Report what may be done to a folder before attempting it |
| read | Get the unified folder that spans every account, e.g. all inboxes at once |
| write | Create a folder inside another folder, or at the top of an account |
| write | Rename a folder, keeping its messages and subfolders |
| write | Move a folder under a different parent, with its subfolders |
| write | Copy a folder and its contents under another parent, leaving the original |
| destructive | Delete a folder, its subfolders and every message in them |
| write | Mark every message in a folder as read |
| write | Add or remove a folder from the user's favourites |
| destructive | Permanently delete everything in one account's Trash |
| destructive | Permanently delete everything in one account's Junk folder |
| write | Fetch an IMAP folder's message bodies so they are available offline |
| write | Reclaim the disk space left behind by deleted messages |
Tool | What it does | |
| destructive | Write a message. Saves a reviewable draft unless |
| destructive | Reply to a message. Saves a reviewable draft unless |
| destructive | Forward a message. Saves a reviewable draft unless |
| write | Save a message without sending it, as a draft or a template |
| write | Open a populated compose window for the user to finish by hand |
| read | List messages sitting in the Outbox, unsent |
Tool | What it does | |
| read | Ranked full-corpus search across every indexed folder and account |
| read | Every message in one thread, oldest first, across folders and accounts |
| read | Whether Thunderbird's global index is enabled, and how far along it is |
| read | List the saved searches (virtual folders) and what each one matches |
| write | Create a saved search that appears in the folder pane |
| write | Redefine an existing saved search, by name or uri |
| destructive | Remove a saved search. The messages it listed are not touched |
Tool | What it does | |
| read | Look someone up in the address book |
| read | List contacts, across every address book unless one is named |
| read | Read one contact in full, including its raw vCard |
| write | Add a contact to an address book |
| write | Change fields on an existing contact |
| destructive | Delete a contact. There is no Trash for contacts, so this cannot be undone |
| read | List the address books, with how many contacts and lists each holds |
| write | Create an empty local address book |
| destructive | Delete an address book together with all its contacts and mailing lists |
| read | List address book mailing lists, with member counts |
| write | Create an empty mailing list in an address book |
| write | Add an existing contact to a mailing list |
| write | Take a contact off a mailing list. The contact itself is left alone |
Tool | What it does | |
| read | List the user's calendars, with ids, types and whether each is writable |
| write | Create a calendar and register it with Thunderbird |
| write | Rename or recolour a calendar, or toggle read-only and disabled |
| destructive | Remove a calendar. Deletes its events and tasks with it |
| read | List events in a time window, soonest first |
| read | Read one event or task in full, including attendees and recurrence |
| write | Create an event. Omit |
| write | Change an event. Only the fields you pass are touched |
| destructive | Delete an event or a task. Calendars have no trash, so this is final |
| read | List tasks, soonest due first. Completed ones are hidden by default |
| write | Create a task. Everything but the title is optional |
| write | Change a task, or tick it off with |
Tool | What it does | |
| read | List filters in execution order, with their conditions and actions |
| read | Read one filter in full, by account and index |
| write | Create a filter. It is appended, so existing rules keep their order |
| write | Change a filter in place. Only what you pass is touched |
| write | Turn one filter on or off without changing its definition |
| write | Move a filter to a different position in the execution order |
| destructive | Delete a filter. Thunderbird keeps no history, so the rule is gone |
| write | Apply filters to folders on demand, as "Run Filters on Folder" does |
Tool | What it does | |
| read | List the mail accounts and how each one is configured |
| read | Read one account's incoming server settings |
| write | Change one incoming server setting. Getting the connection wrong stops mail |
| read | Read one account's junk-mail handling: level, whitelist, move and purge rules |
| write | Change one junk-mail setting for an account |
| read | Read where an identity files sent mail, drafts, templates and archives |
| write | Change where an identity files sent mail, drafts, templates or archives |
| read | Read an account's offline and synchronisation settings |
| write | Change one offline or synchronisation setting for an account |
| read | List the sending identities, across every account or just one |
| read | Read one identity in full: addresses, signature, outgoing server, filing folders |
| write | Change an identity's addresses and composition defaults |
| write | Replace an identity's signature text, or point it at a file |
| read | List the SMTP servers, and which one is the default |
| write | Add an SMTP server. Nothing sends through it until an identity points at it |
| write | Change an existing SMTP server. Only the fields you pass are touched |
| destructive | Remove an SMTP server. Identities using it will be left unable to send |
| write | Make one SMTP server the default for identities that have none of their own |
Tool | What it does | |
| read | Read one Thunderbird preference |
| read | Read up to 100 preferences in one round trip |
| read | List preferences under a branch, e.g. |
| read | Everything the user has changed from the shipped defaults |
| read | Map a human request onto the preference that controls it |
| write | Change one Thunderbird preference |
| write | Clear a user-set preference so the shipped default applies again |
| read | Read the global junk (bayesian) filter settings |
| write | Change the global junk filter settings. Only the fields you pass are touched |
| write | Teach the junk filter that these messages are junk, or are not |
| read | List the OpenPGP keys in Thunderbird's keyring |
Tool | What it does | |
| read | Whether Thunderbird is attached, and which halves of the add-on loaded |
| read | Block until Thunderbird attaches to the bridge, then report status |
| read | Read buffered Thunderbird notifications: new mail, folder and account changes |
| read | One report: versions, profile, which capabilities loaded, accounts, indexing |
| read | Recent lines from Thunderbird's error console, newest last |
| read | List installed add-ons with their enabled and signature state |
| destructive | Restart Thunderbird. Every call in flight fails, including other clients' |
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 |
| lets the host decide when to ask | hosts that read annotations |
| prompts even under | Claude Code |
an explicit | 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 ThunderbirdThe daemon picks a free port and writes <profile>/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 <profile>/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 → |
settings tools fail but mail tools work | same cause; check |
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 → |
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 |
Codex reports a startup timeout | raise |
Claude Code truncates a large result | raise |
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 # 85 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 unchangedThree 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 | |
why it is built this way, and what was rejected | |
the wire protocol between Python and the add-on | |
the contract: tool → bridge method → implementation | |
every tool and parameter, generated from the code | |
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.pycheck_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.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/U-C4N/Thunderbird-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server