Skip to content

MCP Tools

Version scope: The mail-only catalog on this page is Local Email App V2 behavior. See Version availability before using this contract with a PyPI installation.

mcp-email-server exposes bounded account discovery, message, mailbox, and composition operations as MCP tools. Tool schemas are generated from the running server, so the MCP client can inspect each parameter and response type directly.

Typical workflow

Most message workflows follow this sequence:

  1. Call list_available_accounts to select an account_name.
  2. Call list_emails_metadata to search a mailbox and obtain email_id values.
  3. Pass those IDs to a read or mutation tool with the same mailbox name.
  4. Call get_emails_content only for messages whose bodies are needed.

This separates lightweight metadata searches from potentially large body retrievals.

MCP input schemas advertise the enforceable string and collection envelopes from the centralized application limits, including maxLength, minItems, and maxItems for account/mailbox names, UID collections, recipients, attachments, and flags. JSON Schema counts characters while the application limits UTF-8 bytes, so every application service independently revalidates direct and MCP callers; aggregate recipient and payload limits also remain application-owned.

Account resource

The resource URI email://{account_name} returns the same stable non-secret capability record used by account discovery. It does not return configuration or masked credential objects.

Account tools

list_available_accounts

Lists all enabled accounts from the selected configuration mode as explicit capability records. Each record contains account_name, account_type, description, optional email_address, can_receive, and can_send. Account descriptions are limited to 4 KiB of UTF-8 data and expose the same structural bound in the output schema. In managed mode, disabled accounts are omitted before any credential lookup or provider access. Use only an account with can_receive=true for mail reads and can_send=true for send_email and forward_email. Text content, structured content, and the output schema describe the same fields.

If the result is empty, account setup is unavailable over MCP. The agent should ask the user to run mcp-email-server ui or the documented interactive CLI in their own terminal and must never request or relay credentials. The output schema and application boundary allow at most 1,000 accounts; the canonical JSON must also fit the shared 8 MiB response ceiling. Oversized authority data is rejected with limit_exceeded rather than truncated.

MCP exposes no account, endpoint, policy, catalog, or credential mutation tool in either mode. Use mcp-email-server ui or the user-operated config and account CLI commands. This prevents an agent or chat transcript from becoming a credential handoff surface. The complete tool names, descriptions, input and output schemas, annotations, resource template, and visibility are static and covered by an exact catalog contract test.

add_email_account, which exists in PyPI 0.16.0 and earlier, is intentionally absent from Local Email App V2 rather than renamed. See Upgrading to Local Email App V2 for client discovery and configuration migration steps.

Agent planning annotations

Every tool advertises reviewed MCP readOnlyHint, destructiveHint, idempotentHint, and openWorldHint values:

Tools Read-only Destructive Idempotent Open world
list_available_accounts, list_allowed_recipients, list_allowed_senders yes no yes no
list_emails_metadata, list_mailboxes yes no yes yes
get_emails_content no no yes yes
send_email, forward_email, save_to_mailbox no no no yes
set_email_flags, mark_emails_as_read no no yes yes
delete_emails, move_emails, archive_emails, download_attachment no yes no yes

get_emails_content is conservatively non-read-only because mark_as_read=true changes remote flags. Download is destructive because the caller-selected destination may be replaced. Send, forward, and append create externally meaningful effects but do not delete or replace an existing mailbox item, so their destructive hint is false while their read-only and idempotent hints are also false.

Annotations are advisory host/agent planning hints, not authorization or a safe-retry guarantee. Tool descriptions, current policy, typed outcomes, and the rule against replay after an ambiguous effect remain authoritative.

Reading and searching

list_emails_metadata

Searches one mailbox without downloading message bodies.

Important parameters include:

Parameter Default Description
account_name Required Configured account identifier.
page 1 One-based result page.
page_size 10 Number of results per page, from 1 to 100.
mailbox INBOX Mailbox to search.
before / since None UTC datetime boundaries.
subject None Subject filter.
from_address / to_address None Address filters.
seen None Filter by read status.
flagged None Filter by flagged or starred status.
answered None Filter by replied status.
body None Search message bodies with IMAP BODY.
text None Search headers and bodies with IMAP TEXT.
has_attachment None Apply a multipart attachment heuristic.
order desc Return ascending or descending results.

