Skip to main content
Glama
fastmail-mcp

fastmail-mcp

fastmail-mcp

A Model Context Protocol (MCP) server for Fastmail built with FastMCP, httpx, and Pydantic.

Communicates with Fastmail via JMAP (RFC 8620 / 8621) — no IMAP, no Node, no forks.

Features

Area

Tools

JMAP methods

Session

get_jmap_session

GET /jmap/session

Mailboxes

list_mailboxes, create_mailbox, update_mailbox, destroy_mailbox

Mailbox/get, Mailbox/set

Email (read)

query_emails, get_emails, get_recent_emails, get_thread

Email/query, Email/get, Thread/get

Submission

list_identities, send_email, create_draft, edit_draft, send_draft, reply_email

Identity/get, Email/set, EmailSubmission/set

Mutations

mark_email_read, pin_email, delete_email, move_email, add_labels, remove_labels

Email/set

Bulk mutations

bulk_mark_read, bulk_pin, bulk_delete, bulk_move, bulk_add_labels, bulk_remove_labels

Email/set (batch)

Attachments & stats

get_email_attachments, download_attachment, get_mailbox_stats, get_account_summary

Email/get, Mailbox/get

Masked Email

list_masked_emails, create_masked_email, update_masked_email, destroy_masked_email

MaskedEmail/get, MaskedEmail/set

Vacation

get_vacation_response, set_vacation_response

VacationResponse/get, VacationResponse/set

Sieve Scripts

list_sieve_scripts, get_sieve_script, create_sieve_script, update_sieve_script, destroy_sieve_script, validate_sieve_script

SieveScript/get, SieveScript/set, SieveScript/validate

Related MCP server: Fastmail MCP Server

Setup

1. Create a Fastmail API token

  1. Go to Fastmail → Settings → Privacy & Security → Integrations → API tokens (or see Fastmail developer docs).

  2. Create a new API token with the scopes you need:

Scope

Required for

urn:ietf:params:jmap:core

All tools (session discovery)

urn:ietf:params:jmap:mail

Mailboxes, email read/query, mutations, threads, attachments, stats

urn:ietf:params:jmap:submission

Sending email, draft lifecycle, reply, identities (list_identities, send_email, send_draft, reply_email)

https://www.fastmail.com/dev/maskedemail

Masked email tools

urn:ietf:params:jmap:vacationresponse

Vacation response tools

urn:ietf:params:jmap:sieve

Sieve script tools (if available — see Filters & Rules note)

2. Environment variables

Copy .env.example to .env and fill in your token:

# Required
FASTMAIL_API_TOKEN=fmu1-...

# Optional — override the JMAP session URL (default: Fastmail production)
# FASTMAIL_JMAP_SESSION_URL=https://api.fastmail.com/jmap/session

# HTTP transport only — see "Deployment" section
# FASTMAIL_MCP_HTTP_ALLOW_ENV_API_KEY=false

Usage

Local (stdio) — Cursor, Claude Desktop, etc.

Install from the repo (or a built wheel):

uv pip install -e .

Run the MCP server over stdio:

fastmail-mcp

Cursor configuration

Add to your Cursor MCP settings (.cursor/mcp.json or global):

{
  "mcpServers": {
    "fastmail": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/fastmail-mcp", "fastmail-mcp"],
      "env": {
        "FASTMAIL_API_TOKEN": "fmu1-..."
      }
    }
  }
}

Claude Desktop configuration

{
  "mcpServers": {
    "fastmail": {
      "command": "/path/to/fastmail-mcp/.venv/bin/fastmail-mcp",
      "env": {
        "FASTMAIL_API_TOKEN": "fmu1-..."
      }
    }
  }
}

Remote (HTTP) — hosted server

Start the HTTP transport:

fastmail-mcp-http

Callers authenticate per-request via:

  • Header (preferred): X-Fastmail-Api-Token: fmu1-...

  • Query parameter: ?fastmail_api_token=fmu1-...

Connect from Cursor or Claude Desktop using mcp-remote:

