fastmail-mcp-remote
Health Uyari
- No license — Repository has no license file
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 7 GitHub stars
Code Uyari
- network request — Outbound network request in cli/auth.ts
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Remote MCP server for Fastmail on Cloudflare Workers with Cloudflare Access OAuth
Fastmail Remote
Fastmail has a powerful API (JMAP) and supports IMAP, but it doesn't offer a native MCP server. That means AI assistants like Claude, Copilot, and OpenClaw can't talk to your Fastmail account out of the box.
Fastmail Remote bridges this gap. It's a remote MCP server that runs on Cloudflare Workers, translating MCP tool calls into Fastmail JMAP API requests. Your Fastmail API token is stored as an encrypted Cloudflare secret, and all access is protected by Cloudflare Zero Trust authentication. You deploy it once, and any MCP client can connect via OAuth.
Client Interfaces
- Any MCP client (Claude.ai, Claude Code, GitHub Copilot) connects directly to the Worker via OAuth
- Fastmail CLI — a local command-line tool that calls the Worker and formats responses as compact text, saving 5-7x tokens
- Fastmail CLI for OpenClaw — an OpenClaw plugin (37 agent tools) published as
fastmail-clion npm
Architecture
┌─────────────┐ OAuth ┌──────────────────────┐ API Token ┌──────────────┐
│ Claude.ai │ ───────────► │ Cloudflare Worker │ ────────────────► │ Fastmail │
│ (MCP Client)│ (CF Access) │ (Remote MCP Server) │ (stored as secret) │ API │
└─────────────┘ └──────────────────────┘ └──────────────┘
┌─────────────┐ Bearer Token ┌──────────────────────┐ API Token ┌──────────────┐
│ fastmail CLI│ ───────────► │ Cloudflare Worker │ ────────────────► │ Fastmail │
│ (local) │ (PKCE OAuth) │ (Remote MCP Server) │ (stored as secret) │ API │
└─────────────┘ └──────────────────────┘ └──────────────┘
Fastmail CLI
A token-efficient CLI that calls the remote MCP server and formats responses as compact text, saving 5-7x tokens compared to raw MCP tool calls.
Quick Start
# Add alias to ~/.zshrc
alias fastmail="npx tsx ~/GitHub/fastmail-mcp-remote/cli/main.ts"
# Authenticate (one-time, tokens last 30 days)
fastmail auth --url https://your-worker.example.com --team yourteam
fastmail auth status # Shows authenticated user, token expiry
fastmail auth logout # Remove cached credentials
# Headless auth (SSH / no-browser environments)
fastmail auth --headless --url https://your-worker.example.com
# Prints a URL to open in any browser, then paste the token back
Commands
# Inbox & reading
fastmail inbox # 10 most recent inbox emails
fastmail inbox --limit 20 # More emails
fastmail email <id> # Read email (markdown format)
fastmail email thread <threadId> # Full conversation thread
# Searching
fastmail email search "query" # Text search
fastmail email search "invoice" --from [email protected]
# Composing
fastmail email send --to [email protected] --subject "Hi" --body "Hello!"
fastmail email draft --to [email protected] --subject "Draft" --body "..."
fastmail email reply <id> --body "Thanks!" --send
# Actions & bulk
fastmail email read|unread|flag|unflag|delete <id>
fastmail bulk read|delete|flag <id1> <id2> <id3>
# Mailboxes, contacts, calendar
fastmail mailboxes
fastmail contacts
fastmail calendars
fastmail events
# Memos (private notes)
fastmail memo <emailId>
fastmail memo create <emailId> --text "Note"
fastmail memo delete <emailId>
All commands support --json for raw JSON output.
CLI Structure
cli/
├── main.ts # Entry point, commander setup
├── mcp-client.ts # MCP SDK client (StreamableHTTPClientTransport)
├── auth.ts # PKCE OAuth flow + token caching
├── formatters.ts # Compact text output formatters
├── commands/
│ ├── email.ts # Email, bulk, mailbox, account commands
│ ├── contacts.ts # Contact commands
│ ├── calendar.ts # Calendar commands
│ └── memo.ts # Memo commands
└── skill.md # Claude Code skill documentation
Available Tools
list_mailboxes- List all mailboxeslist_emails- List emails from a mailboxget_email- Get a specific email by ID (includes threading: messageId, inReplyTo, references, threadId)send_email- Send an email (supports attachments and reply threading)create_draft- Create a draft email (supports attachments and reply threading)update_draft- Edit an existing draft's body (creates a replacement draft — the ID changes; re-derives the quoted original for reply drafts)reply_to_email- Reply to an email with automatic threading and quoting (like Fastmail's reply button)search_emails- Search emailsget_recent_emails- Get most recent emailsmark_email_read- Mark email as read/unreaddelete_email- Delete an email (move to trash)move_email- Move email to different mailboxget_email_attachments- Get attachment listdownload_attachment- Get attachment download URLadvanced_search- Advanced email search with filtersget_thread- Get all emails in a threadget_mailbox_stats- Get mailbox statisticsget_account_summary- Get account summarybulk_mark_read- Mark multiple emails read/unreadbulk_move- Move multiple emailsbulk_delete- Delete multiple emailsflag_email- Flag or unflag an emailbulk_flag- Flag or unflag multiple emailsget_inbox_updates- Get inbox changes since a previous state (incremental sync)create_memo- Add a private note (memo) to an email, rendered as a yellow inline annotation in Fastmailget_memo- Get the memo attached to an emaildelete_memo- Delete a memo from an emailgenerate_email_action_urls- Generate HMAC-signed URLs for email archive/delete actions (24h expiry, single-use)
Email Body Formats
The send_email, create_draft, and reply_to_email tools support three body formats:
| Parameter | Format | Description |
|---|---|---|
textBody |
Plain text | Simple text content |
htmlBody |
HTML | Rich HTML content |
markdownBody |
Markdown | GitHub-flavored Markdown (auto-converted to HTML) |
At least one body format is required. If markdownBody is provided, it takes precedence over htmlBody.
Example with Markdown:
{
"to": ["[email protected]"],
"subject": "Meeting notes",
"markdownBody": "# Meeting Summary\n\n- Point 1\n- Point 2\n\n**Action items:**\n1. Review proposal\n2. Send feedback"
}
Sending Emails with Attachments
Both send_email and create_draft support file attachments. Attachments are passed as an array of objects with base64-encoded content:
{
"to": ["[email protected]"],
"subject": "Document attached",
"textBody": "Please see the attached file.",
"attachments": [
{
"filename": "report.pdf",
"mimeType": "application/pdf",
"content": "<base64-encoded-file-content>"
}
]
}
Attachment limits:
- Maximum file size: 25MB per attachment
- Supported: Any file type (PDF, images, documents, etc.)
Validation:
- Filenames are validated to prevent path traversal attacks
- MIME types must be in valid
type/subtypeformat - Base64 content is validated before upload
Replying to Emails
The reply_to_email tool provides a convenient way to reply to emails with proper threading and quoting, just like Fastmail's reply button:
{
"emailId": "abc123",
"body": "Thanks for the information!",
"replyAll": false,
"sendImmediately": false
}
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
emailId |
string | required | ID of the email to reply to |
body |
string | required | Your reply message (plain text) |
htmlBody |
string | optional | Your reply message (HTML) |
markdownBody |
string | optional | Your reply message (Markdown, auto-converted to HTML) |
replyAll |
boolean | false |
Reply to all recipients (sender + CC) |
sendImmediately |
boolean | false |
Send immediately vs create draft |
excludeQuote |
boolean | false |
Skip quoting the original message |
What it handles automatically:
- Recipients: Uses
replyToorfromaddress;replyAllincludes CC recipients - Subject: Adds "Re:" prefix if not already present
- Threading: Sets
inReplyToandreferencesheaders for proper threading - Quoting: Formats quoted original in Fastmail style with attribution line
Email Threading (Low-Level)
For manual control over threading, send_email and create_draft support inReplyTo and references parameters, and get_email returns threading properties:
{
"to": ["[email protected]"],
"subject": "Re: Original subject",
"textBody": "My reply...",
"inReplyTo": ["<[email protected]>"],
"references": ["<[email protected]>", "<[email protected]>"]
}
Threading properties returned by get_email:
messageId- The email's Message-ID headerinReplyTo- Message-IDs this email replies toreferences- Full thread chain of Message-IDsthreadId- JMAP's internal thread identifier
Memos (Private Notes)
Add private notes to emails that render as yellow inline annotations in Fastmail's UI. Memos are personal — only visible to you.
{
"emailId": "abc123",
"text": "Follow up on this next week"
}
How it works: Memos are stored as special emails in a hidden Memos mailbox, linked to the target email via the In-Reply-To header. The $memo JMAP keyword triggers Fastmail's yellow annotation rendering.
| Tool | Description |
|---|---|
create_memo |
Add a memo to an email |
get_memo |
Read the memo on an email (returns text, date, memoId) |
delete_memo |
Remove the memo from an email |
Identity
list_identities- List sending identities
Contacts
list_contacts- List contactsget_contact- Get a specific contactsearch_contacts- Search contacts
Calendar
list_calendars- List all calendarslist_calendar_events- List calendar eventsget_calendar_event- Get a specific eventcreate_calendar_event- Create a new event
Utility
check_function_availability- Check available functionsgenerate_email_action_urls- Generate pre-signed action URLs for email operations
Setup Instructions
1. Create KV Namespace
npx wrangler login
npx wrangler kv namespace create "OAUTH_KV"
Copy the output ID and update wrangler.jsonc:
"kv_namespaces": [
{
"binding": "OAUTH_KV",
"id": "<YOUR_KV_NAMESPACE_ID>"
}
]
2. Configure Cloudflare Access
Create a Cloudflare Access SaaS application for OAuth:
- Go to Cloudflare Zero Trust Dashboard → Access → Applications
- Click Add an application → SaaS
- Fill in:
- Application name:
Fastmail MCP - Application type: Custom
- Application name:
- Configure OIDC settings:
- Auth Type: OIDC
- Redirect URI:
https://your-worker.example.com/mcp/callback
- Configure access policy to allow your users (e.g., email domain or specific emails)
- Copy the Client ID and generate a Client Secret
3. Configure Environment Variables
Add ACCESS_TEAM_NAME and ALLOWED_USERS as plaintext vars in your wrangler.jsonc (gitignored — PII stays local):
"vars": {
"ACCESS_TEAM_NAME": "yourteam",
"ALLOWED_USERS": "[email protected],[email protected]"
}
- ACCESS_TEAM_NAME: Your Cloudflare Zero Trust team name (the subdomain before
.cloudflareaccess.com) - ALLOWED_USERS: Comma-separated list of email addresses allowed to access the server
Two optional vars tighten things further:
- ALLOWED_REDIRECT_HOSTS: Extra hostnames permitted as OAuth
redirect_uritargets. Loopback (native/CLI clients) and the worker's own origin are always allowed; everything else must be anhttpshost on this list. Defaults toclaude.ai,claude.com,anthropic.com. Setting it replaces the defaults rather than adding to them. - ACTION_ALLOWED_ORIGINS: Browser origins allowed to call the signed
/api/action/*endpoints. Leave unset (*) if you open the reading-digest page from a local file, which sendsOrigin: null.
For local development, also add these to .dev.vars (gitignored).
Warning: Deploying without the
varssection inwrangler.jsoncwill wipe all dashboard-set plaintext vars. Always keepvarsin your local config.
4. Get Fastmail API Token
- Go to https://www.fastmail.com/settings/security/tokens
- Create a new API token with the scopes you need (Email, Contacts, Calendars)
- Copy the token
5. Configure Local Secrets
Create .dev.vars with your local development credentials:
ACCESS_CLIENT_ID="your-cloudflare-access-client-id"
ACCESS_CLIENT_SECRET="your-cloudflare-access-client-secret"
FASTMAIL_API_TOKEN="your-fastmail-api-token"
WORKER_URL="http://localhost:8788"
Note: ACCESS_TEAM_NAME and ALLOWED_USERS are configured in wrangler.jsonc vars, not in .dev.vars.
6. Test Locally
npm start
Server runs at http://localhost:8788/sse
Test with MCP Inspector:
npx @modelcontextprotocol/inspector@latest
# Open http://localhost:5173
# Enter http://localhost:8788/sse
# Click "Open OAuth Settings" → "Quick OAuth Flow"
# Authenticate via Cloudflare Access
# Click "Connect" → "List Tools"
7. Deploy to Cloudflare
npx wrangler deploy
8. Set Production Secrets
npx wrangler secret put ACCESS_CLIENT_ID
npx wrangler secret put ACCESS_CLIENT_SECRET
npx wrangler secret put FASTMAIL_API_TOKEN
npx wrangler secret put WORKER_URL
npx wrangler secret put ACTION_SIGNING_KEY
# Generate a 256-bit hex key: openssl rand -hex 32
9. Connect Clients
Fastmail CLI
# Add alias to ~/.zshrc
alias fastmail="npx tsx ~/GitHub/fastmail-mcp-remote/cli/main.ts"
# Authenticate (opens browser for CF Access login)
fastmail auth --url https://your-worker.example.com --team yourteam
# Or headless auth for SSH / no-browser environments
fastmail auth --headless --url https://your-worker.example.com
# Test
fastmail inbox
Claude.ai
- Go to https://claude.ai/settings/connectors
- Click Add custom connector
- Enter your MCP server URL:
https://your-worker.example.com/sse - Click Add
- Click Connect and authenticate via Cloudflare Access
Claude Code
claude mcp add --scope user --transport http fastmail "https://your-worker.example.com/mcp"
Then run /mcp in Claude Code to complete the OAuth flow.
GitHub Copilot CLI
GitHub Copilot CLI doesn't support automatic OAuth client registration, so you need to register a client first:
Register an OAuth client:
curl -X POST "https://your-worker.example.com/register" \ -H "Content-Type: application/json" \ -d '{"client_name": "github-copilot", "redirect_uris": ["http://localhost", "http://127.0.0.1"]}'Save the returned
client_id.Add the MCP server:
copilot mcp add fastmail --url "https://your-worker.example.com/mcp"When prompted for OAuth credentials:
- Client ID: Paste the
client_idfrom step 1 - Client Type: Select
[1] Public (no secret) - Press
Ctrl+Sto save and authenticate
- Client ID: Paste the
Security Notes
- Authentication via Cloudflare Access (supports GitHub, email OTP, and other identity providers)
- Fastmail API token stored encrypted in Cloudflare secrets
- OAuth tokens stored in Cloudflare KV with TTL expiration
- All traffic over HTTPS
- Email-based allowlist for access control
- Email action URLs use HMAC-SHA256 signatures with 24-hour expiry and single-use nonces
Delegate Access (Role-Based Permissions)
The server supports role-based access control with two layers:
- Role-based —
admin(full access) vsdelegate(read + inbox management + drafts) - Category-based — per-user disabled categories (e.g., hide contacts/calendar)
Roles
| Role | Can Do | Cannot Do |
|---|---|---|
| admin | Everything (unless categories are disabled) | — |
| delegate | Read email, manage inbox, create drafts, reply as draft | Send email, create calendar events |
Tool Categories
| Category | Tools | Admin | Delegate |
|---|---|---|---|
EMAIL_READ |
list_mailboxes, list_emails, get_email, search_emails, get_recent_emails, get_inbox_updates, get_email_attachments, download_attachment, advanced_search, get_thread, get_mailbox_stats, get_account_summary, list_identities, get_memo | Yes | Yes |
CONTACTS |
list_contacts, get_contact, search_contacts | Yes | Yes |
CALENDAR_READ |
list_calendars, list_calendar_events, get_calendar_event | Yes | Yes |
CALENDAR_WRITE |
create_calendar_event | Yes | No |
INBOX_MANAGE |
mark_email_read, flag_email, delete_email, move_email, bulk_mark_read, bulk_move, bulk_delete, bulk_flag, create_memo, delete_memo, generate_email_action_urls | Yes | Yes |
DRAFT |
create_draft, update_draft | Yes | Yes |
REPLY |
reply_to_email | Yes | Yes* |
SEND |
send_email | Yes | No |
META |
check_function_availability | Yes | Yes |
* Delegates can use reply_to_email to create draft replies, but sendImmediately: true is denied.
Configuration
Permissions are stored in Cloudflare KV under the key config:permissions as JSON:
{
"users": {
"[email protected]": {
"role": "admin",
"disabled_categories": []
},
"[email protected]": {
"role": "delegate",
"disabled_categories": ["CONTACTS", "CALENDAR_READ", "CALENDAR_WRITE"]
}
},
"default_role": "admin",
"default_disabled_categories": []
}
Set the config via wrangler:
# Write permissions config to KV
npx wrangler kv key put --binding=OAUTH_KV "config:permissions" '{
"users": {
"[email protected]": {
"role": "delegate",
"disabled_categories": ["CONTACTS", "CALENDAR_READ", "CALENDAR_WRITE"]
}
},
"default_role": "admin",
"default_disabled_categories": []
}'
How It Works
- tools/list filtering: Delegates only see tools they're allowed to use. Disabled categories are hidden for all roles.
- tools/call interception: If a delegate tries to call a denied tool, they receive a JSON-RPC error with an actionable hint (e.g., "Use 'create_draft' to compose emails as drafts instead").
- Config caching: Permissions config is cached for 5 minutes to minimize KV reads.
- Unknown users: Fall back to
default_roleanddefault_disabled_categories. - Case-insensitive: Email lookups are case-insensitive.
Troubleshooting
"OAuth error" when connecting
- Verify Cloudflare Access redirect URI matches your worker URL exactly
- Check that ACCESS_CLIENT_ID and ACCESS_CLIENT_SECRET are set correctly
- Ensure KV namespace is created and bound
- Verify
ACCESS_TEAM_NAMEinwrangler.jsoncvars matches your Zero Trust team name
Tools not appearing
- Check worker logs: Cloudflare Dashboard → Workers → Logs
- Verify tools are registered in the
init()method - Test with MCP Inspector first
"User not authorized" after login
- Verify your email is in
ALLOWED_USERSinwrangler.jsoncvars - Check the user email matches exactly (case-insensitive)
- Ensure
wrangler.jsonchas avarssection — deploying without it wipes plaintext vars
CLI "Not authenticated"
- Run
fastmail auth --url <url> --team <team>to authenticate - Run
fastmail auth --headlessfor SSH / no-browser environments - Run
fastmail auth statusto check token validity and authenticated user - Tokens expire after 30 days — re-run
fastmail authto refresh - Run
fastmail auth logoutto clear cached credentials
Fastmail API errors
- Verify FASTMAIL_API_TOKEN is set as a secret
- Check token has correct permissions in Fastmail settings
- Test token manually with curl first
Acknowledgments
This project is based on fastmail-mcp by MadLlama25. The original project provided the foundation for the Fastmail JMAP integration and MCP tool implementations.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi