The server is stateless: every request is authenticated independently, and all tools operate on the mailbox of the user who granted the token.
Tools
Email — read
Message content comes from whoever sent it. Agents are told, in the tool descriptions themselves, to treat it as data to read and report on rather than as instructions to follow.
Email — compose and send
Stage by default, send explicitly. Composition and delivery are separate tools, so an agent that has written a message has not thereby sent one — the default outcome of asking an agent to write mail is a draft in the user’s Drafts folder, indistinguishable from one they typed. That is a posture Langmail can afford precisely because it is the mail client: the confirmation step happens in-product rather than being deferred to somebody else’s UI.
Threading is never assembled by the agent. Pass the id of the message being answered and the recipients, the
Re: subject, and the In-Reply-To/References headers are derived server-side from that message.
Duplicate delivery is guarded three ways, because each closes a window the others leave open: a sent message no longer looks like a draft (server-side, no client cooperation needed); an in-flight send reserves its draft, so a retry that arrives before the first call answers is refused; and an idempotencyKey replays the first answer for a retry that arrives after. Keys are scoped per tool — reusing one across create_draft and send_draft is not a retry. A send that fails without saying whether the mail went out is reported as unknown and refused rather than retried. See send_draft for the full picture.
Drafts carry attachments, named either by the blobId of a file already in the mailbox or as base64 content for one the agent generated. The blobId form is the one to reach for: it re-references a blob the server already holds, so forwarding an attachment transfers nothing and the bytes never pass through the model’s context. There is no path parameter — the MCP server is a remote service and cannot read the agent’s filesystem.
There is no bulk or campaign sending, and no direct send_message — create_draft followed by send_draft expresses the same intent with an inspection point in the middle. Listing drafts needs no dedicated tool either: search_emails with mailbox: "Drafts" does it.
Drafts cannot yet be scheduled for later delivery, and there is no send-and-undo window.
Mail rules — read-write
Rules live as one managed Sieve script in the user’s own account and run server-side on every delivery. Future automation and historical application are distinct: rules affect future mail by default, and backfills over existing mail are explicit, scoped (Inbox or all mail), and confirmation-gated when large.
Contacts — read-write
Most contacts are collected automatically from mail headers and calendar attendees, alongside any address books on the account — so someone who has already written to you is findable without any setup. The write tools cover the people that derivation structurally cannot reach: someone you met in person, spoke to on the phone, or know through a channel Langmail does not see. Every record carries a
source marker (derived, authored, imported) because a confidence rating means different things across them, and collection only ever fills blanks on a record — it never overwrites a field a user typed.
Writes to contacts are durable: the address book syncs to every device connected over CardDAV, so a contact an agent creates appears on the user’s phone. The tool annotations reflect this — create_contact and update_contact are account writes, delete_contact is destructive — so a client can gate on the annotations without hard-coding tool names.
Calendar — read-write
Risk annotations
Every tool carries MCP tool annotations, so a client can decide which calls need a confirmation step without hard-coding tool names — the only version of that check that survives new tools being added. Two hints carry the decision:openWorldHint— the call reaches someone outside the user’s own account: a recipient, an attendee, an iMIP notice.destructiveHint— the call cannot be taken back: it removes something, or it hands something to a third party.
Staging is deliberately distinguishable from sending on the hints alone:
create_draft is an account write, send_draft is outward destructive. update_draft counts as destructive because editing a draft replaces the message — the previous version does not come back, and the draft being edited need not be one the agent wrote.
The mail-rule writers count as outward, not account-local, because a rule may carry a forward action — which redirects a copy of every matching future message to an arbitrary address. A static annotation has to describe what a tool can do, and quietly installing a persistent forwarding rule is the furthest outward reach on this surface.
idempotentHint is false on every write. The composition tools accept an idempotencyKey that does make a retry safe, but the hint describes the tool unconditionally, and a caller that omits the key gets a second send.
Result conventions
- Every tool returns a single plain-text content block, formatted for direct agent consumption — compact and stable, no JSON envelope to parse.
- List results are numbered and include the identifiers (
id,uid,calendarUrl) that downstream tools take. - Search and thread results include snippets but omit full message bodies by design; full bodies come from
get_emailonly. - Failures return a text block with the MCP
isErrorflag and a one-line reason, for exampleEmail not found: M123orCalendar not found: <url>. Where a failure could be mistaken for a partial send, the reason says so explicitly —Nothing was sent. - Attachment bytes never appear in a tool result.
download_attachmentreturns metadata plus a short-lived capability URL underhttps://mcp.langmail.me/attachments/…; the file is fetched out-of-band from that URL (e.g. withcurl -o) straight to disk.
Datetimes
- Email filters (
after,before) take UTC ISO 8601 timestamps ending inZ. - Calendar times (
start,end,until) take ISO 8601 with an offset, so events keep their intended local time —2026-07-14T10:00:00+02:00or2026-07-14T08:00:00Z.