{
  "mcpServers": {
    "fastmail": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-host.example.com/mcp",
        "--header",
        "X-Fastmail-Api-Token: fmu1-..."
      ]
    }
  }
}

Sending email

The send_email, reply_email, and send_draft tools require a token with the urn:ietf:params:jmap:submission scope in addition to urn:ietf:params:jmap:mail.

Typical flow:

  1. List identities — list_identities shows which "from" addresses are available.

  2. Send directly — send_email composes and sends in one step (creates the email + submission as a single JMAP batch).

  3. Draft workflow — create_draft → edit_draft (optional, can repeat) → send_draft.

  4. Reply — reply_email automatically builds Re: subject, In-Reply-To, and References headers. Use reply_all=true to include all original recipients.

JMAP emails are immutable — edit_draft atomically creates a new draft and destroys the old one.

Deployment

  • Never store a shared user token in server environment on a hosted deployment. Set FASTMAIL_MCP_HTTP_ALLOW_ENV_API_KEY=false (the default) so the server always requires callers to send their own token via header or query parameter.

  • Never log tokens. The server uses SecretStr for the env token and never writes it to stdout/stderr.

  • Never log message bodies or email addresses. Error messages from JMAP are surfaced but the server does not log email content.

  • download_attachment returns a URL only — it does not write files to the server filesystem, making it safe for hosted deployments.

  • For single-user local setups, FASTMAIL_API_TOKEN in the process env is fine — it's only read when running over stdio, or when FASTMAIL_MCP_HTTP_ALLOW_ENV_API_KEY=true.

Filters & Rules (web UI)

Fastmail's Settings → Filters & Rules (move-to-mailbox rules, snooze actions, blocked senders, etc.) are configured through the web UI. These rules are not part of the standard JMAP API exposed to API tokens.

  • If the JMAP session advertises urn:ietf:params:jmap:sieve (RFC 9661), the Sieve script tools in this server can list, create, and manage server-side Sieve filter scripts.

  • If the capability is absent, the Sieve tools will return a clear error message. In that case, manage your filters at https://app.fastmail.com/settings/rules.

  • Snooze-specific actions and UI-only rule types are not available via any public API.

Development

# Clone and install
git clone https://github.com/your-user/fastmail-mcp.git
cd fastmail-mcp
uv sync

# Run locally
uv run fastmail-mcp

# Lint
uv run ruff check .
uv run basedpyright

Available Tools

43 tools
add_labelsA

Add one or more mailbox labels to an email (without removing existing ones).

Args: email_id: ID of the email. mailbox_ids: Comma-separated mailbox IDs to add.

Inputs: email_id (required), mailbox_ids (required). Returns: object

bulk_add_labelsA

Add mailbox labels to multiple emails.

Args: email_ids: Comma-separated email IDs (max 50). mailbox_ids: Comma-separated mailbox IDs to add.

Inputs: email_ids (required), mailbox_ids (required). Returns: object

bulk_deleteA

Move multiple emails to Trash.

Args: email_ids: Comma-separated email IDs (max 50).

Inputs: email_ids (required). Returns: object

bulk_mark_readA

Mark multiple emails as read or unread.

Args: email_ids: Comma-separated email IDs (max 50). read: True to mark as read, false for unread.

Inputs: email_ids (required), read. Returns: object

bulk_moveA

Move multiple emails to a mailbox (replaces existing mailbox assignments).

Args: email_ids: Comma-separated email IDs (max 50). mailbox_id: Target mailbox ID.

Inputs: email_ids (required), mailbox_id (required). Returns: object

bulk_pinA

Pin (flag) or unpin multiple emails.

Args: email_ids: Comma-separated email IDs (max 50). pinned: True to pin, false to unpin.

Inputs: email_ids (required), pinned. Returns: object

bulk_remove_labelsA

Remove mailbox labels from multiple emails.

Args: email_ids: Comma-separated email IDs (max 50). mailbox_ids: Comma-separated mailbox IDs to remove.

Inputs: email_ids (required), mailbox_ids (required). Returns: object

create_draftA

Save a new email as a draft (not sent).

