thunderbird-extension · v2.0.0 · 2026-10-28 · sha256 371bd9b54c6032be

thunderbird-extension v2.0.0A

Immutable. This exact content never changes and is served at /api/v1/blob/371bd9b54c6032be.

---
name: thunderbird-extension
description: Use when developing Thunderbird MailExtensions - Manifest V2/V3 configuration, messenger.* APIs for accounts, messages, folders and compose, UI actions, message display scripts, Experiment APIs, ATN submission, or migration from legacy extensions
metadata:
  author: mte90
  version: 2.0.0
  tags:
    - thunderbird
    - mailextension
    - email-extension
    - mozilla
    - atn
    - messenger-api
---

> **Objective:** Use this skill to reduce manifest-schema rejections, messenger-API-typed errors, and ATN-review failures by enforcing manifest-version discipline, API-boundary rules, and permission-scoping patterns below.

# Thunderbird MailExtension Development

## Core Rules

**Do manifest_version discipline because MV2 deprecation will break extensions at Thunderbird 137+.** Set manifest_version: 3 for new extensions; migrate existing MV2 extensions now.

**Do declare messenger.* permissions explicitly because missing permissions fail silently at runtime.** Enumerate every API namespace in permissions before calling it.

**Do use Experiment APIs only when messenger.* cannot reach required functionality because reviewers reject unnecessary Experiment access.** Cite the missing built-in API in submission notes.

**Do validate message content before processing because untrusted email data causes XSS in display scripts.** Sanitize HTML, escape user input, verify MIME types.

**Do use vCard format for contacts in MV3 because legacy contacts API is removed.** Call messenger.addressBooks.contacts.create with vCard string, not name/email fields.

**Do implement onStartup/onShutdown in Experiment APIs because missing cleanup causes crashes on extension reload.** Unregister observers, clear caches, remove listeners.

**Do place extension ID in browser_specific_settings.gecko.id because ATN rejects extensions without stable IDs.** Generate UUID-formatted ID, do not use auto-generated values.

**Do set strict_min_version to "128.0" because older Thunderbird versions lack MV3 support.** Test on Thunderbird 128 ESR minimum.

## UI Actions

**Do attach listeners to action.onClicked because extensions with no popup need click handlers.** Register in background.js, not in popup script.

**Do update badge text asynchronously because setBadgeText returns a Promise.** Awaiting prevents race conditions with badge color updates.

**Do use compose_action for compose-window tools because message_display_action only works in message view.** Match button location to API scope.

## Message Display Scripts

**Do execute display scripts via messenger.scripting.messageDisplay.executeScript in MV3 because manifest declaration is removed.** Call dynamically after tab loads.

**Do limit display script APIs to runtime/storage/i18n because full messenger.* is unavailable.** Communicate with background via runtime.sendMessage for privileged operations.

**Do validate message DOM before manipulation because email content varies widely.** Check element existence, handle plain-text vs HTML, catch parsing errors.

## MV2 → MV3 Migration Decision Guide

**Do migrate now because Thunderbird 137 removes MV2 support.** Priority order:
1. Replace browser_action with action
2. Migrate background.scripts to background.service_worker
3. Convert composeScripts to scripting.compose
4. Update contacts API to messenger.addressBooks.contacts.*

**Do not use messenger.contacts.* in MV3 because it is removed.** Use messenger.addressBooks.contacts.quickSearch and contacts.create with vCard.

**Do test display scripts on both HTML and plain-text messages because rendering differs.** Verify script handles text/plain bodies gracefully.

## Deep Dives

See references for detailed API catalogs loaded on demand:

- **Messenger APIs** — Complete messenger.* reference (accounts, messages, folders, addressBooks, compose, mailTabs): `./references/messenger-apis.md`
- **Experiment APIs** — Custom Experiment API development: `./references/experiment-apis.md`
- **ATN Submission** — Publishing workflow and review criteria: `./references/atn-submission.md`
- **Testing & Migration** — Debugging, testing workflow, legacy migration: `./references/testing-migration.md`