The response contains pagination metadata, a filtered total, and message metadata including email_id, message_id, subject, sender, recipients, and date. To and Cc are parsed as structured RFC 5322 address fields: a comma inside a quoted display name is preserved, while addresses inside a group are returned as individual recipient entries. Because this operation fetches headers only, its attachments field is empty. get_emails_content populates attachment names from the full message.

has_attachment uses a multipart/mixed heuristic. It can miss inline content or report multipart messages that do not contain a conventional attachment.

When a sender allowlist is configured, blocked messages are removed before pagination, so total and page sizes describe only visible messages.

The application keeps a rebuildable SQLite projection for unfiltered mailbox pages. It uses that projection only after a small IMAP STATUS probe confirms the same UIDVALIDITY, UIDNEXT, and message count and the projection covers the whole mailbox. Text, date, address, flag, body, and attachment filters remain on the bounded IMAP path so provider-specific search semantics and mutable flags stay authoritative. ASCII filter values retain their exact text through IMAP atom or quoted-string encoding. Non-ASCII filter values use synchronizing UTF-8 literals with CHARSET UTF-8; a provider that rejects that charset returns a bounded search failure rather than receiving malformed raw UTF-8 command text. Date criteria always use the protocol's English month tokens regardless of the server process locale. A response normally omits warnings; if a validated IMAP result was returned but its rebuildable projection could not be persisted, the response includes warnings: ["projection_write_failed"]. It never includes the local exception detail.

A refresh stores at most the 1,000 most recent UIDs and claims complete coverage only when the whole mailbox fits that window and provider state is unchanged across the refresh. Provider fallback accepts at most 10,000 unique canonical single UIDs; ranges, sets, zero, duplicates, and values outside the IMAP UID range are rejected before any UID FETCH. Metadata header requests use IMAP partial fetches, with limits of 64 KiB per message and 4 MiB total per metadata query or refresh. Each wire FETCH is also sized below that aggregate ceiling. Missing, duplicate, or mismatched sender or INTERNALDATE evidence is a bounded error because the server cannot otherwise prove the exact total or ordering. Transport and protocol failures are mapped to bounded categories without returning provider-controlled detail. If a work or payload ceiling is exceeded, the tool returns a bounded error instead of an inexact total, partial page, or unbounded projection.

get_emails_content

Fetches the body of one or more messages by email_id.

Parameter Default Description
account_name Required Configured account identifier.
email_ids Required IDs returned by list_emails_metadata.
mailbox INBOX Mailbox containing the messages.
mark_as_read false Mark successfully retrieved messages as read.
body_offset 0 Character offset at which body output starts.
max_body_length 20000 Maximum body characters returned per message, from 1 to 100000.

If a body extends beyond the requested window, the returned body ends with ...[TRUNCATED]. Fetch the next chunk by increasing body_offset by max_body_length.

The batch response reports requested and retrieved counts and includes failed_ids for messages that could not be fetched. A full-message literal from a successful IMAP FETCH is parsed regardless of its byte length; protocol metadata without a message literal is not treated as content. Each returned email also includes nullable in_reply_to and references values from the corresponding RFC headers. references is returned as one decoded, unfolded string with folding spaces and tabs normalized. Missing and whitespace-only values become null; if an invalid message repeats either header, the parser's first observed value is returned. This is untrusted observational header data, not a validated list of Message-IDs. Well-formed values can be passed back to the compose tools, but malformed values containing other control characters can be returned and will be rejected by compose validation. These fields are available only from full-content reads: they are not part of list_emails_metadata and are not persisted in the SQLite metadata projection.

MIME body extraction does not descend into attachment subtrees. In particular, the body of an attached or forwarded message/rfc822 message is never merged into the containing message body, even when that part has no filename. If one text part declares an unknown charset or contains invalid bytes, it falls back to UTF-8 replacement decoding without hiding the other readable parts.

A request accepts 1 to 500 canonical positive decimal ASCII IMAP UIDs; zero, leading zero, non-ASCII digits, signs, ranges, sets, and values above the IMAP UID limit are rejected before provider access. The provider adapter repeats this validation before opening an IMAP connection as defense in depth. Raw messages above 50 MiB are rejected before MIME parsing. The production provider also counts each returned body's UTF-8 bytes before retaining it and stops immediately if the batch would exceed the 50 MiB aggregate body budget; the application validates the aggregate again at its provider boundary. Each returned thread header is limited to 64 KiB of UTF-8 data and both count toward the 4 MiB aggregate returned-header budget. The production provider enforces these header budgets before retaining each parsed result, and the application independently revalidates them at its provider boundary. Oversized values fail explicitly rather than being truncated into an invalid thread chain.

