Skip to main content

Thunderbird Marionette MCP

CI PyPI version Python versions License: MIT

An MCP (Model Context Protocol) server exposing UI automation of a running Thunderbird via the native Marionette protocol. Lets an AI assistant click buttons, type text, install extensions, capture screenshots, run chrome-scope JavaScript, and drive Thunderbird end-to-end.

What & why

Existing Thunderbird MCP servers (TKasperczyk/thunderbird-mcp, U-C4N/Thunderbird-MCP, and others) all work through the WebExtension API from inside Thunderbird. They handle mail/folder/contact data but cannot click UI, invoke hotkeys, or interact with extension popups.

Marionette is Gecko's built-in automation protocol. It gives full chrome and content scope, including popups, dialogs, Services, Cc/Ci, MailServices, and the WebExtension popup DOM. This server wraps Marionette as MCP tools, so an LLM client can drive Thunderbird for extension development and end-to-end testing.

Like "playwright for Thunderbird".

Install

uv tool install tb-marionette-mcp

Or with pipx:

pipx install tb-marionette-mcp

Or plain pip (into an isolated venv, not system Python):

pip install tb-marionette-mcp

Prerequisites

  • Thunderbird 153 on PATH (or via TB_MCP_BINARY). Live-tested against TB 153; TB 140–152 may work but is untested — chrome-only XUL window fallbacks target TB 153 API surface.
  • Python 3.11+
  • Linux (Fedora / Ubuntu tested), macOS. Windows is not supported yet.

Install Thunderbird:

# Fedora
sudo dnf install thunderbird

# Ubuntu / Debian
sudo apt install thunderbird

# macOS
brew install --cask thunderbird

Configure your MCP client

Claude Desktop

Add to ~/.config/Claude/claude_desktop_config.json (or the platform equivalent):

{
  "mcpServers": {
    "tb-marionette": {
      "command": "uv",
      "args": ["tool", "run", "tb-marionette-mcp"]
    }
  }
}

Claude Code CLI

claude mcp add tb-marionette -- uv tool run tb-marionette-mcp

opencode

Add to opencode.json:

{
  "mcp": {
    "tb-marionette": {
      "type": "local",
      "command": ["uv", "tool", "run", "tb-marionette-mcp"]
    }
  }
}

Quickstart

End-to-end: launch Thunderbird, install a dev extension, click its button, capture a screenshot.

1. Launch Thunderbird with a dedicated profile:

{"tool": "thunderbird_launch", "arguments": {"profile": "test-profile"}}

2. Install a temporary WebExtension:

{"tool": "extension_install", "arguments": {"xpi_path": "/abs/path/to/ext.xpi", "temporary": true}}

3. Find a chrome-scope button:

{"tool": "find_element", "arguments": {"strategy": "id", "selector": "button-appmenu", "context": "chrome"}}

4. Click it:

{"tool": "click", "arguments": {"element_id": "<element_id from step 3>"}}

5. Capture a screenshot:

{"tool": "screenshot", "arguments": {"full": true}}

The result contains base64-encoded PNG under data_base64.

Tool reference

All 30 tools are pydantic-validated. Response objects are always dicts (or lists of dicts) so future fields can be added without breaking clients.

Process

Tool Description Key params
thunderbird_launch Start TB with --marionette and profile profile, marionette_port=2828, wait_ready=True
thunderbird_terminate SIGTERM tracked or given pid pid=None
thunderbird_status Report running / connected

Extensions

Tool Description Key params
extension_install Install XPI via Addons.install xpi_path, temporary=True
extension_uninstall Remove addon by id addon_id
extension_reload Uninstall + install (dev cycle) addon_id, xpi_path
extension_list List all addons via AddonManager

UI

Tool Description Key params
find_element Locate one element strategy, selector, context="chrome"
find_elements Locate many elements strategy, selector, context="chrome"
click Click an element element_id
type_text Type text (optional clear first) element_id, text, clear=False
get_text Element inner text element_id
get_attribute HTML attribute value element_id, name
get_property DOM property value element_id, name
is_displayed Visibility check element_id
list_windows All open window handles + title/url
switch_to_window Switch active window handle
switch_to_frame Switch into an iframe element_id
switch_to_default Switch back to top-level content
wait_for_element Poll until element is present (and visible) strategy, selector, timeout=10.0, visible

Strategy enum: id | css | xpath | link_text | partial_link_text | tag_name | class_name | name.

Keys

