Skip to content

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 13:51
· 56 commits to main since this release
Immutable release. Only release title and notes can be modified.

Breaking

  • replyToMessage with skipReview now needs explicit to and from, and forwardMessage with skipReview an
    explicit from. A direct send no longer takes any address from the original message, which anyone can write: it
    goes only to the caller's to / cc / bcc plus the sending identity's own automatic Cc / Bcc, with that
    identity's Reply-To (a direct reply-all no longer adds the original's To / Cc). The result of a direct send lists
    the from, to, cc, bcc and replyTo it went out with. By @mazixs in #26.
  • The error that replyToMessage and forwardMessage return when direct sending is blocked has new text, and it now
    covers mode: "send" as well as skipReview: it was "User preference blocks skipReview. Retry with skipReview:
    false (or omitted) to open the review window instead.", it is now "User preference blocks direct sending (mode
    "send" or skipReview). Use mode "draft" to save a draft, or "window" (the default) to open a review
    window." A client that matches the old string exactly must be updated (sendMail, createEvent and createTask
    keep theirs). By @mazixs in #27.

Added

  • replyToMessage and forwardMessage take mode: window (the default, the review window as before), draft or
    send. mode: "send" is a direct send, the same as skipReview: true, with the same rules: explicit to and
    from, subject to Block skipReview, and the bridge waits up to 150 s for it. mode: "draft" saves the
    reply or forward to the identity's Drafts folder without opening a window and returns its messageId and
    folderPath. It sends nothing, so Block skipReview does not block it. By @mazixs in #27.
    mode: "draft" needs the saveDraft tool to be enabled: a tool the user disabled is not reachable through
    another tool, and the call returns an error saying so.
    • A reply draft gets the recipients Thunderbird's Reply / Reply All computes (nsMsgCompose.cpp, ported to a
      pure function): Reply-To and Mail-Reply-To, Mail-Followup-To for Reply All, the author instead of a mailing list
      that rewrites Reply-To, the recipients of your own message from the identity that sent it, your own addresses
      dropped, and the identity's automatic Cc / Bcc and Reply-To. A to or cc from the caller replaces the
      computed one.
    • The draft stores the state Thunderbird's own reply or forward draft stores (origURIs, queuedDisposition), so
      the original is marked as replied or forwarded when the draft is sent, not when it is saved.
    • The body is quoted by the tool, as for a direct send.
  • The bridge tells the user, once per connection, when the add-on is older than the version it needs
    (MIN_EXTENSION_VERSION, 0.12.0). After the first tools/list or tools/call that Thunderbird answers, it sends
    the add-on an initialize on the same validated connection (1.5 s at most, answer capped at 64 KB) and, if the
    add-on is older, appends one text item to the result of the next tools/call (Check for Updates, then restart
    Thunderbird; the release page) and writes one line to stderr. Never for a direct send (skipReview or
    mode: "send"); silent when a version is missing, 0.0.0 or malformed, or when the server is not commonpost-mcp; only
    digits from the add-on reach the text. When the add-on is newer, the add-on judges the bridge (below).
  • A .mcpb bundle for Claude Desktop (macOS and Windows), attached to each release from the next one on: commonpost-mcp-v<version>.mcpb, with a provenance attestation. It contains only the stdio bridge (mcp-bridge.cjs, LICENSE, THIRD-PARTY.md, an icon and a manifest.json in mcpb/, manifest_version 0.3), needs Node.js 22 or later and the add-on of the same release or newer, has no settings and is not signed. It is built by scripts/build-mcpb-reproducible.cjs like the XPI (committed files only, sorted stored entries, fixed date: the same bytes on any Node version), and the release job rebuilds it from the tag and refuses any difference. The zip writer moved to scripts/zip-stored.cjs (the XPI is byte for byte unchanged). The Version sync check and the tag check now cover mcpb/manifest.json too.
  • The bridge ignores COMMONPOST_MCP_CONNECTION_FILE when its value is exactly an unexpanded ${user_config.…} placeholder (a client passing an empty optional field through as text) and runs the automatic discovery, instead of pinning itself to a path that cannot exist.
  • The bridge answers server/discover itself, at once, with the JSON-RPC error -32601 "Method not found", without
    contacting Thunderbird. Clients that speak both protocol eras (MCP 2026-07-28 and earlier, such as Claude Desktop)
    probe with it first and fall back to initialize; the probe used to reach Thunderbird and, with Thunderbird closed,
    came back after about 5 s with a discovery error that named local paths.
  • The bridge announces itself on every request to the add-on, in the X-Commonpost-Bridge header: its version, how it
    was installed (packaging=mcpb when the .mcpb bundle sets COMMONPOST_MCP_PACKAGING=mcpb, otherwise file) and,
    when COMMONPOST_MCP_PROFILE holds a valid name (a-z, 0-9 and -, 32 at most), profile=<name>, reserved for
    per-client tool sets (nothing uses it yet). The header is self-declared: it is not a security boundary, the token
    is.
  • The add-on judges bridges older than itself: once per client session (at most every 10 minutes) it adds a notice to
    a tool result when the bridge is older than 0.12.0 or reports no version (0.11 or older), with the release page and
    what to do (.mcpb or file); never on a direct send. It refuses replyToMessage / forwardMessage with
    mode: "send" or "draft" (without skipReview) from such a bridge before doing anything, because an old bridge stops
    waiting after 30 s and can report a failure for a message that is still being sent; mode: "window" and every other
    call keep working. A program that calls the HTTP API directly can declare itself with the header (README,
    Development).
  • A local security floor in the add-on (BRIDGE_SECURITY_FLOOR), shipped disarmed (0.0.0): if a future release arms
    it, every tool call from an older bridge, or from a client without the header, is refused before anything acts, with
    the reason and the release page; tools/list keeps answering. No remote switch, no setting to bypass it.
  • Options page, section "Bridge": the bridges that connected since Thunderbird started (version, installation,
    profile, last seen, status) and the release page to update one. Kept in memory only.