When the complete valid batch exceeds the inline MCP response ceiling, the server writes the canonical JSON response to a randomly named owner-only file in a process-private temporary directory. The bounded response then has content_omitted=true, an empty emails preview, and output_file_path, output_media_type, output_bytes, output_sha256, and output_lifetime fields. A local MCP host can inspect that exact path with its own filesystem tool. The file is available only until the email-server process exits; copy needed content before restarting. The server does not add a generic file-download tool or remote URL. Spill requires the complete POSIX owner/no-follow profile or the local fixed NTFS Windows DACL/reparse/identity profile. Windows crash remnants are removed only after bounded prefix, type, owner, DACL, and identity validation. Spill never falls back to a broadly accessible temporary file. Without the required profile, bounded inline results remain available, while a batch that requires spill returns a bounded error. The process-lifetime notice still applies.

Body retrieval always uses IMAP PEEK. When requested, successfully retrieved IDs are deduplicated and marked through the same application mutation workflow as mark_emails_as_read in batches of at most 100. A known mark failure is logged but does not discard successfully retrieved content. An unknown or reconciliation-needed mark outcome stops later mark batches so the application does not continue after ambiguous state.

Composing messages

send_email

Sends a message through the selected account's SMTP server. It supports:

  • To, CC, and BCC recipients.
  • Plain-text or HTML bodies.
  • Attachments from file paths available to the server process. Relative paths use the process working directory; absolute paths are recommended.
  • Reply-To, In-Reply-To, and References headers.

The tool is always present in the stable MCP catalog. The selected account_name must itself be enabled and send-capable; an IMAP-only account is rejected before SMTP access.

If a recipient allowlist is configured, every To, CC, and BCC address must be allowed. SMTP delivery reports accepted, rejected, and unknown recipients separately when the result is partial or ambiguous. Failed and unknown targets include reviewed fixed diagnostics when available, for example smtp-mail-rejected, smtp-recipient-rejected, smtp-data-rejected, smtp-data-unknown, or provider-timeout. Unrecognized detail and raw provider response text are omitted.

Internationalized addr-specs in the envelope or From, Sender, To, Cc, Bcc, or Reply-To fields, and non-ASCII Message-ID, In-Reply-To, or References syntax, require the provider's SMTPUTF8 extension. The server requests SMTPUTF8 and serializes the complete message with the matching policy. If the extension is unavailable, every target fails with smtp-utf8-unsupported before MAIL FROM, RCPT TO, or message data is sent. RFC 6531 also requires SMTPUTF8 messages to use BODY=8BITMIME; if the provider advertises SMTPUTF8 without 8BITMIME, every target instead fails with smtp-8bitmime-required before MAIL FROM. A non-ASCII display name with an ASCII addr-spec is encoded as an ordinary RFC 5322 display name and does not by itself require SMTPUTF8.

The server also classifies the final serialized message body before MAIL FROM. Outside the SMTPUTF8 case above, a 7-bit-clean body uses ordinary SMTP DATA; raw high-bit body bytes require the provider's 8BITMIME extension and are sent with BODY=8BITMIME. Without that extension, every target fails with smtp-8bitmime-required before MAIL FROM. A leaf containing raw high-bit payload bytes under a missing, 7bit, base64, or quoted-printable transfer-encoding label is rejected as smtp-mime-transport-invalid; 8BITMIME cannot repair a mismatched MIME label. The same failure applies when a composite multipart or message entity uses a forbidden base64/quoted-printable encoding, or labels actual 8-bit child data as 7-bit. Content that requires binary transport, including a MIME part declaring Content-Transfer-Encoding: binary, NUL, bare line endings, or an overlong DATA line, fails with smtp-binarymime-unsupported. The server does not currently submit BINARYMIME through CHUNKING/BDAT and does not silently rewrite MIME parts to downgrade them. Both transport failures occur before MAIL FROM, RCPT TO, or message data is sent.

