email-thread-mcp
Parses Apple Mail HTML email threads, detecting Apple/Thunderbird blockquote type="cite" quote boundaries to separate the latest message from quoted history and extract signer/greeting information.
Parses Gmail HTML email threads by recognizing Gmail quote markers (gmail_quote) to split the latest message from quoted history, extract signer/greeting, and support AR email preprocessing.
Parses Thunderbird HTML email threads, detecting blockquote type="cite" quote boundaries to separate the latest message from quoted history and extract signer/greeting information.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
email-thread-mcp
MCP server (stdio) with six deterministic tools for an accounts-receivable (AR) email agent: no LLM inside, no network access, no credentials.
Tool | Role in the agent | Output contract |
| very first step: checks the incoming request before any model runs. Parses it as JSON (a text that is not JSON, such as one indented with non-breaking spaces from a rich-text copy, is reported with the reason) and checks it against the caller's JSON Schema. When it fails, | |
| first step: request preprocessing. Splits the raw email body (Outlook/Gmail/Apple HTML or plain text) into the latest message, its signer and greeting, and the quoted history (oldest first). A table row of short cells becomes one line, cells joined by | |
| last step: deterministic checks of the final envelope. Categories are matched by name, or by integer id when the request's categories carry an | |
| last step of a workflow whose engine assembles the envelope from earlier steps' values with a template, so no model copies them: the | |
| after a step that writes free text in a requested language: gives each text a verdict, | |
| after |
Use
Any MCP client that starts stdio servers can run it with uvx:
{
"mcpServers": {
"email-thread-mcp": {
"command": "uvx",
"args": ["email-thread-mcp@0.9.0"]
}
}
}Pin a version: a client that keeps the server running may otherwise serve a stale cached build.
Each tool returns one JSON object. Structured arguments (ar_mailboxes, categories, output,
envelope, schema, allowed_categories, allowed_actions, thread) may be passed as values or as text: clients that render arguments through a template
engine first can send JSON or a Python-style repr, and both are accepted.
A typical agent calls parse_email_thread first, as preprocessing, and uses latest_message,
reply_to_name and thread_context in later steps; it calls validate_output last, on its final
envelope.
Use assemble_output instead when a workflow engine can build the envelope itself. A model that copies
identifiers, invoice numbers or a draft into a final JSON answer changes some of them; a template that
renders each value with a JSON filter does not. Its argument names (envelope, schema,
allowed_categories, thread, allowed_actions) are chosen not to clash with the usual step outputs, since some engines
fill an argument from a context variable of the same name. A failed check does not raise: it is
reported in validation, so the caller still gets the output to review. With allowed_actions (the
suggested-action vocabulary), an action that differs from a listed one only in case, punctuation or
underscores is corrected; one outside the list is kept as a new action, with a warning, after its
underscores become spaces and every word gets an uppercase first letter (send_reminder → Send Reminder). An argument that is still an
unrendered template (a variable it uses was not set) raises an error that says so.
With thread, a draft greeting that shares no part of a name with reply_to_greeting or
reply_to_name ("Dear Mr. Smith," when the customer is Jan Berg) has that name replaced by
reply_to_greeting, with a corrected warning. A greeting by first name only is kept.
For the reply, two fields save the drafting model from reading the whole thread:
reply_to_greeting: how to addressreply_to_name. If our earlier message opened "Dear Mr. Berg,", it isMr. BergforJan Berg; otherwise the name as given. No honorific is added that no message states.our_signature: the signature block of our quoted message, to sign the reply with. Our message is the one fromar_mailboxesor, without them, the one signed by the person the customer greets ("Dear Ms. Weiss" → Clara Weiss's message). Of that person's quoted messages, the fullest block is used, since a short follow-up is often signed with the name alone. Phone, email, URL and address lines are dropped;nullwhen no quoted message is ours. If parsing fails, falling back to the plain-text body before the first quote marker keeps the agent working.
Related MCP server: Kontor MCP
Language support
Inbound mail may be in any language, so the parser treats its three kinds of miss differently:
Miss | Effect | How it is avoided |
quote boundary | old history read as the new message | found by structure in any language: the mail client's HTML quote marker (Outlook |
greeting / signer name |
| per-language words in |
sent date |
|
|
vocab.py covers EN, DE, FR, NL, IT, ES, PT, PL, CS, SV, DA, NO, FI. Only EN is verified against
real threads; the others are built from Outlook's localized reply headers. A language outside the
list still splits, and parse_info.warnings names what was not understood, e.g.
quoted message 1: header labels not recognised: Kimden, Gönderildi, Kime, Konu. Search traces
for those warnings to see which language to add next.
To add or verify a language: add its entry to vocab.py and a case to tests/test_languages.py.
Once a real thread in it (anonymised as below) parses correctly, add it as a fixture and set verified.
Untrusted input
The body is written by the customer, and anyone can send one. The parser:
drops text a reader cannot see (CSS
display:none,visibility:hidden,font-size:0,opacity:0,mso-hide:all, thehiddenattribute) and zero-width, bidirectional and control characters, and reports both inparse_info.warnings;bounds its own cost: the body is cut at 2M raw / 300k text characters, and dates beyond 50 parser calls per email stay null.
It does not make the content trustworthy. The quoted history in particular is plain text the customer
can edit, including a fake message "from" the AR team (role recipient). Prompts must treat
latest_message and thread_context as data, never as instructions.
Release
Bump
versioninpyproject.toml, thenuv run pytest.Add the version to
CHANGELOG.md.Commit, tag
v<version>, push both. Never move or reuse a tag or a version.Publish a GitHub release for the tag:
.github/workflows/publish.ymlruns the tests and publishes to PyPI (Trusted Publishing; the tag must match the version).Check:
uvx email-thread-mcp@<version>starts and waits on stdin.
Develop
uv sync
uv run pytest # 14 real Outlook threads, synthetic variants, 13 languages, hostile input
uv run email-thread-mcp # stdio serverReal customer threads stay out of this repository. Locally, tests/fixtures/ and
tests/test_client_threads.py (both gitignored) hold anonymised real threads and their expected
results; the suite runs them when present. Replace every real address and name before using a
thread there.
This server cannot be deployed
Maintenance
Related MCP Connectors
Quote-to-cash tools: parse invoices and POs, match POs to invoices, collection emails, billing math.
Turn messy text into strict JSON schemas agents can trust (invoice, receipt, contact, resume).
Email inboxes for AI agents. Send, receive, and read email with a trust report on every message.
300+ deterministic data utilities for AI agents: validate, normalize, parse, match, redact.
Related MCP Servers
- AlicenseCqualityDmaintenanceProvides deterministic data parsing and enrichment for AI agents, including bank statements, trade history, EDI, PDFs to structured markdown, and atomic enrichment for amounts, dates, and addresses, with strict schema enforcement to prevent hallucinations.3733 npm2ISC
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to locally parse, validate, audit, explain, generate, and convert XRechnung and ZUGFeRD/Factur-X e-invoices using official rule sets, fully offline with no API keys required.Apache 2.0
- AlicenseAqualityCmaintenanceSub-15ms edge intelligence tools for Claude Desktop, Cursor, and LLM agent loops. Provides sub-millisecond LLM JSON repair, web-to-markdown token compression (cuts context tokens up to 90%), PII redaction, and RFC 5322 B2B email deliverability verification at zero marginal token cost.6MIT
- FlicenseAqualityCmaintenanceProvides AI agents with deterministic, offline finance tools for commodity margin analysis, loan covenant compliance, invoice auditing, AP exception classification, and five-day close readiness.143 npm-