bes-mcp
Provides tools to query and search a local SQLite database of archived email messages, including full-text search, message retrieval, threading, attachments, and contact history.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@bes-mcpsearch my mail archive for the PEN-100 quote thread from alice@example.com"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mailattic
mailattic turns years of Outlook PST archives plus an incremental IMAP sync into one local SQLite database with full-text search. AI agents query it through a CLI, a Python API, or an MCP server.
It was built for one long-running mailbox on a self-hosted mail server that only keeps recent mail.
Quick start
git clone https://github.com/sean3808/mailattic.git
cd mailattic
uv sync
uv run bes init-db --database .\mailattic.db
uv run bes import --database .\mailattic.db --maildir C:\path\to\maildir-account --account-email you@example.com
uv run bes search --database .\mailattic.db --keyword '"PEN-100" AND quote' --sender alice@example.com --limit 10 --json
uv run bes thread 1 --database .\mailattic.db --format jsonYou need Windows 11, Python 3.13, and uv. The import above reads a maildir and does not use Outlook. For PST import, read Requirements first.
Use bes from any directory
Agents in other projects call a global bes. Install it from the clone, then point it at your database:
pwsh -ExecutionPolicy Bypass -File scripts\reinstall-global.ps1
setx MAILATTIC_DB C:\path\to\mailattic.dbThe global bes is a snapshot of the build, so run the script again after you pull new code. Removal and the database path order are in docs/global-install-and-release.md. To register the MCP server with Claude Desktop, see docs/cowork-mcp-bridge-validation.md (written in Traditional Chinese).
Related MCP server: work-assistant
Why query an archive
Most email tools for AI agents are live proxies. The agent asks, the server connects to IMAP or Gmail, and the answer can only come from mail the server still holds. That fails for a quote from five years ago, for a mailbox that was only ever downloaded over POP, and for old mail on a server with a retention limit. The only copy of that mail is an archive on someone's disk, and that archive is what mailattic queries.
Three interfaces
Interface | For | Notes |
CLI | Coding agents and scripts on the same machine |
|
Python API | In-process callers |
|
MCP server | Sandboxed assistants that cannot run a host CLI | Six read tools, plus attachment export, draft reply, and send |
All three run the same query code, so a date filter or a keyword means the same thing in each.
from pathlib import Path
from mailattic.query import SearchFilters, get_message, search_messages
results = search_messages(
database_path=Path("mailattic.db"),
filters=SearchFilters(keyword="contract", sender="alice@example.com"),
limit=20,
)
message = get_message(Path("mailattic.db"), results[0]["id"])Design choices
Keyword search (SQLite FTS5)
Business mail is searched by things that must match exactly: part numbers, order numbers, names, addresses, dates. A hit is a message that contains the words you searched for, so results are easy to check. The cost is that a query must use words that appear in the mail; there is no semantic recall. The choice was made for a single archive on one machine.
The agent drafts, a person approves, and the tool sends what was approved
A reply is written to a draft file first, and the draft has a SHA-256 fingerprint. An agent sends by passing that fingerprint, so a draft that changed after review is refused. At a real terminal a person can type an authorisation phrase instead. The tool cannot know whether the person was really asked. That part is the agent's duty, and the documentation says so.
Windows first
PST import reads through a running Outlook profile, and the test launcher is PowerShell. Maildir import (bes import --maildir) needs neither Outlook nor Windows-specific tools.
Limits
Developed and tested on Windows 11 only. Cross-platform use and support are not promised.
The real database, attachments, and credentials were never in version control. Addresses in docs and tests are placeholders such as
mailattic.testandexample.com.The commit history starts fresh here. The earlier private history is not included.
The repository ships no data, so you have to import your own. A small de-identified sample database is planned.
One database is one person's mailbox. Sharing one archive so that several mailboxes are searched together, needs new rules for "which addresses are us" and for access control, and none of that is built.
Not in scope: enterprise mail server, eDiscovery, GUI client, push notifications, semantic vector search, attachment OCR, task tracking.
Requirements
Windows 11, Python 3.13,
uvSQLite with FTS5
PST import only: a running Outlook with the target PST already mounted. The importer reads the store and never mounts, unmounts, or changes it. Run the smallest PST first; the run summary and the stop rules are in
docs/cli-reference.md.
Documentation
Question | Go to |
Every subcommand, flag, and query rule |
|
MCP tools and their limits | |
Database tables | |
Global install, release, and scheduling | |
Project rules, architecture map, behaviour contracts | |
Exact meaning of a project term | |
Why a design was chosen |
|
All documents |
Acknowledgements
The development workflow uses Matt Pocock's skills.
License
Available Tools
9 toolsmail_attachment_exportExport attachments to a host folderAIdempotent
Copy stored attachments into a host directory and report where each one landed. attachment_ids are the attachment_id values from mail_attachments (or attachments[].id from mail_show / mail_thread), 1 to 50 per call; a repeated id is reported once. dest_dir must be an absolute host path of at most 200 characters, inside a folder you can read back; it is created if missing. Never overwrites: a file already at the destination is left untouched and reported as skipped-existing. Never exports a file type Windows runs when opened (.exe, .bat, .cmd, .lnk, .vbs, .js, .ps1, .msi, ...): those are reported as skipped-blocked-type and the user must save them from Outlook. A dest_dir inside the Startup folder or an Office startup folder is refused. Each item reports status exported, skipped-existing, skipped-non-exportable (no stored payload, metadata only), skipped-blocked-type, or failed, with the absolute dest_path. Returns paths, never file content.
| Name | Required | Description | Default |
|---|---|---|---|
| dest_dir | Yes | ||
| attachment_ids | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (readOnlyHint=false, destructiveHint=false, idempotentHint=true) by disclosing the no-overwrite policy, blocked executable file types, refusal of Startup/Office-startup destinations, the full status enumeration, and the explicit statement that paths are returned but never file content. This is the behavioral detail an agent needs before committing a write.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded purpose sentence followed by tightly packed constraints, each of which carries load. Slightly long and the truncated file-type list ('...') plus the parenthetical status glosses make it denser than ideal, but there is no filler sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 2-parameter write tool with an output schema, everything an agent needs is covered: valid id range, path constraints, side effects, failure taxonomy, and the fact that content is not returned. The output schema can carry the response shape, so the description need not restate it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description fully compensates: attachment_ids is scoped to 1-50 ids, sourced from specific fields, with duplicate handling defined; dest_dir must be an absolute host path, max 200 characters, readable-back, auto-created if missing, with forbidden locations enumerated.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Copy/export) and resource (stored attachments) plus the destination (host directory) and the reporting behavior. It is distinguishable at a glance from sibling mail_attachments, which lists attachments rather than writing them to disk.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description grounds usage by naming where attachment_ids come from (mail_attachments, mail_show, mail_thread), which routes the agent through the right sibling tools first. It also states the preconditions for dest_dir. It never explicitly says when NOT to use this tool or names a direct alternative for the export step.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_attachmentsSearch attachment metadataARead-onlyIdempotent
Return one attachment-metadata result object with attachment rows, totals, and bounded carrying-message summaries. This tool never returns attachment content bytes; use mail_search or mail_show for message content.
| Name | Required | Description | Default |
|---|---|---|---|
| ext | No | ||
| limit | No | ||
| dedupe | No | ||
| to_date | No | ||
| from_date | No | ||
| name_contains | No | ||
| sender_domain | No | ||
| recipient_domain | No | ||
| participants_domain | No | ||
| carrying_messages_limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, closed-world, so the safety profile is covered. The description adds genuinely new context beyond the schema: it never returns content bytes, and carrying-message summaries are bounded. An explicit note on result sizing/pagination behavior would have earned a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler, and the core capability plus the content-boundary caveat are front-loaded. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be detailed, and annotations cover the safety profile. But for a 10-parameter tool with no schema descriptions, the definition is not complete enough for an agent to pick correct filter values, and it fails to point at mail_attachment_export for retrieving actual content.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Ten parameters with 0% schema description coverage, and the description explains none of them — ext, dedupe, from_date/to_date, the three domain filters, limit, and carrying_messages_limit are all left opaque. Only 'bounded carrying-message summaries' loosely gestures at carrying_messages_limit. The description does not compensate for the schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (attachment-metadata result object) and enumerates its contents (attachment rows, totals, carrying-message summaries), and explicitly distinguishes itself from content-returning siblings mail_search/mail_show. It never states the filtering/search capability that the 10 parameters imply, so the verb is weaker than it could be.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-not and names two alternatives: 'never returns attachment content bytes; use mail_search or mail_show for message content.' However, it omits the most relevant sibling, mail_attachment_export, which is presumably where an agent should go to actually retrieve attachment content — a notable routing gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_contact_historySummarize correspondence historyARead-onlyIdempotent
Return one aggregated correspondence-summary object for exactly one contact address or one domain. This is not a message search or thread tool; use mail_search, mail_show, or mail_thread for individual messages.
| Name | Required | Description | Default |
|---|---|---|---|
| domain | No | ||
| contact | No | ||
| date_to | No | ||
| date_from | No | ||
| contacts_limit | No | ||
| attachments_limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is fully covered externally. The description adds that the result is a single aggregated object and that input is scoped to one contact/domain, but says nothing about how the limits or mutual exclusivity between contact and domain behave. A 3 is appropriate given annotations carry the primary behavioral burden.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero waste; the purpose and scope are front-loaded before the disambiguating second sentence. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The existence of an output schema means return values need not be explained, and the disambiguation from siblings is strong. However, with 6 undocumented parameters and no mention of the date-range filters or limit defaults, the definition is only partially complete for the tool's input complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for all 6 parameters. It only clarifies contact/domain (exactly one of a contact address or a domain) and leaves date_from, date_to, contacts_limit, and attachments_limit entirely undocumented, so most parameters gain no meaning beyond their bare names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Return one aggregated correspondence-summary object') and scopes it precisely to 'exactly one contact address or one domain.' It explicitly distinguishes itself from the sibling message tools (mail_search, mail_show, mail_thread), so an agent can route correctly without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-not ('This is not a message search or thread tool') and names the alternatives with the condition that selects them ('use mail_search, mail_show, or mail_thread for individual messages'). The selection logic is fully inferable with no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_draft_replyDraft a reply to a messageA
Generate an RFC5322 reply-all draft for a stored message and write it to the MCP-dedicated draft folder. message_id is the RFC Message-ID (e.g. 'abc@example.com') from mail_show, not the numeric id from mail_search. Returns its draft_id, content fingerprint (SHA-256), and full draft text for review. The draft is NOT sent. Present the complete draft (Subject, To, Cc, Bcc, body, attachments, draft_id, and fingerprint) to the user for conversational review before calling mail_send.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | ||
| to | No | ||
| bcc | No | ||
| exclude | No | ||
| body_text | Yes | ||
| message_id | Yes | ||
| attachments | No | ||
| replace_recipients | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false with destructiveHint=false, and the description adds the key non-obvious facts: the draft is written to a dedicated folder and is NOT sent. It also discloses the return payload (draft_id, SHA-256 fingerprint, full text). It doesn't state idempotency or overwrite behavior for repeated calls, which is the remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tightly packed sentences, front-loaded with the action and destination, then the id trap, then the return values, then the safety/workflow instruction. No filler and no repetition of schema or annotation content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values needn't be explained, yet the description still summarizes them usefully. It covers the safety posture and the review workflow completely, but omits how default recipients are derived for a reply-all and what replace_recipients/exclude do, which an agent needs to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across 8 parameters, so the description carries the full burden. It compensates well for the critical message_id (RFC Message-ID format, example, and explicitly not the mail_search numeric id) but leaves cc, bcc, exclude, attachments, and replace_recipients entirely unexplained, including the two least self-evident ones.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (generate), a precise artifact (RFC5322 reply-all draft), and the destination (MCP-dedicated draft folder). It is immediately distinguishable from mail_send and mail_show, and even clarifies the reply-all scope that the name implies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: message_id comes from mail_show (not the numeric id from mail_search), and the tool is a pre-send step whose output must be reviewed conversationally before calling mail_send. It names both the source tool and the follow-up tool with their selection conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_searchSearch mail messagesARead-onlyIdempotent
Find message candidates by keyword and structured filters, returning a bare list of summary items or full-body items. Use mail_show for one stored id and mail_thread for a whole conversation or an RFC822 Message-ID string. keyword syntax: terms separated by spaces are ANDed; uppercase AND / OR / NOT, parentheses, and double-quoted phrases are honoured, and lowercase and/or/not are ordinary search terms. Results with a keyword are ordered by relevance, not by date, and each item reports match_field (subject, body, or null when the hit is in an indexed field this item does not show, such as recipients or attachment names) and match_in (new or quoted -- whether the hit the snippet shows is in the message's own new content or in its quoted history). snippet is centred on the hit when there is one, on the position covering the most of what was asked for.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| sender | No | ||
| keyword | No | ||
| to_date | No | ||
| from_date | No | ||
| full_body | No | ||
| recipient | No | ||
| sender_domain | No | ||
| body_max_chars | No | ||
| attachment_name | No | ||
| recipient_domain | No | ||
| recipients_limit | No | ||
| attachments_limit | No | ||
| participants_domain | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive/openWorld=false, so the safety profile is covered. The description adds genuine behavioral context beyond them: results with a keyword are ordered by relevance rather than date, and each item reports match_field and match_in to explain where a hit landed. It stops short of disclosing pagination or truncation behavior for the limit parameters.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the sibling routing before the syntax details. It is dense and mostly earns its length, though the keyword-syntax sentence is long and the match_field/match_in discussion pushes toward verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained and the description need not cover them. However, with 14 params at 0% schema coverage, the absence of any guidance on the many filters and limit parameters leaves meaningful gaps for correct invocation, even if most names are self-explanatory.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across 14 parameters, so the description must compensate. It thoroughly documents the keyword syntax and indirectly ties full_body to summary vs full-body items, but the other 12 parameters (date filters, sender/recipient/domain filters, body_max_chars, recipients_limit, attachments_limit) are left entirely undocumented in both schema and description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Find message candidates by keyword and structured filters.' It also explicitly routes away from siblings, naming mail_show for a single id and mail_thread for a conversation or Message-ID, so an agent can distinguish this tool without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternatives and the conditions that select them: mail_show for one stored id, mail_thread for a whole conversation or an RFC822 Message-ID. The condition that selects search (keyword and structured filters returning candidates) is clear, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_sendSend a reviewed draft emailA
Send a reviewed draft from the MCP-dedicated draft folder. Requires the draft_id and the matching SHA-256 fingerprint obtained from mail_draft_reply. Requires MAILATTIC_SMTP_ENV_FILE environment variable to point to a valid SMTP configuration file. Only call this after the user has reviewed the draft and explicitly confirmed to send. status='partial' means SMTP refused some recipients (see refused: address/code/reason): the message WAS sent to the accepted ones, so never call mail_send again for the same draft; create a new draft for the refused recipients only. An error does not always mean the mail was not sent: when it says the outcome is unknown or that the mail may already be delivered, verify delivery status before sending again; never re-draft and resend on your own.
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | ||
| fingerprint | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (which only declare non-read-only, non-idempotent, open-world), the description discloses the MAILATTIC_SMTP_ENV_FILE configuration requirement and rich failure semantics: what status='partial' means, that accepted recipients still received the message, and that an error may not mean the mail was undelivered. This is exactly the extra behavioral context the annotations cannot carry, and nothing contradicts them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action, then prerequisites, then hard safety constraints. Every sentence carries operational weight — env requirement, review/confirmation gate, partial-send rule, unknown-outcome rule — with no restatement of the name or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a non-idempotent send operation with an output schema, the description covers the two things an agent most needs and the schema cannot express: the confirmation gate and the re-send failure modes. Return-value shape is left to the output schema, appropriately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: it identifies draft_id as coming from the dedicated draft folder and the fingerprint as a SHA-256 value matching that draft, obtained from mail_draft_reply. That supplies provenance and a matching requirement not present in the bare schema, though it gives no format example or guidance on fingerprint mismatch.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Send a reviewed draft') and narrows the scope to drafts in the MCP-dedicated draft folder, which distinguishes it from sibling tools like mail_draft_reply that create drafts rather than send them. An agent can identify the operation and its source object without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the explicit precondition for use ('only call this after the user has reviewed the draft and explicitly confirmed to send'), points to mail_draft_reply as the source of the required fingerprint, and gives clear when-not-to-call rules for partial sends ('never call mail_send again for the same draft') and unknown-outcome errors ('verify delivery status before sending again'). This is a full when/when-not/alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_showShow one stored messageARead-onlyIdempotent
Return one stored message object by integer stored id, including bounded body and collection fields. Use mail_search to find valid ids and mail_thread when the identifier is an RFC822 Message-ID string or the full conversation is needed.
| Name | Required | Description | Default |
|---|---|---|---|
| new_only | No | ||
| message_id | Yes | ||
| body_max_chars | No | ||
| recipients_limit | No | ||
| attachments_limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), so the bar is lower. The description adds useful context that the body and collection fields are 'bounded', signalling truncation, though it does not state what the bounds are or how truncation is surfaced.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler. The primary action and identifier form are front-loaded, and the disambiguation guidance follows immediately.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be described, and the routing guidance is complete. The only residual gap is that the default bound values and truncation behavior of the limit parameters are not described anywhere in prose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the load. It clarifies that message_id is an integer stored id and implies bounding of body/collection fields (mapping to body_max_chars, recipients_limit, attachments_limit), but leaves new_only and the string form of message_id unexplained.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Return one stored message object by integer stored id') and explicitly names the scoping mechanism. It clearly separates itself from mail_search (id discovery) and mail_thread (RFC822 Message-ID / full conversation), so an agent can choose without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing rules: use mail_search to obtain valid ids, and use mail_thread when the identifier is an RFC822 Message-ID string or a full conversation is wanted. Both the when and the when-not alternative are named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_sync_statusShow mail sync statusARead-onlyIdempotent
Report the state of the most recent synchronization run together with the timestamp of the newest stored message, in one response. Accepts no parameters and never starts or affects a synchronization run. The four fields -- last_sync_started_at, last_sync_ended_at, last_sync_status (never_synced, running, completed, or failed), and latest_message_at -- are always present; a field whose value is unknown is null rather than omitted. Reports facts only: no freshness verdict, staleness duration, or composite score is computed.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish read-only, idempotent, non-destructive behavior, and the description adds substantive guarantees on top: the four fields are always present with null rather than omission for unknown values, and no freshness verdict, staleness duration, or composite score is computed. That last point is important, since it warns the agent the tool will not answer 'is mail stale?' directly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: scope/no-params first, field contract second, computation limits last. The field enumeration overlaps slightly with the output schema, but it carries the null-instead-of-omitted guarantee, so it is not wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a zero-parameter, read-only tool with an output schema already present, the description covers everything an agent needs: no side effects, no arguments, guaranteed field presence, null semantics, and the explicit absence of derived judgments. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so the baseline is 4. The description reinforces this by stating it accepts no parameters, which prevents an agent from hallucinating filter arguments, but there is nothing further to document semantically.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource — reporting the state of the last synchronization run plus the newest stored message timestamp — which no sibling (mail_search, mail_show, mail_thread, etc.) covers. An agent can identify this as the lone sync-diagnostics tool without inspecting the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly delimits usage: accepts no parameters and never starts or affects a synchronization run, which tells the agent this is a passive diagnostic read and not a trigger. It stops short of explicit routing language (e.g., 'use this when the user asks whether mail is up to date'), so it is clear context rather than full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mail_threadShow one mail threadARead-onlyIdempotent
Return one conversation object with messages plus total/returned counts, using either a stored id or an RFC822 Message-ID string. Use mail_search to find identifiers and mail_show when you only need one stored message.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| new_only | No | ||
| full_body | No | ||
| identifier | Yes | ||
| body_max_chars | No | ||
| recipients_limit | No | ||
| attachments_limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false and openWorldHint=false, so the safety profile is fully covered. The description adds the accepted identifier forms and the shape of the result (conversation plus counts), but says nothing about rate limits, paging behavior beyond counts, or truncation of large bodies. With annotations carrying the safety burden, this is adequate but thin.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler, and the core behavior is front-loaded ahead of the sibling routing. Every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be re-explained, and the identifier ambiguity is handled. However, with 7 parameters at 0% schema coverage, an agent still cannot tell what new_only, full_body, body_max_chars, recipients_limit or attachments_limit do — a real gap for a tool with this many tuning knobs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate and largely does not. It does explain the required 'identifier' parameter (stored id vs RFC822 Message-ID string), which usefully resolves the int|string anyOf, but limit, new_only, full_body, body_max_chars, recipients_limit and attachments_limit are left entirely undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (return one conversation object) and names its scope (messages plus total/returned counts). It clearly separates itself from mail_show ('one stored message') and mail_search, so an agent can pick it without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use mail_search to obtain identifiers, and use mail_show when a single stored message suffices — implying this tool is for the whole thread. Alternatives and the conditions that select them are named rather than left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
9 tool updates
v0.1.0- First observed
mail_attachment_export - First observed
mail_attachments - First observed
mail_contact_history - First observed
mail_draft_reply - First observed
mail_search - First observed
mail_send - First observed
mail_show - First observed
mail_sync_status - First observed
mail_thread
TDQS
Scored across 9 tools
Each tool targets a distinct resource and action: search vs. single-message show vs. whole-thread, plus separate tools for contact aggregation, attachment metadata, attachment export, drafting, sending, and sync status. The descriptions even cross-reference each other (e.g. 'use mail_show for one stored id', 'use mail_search for message content'), eliminating boundary confusion.
All nine tools share a uniform mail_ snake_case prefix with predictable descriptive suffixes (mail_search, mail_show, mail_thread, mail_draft_reply, mail_send, mail_attachment_export). The few noun-only names like mail_attachments and mail_sync_status still fit the same readable convention.
Nine tools is well-scoped for an email server spanning sync status, retrieval, attachments, contacts, and a gated draft/send workflow. Each tool earns its place with no redundant capabilities.
Read/search/thread/attachment/contact coverage plus a safe reply-and-send lifecycle is solid, but the surface only drafts replies and offers no compose-new-message, delete, move/folder, or read-state management. These are workable gaps rather than blocking ones for the stated reading-plus-reply purpose.
Maintenance
Related MCP Connectors
- Lettio MCPOAutheu.lettio
Private, EU-hosted email for AI agents over JMAP: read, search, reply, organize, send.
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
Email inboxes and calendars for AI agents: send, receive, search, draft and schedule.
Email inboxes and calendars for AI agents: send, receive, search, draft and schedule.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage multiple email accounts with secure credentials, local full-text search, thread-aware replies, and automation.7 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to search and read a local, provider-independent email archive, reconstruct contacts and interactions, and prepare draft responses without sending anything.1MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to read, send, and reply to GPG-encrypted email over IMAP/SMTP, manage a contact book with key provenance, and archive and search everything read. Key material and message bodies are kept out of the model's context window, with encryption enforced by default.1MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to search a local email archive using hybrid keyword and semantic search, and retrieve individual emails with thread context.1MIT