Saving the Sent copy is a second IMAP effect and is reported in its own sent-copy section; a failed or unknown copy never changes an accepted delivery into a failure. Do not retry the whole send to repair a Sent copy. Sent-copy APPEND payloads use CRLF line endings for compatibility with strict IMAP providers. An internationalized Sent copy additionally requires RFC 6855 ENABLE plus UTF8=ACCEPT or UTF8=ONLY; unsupported negotiation is reported as utf8-append-unsupported without changing the successful SMTP outcome.

save_to_mailbox

Composes a message and appends it to an IMAP mailbox instead of sending it. It works without SMTP and is useful for drafts or templates. It shares recipient, body, attachment, and threading fields with send_email, adds mailbox and flags, and does not support reply_to. For both compose tools, simple Message-IDs in in_reply_to and references may be supplied with or without angle brackets. The server adds missing brackets to each simple whitespace-separated ID when constructing the RFC headers and does not double-wrap bracketed IDs.

The default mailbox is Drafts. When no explicit flags are supplied, the message is saved with \Draft and \Seen. The response includes the RFC message ID. It includes an assigned IMAP email_id only when the server returns RFC 4315 APPENDUID; otherwise the value is unknown, and the target mailbox must be searched before a later operation can address the saved message.

The same recipient allowlist used by send_email applies to this tool. The complete MIME payload is serialized with CRLF line endings before IMAP APPEND for compatibility with strict providers. Saved-message flags may be system flags or provider keywords, but each must be one valid IMAP atom; legal values such as $Forwarded, project.name, and 123flag are accepted, while whitespace, controls, and IMAP protocol specials are rejected.

The server refreshes capabilities before mailbox selection. A message with internationalized address or thread-header syntax requires RFC 6855, and a UTF8=ONLY server requires ENABLE UTF8=ACCEPT even for an ordinary ASCII-header message. In an enabled session, LIST names retain their literal UTF-8 spelling and internationalized mailbox arguments use escaped UTF-8 rather than Modified UTF-7. Only a message whose headers require RFC 6532 uses the RFC 6855 UTF8 literal form. Missing capability or incomplete ENABLE evidence fails before SELECT/APPEND with utf8-append-unsupported. A known APPEND success without APPENDUID returns email_id: unknown. A lost APPEND result is instead tagged unknown; the server does not replay it because that could create a duplicate draft.

forward_email

Forwards an existing message to new recipients through the selected account's SMTP server. The server reads the source message over IMAP, composes a new message below an optional note from the caller, and re-attaches the original's attachments.

Parameter Default Description
account_name Required Configured account identifier.
email_id Required UID of the source message to forward.
recipients Required Addresses that receive the forwarded message.
source_mailbox INBOX Mailbox that contains the source message.
body "" Note placed above the forwarded content.
cc None Additional CC recipients of the forward.
bcc None Additional BCC recipients of the forward.
include_attachments true Re-attach the source message's attachments.

The subject is derived from the source message as Fwd: <original subject>. A source subject that already begins with Fwd: in any letter case is not prefixed a second time.

The forwarded content is appended below the caller's note as a plain-text Forwarded message block reporting the original's From, Recipients, Date, and Subject. That block reports Recipients: rather than To: because the parsed recipient list folds in Cc entries.

The block is re-composed from the parsed plain-text body, so the original's HTML formatting is not preserved in the quoted text. The forwarded content is never silently truncated: the composed body, including any note you supply, is bounded at 1 MiB and an oversized forward is rejected outright. Forward a message when the recipient needs its attachments and substance; when byte-exact rendering matters, save the parts with download_attachment and compose the message explicitly with send_email.

Attachments carried into the forward keep the source part's MIME main type, subtype, and parameters instead of being coerced into application/*. Set include_attachments=false to forward only the text.

Re-attached parts are bounded by the shared application limits: at most 20 retained parts, 25 MiB per part, and 50 MiB in total. Each size is the conservative maximum of the part serialized under the SMTP and SMTPUTF8 wire policies, including CRLF expansion. A source with more retained parts than the limit is rejected after the read; forward its text with include_attachments=false instead. Only parts the server classifies as attachments are re-attached — an inline part with no filename and no attachment disposition (for example a Content-ID image referenced by an HTML body) is not carried, matching what the metadata and content tools report as attachments. The quoting block reports the source's own Date header and omits the line entirely when the source has none.

