Emailgents MCP server
One remote MCP server over streamable HTTP with OAuth. Nine tools, every answer tagged with what it covers and how fresh it is, and errors an agent can act on.
https://emailgents.com/mcp- Streamable HTTP; no local process.
- OAuth 2.1 with PKCE; the client opens a browser sign-in once.
- Discovery at /.well-known/oauth-protected-resource.
Connecting a client
Pick the client for exact settings and config snippets.
Tools
All tools are read-only except set_flags. There is no tool that sends, replies, moves or deletes.
inbox_overview
Every mailbox with unread counts, newest mail and coverage. The right first call.
- fresh
- boolean, default true. Bounded refresh of mailboxes not synced in the last 45 s; false answers straight from the index.
Returns: Per-account unread totals (marked partial when the index is), newest messages, freshness and refresh outcome.
search_messages
Full-text search and listing across all or selected mailboxes.
- query
- string ≤ 500. Web-search syntax: quotes for phrases, - to exclude. Omit to list newest first.
- account_ids
- uuid[] ≤ 50. Default all.
- folder
- inbox | sent | drafts | archive | trash | junk | all | starred | important | other
- folder_id
- uuid from list_folders.
- from, to
- substring match on sender or To/Cc.
- after, before
- ISO-8601 date or datetime; a date in before means end of that day.
- unread_only, starred_only, has_attachments
- boolean filters.
- limit
- 1–100, default 25.
- cursor
- opaque nextCursor from the previous page, bound to the first page's snapshot.
- order
- date (default) or relevance when a query is given.
- fresh
- boolean, default true.
Returns: total, hasMore, nextCursor, snapshotAt, coverage, refresh, freshness and messages with cleaned snippets.
get_message
Headers, body and attachment metadata for one message.
- message_id
- uuid from search_messages or get_thread.
- format
- text (default) | html | both.
- max_chars
- 500–400000, default 60000.
- body_offset
- continue a truncated body from bodyInfo.nextOffset.
- mark_read
- boolean, default false.
Returns: message, body, bodyInfo (source: cached | fetched | truncated | unavailable, nextOffset), attachments with size, downloadable flag and an authenticated downloadUrl, and a webUrl into Emailgents.
get_thread
The whole conversation, oldest first, with coverage so a partial thread is never mistaken for a complete one.
- message_id
- any message id in the thread.
- include_bodies
- boolean, default false.
- max_chars_per_message
- 500–100000, default 12000.
Returns: threadId, messages, per-message bodyInfo (not_requested | cached | fetched | truncated | unavailable), coverage.
get_attachment
One attachment inline, up to 4 MB.
- message_id
- uuid.
- attachment_id
- from get_message.
Returns: Text for text-like files, base64 resource otherwise. Larger files fail with too_large; use downloadUrl (up to 25 MB, authenticated).
set_flags
The only write. Toggle read and starred on up to 100 messages.
- message_ids
- uuid[] 1–100.
- read
- boolean.
- starred
- boolean.
Returns: Per-message outcome: requested flags, status updated | failed | not_found, and whether the provider confirmed the change.
list_accounts
Connected mailboxes with provider, status, sync window, initial-sync state and coverage.
No parameters.
Returns: accounts[] with id, email, provider, status, lastSyncedAt, coverage (status, pending and excluded folders, remaining).
list_folders
Folders and labels per account with counts and per-folder sync state.
- account_id
- uuid, optional.
Returns: folders[] with role, counts, syncEnabled, lastSyncedAt and whether the initial pass is complete.
sync_account
Force a bounded sync of one mailbox now.
- account_id
- uuid.
Returns: added, updated, removed, duration and whether work remains.
Coverage: what the answer is based on
Every search, overview and thread result carries a coverage object. Agents should read it before telling a user that something does not exist.
- status: complete, complete_within_window or partial.
- complete_within_window means every searched folder is fully indexed inside the mailbox's sync window (90 days by default, configurable per mailbox).
- partial lists reasons: folders still indexing, folders excluded from sync, or an account mid initial sync, with remaining counts where known.
- Zero matches with partial coverage means none in the indexed part, never none in the mailbox.
- Unread totals in inbox_overview are marked partial while the index is incomplete.
Freshness: how new the index is
Mailboxes sync in the background. With fresh=true (the default) a stale mailbox is refreshed within a bounded time before the answer; fresh=false returns immediately.
- refresh reports, per mailbox, whether a refresh was attempted, completed, timed out or left work pending.
- freshness lists each mailbox's lastSyncedAt and status so the agent can say how current the answer is.
- sync_account forces a bounded sync when the user insists on the very latest state.
Pagination
- Pass nextCursor back as cursor with the same filters. Pages are bound to the first page's index snapshot, so new arrivals and background sync cause neither duplicates nor skips.
- Ordering is deterministic: date descending, then message id.
- hasMore tells the agent whether to continue; offset remains for compatibility.
- invalid_cursor is returned when filters changed; repeat the query without a cursor.
Identities and bodies
- Message ids are stable Emailgents uuids scoped to the account; they survive moves between folders and resyncs. Copies of one message in several Gmail labels resolve to one id.
- threadId is an account-scoped conversation id built from provider threading and Message-ID references.
- Bodies are fetched on demand and capped by max_chars; bodyInfo reports source and nextOffset for continuation.
- Attachments: size and downloadable up front; 4 MB inline through get_attachment, 25 MB through the authenticated downloadUrl.
Errors
Failures return isError with a structured error: code, retryable, retry_after, target, next_step and a correlation_id that matches the server log. No credentials or raw provider payloads ever appear in messages.
| code | retryable | next step |
|---|---|---|
| unauthenticated | no | Reconnect this MCP server with the user's Emailgents account. |
| not_found | no | Check the id; get fresh ids from list_accounts or search_messages. |
| forbidden | no | Use an account this connection is allowed to read. |
| invalid_input | no | Fix the listed argument and call again. |
| invalid_cursor | no | Repeat the original query without a cursor. |
| reauth_required | no | Ask the user to reconnect this mailbox in Emailgents. |
| provider_error | yes, after 30 s | Retry later; if it persists, ask the user to check the mailbox. |
| provider_timeout | yes, after 5 s | Retry, or call again with fresh=false for indexed results. |
| too_large | no | Use downloadUrl or ask the user to open the item. |
| server_unavailable | yes, after 5 s | Retry in a few seconds. |
| internal | yes, after 5 s | Retry once; then report the correlation id. |
Provider capabilities
list_accounts reports the effective capabilities of each mailbox so agents do not plan on features a provider lacks.
| Provider | Transport | Threads | Flags written back | Change detection |
|---|---|---|---|---|
| Gmail (OAuth) | Gmail API | Gmail conversations | read, starred | history id |
| Outlook / Microsoft 365 | Microsoft Graph | conversationId | read, flagged | delta queries |
| iCloud, Yahoo, Fastmail, AOL, Zoho, GMX, IMAP | IMAP over TLS | References / In-Reply-To | \Seen, \Flagged | UIDs, CONDSTORE where offered |
Reference questions
Does get_message mark mail as read?
Not unless the agent passes mark_read=true. The default leaves read state untouched.
How does an agent open a message for the user?
get_message returns a webUrl that opens the message in the Emailgents app after the user signs in. There are no public share links.
What is the rate limit?
Searches answer from the index and are cheap. Provider fetches (bodies, attachments, fresh refreshes) are bounded per call and per mailbox; a provider_timeout tells the agent to retry or use fresh=false.
Is there a REST equivalent?
Yes. Every tool has an HTTP route under /api/public/v1 with the same shapes, authenticated with a scoped API key.
Looking for HTTP? REST API reference.