Skip to content
cccskills
FreePublished on GitHub

thunderbird-extension

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

The English summary is being prepared. Showing the original description.

See how to install

Files included (5)

  • SKILL.md11.7 KB
  • references/atn-submission.md2.3 KB
  • references/experiment-apis.md3.5 KB
  • references/messenger-apis.md6.8 KB
  • references/testing-migration.md3.7 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Thunderbird MailExtension Development

Complete reference for building, testing, and publishing email extensions for Mozilla Thunderbird.

Overview

Thunderbird extensions use the MailExtension API (based on WebExtensions) with the messenger.* namespace. Thunderbird supports both Manifest V2 and V3 since version 128.

Key Characteristics:

  • Global namespace: messenger (Thunderbird-specific) + browser (standard WebExtensions)
  • Both MV2 and MV3 supported (Thunderbird 128+)
  • Thunderbird-specific APIs: accounts, addressBooks, compose, folders, mailTabs, messages, messageDisplay
  • Submission via ATN (addons.thunderbird.net)

Version Requirements

VersionStatusNotes
128.x (ESR)CurrentFull MV2 + MV3 support
115.xLegacyEnd of support
< 115DeprecatedNot recommended

Best Practice: Set strict_min_version to "128.0"

Manifest Structure

Manifest V3 (Recommended, Thunderbird 128+)

{
  "manifest_version": 3,
  "name": "My Thunderbird Extension",
  "version": "1.0.0",
  "description": "Extension description",
  "author": "Your Name",

  "browser_specific_settings": {
    "gecko": {
      "id": "extension@example.com",
      "strict_min_version": "128.0"
    }
  },

  "icons": {
    "16": "icons/icon-16.png",
    "32": "icons/icon-32.png",
    "64": "icons/icon-64.png"
  },

  "background": {
    "service_worker": "background.js",
    "type": "module"
  },

  "action": {
    "default_popup": "popup.html",
    "default_title": "My Extension",
    "default_icon": "icons/icon-32.png"
  },

  "permissions": [
    "storage",
    "messagesRead",
    "addressBooks"
  ]
}

Manifest V2 (Still Supported)

{
  "manifest_version": 2,
  "name": "My Thunderbird Extension",
  "version": "1.0.0",
  "author": "Your Name",

  "browser_specific_settings": {
    "gecko": {
      "id": "extension@example.com",
      "strict_min_version": "128.0"
    }
  },

  "background": {
    "scripts": ["background.js"],
    "type": "module"
  },

  "browser_action": {
    "default_popup": "popup.html",
    "default_title": "My Extension"
  },

  "permissions": [
    "storage",
    "messagesRead",
    "addressBooks"
  ]
}

MV2 vs MV3 Key Differences

FeatureMV2MV3
Toolbar buttonbrowser_actionaction
Backgroundbackground.scriptsbackground.service_worker
Execute scripttabs.executeScriptmessenger.scripting.messageDisplay.executeScript
Compose scriptscomposeScriptsscripting.compose
Contacts APImessenger.contacts.*messenger.addressBooks.contacts.* (vCard only)

All Manifest Keys Reference

Metadata:

  • name (required) - Extension name
  • version (required) - Version string
  • description - Short description
  • author - Author name
  • icons - Extension icons

Thunderbird-Specific:

  • browser_specific_settings.gecko.id - Required for ATN
  • browser_specific_settings.gecko.strict_min_version - Minimum version

Background & Scripts:

  • background - Service worker (MV3) or scripts (MV2)
  • message_display_scripts (MV2) - Scripts for displayed messages

UI Components:

  • action (MV3) / browser_action (MV2) - Main toolbar button
  • compose_action - Compose window toolbar button
  • message_display_action - Message view toolbar button

Permissions:

  • permissions - API permissions
  • experiment_apis - Custom Experiment APIs

Other:

  • commands - Keyboard shortcuts
  • options_ui - Options page

UI Actions (Toolbar Buttons)

Main Toolbar (action / browser_action)

{
  "action": {
    "default_popup": "popup.html",
    "default_title": "My Extension",
    "default_icon": {
      "16": "icons/icon-16.png",
      "32": "icons/icon-32.png"
    }
  }
}
// Listen for clicks (if no popup)
messenger.action.onClicked.addListener((tab) => {
  console.log("Action clicked");
});

// Update badge
await messenger.action.setBadgeText({ text: "5" });
await messenger.action.setBadgeBackgroundColor({ color: "#ff0000" });

// Update icon
await messenger.action.setIcon({ path: "icons/icon-active.png" });

Compose Window (compose_action)

{
  "compose_action": {
    "default_popup": "compose_popup.html",
    "default_title": "Compose Tool",
    "default_icon": "icons/compose-icon.png"
  }
}
// Listen for clicks in compose window
messenger.composeAction.onClicked.addListener((tab) => {
  const details = await messenger.compose.getComposeDetails(tab.id);
  console.log("Compose action clicked:", details.subject);
});

Message Display (message_display_action)

{
  "message_display_action": {
    "default_popup": "message_popup.html",
    "default_title": "Message Tool",
    "default_icon": "icons/message-icon.png"
  }
}
// Listen for clicks on message
messenger.messageDisplayAction.onClicked.addListener(async (tab) => {
  const message = await messenger.messageDisplay.getDisplayedMessage(tab.id);
  console.log("Message action clicked:", message.subject);
});

Message Display Scripts

MV2 Configuration

{
  "message_display_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["message_content.js"],
      "css": ["message_styles.css"]
    }
  ]
}