The tool is always present in the stable MCP catalog. The selected account_name must itself be enabled and send-capable; an IMAP-only account is rejected before the source message is read over IMAP and before any SMTP access, so a non-send-capable account never downloads or parses the source.

The delivered forward passes through the same SMTP transport classification as any send. A re-attached source part correctly labeled with an 8bit transfer encoding rides BODY=8BITMIME when the provider advertises it (the composed container is labeled 8bit to keep the message well formed) and fails with smtp-8bitmime-required when it does not; mislabeled or binary source parts fail with the shared smtp-mime-transport-invalid and smtp-binarymime-unsupported diagnostics before MAIL FROM.

A forward performs three independent provider effects: the IMAP read of the source message, SMTP delivery, and the IMAP Sent copy. Current account authority is resolved for each effect. Send capability and recipient policy are checked before the source read and again before SMTP; the source sender is also checked against the freshly resolved sender policy before SMTP. Sent-copy follows the same post-delivery authority rules as send_email. If the source message cannot be read, the call fails before any SMTP session is opened, so a forward is never delivered without the content and attachments it was supposed to carry. Delivery and sent-copy outcomes are reported separately under the same rules as send_email, and an ambiguous SMTP outcome is reported unknown and is never replayed automatically.

Reading the source message is a mail read. When a sender allowlist is configured, a message from a blocked sender is indistinguishable from a missing message, so the forward fails without revealing that the message exists. If the sender policy is tightened after the source read but before SMTP, the fresh policy also aborts delivery with the same not-found-shaped error. The recipient allowlist applies to the forward's To, CC, and BCC addresses exactly as it does for send_email.

For a worked example, see Forward a message with its attachments.

Mailbox and mutation tools

list_mailboxes

Lists IMAP mailboxes with their names, hierarchy delimiters, and flags. Call it before moving or saving messages when provider-specific folder names are not known.

pattern defaults to *, and reference defaults to an empty string. The account name and both IMAP LIST values are validated before provider access; pattern must be non-empty and pattern/reference values are each limited to 1,024 UTF-8 bytes. Literal mailbox names are reassembled at their declared byte length, tagged LIST completion text is not returned as a mailbox, and malformed literal framing fails the request. Special-use flags such as \Sent are matched case-insensitively for folder discovery.

set_email_flags

Adds or removes approved IMAP flags from one or more message IDs in the selected mailbox. operation must be add or remove, and applies to every supplied flag. The non-empty flags list accepts unique values from:

  • \Seen
  • \Flagged
  • \Answered
  • \Draft

The provider sends one UID-scoped +FLAGS.SILENT or -FLAGS.SILENT operation per message so results retain caller order and per-ID evidence. The operation is logically idempotent, but an unknown result is not retried automatically because the mailbox UID epoch or current authority may have changed.

\Deleted is intentionally rejected and remains owned by delete_emails, which applies target-scoped expunge safety. \Recent is server-controlled, and provider-specific keywords are not part of the portable public contract. To mark a message unread, remove \Seen.

mark_emails_as_read

Marks one or more message IDs as read in the selected mailbox. This focused common-workflow tool uses the same implementation as set_email_flags with operation="add" and flags=["\\Seen"].

move_emails

Moves messages from source_mailbox, which defaults to INBOX, to a required destination_mailbox. Native IMAP MOVE is preferred. The COPY-and-delete fallback is available only when the server advertises UIDPLUS, allowing the source to be removed with target-scoped UID EXPUNGE; otherwise the operation fails before copying a message.

archive_emails

Moves messages to the account's archive mailbox. The server first uses the RFC 6154 \Archive mailbox flag and then falls back to Archive, Archives, or [Gmail]/All Mail. Archive uses the same native-MOVE or safe UIDPLUS fallback rules as move_emails.

delete_emails

Deletes one or more messages from the selected mailbox. The provider must advertise UIDPLUS: the server flags and expunges only the requested UIDs with UID EXPUNGE and never sends mailbox-wide EXPUNGE. Without UIDPLUS, the operation fails before adding the \Deleted flag.