**Do structure extensions with clear separation because monolithic files become unmaintainable.** Pattern:
```
my-extension/
├── manifest.json
├── background.js          # Event handlers, API orchestration
├── popup.html/js          # UI, user interaction
├── compose_popup.html/js  # Compose window UI
├── message_popup.html/js  # Message view UI
├── message_content.js     # Display script (limited API)
├── api/                   # Experiment APIs (if needed)
│   └── myapi/
│       ├── schema.json
│       └── implementation.js
├── icons/                 # 16, 32, 64 px
└── _locales/              # i18n strings
```

## Error Handling

**Do wrap async calls with explicit error logging because silent failures hide bugs.** Log error message plus context (message ID, folder, operation).

**Do check messenger.runtime.lastError after promises because unresolved errors accumulate.** Clear lastError before next operation.

**Do return structured error objects because callers need to distinguish failure modes.** Include error type, offending values, suggested fix.

## Performance

**Do use messages.query with filters because server-side filtering is faster than client-side.** Avoid list() + filter manually for large folders.

**Do debounce rapid events because user actions fire multiple times.** Throttle onMessage, onMessageDisplayed handlers.

**Do paginate large message lists because loading all messages blocks the UI.** Use limit/offset in queries.

## Security

**Do validate all user input before storage because unvalidated data corrupts state.** Schema-validate permissions, folder IDs, message IDs.

**Do sanitize HTML before injection because email content may contain scripts.** Use DOMParser, strip dangerous elements.

**Do use minimal permissions because excessive permissions trigger ATN review rejection.** Request only what the extension needs.

**Do not store sensitive data unencrypted because storage.local is readable.** Encrypt passwords, tokens before storage.

## Differences from Firefox WebExtensions

**Do use messenger.* for mail APIs because browser.* lacks email functionality.** messenger.accounts, messenger.messages, messenger.compose are Thunderbird-specific.

**Do expect content scripts to work only in web tabs because email content is sandboxed.** Use message_display_scripts for email body access.

**Do implement Experiment APIs for XPCOM access because WebExtensions cannot reach internals.** Experiments bridge the gap but require justification.
## Quick Reference

### Essential Permissions

```json
{
  "permissions": [
    "storage",           // Data persistence
    "messagesRead",      // Read message content
    "messagesMove",      // Move/copy/delete
    "addressBooks",      // Contact access
    "compose",           // Compose windows
    "accountsRead",      // Account listing
    "accountsFolders"    // Folder access
  ]
}
```

### Essential APIs

```javascript
// Messages — list, get, query, update, move, delete
messenger.messages.list(folderId)
messenger.messages.get(messageId)
messenger.messages.query({ from, unread, limit })
messenger.messages.update(messageId, { read: true })
messenger.messages.move([messageId], destinationFolderId)

// Folders — get, create, rename, delete
messenger.folders.get(folderId)
messenger.folders.getSubFolders(account)
messenger.folders.create(accountId, name)

// Compose — open, modify, send拦截
messenger.compose.beginNew({ to, subject, body })
messenger.compose.getComposeDetails(tabId)
messenger.compose.onBeforeSend.addListener(handler)

// Address Books — vCard-only in MV3
messenger.addressBooks.list()
messenger.addressBooks.contacts.create(addressBookId, { vCard })
messenger.addressBooks.contacts.quickSearch(query)

// Display — event-driven
messenger.messageDisplay.onMessageDisplayed.addListener(handler)
messenger.messageDisplay.getDisplayedMessage(tabId)
```

### Workflow

1. **Develop:** Load temporarily via Debug Add-ons
2. **Debug:** Inspect background/popup via DevTools
3. **Test:** Verify on HTML and plain-text messages
4. **Build:** Create .zip with manifest and all scripts
5. **Submit:** Upload to ATN with inline privacy policy
6. **Review:** Respond within 10 days
7. **Publish:** Extension goes live after approval

## References

- [Thunderbird Developer Hub](https://developer.thunderbird.net/add-ons/)
- [Supported APIs](https://developer.thunderbird.net/add-ons/mailextensions/supported-webextension-api)
- [MV3 Migration Guide](https://github.com/thunderbird/webext-docs/blob/beta-mv2/guides/manifestV3.rst)
- [Example Extensions](https://github.com/thunderbird/webext-examples)
- [Experiment Support](https://github.com/thunderbird/thunderbird/webext-support)
- [ATN Developer Hub](https://addons.thunderbird.net/developers/)
- [ATN Review Policy](https://thunderbird.github.io/atn-review-policy/)