At least one of to, subject, or a body field should be provided.

Args: to: Recipients (comma-separated). subject: Email subject line. text_body: Plain-text body content. html_body: HTML body content. cc: CC recipients (comma-separated). bcc: BCC recipients (comma-separated). in_reply_to: Message-ID being replied to. references: Space-separated Message-IDs for References header.

Inputs: to, subject, text_body, html_body, +4 more. Returns: object

create_mailboxA

Create a new mailbox (folder).

Args: name: Display name for the mailbox. parent_id: ID of the parent mailbox for nested folders (empty for top-level).

Inputs: name (required), parent_id. Returns: object

create_masked_emailA

Create a new masked (alias) email address.

Addresses created in "pending" state expire after 24 hours if not enabled.

Args: for_domain: Domain this masked email is associated with (e.g. "example.com"). description: Human-readable description. url: URL associated with this masked email. state: Initial state — "enabled" (default) or "pending".

Inputs: for_domain (required), description, url, state. Returns: object

create_sieve_scriptA

Create a new Sieve filter script.

Args: name: Script name. content: Sieve script source. is_active: Activate this script immediately (only one script can be active).

Inputs: name (required), content (required), is_active. Returns: object

delete_emailB

Move an email to the Trash mailbox.

Inputs: email_id (required). Returns: object

destroy_mailboxA

Destroy (delete) a mailbox. Fails if the mailbox has children or is a system role.

Inputs: mailbox_id (required). Returns: object

destroy_masked_emailB

Destroy (permanently delete) a masked email address.

Inputs: masked_email_id (required). Returns: object

destroy_sieve_scriptC

Destroy (delete) a Sieve script. Cannot destroy the currently active script.

Inputs: script_id (required). Returns: object

download_attachmentA

Get a download URL for an email attachment.

Returns the fully-resolved URL only — does not write to the filesystem. Safe for hosted deployments.

Args: blob_id: Blob ID of the attachment (from get_email_attachments). name: Filename for the download. content_type: MIME type for the download.

Inputs: blob_id (required), name, content_type. Returns: object

edit_draftA

Replace a draft with updated content.

JMAP emails are immutable — this creates a new draft and destroys the old one atomically. Empty fields keep the original value.

Args: draft_id: ID of the existing draft to replace. to: Recipients (comma-separated). Empty keeps original. subject: Email subject. Empty keeps original. text_body: Plain-text body. Empty keeps original. html_body: HTML body. Empty keeps original. cc: CC recipients. Empty keeps original. bcc: BCC recipients. Empty keeps original.

Inputs: draft_id (required), to, subject, text_body, +3 more. Returns: object

get_account_summaryA

Get an aggregate account summary: total emails, unread count, mailbox breakdown.

Inputs: no named parameters. Returns: object

get_email_attachmentsA

List attachments on an email (metadata only — use download_attachment for content).

Args: email_id: ID of the email.

Inputs: email_id (required). Returns: result

get_emailsA

Get full details for one or more emails by ID.

Args: email_ids: Comma-separated email IDs. fetch_body: Include text body content (default true). max_body_bytes: Max bytes per body part (default 10 000; protects LLM context).

Inputs: email_ids (required), fetch_body, max_body_bytes. Returns: result

get_jmap_sessionA

Return the JMAP session: capabilities, account IDs, username, and feature availability.

Use this to discover what features and scopes are available with your API token.

Inputs: no named parameters. Returns: object

get_mailbox_statsA

Get email count statistics for one or all mailboxes.

Args: mailbox_id: Specific mailbox ID (empty for all mailboxes).

Inputs: mailbox_id. Returns: result

get_recent_emailsA

Get the most recent emails in a mailbox (sorted by date, newest first).

Args: mailbox: Mailbox name or role (e.g. "inbox", "sent", "drafts", "trash", or a custom name). limit: Max results (1–50, default 25).

Inputs: mailbox, limit. Returns: object

get_sieve_scriptA

Get a Sieve script by ID, including its full content (downloaded from blob storage).

Inputs: script_id (required). Returns: object

get_threadA