An all-known-success mutation keeps the existing success sentence. Partial or ambiguous results use tagged succeeded, failed, and unknown sections in input order. Unknown targets can include a fixed substep tag such as store, copy, or expunge-after-copy. unknown means the provider effect may have started but its final result was lost; the server does not replay it automatically, and every result containing unknown includes a reconciliation needed warning. The same warning also appears when a known provider effect may be authoritative but the rebuildable local metadata projection could not be invalidated.

Mutation requests accept 1 to 100 unique canonical positive decimal IMAP UIDs. set_email_flags accepts one to four unique approved flags and exactly one add/remove operation. Mailbox names are limited to 1,024 UTF-8 bytes. Compose requests allow at most 100 total To/CC/BCC entries of at most 1,024 UTF-8 bytes each; every entry must contain exactly one address. Compose requests also allow a 64 KiB UTF-8 subject, a 1 MiB UTF-8 body, and 20 attachments. Threading and Reply-To values are limited to 64 KiB each. Each attachment path is limited to 4,096 bytes, each existing attachment to 25 MiB, and their combined size to 50 MiB. Outbound attachments preserve the inferred MIME main type and subtype (for example, image/png remains image/png) instead of coercing every file to application/*. Saved messages accept at most 100 flags of 128 bytes each before protocol syntax validation. Mailbox names, recipient/header values, and subjects reject control characters before provider access.

When a sender allowlist is active, blocked messages are never changed. See Sender allowlist for the privacy behavior of blocked IDs.

Attachments

download_attachment

Downloads one named attachment from a message to the server host. By default, the server creates a safe randomized filename under the current user's Downloads/mcp-email-server directory. On Windows, it uses a valid Downloads Known Folder registry value and otherwise falls back to the profile's ~/Downloads; on other platforms it uses ~/Downloads. The returned saved_path reports the resolved absolute destination.

save_path is optional. When supplied, it remains an exact destination: use an absolute path when possible. A relative explicit path is resolved against the server process's working directory.

The tool is registered even when downloading is disabled, but calling it then raises a permission error. Enable it explicitly with:

enable_attachment_download = true

The application checks current account and feature policy, resolves and preflights the local destination before provider construction, credential resolution, download, or MIME decoding. For a default destination, it sanitizes the attachment name, removes path/device syntax, adds a cryptographically random suffix, and creates the application subdirectory with private permissions. It checks authority again after fetch immediately before the write, so revocation during a slow fetch discards the payload. Raw messages above 50 MiB and decoded attachments above 25 MiB are rejected. The mail adapter returns bytes only and never receives the resolved path.

The artifact adapter writes only the explicit or preflight-resolved destination. It never falls back to the process working directory when default resolution fails. POSIX uses pinned no-follow directory descriptors and owner-only files. Windows supports only a local fixed NTFS drive-letter path and uses held non-reparse handles, protected DACLs, hard-link/identity checks, FlushFileBuffers, and same-volume write-through replacement. Symlinked or junction parents, linked/permissive or non-regular targets, UNC/network/device/alternate-stream/non-NTFS paths, and replacement races fail closed. Existing private regular files may be replaced; there is no weaker fallback.

Review Attachment access before enabling this operation.

Stable tool catalog

MCP initialization reports the installed mcp-email-server application version in serverInfo.version, not the MCP SDK dependency version. The tool list is static for the lifetime of a server process. send_email, list_allowed_recipients, list_allowed_senders, and download_attachment are always advertised. Account existence, enabled state, SMTP capability, and current policies are enforced when each tool is called. The allowlist tools have distinct empty semantics: an empty recipient list disables sending, while an empty sender list does not restrict reading. Each list is limited to 1,000 entries, and the complete effective-configuration snapshot is canonically serialized against the shared 8 MiB ceiling before either policy result is returned; oversized authority data fails with limit_exceeded.

Account lifecycle changes therefore do not require a tools-list notification. A bootstrap mode selection still requires a server restart because it changes the selected configuration authority.

Reply threading

To preserve conversation threading:

  1. Fetch the original message with get_emails_content.
  2. Use its RFC message_id as in_reply_to.
  3. Build references from the returned references value followed by the original message_id, omitting missing values.
  4. Send the reply with a suitable Re: subject.

Simple Message-IDs may be bare or already enclosed in angle brackets; the compose path emits the required bracketed RFC form for both headers.

For a complete example, see Reply with proper threading.