Getting started
Install
npm install @mvrx/mail @mvrx/wbxml
@mvrx/wbxml is only needed if you’re working with Exchange ActiveSync payloads —
@mvrx/mail alone is enough for parsing inbound email.
The parse() Worker example
This is the canonical example from the @mvrx/mail README — a Cloudflare Email
Worker that parses an inbound message and forwards its AI-ready content to an
agent endpoint:
import { parse, wrappers } from "@mvrx/mail";
export default {
async email(message: ForwardableEmailMessage) {
const email = await parse(message, {
wrapper: wrappers.xml("email"),
});
await fetch("https://agent.example.com/inbox", {
method: "POST",
body: JSON.stringify({
messageId: email.messageId,
threadId: email.threadId,
from: email.metadata.from,
subject: email.metadata.subject,
input: email.content.forAI,
}),
});
},
};
A single parse() call returns a fully normalized NormalizedEmail object — no
configuration required. wrappers.xml("email") wraps content.forAI in an
<email>...</email> tag, which is the recommended delimiter style for Claude and
other models that follow XML-structured instructions (see
Threads & wrappers).
What parse() accepts
function parse(
source: ForwardableEmailMessage | ReadableStream<Uint8Array> | string,
options?: ParseOptions,
): Promise<NormalizedEmail>
source can be a Cloudflare ForwardableEmailMessage, a raw RFC 5322 string, or a
ReadableStream<Uint8Array> — so the same parser works identically on Cloudflare
Workers, Node.js, Deno, and Bun; it doesn’t require the Cloudflare Email Routing
product to run.
Reading attachments
Attachment bytes are never loaded during parse() — they’re lazy. Loop and call
content() only for the attachments you actually need:
for (const att of email.attachments) {
if (att.size > 10 * 1024 * 1024) continue; // skip > 10 MB
const bytes = await att.content();
// ... store bytes wherever you need them
}
Options you’ll actually use today
| Option | Default | Purpose |
|---|---|---|
wrapper |
none | Delimiter wrapper applied to content.forAI (see Threads & wrappers) |
cleaner |
built-in heuristic | Replace the default quote/signature stripper with your own |
onAttachment |
none | Callback invoked per attachment during parsing |
threadIdResolver |
AECS-1 §5 algorithm | Override threadId calculation entirely |
maxBodyBytes |
10_000_000 |
Max bytes read from the message source |
forAIMaxChars |
8_000 |
Max characters in content.forAI before truncation |
The full ParseOptions reference — including options that are part of the
AECS-SDK-1 roadmap and not yet implemented — is documented in the
AECS-SDK-1 specification.