A side-by-side look at the Imap MCP Server and Outlook Assistant MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Imap MCP Server for communication work as a local process; choose Outlook Assistant for communication as a local process. You can also run both side by side in one mcpServers block.
imap_add_account, imap_update_account, imap_list_accounts.auth, list-events, create-event.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Communication | Communication |
| Quality signal | 68/100 · Great | 68/100 · Great |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | Not declared |
| Pricing | Not declared | Not declared |
| Required env vars | None listed | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 111 | 39 |
| Directory activity | 1 views 0 installs 0 upvotes | 1 views 0 installs 0 upvotes |
| Tools | 40 | 22 |
| Full listing | View Imap MCP Server | View Outlook Assistant |
imap_add_accountAdd a new IMAP account configurationimap_update_accountUpdate an existing IMAP account. Useful for fixing SMTP settings without removing and re-adding the account.imap_list_accountsList all configured IMAP accountsimap_remove_accountRemove an IMAP account configurationimap_connectConnect to an IMAP accountimap_disconnectDisconnect from an IMAP accountimap_test_accountTest an existing account connection without re-entering credentials. Validates IMAP connectivity and returns folder count and message count.imap_search_emailsNote: on some servers a 'flagged' or starred message carries a custom keyword (e.g. an Open-Xchange color label or Apple's $MailFlagBit*) instead of, or in addition to, the \Flagged system flag. After any flagged search, inspect each result's customKeywords field before concluding a message is or isn't flagged — do not rely on the flagged filter alone. Search for emails matching criteria (sender, recipient, subject, body text, date range, read/flagged status). Use this to FIND messages when you know something about them but not their UID — e.g. "emails from amazon last week", "unread invoices". By default searches a single folder (INBOX). Set searchAllFolders=true to scan every mailbox at once — this catches messages filed away by rules (e.g. a receipt routed to a custom folder); Trash/Spam/Drafts are skipped unless you opt in. By default returns lightweight headers (uid, from, subject, date, and folder when searching across folders); set `includeBody=true` to also return the parsed body in one round-trip instead of paying the N+1 cost of calling imap_get_email per match. For the newest messages without criteria, prefer imap_get_latest_emails.imap_get_emailRead the FULL content of a single email by its UID (body, sender/recipients, date, attachment list, optional raw headers and text-attachment previews). By default the body is returned as clean Markdown in markdownContent and raw HTML is omitted so it never crosses the boundary; set bodyFormat to "html" for the legacy raw htmlContent, or "text" for plain text only. Use after imap_search_emails or imap_get_latest_emails gives you a uid. Body text is truncated to maxContentLength to protect the context window — raise it for long messages. To fetch attachment bytes, use imap_download_attachment.imap_upload_fileUpload a file to the server for use as an email attachment. Returns a path that can be used with imap_send_email attachments. This allows sending large attachments without hitting context window limits. Max size: 26214400 bytes (configurable via IMAP_MAX_UPLOAD_SIZE). Uploads are auto-deleted after 86400000 ms (configurable via IMAP_UPLOAD_TTL_MS).imap_download_attachmentDownload a single attachment from an email (folder + uid + attachment filename/contentId, as listed by imap_get_email). Images are returned inline for viewing; PDFs are saved and their text is extracted inline (extractText); other files are saved to the shared downloads directory (or savePath). Use when the user wants the actual file contents, not just the message body.imap_mark_as_readMark one or many emails as read. Accepts a single UID or an array — pass an array to flag N messages in one IMAP STORE round-trip (useful when triaging).authManage authentication with the Microsoft Graph API. action=`status` (default) returns the current auth state, refreshing an expired access token while the refresh token is still valid (~90 days); call it first to check before other tools. action=`authenticate` starts sign-in: `method: "device-code"` (default, works headlessly) returns a code + URL for the user to visit; `method: "browser"` uses the local auth server on :3333 (run `npm run auth-server` first). `force: true` re-authenticates over an existing valid session. If sign-in reports that OUTLOOK_CLIENT_ID is not configured, ask the user for their Azure Application (client) ID and pass it as `clientId`. action=`device-code-complete` finishes device-code sign-in once the browser shows it succeeded. action=`about` returns server version, configured audience, scope list and other diagnostics. Tokens persist to `~/.outlook-assistant-tokens.json` and survive server restarts.list-eventsList calendar events for the signed-in user (read-only). By default returns upcoming events (start ≥ now). Optional `startAfter`, `startBefore` and `subject` filters find past, current or specifically-named events; supplying any of them replaces the default "now" lower bound and the filters are AND-ed together. Results are oldest first, except when the search only looks backwards (`startBefore` without `startAfter`, or `subject` alone), where they are newest first. Each event shows its subject, location, start/end, a body preview and its id. Use `count` (default 10, max 100) to control page size. Each start/end is returned as a canonical UTC ISO-8601 instant (e.g. `2026-04-02T22:00:00.000Z`) followed by a labelled local rendering in the configured display timezone (default Australia/Melbourne; override with `OUTLOOK_DEFAULT_TIMEZONE`) — the UTC value is authoritative, so consumers never have to guess the zone.create-eventCreate a new calendar event on the signed-in user's default calendar. Returns the created event with its `id`, `webLink`, and (if attendees are present) an auto-generated online-meeting URL — attendees receive invitations on save, so pass `dryRun: true` first to preview who would be invited (and how many are external) without creating anything. When `OUTLOOK_ALLOWED_RECIPIENTS` is set, every attendee must be allowed or nothing is created. Times use the configured timezone (default Australia/Melbourne; override with `OUTLOOK_DEFAULT_TIMEZONE`); omit the `Z` suffix to send local time. Use `manage-event` action=`update` to modify an event after creation, or `manage-event` action=`cancel`/`delete` to remove it. Real creates count against the session limit (`OUTLOOK_MAX_CREATE_EVENT_PER_SESSION`, else `OUTLOOK_MAX_EMAILS_PER_SESSION`); 0 refuses every create.manage-eventManage an existing calendar event. `dryRun: true` previews any action without changing or sending anything: who would be emailed, with an external count (update also: who is added or removed, and the PATCH body). action=`update` edits fields via PATCH (subject, start, end, attendees, body, location, isOnlineMeeting, sensitivity, showAs, importance, categories, reminderMinutesBeforeStart); only fields you pass change. action=`decline` declines an invitation (optional `comment`; `sendResponse: false` declines without notifying the organiser). action=`cancel` cancels an event you organised and emails attendees. action=`delete` removes the event from your calendar (Graph documents no guaranteed recovery); deleting a meeting you organised that has attendees still emails them a cancellation, so use `cancel` with a `comment` to control that message. Returns the updated event on update; a confirmation otherwise. There is no `accept` action: accept invitations in the Outlook UI, as Graph's accept verb is unreliable.search-emailsSearch, list, delta-sync or thread-group emails (read-only); parameters set the mode. No params: recent emails in `folder` (default `inbox`). `query`/`from`/`to`/`subject`/date filters: search, combined as an OData filter. `searchExpression` (deprecated alias `kqlQuery`): a raw Graph `$search` expression. `deltaMode: true`: current state plus a `deltaToken` to pass back next time for changes only. `groupByConversation: true`: conversation threads. `conversationId`: every message in one thread. `internetMessageId`: the message with that RFC Message-ID. `sharedMailbox` (alias `email`) searches a shared/delegated mailbox, custom folders and nested paths included. Personal Outlook.com accounts have limited `$search`, so the tool falls back to OData filters and a recent listing automatically; structured filters (`from`/`subject`/`receivedAfter`/`hasAttachments`/`unreadOnly`) give cleaner results there. Returns up to `count` messages (id/subject/from/receivedDateTime/preview); `outputVerbosity` expands them.read-emailRead a single email by id (read-only). Returns subject, from/to/cc, date and the body as Markdown (HTML stripped to text): up to 2,000 characters by default, up to 40,000 with `outputVerbosity: full`; a cut body ends with a note on how to get the rest. With `headersMode: true`: returns RFC-822 forensic headers in place of the body (DKIM, SPF, DMARC, Received chain, Message-ID, Authentication-Results) — `importantOnly: true` for the security-relevant subset, `groupByType: true` for a category-bucketed view, `raw: true` for JSON. With `includeHeaders: true` (non-headers-mode): adds basic headers alongside the body. **If the id came from a shared/delegated mailbox (e.g. via `search-emails` or `access-shared-mailbox` with `sharedMailbox` set), you MUST pass the same `sharedMailbox` (or alias `email`) here** — message IDs are mailbox-scoped, and reading a shared-mailbox id without it fails with 404 ErrorInvalidMailboxItemId.send-emailCompose and send an email immediately (destructive: sends external comms). Returns a confirmation. Safety controls: `dryRun: true` returns the composed message for review without sending; `checkRecipients: true` runs `get-mail-tips` first and returns its warnings. If the tips show an out-of-office reply, a full mailbox, a delivery restriction or external recipients, the send is refused until repeated with `acknowledgeWarnings: true`. Personal Outlook.com accounts return no tips, and no warnings is not proof of delivery. Subject to the session limit (`OUTLOOK_MAX_SEND_EMAIL_PER_SESSION`, else `OUTLOOK_MAX_EMAILS_PER_SESSION`; 0 refuses every send) and recipient allowlist (`OUTLOOK_ALLOWED_RECIPIENTS`) when configured; both refuse before any Graph request. For a review-before-send workflow, use `draft` (action=`create` → `update` → `send`); a draft can be checked in Outlook before it goes. Comma-separated recipient strings or arrays both accepted.draftDraft lifecycle for review-before-send workflows (destructive: covers `send` and `delete`). action=`create` saves a new draft and returns its id (`dryRun: true` previews without saving; `checkRecipients: true` runs mail-tips first). action=`update` patches a draft by `id` (only fields passed change). action=`send` sends a draft and counts against the `send-email` session limit (0 refuses every send). action=`delete` moves a draft to Recoverable Items (restorable for a limited time). update/send/delete refuse any `id` that is not an unsent draft. action=`reply`/`reply-all` creates a reply draft from a message `id` (`comment` prepends text; not with `body`). action=`forward` creates a forward draft (needs `id` and `to`). The recipient allowlist (OUTLOOK_ALLOWED_RECIPIENTS) applies to create/update/forward, to the draft's current to/cc/bcc on send, and to reply/reply-all, whose draft is deleted if a recipient is not allowed. Returns the draft on create/update/reply/forward; a status on send/delete.update-emailUpdate message state without modifying content (idempotent — safe to retry). action=`mark-read`/`mark-unread` sets `isRead` on a single message by `id`. action=`flag` sets a follow-up flag with optional `dueDateTime`/`startDateTime`: ISO 8601 with a time, kept as that exact instant when it has `Z` or a ±hh:mm offset and read in OUTLOOK_DEFAULT_TIMEZONE when it has none; date-only or unparseable values are refused before any change. With only `dueDateTime`, the start is 09:00 on the due date, or the due time if earlier. action=`unflag` clears the flag; action=`complete` marks it done. Flag/unflag/complete take `id` (single) or `ids` (batch, updated one at a time: one PATCH each, not Graph `$batch`). `sharedMailbox` (alias `email`) updates messages in a shared/delegated mailbox (default: the signed-in account; needs Mail.ReadWrite.Shared and delegate access). Returns a status per message.attachmentsInspect or retrieve email attachments. action=`list` (default) returns metadata for all attachments on `messageId` (id, name, contentType, size, isInline) — read-only. action=`view` returns inline content for text/JSON/XML attachments via `attachmentId`; binary types require download. action=`download` saves the attachment under a new, unique name in `outputDir` (default system temp directory, auto-created; must be inside the temp directory, ~/Downloads, ~/Documents or OUTLOOK_EXPORT_DIR) and returns the saved file path. `messageId` is required for all actions; `attachmentId` is required for view/download. If `messageId` came from a shared/delegated mailbox, pass the same `sharedMailbox` (or alias `email`) — attachment IDs are scoped to the message and fail under /me otherwise. Use `outputVerbosity` to control list field count.exportExport emails to files. target=`message` (default) exports one email by `id` (mime/eml/markdown/json/csv) to `savePath`: a directory (or a path ending in `/`, created if missing) gets a new, unique file name; a file path is created new and an existing file is replaced only with `overwrite: true`. target=`messages` batch-exports `emailIds`, or matches for `searchQuery`/`query`, into `outputDir`, at most 100 messages per call. target=`conversation` exports a thread (up to 1000 messages) by `conversationId` into `outputDir` (eml/mbox/markdown/json/html/csv; `order: "reverse"` for newest first). target=`mime` returns raw RFC-822 MIME for `id` (`headersOnly`, `base64`, `maxSize`, default 1MB). Files are written only inside the system temp directory (the default), ~/Downloads, ~/Documents or OUTLOOK_EXPORT_DIR, never to dot-prefixed names. Pass `sharedMailbox` (alias `email`) when the ids come from a shared mailbox. `includeAttachments` defaults to true for one message, false for batch.get-mail-tipsPre-send recipient validation via Graph `POST /me/getMailTips` (read-only; uses the existing `Mail.Read` scope — no extra permissions). Returns per-recipient tips covering automatic replies (out-of-office), mailbox full status, custom admin mail tips, delivery restrictions, moderation requirements, external-vs-internal scope, max message size, and group member counts (total + external). Use ahead of `send-email` or `draft` action=`create` to catch issues like OOO replies or external-recipient warnings before the message goes out; `send-email`/`draft` accept `checkRecipients: true` to invoke this automatically. Accepts either a comma-separated string or an array of addresses; `tipTypes` filters which tips are requested (defaults to all).Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"imap-mcp-server": {
"command": "npx",
"args": [
"-y",
"imap-mcp-server"
]
}
}
}
{
"mcpServers": {
"littlebearapps-outlook-assistant": {
"command": "npx",
"args": [
"-y",
"@littlebearapps/outlook-assistant"
]
}
}
}