MV3 Configuration

// In background.js
await messenger.scripting.messageDisplay.executeScript({
  tabId: tabId,
  files: ["message_content.js"]
});

Available APIs in Display Scripts

Limited APIs available:

  • messenger.runtime.connect(), messenger.runtime.sendMessage()
  • messenger.runtime.onConnect, messenger.runtime.onMessage
  • messenger.i18n.getMessage(), messenger.i18n.getAcceptLanguages()
  • messenger.storage.*
// message_content.js
// Send message to background
const response = await messenger.runtime.sendMessage({
  action: "processMessage",
  content: document.body.innerText
});

// Listen for messages from background
messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === "highlight") {
    // Highlight text in message
    document.body.innerHTML = document.body.innerHTML.replace(
      message.text,
      `<mark>${message.text}</mark>`
    );
  }
});

Deep Dives

For detailed reference on specific topics, load these reference files on demand:

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

Best Practices

Code Organization

my-extension/
├── manifest.json
├── background.js
├── popup.html
├── popup.js
├── compose_popup.html
├── compose_popup.js
├── api/
│   └── myapi/
│       ├── schema.json
│       └── implementation.js
├── icons/
│   ├── icon-16.png
│   ├── icon-32.png
│   └── icon-64.png
├── _locales/
│   ├── en/
│   │   └── messages.json
│   └── it/
│       └── messages.json
└── README.md

Error Handling

async function safeAsync(fn) {
  try {
    return await fn();
  } catch (error) {
    console.error("Error:", error);
    return { error: error.message };
  }
}

// Usage
const result = await safeAsync(() => messenger.messages.get(messageId));
if (result.error) {
  console.error("Failed to get message:", result.error);
}

Performance

  • Use pagination for large message lists
  • Cache frequently accessed data
  • Debounce rapid events
  • Use messages.query() with filters instead of list() + filter manually

Security

  • Validate all user input
  • Sanitize HTML before display
  • Use minimal permissions
  • Don't store sensitive data in storage.local unencrypted
  • Validate message content before processing

Differences from Firefox WebExtensions

FeatureFirefoxThunderbird
Namespacebrowser.*messenger.* (mail) + browser.* (common)
ContextWeb browserEmail client
Content scriptsWork on web pagesOnly in web tabs, not email content
Main actionbrowser_action / actionSame + compose_action, message_display_action
Mail APIsNoneaccounts, compose, messages, etc.
ExperimentsLimitedCommon for email-specific features
StoreAMOATN

File Structure Template

my-thunderbird-extension/
├── manifest.json
├── background.js
├── popup.html
├── popup.js
├── compose_popup.html
├── compose_popup.js
├── message_popup.html
├── message_popup.js
├── message_content.js
├── styles/
│   └── popup.css
├── icons/
│   ├── icon-16.png
│   ├── icon-32.png
│   └── icon-64.png
├── api/
│   └── myapi/
│       ├── schema.json
│       └── implementation.js
├── _locales/
│   ├── en/
│   │   └── messages.json
│   └── it/
│       └── messages.json
└── README.md

Quick Reference

Essential Permissions

{
  "permissions": [
    "storage",           // Data storage
    "messagesRead",      // Read messages
    "messagesMove",      // Move/copy/delete messages
    "addressBooks",      // Access contacts
    "compose",           // Compose windows
    "accountsRead",      // Read accounts
    "accountsFolders"    // Access folders
  ]
}

Essential APIs

// Messages
messenger.messages.list(folderId)
messenger.messages.get(messageId)
messenger.messages.query({ from, unread })
messenger.messages.update(messageId, { read: true })

// Folders
messenger.folders.get(folderId)
messenger.folders.getSubFolders(account)

// Compose
messenger.compose.beginNew({ to, subject, body })
messenger.compose.getComposeDetails(tabId)

// Address Books
messenger.addressBooks.list()
messenger.addressBooks.contacts.create(addressBookId, { vCard })

// Display
messenger.messageDisplay.getDisplayedMessage(tabId)
messenger.messageDisplayAction.onClicked

Workflow Summary

  1. Develop: Write code, load temporarily
  2. Debug: Use Debug Add-ons → Inspect
  3. Test: Test all functionality
  4. Build: Create .zip with manifest and scripts
  5. Submit: Upload to ATN
  6. Review: Respond to reviewer feedback
  7. Publish: Extension goes live

References

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use when building Python async HTTP services or clients with aiohttp - web server routing, middleware, WebSocket, SSE, streaming, client sessions, pytest-aiohttp testing, or troubleshooting SSL and timeout issues

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

Use when doing structural code search and rewriting - ast-grep linting, refactoring, multi-language patterns

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

Use when building GBA games with the BPCore Lua engine - entity, sprite and tilemap functions, SRAM save and load, link cable multiplayer protocol, camera and scrolling, or optimization patterns

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

celery

Free

Use when running background tasks with Celery - worker and broker configuration (Redis, RabbitMQ), task routing by name vs queue, chains/groups/chords, retry patterns (autoretry_for, retry_backoff), acks_late semantics, failure detection, and monitoring with Flower

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

django

Free

Use when building Django applications - security hardening, authentication and permissions, ORM optimization, PostgreSQL features, Django 6.0, migrations, testing, and ecosystem libraries

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

Use when customizing Django Admin - save_formset, get_search_results, formsets, queryset optimization, db_index, custom URLs

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

See all skills by CodeAtCode

Report a problem with this skill