# mailpouch — AI Agent Guide > **Read this before using any tools.** This document is written for AI agents > (Claude, GPT, Gemini, etc.) operating through the mailpouch server. It > covers what each tool does, when to use it, the permission model, limits you > must respect, and how to handle errors correctly. --- ## Getting connected (do this first) If mail tools are failing or you're not sure the server is set up, **call `setup_status`** — it is always available (even before credentials exist or you're approved) and returns the single next action. Its `state`: | `state` | What it means | What to do | |---|---|---| | `unconfigured` | No credentials on the machine | Ask the user to run `npx -y mailpouch setup --username --password-stdin` (the Proton **Bridge** password, not the login password) or `npx -y mailpouch-settings`. You cannot do this for them. | | `bridge-unreachable` | Proton Bridge isn't running | Ask the user to start the Proton Bridge app, signed in (IMAP `127.0.0.1:1143`, SMTP `127.0.0.1:1025`). | | `pending-approval` | **Expected on first connect** — you're registered but not yet approved | Ask the user to open `http://localhost:8766/#/agents` and click Approve, then retry. This is not an error; you cannot approve yourself. | | `ready` | Good to go | Call `get_connection_status` to confirm live auth, then use the tools below. | The MCP client config that launches this server (for the user's reference): ```json { "mcpServers": { "mailpouch": { "command": "npx", "args": ["-y", "mailpouch"] } } } ``` --- ## Quick orientation You have access to a user's Proton Mail inbox via Proton Bridge (a local desktop app that decrypts their end-to-end encrypted email). The MCP server runs on the user's machine and connects to Bridge locally — but when you read emails through this server, the content is sent to your provider's API (e.g. Anthropic) for processing. Your access is **gated by a permission preset** set by the human. If a tool call is blocked, it means the human has not granted that level of access. You can ask them to change it in the settings UI, or you can use `request_permission_escalation` to request a temporary upgrade (they must approve it). **You authenticate as your own client.** Over stdio (Claude Desktop and similar) the host spawns the server for you. Over HTTP every caller authenticates with OAuth — there is no shared bearer token. Interactive hosts self-register and the human Approves you in the Agents tab; until then your tool calls are denied even though your token is valid. If you are a **headless agent** (cron, CI, scheduled), the operator issues you a *service account* out-of-band and you log in with the OAuth `client_credentials` grant (your own `client_id` + `client_secret`) — that credential is pre-approved, so no interactive consent is needed. Either way you carry a distinct identity and your calls are individually gated and audited. **Never assume you have broad access.** Always start with read-only tools to understand context before attempting any action that modifies email state. --- ## Permission presets | Preset | What you can do | |---|---| | `read_only` | Reading unlimited; all writes blocked | | `send_only` | Reading unlimited; send/forward/schedule 50/hr, `remind_if_no_reply` 100/hr; actions, deletion, folder writes, and bulk ops disabled | | `supervised` | All tools enabled; reading unlimited; sending 200/hr, schedule 100/hr, bulk actions 100/hr, deletion 20/hr, folder delete 20/hr, server lifecycle 5/hr | | `full` | All tools, no rate limits | The current preset is enforced server-side — you cannot bypass it. If a tool returns `"Blocked: ..."`, the human needs to change the preset in the settings UI (`http://localhost:8766`) or approve an escalation request. --- ## Tool reference ### Reading — always available #### `get_emails` Fetch a page of emails from a folder. ``` folder string Folder path. Default: "INBOX". Examples: "INBOX", "Sent", "Trash", "Folders/Work", "Labels/urgent" limit number Emails per page. 1–200, default 50. cursor string Pass nextCursor from a previous response to get the next page. Omit for the first page. ``` Returns `{ emails: [...], count, folder, nextCursor? }`. `nextCursor` is absent when there are no more pages. Email objects include `id`, `from`, `subject`, `date`, `isRead`, `isStarred`, `hasAttachment`, `bodyPreview` (first ~300 chars), `isAnswered` (has been replied to), and `isForwarded` (has been forwarded). **Use `get_unread_count` first** to decide whether it's worth fetching at all. #### `get_email_by_id` Fetch a single email's full content including the complete body. ``` emailId string IMAP UID from get_emails or search_emails. ``` Returns the full email including `body`, `isHtml`, `cc`, `isAnswered` (has been replied to), `isForwarded` (has been forwarded), and attachment metadata (filenames, MIME types, sizes — not binary content). #### `search_emails` Search within one or more folders. ``` folder string Default "INBOX". Ignored when `folders` is set. folders string[] Search multiple folders. Use ["*"] to search all folders (capped at 20). from string Sender address or name fragment to string Recipient (To field) bcc string BCC recipient subject string Subject text fragment body string Search within email body content text string Full-text search across headers and body hasAttachment boolean Filters locally (not server-side IMAP SEARCH) isRead boolean isStarred boolean answered boolean Filter by whether email has been replied to isDraft boolean Filter by draft status dateFrom string ISO 8601 start date (INTERNALDATE — when received by server) dateTo string ISO 8601 end date (INTERNALDATE — when received by server) sentBefore string ISO 8601 datetime — filter by Date: header (when message was sent) sentSince string ISO 8601 datetime — filter by Date: header (when message was sent) larger number Minimum email size in bytes smaller number Maximum email size in bytes limit number 1–200, default 50 ``` All fields are optional. Most searches run server-side via IMAP SEARCH; `hasAttachment` filters locally after fetching. Use `dateFrom`/`dateTo` for received date, `sentBefore`/`sentSince` for the message's `Date:` header (sent date). **Multi-folder example:** Pass `folders: ["INBOX", "Sent"]` to find a message without knowing which folder it's in. Pass `folders: ["*"]` to search everywhere (first 20 folders, sorted alphabetically). #### `get_unread_count` Cheap call — returns unread counts per folder without fetching email bodies. Use this before `get_emails` to check if a folder has anything new. Returns `{ unreadByFolder: { "INBOX": 3, "Sent": 0, ... }, totalUnread: 3 }`. #### `list_labels` List all Proton Mail labels (folders with `Labels/` prefix) with message counts. Returns `{ labels: [...], count }`. #### `get_emails_by_label` Fetch emails from a specific label folder with cursor pagination. ``` label string Label name without prefix (e.g. Work) limit number 1–200, default 50 cursor string Opaque cursor from previous response ``` Returns `{ emails, folder, count, nextCursor? }`. #### `get_folders` List all folders with `name`, `path`, `totalMessages`, `unreadMessages`. Labels appear as folders with a `Labels/` prefix (e.g. `Labels/Work`). #### `download_attachment` Download the binary content of an email attachment as a base64-encoded string. ``` email_id string UID from get_emails or search_emails. attachment_index number Zero-based index into the email's attachments array. ``` Returns `{ filename, contentType, size, content (base64), encoding: "base64" }`, or `null` if the email or attachment is not found. **Note:** Email must be in the cache; call `get_email_by_id` first if needed. #### `get_thread` Fetch all messages in an email thread, sorted chronologically. ``` emailId string UID of any message in the thread. ``` Returns `{ emails: [...], count }`. Use this instead of manually chaining `get_email_by_id` calls when you need a full conversation view. #### `get_correspondence_profile` Build a profile of your email relationship with a specific contact. ``` email string The contact's email address. ``` Returns sent/received counts, first and last interaction dates, average response time, and recent subject lines. Useful for "how well do I know this person" context before composing a reply. #### `fts_search` Full-text search over a local SQLite FTS5 index. Much faster than IMAP search for repeat queries; search terms never leave your machine. ``` query string Required. Supports phrase ("exact phrase"), boolean (AND/OR/NOT), prefix (word*), and column filters (subject:invoice from:alice). limit number 1–200, default 20. ``` Returns `{ results: [...], count }`. Requires `better-sqlite3` installed and the index populated via `fts_rebuild`. #### `fts_rebuild` Rebuild the local FTS5 index from the current email cache. Run once after initial setup, then periodically to keep the index fresh. Returns `{ success, indexed, duration }`. #### `fts_status` Check whether the FTS index is available and how many documents it contains. Returns `{ available, documentCount, lastBuilt? }`. #### `extract_action_items` Extract action items, to-dos, and deadlines from an email. ``` emailId string UID of the email to analyse. ``` Returns a structured list of action items with optional due dates and assignees inferred from the email content. #### `extract_meeting` Extract meeting details (time, location, participants, agenda) from an email. ``` emailId string UID of the email to analyse. ``` Returns structured meeting metadata. Useful for "add this to my calendar" workflows. --- ### Analytics — always available #### `get_email_stats` Fast summary: total emails in cache, unread count, starred count, sent count, last sync time. Use for a quick overview without heavy computation. #### `get_email_analytics` Comprehensive analytics over cached emails: - Top senders (by received count) - Top recipients (by sent count) - Response time distribution - Email volume by period **Requires populated cache.** Call `sync_emails` first if the cache is cold. #### `get_contacts` Contact frequency table: who you receive from most often and who you send to most often. Useful for "who do I email most" queries. #### `get_volume_trends` Volume broken down by day of week and hour of day. Useful for "when am I busiest" analysis. --- ### System — always available #### `get_connection_status` Check whether SMTP and IMAP are reachable. Returns connection health, config file path, settings UI URL, and current permission preset. **Call this first if you're unsure whether the server is configured.** #### `sync_emails` Refresh the email cache from IMAP. ``` limit number Max emails to fetch per folder. Default 100, max 500. ``` Call this before analytics or search queries to ensure freshness. Returns `{ success, synced, duration }`. #### `get_logs` Fetch recent server log entries for debugging connection or configuration issues. Returns the last N log lines with timestamp, level, and message. #### `clear_cache` Clear the in-memory email and analytics cache. Useful if you suspect stale data. The next `sync_emails` will rebuild from scratch. #### `get_server_version` Return the running mailpouch server version. Use to confirm which version is active. No input args. Returns `{ version: string }`. --- ### Sending — requires `supervised`, `send_only`, or `full` #### `send_email` Send a new email. ``` to string Required. Recipient(s), comma-separated. subject string Required. body string Required. cc string Optional comma-separated CCs. bcc string Optional comma-separated BCCs. isHtml boolean Set true if body contains HTML. Default false. priority string "high" | "normal" | "low" replyTo string Reply-to address. attachments array [{filename, content (base64 string), contentType}] ``` **Limits enforced server-side:** - Max 50 recipients (To + CC + BCC combined) - Max 20 attachments - Max 25 MB per attachment, 25 MB total - Email addresses validated: max 320 chars total, local part ≤ 64, domain ≤ 253 Returns `{ success, messageId? }`. #### `reply_to_email` Reply to an existing email. The server fetches the original to set correct `In-Reply-To`, `References`, and a `Re:`-prefixed subject automatically. ``` emailId string UID of the email to reply to. body string Your reply body. isHtml boolean Default false. replyAll boolean Include original CC recipients. Default false. ``` #### `forward_email` Forward an existing email to a new recipient. Prepends an optional message and sends with `Fwd:`-prefixed subject. ``` emailId string UID of the email to forward. to string Recipient address(es), comma-separated. message string Optional message to prepend before forwarded content. ``` #### `send_test_email` Send a test email to verify SMTP is working. Returns `{ success, messageId }`. --- ### Drafts & Scheduling — requires `supervised`, `send_only`, or `full` #### `save_draft` Save an email as a draft without sending it. Writes to the Drafts folder via IMAP APPEND. ``` to string Optional recipient(s), comma-separated. cc string Optional. bcc string Optional. subject string Optional. body string Optional. isHtml boolean Default false. attachments array Same format as send_email. inReplyTo string Optional Message-ID to thread the draft. references string[] Optional thread reference IDs. ``` Returns `{ success: true, uid: }` or `{ success: false, error: "..." }`. All fields are optional — a draft can be completely empty. #### `schedule_email` Queue an email for delivery at a future time. ``` to string Required. Recipient(s), comma-separated. subject string Required. body string Required. send_at string Required. ISO 8601 datetime. Must be 60 s–30 days in the future. cc string Optional. bcc string Optional. isHtml boolean Default false. ``` Returns `{ success: true, id: "" }`. The ID can be used with `cancel_scheduled_email`. Scheduled emails survive server restarts (persisted to disk). The server polls every 60 s to send due emails. #### `list_scheduled_emails` List all scheduled emails (pending, sent, failed, and cancelled), sorted by scheduled time ascending. Returns `{ emails: [...], count }`. #### `list_proton_scheduled` List emails natively scheduled via the Proton Mail web or mobile app. Reads the "All Scheduled" IMAP folder exposed by Proton Bridge. This is separate from emails queued via `schedule_email`. Returns `{ emails, count, folder, note? }`. #### `cancel_scheduled_email` Cancel a pending scheduled email before it is sent. ``` id string The UUID returned by schedule_email. ``` Returns `{ success: true }` or `{ success: false, error: "..." }` (e.g. if the email was already sent or the ID is not found). #### `remind_if_no_reply` Queue a follow-up reminder that fires if no reply arrives within N days. ``` emailId string UID of the sent email to watch. withinDays number Days to wait for a reply before triggering. Min 1, max 30. reminderNote string Optional note to surface in the reminder notification. ``` Returns `{ success: true, reminderId: "" }`. Reminders persist across server restarts (JSONL store). Only fires if no reply is detected in the inbox. #### `list_pending_reminders` List all active (not yet fired or cancelled) follow-up reminders. Returns `{ reminders: [...], count }`. #### `cancel_reminder` Cancel a pending follow-up reminder before it fires. ``` reminderId string UUID from remind_if_no_reply. ``` #### `check_reminders` Manually trigger the reminder check cycle (normally runs automatically every 60 s). Returns `{ checked, fired, count }`. --- ### Actions — requires `supervised` or `full` #### `mark_email_read` ``` emailId string UID. isRead boolean Default true. ``` #### `star_email` ``` emailId string UID. isStarred boolean Default true. ``` #### `move_email` Move a single email to a folder. ``` emailId string targetFolder string Full IMAP path, e.g. "Archive", "Folders/Work" ``` #### `archive_email` Move an email to the Archive folder. ``` emailId string ``` #### `move_to_trash` Move an email to the Trash folder. ``` emailId string ``` #### `move_to_spam` Move an email to the Spam folder. ``` emailId string ``` #### `move_to_folder` Move an email to a custom folder (`Folders/`). ``` emailId string folder string Folder name without prefix (e.g. Work). Moves to Folders/Work. ``` #### `move_to_label` Apply a Proton Mail label to an email. The label path is constructed as `Labels/