Repository navigation
v0.11.0
The MCP protocol changes below come from #16 by Konstantin (mazixs), rebased on 0.10.1. Its own removal of
invisible characters was replaced by 0.10.0's handling of untrusted content, which covers the same ground; the
escaping of what is left, the table and dupLocations handling and the disabled-tool wording were added on top.
The search and reading changes come from #17 by Konstantin (mazixs), rebased on top of #16. On top of it, the
maintainers keep encrypted messages withheld in its new outputs, apply the account restriction to its new paths,
treat its new identifiers and the decoded rawSource like the rest of 0.10.0's untrusted-content handling, find
addresses in headers in linear time (two of its regular expressions backtracked quadratically on a long header a
sender can write), and applied the review of #17.
Breaking
-
getFilterConfirmationis removed, which brings the server back to 40 tools, the limit Cursor accepts per MCP
server. CalllistFilterswithconfirmation: trueinstead: it returns the same read-only view (the pending
request and the recent ones, with the limits) and ignoresaccountId; addingconfirmationIdreads one
request, as before (an unknown id is the same error).confirmationIdis ignored withoutconfirmation: true.
Withoutconfirmation,listFiltersis unchanged. A disabled-tools preference that still namesgetFilterConfirmationis harmless. Disabling
listFiltersnow also disables the confirmation view. -
Tool failures are tool results with
isError: trueand{ "error": "..." }instead of JSON-RPC errors, as the
MCP spec asks for errors the model can act on: invalid arguments, a disabled tool, a handler that throws or
returns{ error }(including a message-tool result too large to check for untrusted content, which is still
refused as a whole), an attachment path the bridge refuses, Thunderbird not reachable, a timeout (with a note
that the operation may still complete, except for a direct send, whose error already says that the outcome is
unknown). JSON-RPC errors remain for protocol problems only, with new codes: unknown tool or missing name
-32602, internal errors-32603(the extension used-32000), unparsable input-32700(the bridge used
-32700for every failure). -
Values outside their bounds are rejected with an error that names the bound:
priority0-9 (createTask,
updateTask),percentComplete0-100 (updateTaskclamped 150 to 100 before). Only limits are clamped:
maxResultsoflistEvents/listTasksis an integer from 1 to 500, a larger value becomes 500 and a fraction
is floored, while 0 is an error (it meant the default of 100 before). -
createEvent.statusis an enum (tentative,confirmed,cancelled, in any case); an empty string, which
meant the default, is rejected, so omit the parameter instead.updateEvent.statusstays a free string on
purpose: an empty string there removes the event's status, and any other value is still checked against the
same three values, in any case. -
searchMessagesandgetRecentMessagesreturn a new envelope and compact rows, for every client (#17).
format: "legacy"returns the old envelope and rows: it is deprecated and will be removed in a later release,
keeps the new folder default (passincludeTrash: truefor the old one) and the decodedccList, and cannot be
combined withgroupBy.0.10.x now Result a plain array; { messages, totalMatches, offset, limit, hasMore }only whenoffsetis passedalways { messages, totalMatches, offset, limit, hasMore }, plusincomplete: truewhen the scan stopped at 10,000 matchesDefault rows 50 (max 200) 20 (max 200); format: "legacy"keeps 50Folders every folder, Trash and Junk included Trash and Junk skipped unless includeTrash: trueor an explicitfolderPaththreadId,folderpresent omitted ( threadIdis folder-local;folderPathidentifies the folder)Empty fields, flagged: falsepresent ( "",[],false)omitted previewthe whole stored preview the first 120 characters, then ...subjectof a replywithout Re:, as stored in the databasewith Re:, as Thunderbird displays itccListas stored (may hold MIME encoded-words) decoded -
getMessage/getMessagescut long bodies by default: 20,000 characters forgetMessageand 4,000 per message
forgetMessages(maxBodyChars, up to 200,000). A longer body setsbodyTruncated,nextBodyOffsetand
bodyTotalChars;getMessagereads the rest withbodyOffset. The same cap applies torawSource(#17). -
rawSourceis decoded text instead of a Latin-1 byte string (see Added), and hidden characters are removed from
it and counted, as in a body.rawEncoding: "base64"returns the exact bytes (#17). -
getMessage: thesubjectof a reply keepsRe:as Thunderbird displays it (the database stores it without),
andccListis decoded, also in the result of an encrypted message. The subject of an encrypted message and of
rawSourcestays the wire-level one, as in 0.10.0;displayMessageis unchanged (#17). -
Search parameters are checked like the others (#17):
offsetmust be a whole number from 0,daysBacka whole
number from 1 (0 meant the default of 7, and fractions were floored),bodyOffseta whole number from 0, and
sortOrderascordesc; other values are errors.maxResultsofsearchMessages,getRecentMessagesand
searchContactsis an integer from 1 to 200 andmaxBodyCharsone from 1 to 200,000: a larger value becomes the
maximum and a fraction is floored, while 0 is an error (formaxResultsit meant the default before). -
searchContactsleaves empty fields out of each contact and also matches the organization (#17).
Added
initializereturns short serverinstructions(IDs, untrusted mail content, search, review windows, stale
IMAP folders), identical in the bridge and the extension. The three lines on search (counts, tables, body
paging, company mail, conversations) come with #17.tools/listentries carrytitleand all four annotations (readOnlyHint,destructiveHint,idempotentHint,
openWorldHint) set explicitly, since the spec defaults assume a destructive open-world tool. They err on the
cautious side:sendMail,replyToMessage,forwardMessage,createFilter,updateFilterandapplyFilters
are destructive and open-world (a sent message can't be taken back, and filter rules can forward or reply), and
getMessage/getMessages(saveAttachmentswrites files) anddisplayMessage(a displayed message is marked
read) are not read-only.searchMessages:participant:matches From, To, Cc or Bcc;participant:@example.commatches the domain of each
address exactly, as Gloda'sLIKE '%@domain'does (@example.comno longer matchesexample.community), and
commas list alternatives for companies with several domains. Operators and quoted phrases combine:
participant:@example.com subject:"invoice 42"(#17).searchMessagesthreadOf: { messageId, folderPath }: the whole conversation across folders (Sent included),
oldest first. Headers are read first and then joined by References (union-find), so the result does not depend
on folder order. ARe:message without threading headers joins by subject only the earlier message whose
author (or, for own mail, a recipient) it involves (linkedBy: "subject"), so the same mail sent separately to
several companies and repeated notifications stay apart. It reads at most 10,000 headers (the search limit) and
setsincomplete: truewhen it stops there. Seedocs/thunderbird-internals.md(#17).searchMessagesgroupBy: "sender" | "thread": one row per sender or conversation, withcount,unread, the
first and last date, andlatestId/latestFolderPath, the newest message that is not a draft (#17).format: "table"({ columns, rows }) forsearchMessages,getRecentMessages,searchContacts,listEvents
andlistTasks: fewer tokens on long lists (#17).searchMessagestagaccepts a tag's label (Important) as well as its key ($label1) (#17).searchBodyreports the terms Gloda leaves out (under 3 characters) inwarning, and errors instead of running
an empty full-text query; its description says that only English words are stemmed (#17).getMessage/getMessages:maxBodyCharsandbodyOffsetpage long bodies and raw sources (#17).rawSourcenames its charset inrawCharset: strict UTF-8, else the charset of the top-levelContent-Type,
then those of the parts, else Thunderbird's charset detector. Encodings that cannot decode a whole message are
refused, declared or detected: the "replacement" labels (iso-2022-kr,hz-gb-2312, ...), UTF-16 and
x-user-defined.warningsays when parts declare different charsets (listed inrawMixedCharsets) or when
invalid UTF-8 was replaced with U+FFFD.rawEncoding: "base64"returns the exact bytes instead, for 8bit/binary
parts; its pages are multiples of 4 characters, so each one decodes on its own. Before, 8-bit sources came back
as Latin-1 mojibake (#17).
Changed
- Tool results are compact JSON (no indentation). Hidden characters left in them are written as
\uXXXXescapes: in
what the untrusted-content handling only counts (id,folderPath,filePath,dupLocations, andlatestId/
latestFolderPath, see below) and in the results of the tools it does not clean (folder, account or filter names,
...). The value is the same once parsed, so an id passed back unchanged still finds the same message or folder,
but the character is visible in the text. The bridge passes compact results through unchanged. An older bridge
(0.10.1 or earlier) paired with this extension undoes the escaping: it parses and re-serializes every result
(JSON.parse/JSON.stringify), which writes those characters back raw, as in 0.10.x. Updatemcp-bridge.cjs
along with the add-on. - The untrusted-content handling judges a cell of a
{ columns, rows }table (format: "table") by its column,
exactly like the same property of the object form: the tables of #17 get the same treatment as their rows. getRecentMessagesruns the search code with a date filter (#17): every folder of the accessible accounts except
Trash and Junk (its description said Inboxes; the code already read every folder), newest first, and it asks each
IMAP folder to update (updateFolder) before reading it, assearchMessagesdoes. An encrypted message is still
listed with its content withheld (wire-level subject, no preview,encrypted: true), now also in tables and in
format: "legacy".- Encrypted messages in the other new outputs of #17, while the option is off:
searchMessagesstill leaves out a
message its query matched (also withgroupBy),threadOfleaves them out of its rows and never links a
conversation through their stored subject (it can be the decrypted one), andrawEncoding: "base64"returns the
wire-level subject. - The account restriction covers the new paths of #17: participant search,
groupByandthreadOf(its scope and
its message) read the accessible accounts only, and the own addresses that tell conversations apart come from
their identities only. - The untrusted-content handling counts
latestIdandlatestFolderPath(the newest message of agroupByrow)
without rewriting them, likeidandfolderPath: the assistant passes them back togetMessage. - Argument coercion: enum values match case-insensitively, object parameters passed as JSON strings are parsed.
Calendar and contact tool descriptions say which ids they take and what they return. - README lists
saveDraftandlistCategories, which were missing from the tool tables, and gives the tool count (40, aftergetFilterConfirmationwas folded intolistFilters). - A test fails on any regular expression literal that strips a trailing run (
/x+$/,/^<+|>+$/g), which backtracks
quadratically (CodeQLjs/polynomial-redos); the remaining ones use a linearstripTrailing/stripLeading.
Fixed
searchMessages: the folder URIs indupLocationshad their hidden characters removed by the untrusted-content
handling of 0.10.0, which could desync them from the folder they name. They are now counted only, like
folderPath.- Filters under an account restriction: an "is in address book" condition naming a mailing list of an accessible
address book (the book's URI followed by/<id>), or a URI with a query string, is no longer refused. The match
needs a/separator, so a look-alike such asabook.sqlite2is still refused. - A local folder whose summary (
.msf) Thunderbird finds out of date or missing (mbox changed outside Thunderbird,.msfdeleted) is rebuilt and read again, within 20 s per call, instead of failing with0x80550005/0x80550006or being skipped silently bysearchMessages/getRecentMessages: the folder a message tool names, each folder a search walks, each message ofgetMessages, the Templates folders of reply rules. - README: troubleshooting entry for a bridge in a container or under WSL that is refused after it used to find the
connection file through a mounted temp folder. - README: the automatic-updates section now says that the bridge does not update itself and must be replaced with the one from the same release.