Get all emails in a conversation thread.

Provide either an email_id (the thread is looked up automatically) or a thread_id directly.

Args: email_id: An email ID whose thread to fetch. thread_id: A thread ID to fetch directly. max_body_bytes: Max bytes per body part (default 5000).

Inputs: email_id, thread_id, max_body_bytes. Returns: object

get_vacation_responseA

Get the current vacation / out-of-office auto-reply configuration.

Requires the urn:ietf:params:jmap:vacationresponse capability (Fastmail scope: Vacation responses).

Inputs: no named parameters. Returns: object

list_identitiesA

List sending identities (email addresses you can send from).

Requires the urn:ietf:params:jmap:submission scope.

Inputs: no named parameters. Returns: result

list_mailboxesA

List all mailboxes (folders) in the account.

Returns name, role (inbox/sent/drafts/trash/etc.), parent, email counts.

Inputs: no named parameters. Returns: result

list_masked_emailsA

List masked (alias) email addresses with pagination.

Returns a compact summary by default. Set verbose=True for full fields.

Args: state_filter: Filter by state (pending/enabled/disabled/deleted). Empty for all. limit: Max items to return (default 50, max 200). offset: Skip this many items (for pagination). verbose: If True, return all fields; if False, return compact summary.

Inputs: state_filter, limit, offset, verbose. Returns: object

list_sieve_scriptsA

List Sieve filter scripts (server-side mail routing rules).

Requires urn:ietf:params:jmap:sieve in the JMAP session. If unavailable, manage filters via the Fastmail web UI (Settings → Filters & Rules).

Inputs: no named parameters. Returns: result

mark_email_readA

Mark an email as read or unread.

Args: email_id: ID of the email. read: True to mark as read (default), false to mark as unread.

Inputs: email_id (required), read. Returns: object

move_emailA

Move an email to a different mailbox (replaces all current mailbox assignments).

Args: email_id: ID of the email to move. mailbox_id: Target mailbox ID.

Inputs: email_id (required), mailbox_id (required). Returns: object

pin_emailA

Pin (flag) or unpin an email.

Args: email_id: ID of the email. pinned: True to pin/flag (default), false to unpin.

Inputs: email_id (required), pinned. Returns: object

query_emailsA

Search for emails with structured filters. Returns matching emails with preview.

Args: in_mailbox: Mailbox ID to search within. text: Full-text search across all fields (from, to, subject, body). from_addr: Filter by sender address or name. to_addr: Filter by recipient address or name. subject: Filter by subject text. after: Emails received after this date (ISO 8601, e.g. "2024-01-01T00:00:00Z"). before: Emails received before this date (ISO 8601). has_keyword: Require keyword/flag ("$flagged", "$seen", "$draft", "$answered"). not_keyword: Exclude emails with this keyword. has_attachment: Filter by attachment presence. filter_json: Raw JSON FilterCondition for complex queries (overrides other filter params). sort_property: Sort field — receivedAt, sentAt, from, to, subject, size. sort_ascending: Sort direction (default: newest first). limit: Max results (1–100, default 20).

Inputs: in_mailbox, text, from_addr, to_addr, +10 more. Returns: object

remove_labelsA

Remove one or more mailbox labels from an email.

Args: email_id: ID of the email. mailbox_ids: Comma-separated mailbox IDs to remove.

Inputs: email_id (required), mailbox_ids (required). Returns: object

reply_emailA

Reply to an email. Sends immediately by default; set send=false to save as draft.

Builds Re: subject, In-Reply-To, and References headers automatically.

Args: email_id: ID of the email to reply to. text_body: Plain-text reply body. html_body: HTML reply body. reply_all: If true, reply to all original recipients (not just sender). send: If true (default), send immediately; if false, save as draft. identity_id: Sending identity ID (empty for default).

Inputs: email_id (required), text_body, html_body, reply_all, +2 more. Returns: object

send_draftA

Send an existing draft email.

Args: draft_id: ID of the draft to send. identity_id: Sending identity ID (empty for default).

Inputs: draft_id (required), identity_id. Returns: object

send_emailA

Compose and send an email immediately.

Args: to: Recipients (comma-separated, e.g. "user@example.com, Name other@example.com"). subject: Email subject line. text_body: Plain-text body content. html_body: HTML body content (optional; both text and HTML can be provided). cc: CC recipients (comma-separated, same format as to). bcc: BCC recipients (comma-separated, same format as to). identity_id: Sending identity ID (use list_identities to see options; empty for default). in_reply_to: Message-ID of the email being replied to. references: Space-separated Message-IDs for the References header.

Inputs: to (required), subject (required), text_body, html_body, +5 more. Returns: object

set_vacation_responseA

Set or update the vacation / out-of-office auto-reply.

Args: is_enabled: Whether the vacation response is active. from_date: Start date (ISO 8601, e.g. "2024-12-20T00:00:00Z"). Empty to leave unchanged. to_date: End date (ISO 8601). Empty to leave unchanged. subject: Auto-reply subject line. Empty to leave unchanged. text_body: Plain-text auto-reply body. html_body: HTML auto-reply body (optional).

Inputs: is_enabled (required), from_date, to_date, subject, +2 more. Returns: object

update_mailboxA

Update a mailbox (rename or move under a different parent).

Args: mailbox_id: ID of the mailbox to update. name: New display name (empty to leave unchanged). parent_id: New parent mailbox ID (empty to leave unchanged; "null" moves to top-level).

Inputs: mailbox_id (required), name, parent_id. Returns: object

update_masked_emailA

Update a masked email address (change state, description, domain, or URL).

Args: masked_email_id: ID of the masked email to update. state: New state (enabled/disabled/deleted). Empty to leave unchanged. description: New description. Empty to leave unchanged. for_domain: New associated domain. Empty to leave unchanged. url: New associated URL. Empty to leave unchanged.

Inputs: masked_email_id (required), state, description, for_domain, +1 more. Returns: object

update_sieve_scriptA

Update an existing Sieve script (name, content, or active state).

Args: script_id: ID of the Sieve script to update. name: New name (empty to leave unchanged). content: New Sieve source (empty to leave unchanged). is_active: Set active state (null to leave unchanged).

Inputs: script_id (required), name, content, is_active. Returns: object

validate_sieve_scriptA

Validate Sieve script syntax without saving.

Args: content: The Sieve script source to validate.

Inputs: content (required). Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 43 tools

    Disambiguation5/5

    Each tool targets a distinct operation (single vs bulk, different resources like emails, mailboxes, masked emails, sieve scripts). No two tools have overlapping purposes; descriptions clearly differentiate them.

    Naming Consistency5/5

    All tools follow a consistent verb_noun pattern (e.g., create_mailbox, delete_email, list_identities). Bulk operations are prefixed with 'bulk_', and there are no mixed conventions or abbreviations.

    Tool Count3/5

    With 43 tools, the server covers a wide domain (email, mailboxes, masked emails, siege scripts, vacation, identities). While each tool has its place, the count is on the higher side and could be streamlined by reducing redundant bulk variants.

    Completeness4/5

    The tool surface covers core email workflows (send, reply, draft, labels, move, delete, search) plus mailbox, masked email, and sieve script management. A notable gap is the inability to send emails with attachments (only download is supported).

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      C
      maintenance
      Enables AI assistants to interact with Fastmail email, contacts, and calendars through JMAP API, supporting operations like sending emails, managing attachments, searching conversations, and organizing events.
      52
      76 npm
      131
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to securely access and search Fastmail emails via the JMAP API. Supports searching emails, retrieving full email content, and listing mailboxes with read-only access using Fastmail API tokens.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI models to interact with Fastmail accounts via the JMAP API through the Model Context Protocol. It supports managing emails, accessing usage analytics, and performing account operations using secure token-based authentication.
      27 npm
      9
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      Enables AI assistants to interact with Fastmail services, providing tools for comprehensive email management, contact searching, and calendar event coordination via the JMAP API. It supports advanced features like threaded conversations, attachment handling, and bulk mailbox operations through natural language.
      28
      MIT