Changed

  • Without from, replyToMessage and forwardMessage use the identity Thunderbird's own Reply / Forward picks
    (MailUtils.getIdentityForHeader, as ComposeMessage in mailCommands.js calls it) instead of the account's
    default identity: the identity the message was addressed to (To / Cc, then Delivered-To), the identity that sent
    it for a reply to your own message, and for a catch-all identity the address the message was sent to. Identities
    of accounts the MCP may not access are never picked. By @mazixs in #26.
    An identity shared by an accessible and a restricted account counts as accessible, whatever the order of the
    accounts, as findIdentity already did. Fetching the original message for that choice gives up after 20 s: a
    compose window or a draft then opens without it, and a direct send returns an error instead of hanging. Encrypted
    messages stay withheld from a direct reply or forward while the "read encrypted messages" option is off.
  • saveDraft saves through nsIMsgCompose, as Thunderbird's compose window does, and returns the new draft's
    messageId and folderPath. If nsIMsgCompose cannot be used, it falls back to the previous nsIMsgSend path.
    By @mazixs in #27.
  • forwardMessage no longer requires to, except with mode: "send" (or skipReview). By @mazixs in #27.
  • A reply's References header is the original's References plus its Message-ID, as Thunderbird's reply builds it
    (it used to be the original's Message-ID only); Thunderbird trims a chain longer than the header limit when it
    writes the message, as for its own reply. A reply to a message without Message-ID gets no References. In-Reply-To
    is no longer set by the tool: Thunderbird derives it from the last References entry. By @mazixs in #27.
  • mcp-bridge.cjs: a replyToMessage or forwardMessage call with mode: "send" is a direct send like
    skipReview: true, so it waits up to 150 s and gets the same "outcome unknown" error on a timeout. By @mazixs in
    #27. On top, from the maintainers: the mode is compared trimmed and lower-cased to decide that (so "SEND", which
    the extension refuses, still counts as a send: no version notice, the long wait), without changing what is sent to
    the extension; and mode: "draft" waits 150 s too, since Thunderbird can take up to 120 s to save a draft and a
    client that retries after 30 s would create two. After that wait the error says the draft may still appear in the
    Drafts folder later.
  • README: a "Quick install" section at the top, in five steps, with the Claude Code command (Windows example included) and a bold reminder that the bridge is not updated with the extension. It also corrects the old advice to replace the bridge's package.json too: the release ships only mcp-bridge.cjs, which needs nothing else.
  • README: a new "Other MCP clients" section with the configuration file, location and format of Claude Desktop, VS Code, Cursor, OpenAI Codex CLI and Gemini CLI, checked against each client's documentation, and when and how to set COMMONPOST_MCP_CONNECTION_FILE (only if the bridge cannot find the connection file). The bridge is not tied to any client, and mcpServers is not the key every client uses.
  • RELEASING: the verification step now says that a rebase merge rewrites the commit date, so the reproducible hash built from the pull request branch cannot match the release; compare with a rebuild from a checkout of the tag.
  • BRIDGE_VERSION is now the release in which the bridge (or anything in its .mcpb bundle) last changed, not the
    product version: a release that changes only the add-on ships the same mcp-bridge.cjs and the same
    commonpost-mcp-v<bridge version>.mcpb, byte for byte. scripts/check-versions.cjs replaces the shell check of the
    Version sync job and the version part of the release tag check: product version in package.json,
    package-lock.json and the add-on manifest; BRIDGE_VERSION = .mcpb manifest, never newer than the product;
    bridge bundle compared with the previous release tag; thresholds at or below BRIDGE_VERSION; an armed floor must
    be announced here.
  • .mcpb manifest: homepage points to the latest release, documentation to the README section of the bundle, and
    it sets COMMONPOST_MCP_PACKAGING=mcpb.
  • Known effect: after a downgrade of the add-on below what the bridge needs, the bridge's notice comes back after each
    restart of Thunderbird.

Fixed

  • The bridge did not start under Claude Desktop's built-in Node.js, which loads the entry point of a .mcpb bundle through a host script with import(): it now also starts when process.argv[1] is this file. Found while testing the bundle of #44 in Claude Desktop 2.16120 (Windows).
  • The bridge of a release announced version 0.0.0 in serverInfo: it read package.json, which the release does not ship (only mcp-bridge.cjs), and next to another project's package.json it announced that project's version. Its version is now written in mcp-bridge.cjs (BRIDGE_VERSION), checked by scripts/check-versions.cjs (see Changed).