Email (IMAP)
An email connection gives an AI client read access to one mailbox over IMAP: it can list folders, list and search emails, read an email's text, and fetch its attachments. When a token allows it, the client can also save a plain-text draft in the mailbox's Drafts folder for you to send yourself. It cannot send, delete, move, or mark mail.
Email needs no extra service: Postgres and the web app are enough.
Add the connection
Choose Email (IMAP) under Add a connection and fill in the server, port, security, username, and password. Presets fill in the server for Gmail, iCloud, Fastmail, Zoho, and Yahoo. Automata logs in with the credentials before saving anything, so a wrong password fails the create instead of leaving a connection that never syncs.
- Use an app password. Gmail (with 2-Step Verification on), iCloud, Fastmail, Yahoo, and Zoho issue passwords meant for exactly this, and you can revoke one without changing your main password.
- Microsoft 365 and Outlook.com cannot be connected. Microsoft no longer accepts passwords over IMAP and requires OAuth, which this connection does not support.
- There is no unencrypted option. Choose TLS (usually port
993) or STARTTLS (usually143). STARTTLS is enforced: a server that does not upgrade the connection is refused rather than sent the password in clear. Certificates are verified against the host name.
Change credentials on the connection's page replaces them, checked the same way. Every connector token keeps working. If the new login is a different mailbox, everything synced from the old one is deleted first.
What is stored
Only headers are synced. Bodies and attachments stay on your mail server and are fetched when a tool asks for them.
| Data | Where it comes from | Stored by Automata |
|---|---|---|
| Folders, sender, recipients, subject, date, read and flagged state, threading ids | Synced every two minutes | Yes, about 1 KB per email |
| Attachment names, types, and sizes | Synced from the message structure | Yes |
| Email body | Fetched when read-email asks | No |
| Attachment content | Fetched when get-email-attachment asks | No, neither in the database nor on disk |
| Searching inside email text | Passed to the mail server's own search | No |
Storage stays small however much mail you have. Folders are opened read-only and fetched with BODY.PEEK, so reading an email does not mark it read in your mail client.
Synced headers live in the app database (app.mail_folders, app.mail_messages, app.mail_sync_state) and are deleted with the connection. Like every connection credential, the mailbox password is stored in clear in the app database and its backups.
Sync behavior
- Every folder except All Mail, Junk, and Trash is synced. Gmail's All Mail would hold every message a second time. Skipped folders still appear in
list-mail-folderswithsynced: false, as do folders that cannot hold mail, such as a parentArchiveaboveArchive/2024. - New mail arrives within two minutes, along with changes to read and flagged state. On servers with CONDSTORE the server reports what changed; on others the newest 500 emails per folder are re-checked each run.
- Deleted mail is removed at least every ten minutes.
- Older mail is backfilled newest first, 500 emails per folder per run, up to
NUXT_IMAP_BACKFILL_DAYS(default 365) back andNUXT_IMAP_BACKFILL_MAX(default 50,000) emails per folder. A folder that stopped at a limit says so, and the tools tell the client that reaching the end of a listing means "oldest synced email", not "oldest email". - A folder the server renumbers (its UIDVALIDITY changes, as when a folder is deleted and recreated) is synced again from scratch. An email id issued before that asks the client to list the folder again instead of returning a different message.
- One email in several folders, as with Gmail labels, is listed once with every folder it is in.
- An email whose headers cannot be stored, because of malformed text the database refuses, is listed by date with no subject, sender, or attachments instead of stalling the rest of the folder. Its body is still readable.
The dashboard shows when the sync last succeeded, how far each folder has got, and why it is failing if it is. A refused password stops the sync until the credentials are changed or Sync now is pressed, since retrying a refused login every two minutes is how providers lock accounts. Listings say when the sync is behind, so a client does not present a stale inbox as current.
The mail server's replies are size-capped, and connecting plus logging in must finish within 60 seconds. A server that exceeds either fails that run without affecting other connections.
The sync runs in the web process and is safe with several replicas: each mailbox is claimed with a lease in the database, so only one replica syncs it at a time. A process that stops mid-sync holds the lease for up to ten minutes, during which Sync now reports that a sync is already running.
Tools and limits
| Tool | What it does |
|---|---|
get-mailbox-status | Which mailbox, whether the sync works, when it last ran, and which folders are still backfilling or stopped at a limit |
list-mail-folders | Folders in scope, INBOX first, with synced and unread counts |
list-emails | One page of headers, newest first, from one folder or every synced folder in scope |
search-emails | from, to, and subject against synced headers, and text through the mail server's search, combined with AND |
read-email | Headers, the body as plain text, and the attachment list |
get-email-attachment | One attachment: images as images, text files as text, anything else as an embedded file |
create-email-draft | Write. Saves an unsent plain-text draft in the Drafts folder |
Parameters for each tool are in the MCP tools reference.
- Body search depends on your server. Most IMAP servers match substrings; Gmail matches whole words with its own search syntax. A search covers at most 20 folders within 25 seconds, and any folder it did not reach is named in the result.
- Bodies are fetched up to 2 MiB. A longer body is cut there and
read-emailsays so. - Text attachments are clipped at 100,000 characters, and the result says when one was.
- Attachments are capped at
NUXT_IMAP_ATTACHMENT_MAX_BYTES(default 10 MiB). A larger file is refused with its size before anything is downloaded, and the cap is enforced again on the bytes received. Only parts the synced message lists as attachments can be fetched.
Drafts
create-email-draft writes one message into the mailbox's Drafts folder, flagged as a draft and as read, and does nothing else. It cannot send mail: the connection has no SMTP credentials. The draft waits in your mail client until you send, edit, or delete it.
- Off by default. A new email token starts with the six read tools allowed and
create-email-draftnot. - The folder is not the client's choice. It is the folder your server marks as Drafts, or failing that the one folder named "Drafts". A mailbox with neither, or with more than one, is refused rather than guessed at.
- Plain text only, no attachments. The sender is the login address, so a connection whose username is not an email address cannot save drafts.
- Replies are threaded. Given the id of the email being answered, the draft carries its
In-Reply-ToandReferencesheaders, the subject defaults toRe: …, and the recipient defaults to the original sender (or its recipients, when you sent the original). The original text is not quoted unless the client includes it. - The draft appears in
list-emailsafter the next sync; saving one starts a sync straight away.
Folder scope
An email token is scoped by folder, the same way WhatsApp and Telegram tokens are scoped by chat. A token limited to Clients lists, searches, and reads only emails filed there: an email that is also in INBOX is reachable through its Clients copy only, and asking for a folder outside scope is refused by name. Saving drafts needs the Drafts folder in the allowlist. See Permissions for the rest of a token's scope.
Private network targets
The mail server is an address a user chose, so it passes the same outbound guard as a PostgreSQL connection string. Private and loopback addresses are refused unless NUXT_ALLOW_PRIVATE_TARGETS=true, this deployment's own backends are always refused, and the address approved is the one dialled. A cached connection's host is re-checked every minute.
Settings
All three are optional and set on the web service.
| Variable | Default | Purpose |
|---|---|---|
NUXT_IMAP_BACKFILL_DAYS | 365 | How far back the header sync reaches, per folder |
NUXT_IMAP_BACKFILL_MAX | 50000 | Most emails synced per folder |
NUXT_IMAP_ATTACHMENT_MAX_BYTES | 10485760 | Largest attachment get-email-attachment fetches |