Concepts

Core concepts

MCPMail is built around five primitives: mailboxes, messages, threads, the approval queue, and mail.wait. Understanding these will help you design effective agent workflows.

Mailboxes

A mailbox is an email identity your agent owns. It has an address (ops@acme.com), a display name, and an approval mode. One MCPMail account can have many mailboxes — one per agent, per department, per workflow.

Mailboxes are not shared with humans. No human logs into the mailbox. If a human needs to see what the agent is doing, they review the approval queue or subscribe to webhook events. The mailbox is the agent's workspace.

Each mailbox gets a unique ingest address at *.ingest.mcpmail.app. You forward email from your existing provider to this address. MCPMail receives the message and makes it available to your agent via mail.list and mail.get.

Mailbox isolation

Mailboxes are fully isolated. An agent connected to mailbox A cannot read messages in mailbox B. API keys can be scoped to specific mailboxes for additional security.

Messages

A message is a single email — inbound or outbound. Messages have a sender, recipients, subject, body (text and/or HTML), attachments, and headers.

Inbound messages arrive via the forwarding rule and are stored in the mailbox. Outbound messages are created via mail.send and may pass through the approval queue before sending.

Parsed attachments

When an inbound message has attachments, MCPMail automatically parses them:

  • PDF — text extraction, returns the full text content
  • CSV / TSV — parsed to JSON array of objects using header row as keys
  • Excel (.xlsx) — each sheet parsed to JSON arrays
  • Images (PNG, JPG) — OCR text extraction
  • Other — stored as binary, downloadable via URL

Parsed content is available on the message object at attachments[].parsed. Your agent reads structured data, not raw files.

Threads

MCPMail automatically groups related messages into threads using Message-ID, In-Reply-To, and References headers, plus subject-line matching as a fallback.

When your agent replies to a message, MCPMail sets the correct threading headers automatically. The recipient's email client shows the reply in the same conversation thread.

Threads are useful for:

  • Following a multi-step conversation (RFQ → quote → negotiation → PO)
  • Auto-approve rules ("approve replies to existing threads")
  • Context for mail.wait — wait for the next message in this specific thread

The approval queue

The approval queue sits between your agent's mail.send call and actual email delivery. Every outbound message passes through it (unless approval_mode is none).

Three modes control how the queue behaves:

  • all (default) — Every outbound message is held for human review. Nothing sends automatically.
  • smart — Messages matching auto-approve rules send immediately. Everything else is held.
  • none — Messages send immediately. No review. Use with caution.

See Approval Queue guide for auto-approve rule configuration.

mail.wait

The mail.wait tool is MCPMail's most distinctive primitive. It turns email — an inherently asynchronous protocol — into a synchronous step in an agent workflow.

When your agent calls mail.wait, it specifies filter criteria (sender, subject, thread) and a timeout. The server holds the connection. When a matching message arrives, it's returned immediately. The agent's context is preserved — it resumes exactly where it left off.

How mail.wait works under the hood

MCPMail uses long-lived SSE (Server-Sent Events) connections for mail.wait. When your MCP client calls the tool:

  1. The server registers a "waiter" with your filter criteria
  2. The MCP tool call remains open (the agent appears to be "thinking")
  3. When a matching inbound message arrives, it's delivered as the tool result
  4. If the timeout expires first, a timeout result is returned instead

This works within the MCP protocol's streaming tool results. Your agent doesn't need to poll. It doesn't need to set up webhooks. It just waits.

Timeout behavior

When mail.wait times out, it returns { timed_out: true } instead of a message. Your agent can decide what to do — send a follow-up, escalate, or give up.

const result = await mail.wait({
  from: "vendor@acme.com",
  timeout: "24h"
})

if (result.timed_out) {
  // Send a follow-up
  await mail.send({
    to: "vendor@acme.com",
    subject: "Following up: PO #4821",
    body: "Just checking if you received our PO?"
  })
  // Wait again
  const retry = await mail.wait({
    from: "vendor@acme.com",
    timeout: "48h"
  })
}

Webhooks

For workflows that don't use mail.wait, MCPMail supports webhooks. Register a URL and event types, and MCPMail will POST to your endpoint when events occur.

Available events:

  • message.received — new inbound message
  • message.sent — outbound message sent (after approval)
  • message.bounced — delivery failure
  • queue.pending — new message waiting for approval

Webhooks are signed with HMAC-SHA256 so you can verify they came from MCPMail. The signing secret is provided when you register the webhook.