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.4.tar.gz (31.4 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.4-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tb_marionette_mcp-0.1.4.tar.gz
  • Upload date:
  • Size: 31.4 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.4.tar.gz
Algorithm Hash digest
SHA256 1ef6e72b5e75a5168f52b6c47c147750eaed5d414fca59aec694f3906c58da10
MD5 3f40e307718dbfcf01c22dee186fd438
BLAKE2b-256 fdac2522323e52d74571078080345a89d6f4816840d289e1ddeb91f2b56f6328

See more details on using hashes here.

Provenance

The following attestation bundles were made for tb_marionette_mcp-0.1.4.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.4-py3-none-any.whl.

File metadata

File hashes

Hashes for tb_marionette_mcp-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 286dda81878729a98f58bb67f97e8ed5f49486aa4dee430000f0b92f9bcee31a
MD5 6f341aef776c584ff7bb7f5639acdabf
BLAKE2b-256 714bc4b0fec9eb7b995e220447aa29750ebe07bb3310b6544b36695374df197e

See more details on using hashes here.

Provenance

The following attestation bundles were made for tb_marionette_mcp-0.1.4-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

This release

0.1.4 This release

2 files

0.1.3

2 files

0.1.0

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