Tool Description Key params
send_keys Send raw keys globally or to element keys, element_id=None
send_hotkey Parse & dispatch a chord chord (e.g. "Ctrl+Shift+N")

Chord grammar: Mod (+ Mod)* + Key. Modifiers: Ctrl | Alt | Shift | Meta | Cmd (Cmd = Meta). Named keys: Enter | Escape | Tab | Space | Delete | Backspace | Up | Down | Left | Right | Home | End | PageUp | PageDown | Insert | F1..F12.

Scripts

Tool Description Key params
execute_script Run JS in chrome or content scope script, args=[], context="chrome", async_=False
wait_for_condition Poll JS predicate until truthy script, timeout=30, poll_interval=0.5

chrome context has full Cc / Ci / Services / MailServices access.

Diagnostics

Tool Description Key params
screenshot PNG/JPEG of screen or element element_id=None, format, full
get_page_source Current DOM serialized context="content"
get_current_url URL of active tab / window
get_window_title Title of active window
get_console_logs Chrome console messages (optional filter) clear=False, level=None
get_marionette_log Tail stderr of a TB we launched

Environment variables

Variable Default Meaning
TB_MCP_BINARY which thunderbird Thunderbird executable
TB_MCP_MARIONETTE_HOST 127.0.0.1 Marionette host
TB_MCP_MARIONETTE_PORT 2828 Marionette port
TB_MCP_LOG_LEVEL INFO structlog level
TB_MCP_STARTUP_TIMEOUT 30 seconds to wait for TB port open
TB_MCP_INTEGRATION 1 0 skips integration tests

Troubleshooting

  • Port 2828 already in use — another TB instance is running with Marionette. Either terminate it (thunderbird_terminate or pkill thunderbird) or launch on a different port via marionette_port=2829.
  • TB does not respond — check thunderbird_status; if running=true but connected=false, TB started without --marionette. Kill and relaunch.
  • Extension install fails — for temporary=false the XPI must be signed by Mozilla; for dev use temporary=true (unsigned, cleared on restart).
  • CI without display — wrap the pytest / launch command with xvfb-run -a ... or set up Xvfb and export DISPLAY=:0.
  • Attach to externally-started TB — do not call thunderbird_launch. If TB is already running on TB_MCP_MARIONETTE_PORT, any tool call auto-connects. get_marionette_log returns available=false in that mode (we have no stderr handle).

Development

uv sync                        # install deps + editable
uv run pytest                  # full suite (unit + integration)
uv run pytest --no-integration # unit only
uv run ruff check              # lint
uv run mypy                    # type-check

Integration tests spawn a real Thunderbird under xvfb-run and default ON; opt-out via --no-integration or TB_MCP_INTEGRATION=0.

Roadmap

  • Prebuilt Fedora + Thunderbird CI Docker image (ghcr.io/hubbitus/tb-mcp-ci)
  • Windows support
  • MCP protocol 2.0 migration
  • WebDriver BiDi transport (Marionette wire is on Mozilla's sunset roadmap)

References

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tb_marionette_mcp-0.1.0.tar.gz (29.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tb_marionette_mcp-0.1.0-py3-none-any.whl (21.4 kB view details)

Uploaded Python 3

File details

Details for the file tb_marionette_mcp-0.1.0.tar.gz.

File metadata

  • Download URL: tb_marionette_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 29.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tb_marionette_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b7a88f5cd9bd20a116c08faaf77d37223e9cda4665cfdfe878564191b1eaabfb
MD5 1f59d1e4ae7ce5e3ee3f81da34ef1261
BLAKE2b-256 6e6592947a63fa11802556837a945f75e4440696f24f28f4051d87192fbe011c

See more details on using hashes here.

Provenance

The following attestation bundles were made for tb_marionette_mcp-0.1.0.tar.gz:

Publisher: release.yml on Hubbitus/thunderbird-marionette-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tb_marionette_mcp-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for tb_marionette_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fe5799784446c74af81197b3540336639fa76103e802230fcbea038e18a9784b
MD5 0f888dd8d4fb4f05c03983f510ce39e9
BLAKE2b-256 79206a35f9a830b89c3ae8fca54263d3c39ca88c25a0c2f28702d6f6df4f2b45

See more details on using hashes here.

Provenance

The following attestation bundles were made for tb_marionette_mcp-0.1.0-py3-none-any.whl:

Publisher: release.yml on Hubbitus/thunderbird-marionette-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.4

2 files

0.1.3

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page