MCP Server¶
Expose one email account to any MCP client — Claude Desktop, Claude Code, Cursor — as tools the model can call.
pip install email-profile[mcp]
email-profile-mcp --allow-send
Credentials come from the environment (EMAIL_USERNAME, EMAIL_PASSWORD,
optional EMAIL_SERVER) or a .env file. No tool accepts a password.
Configure a client¶
Claude Code¶
claude mcp add email \
--env EMAIL_USERNAME=you@gmail.com --env EMAIL_PASSWORD=app-password \
-- uvx --from "email-profile[mcp]" email-profile-mcp --allow-send
Claude Desktop¶
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"email": {
"command": "uvx",
"args": ["--from", "email-profile[mcp]", "email-profile-mcp", "--allow-send"],
"env": { "EMAIL_USERNAME": "you@gmail.com", "EMAIL_PASSWORD": "app-password" }
}
}
}
Cursor reads the same shape from .cursor/mcp.json.
As a Claude Code plugin¶
The repository doubles as a plugin — the server from .mcp.json plus the
skills under skills/:
claude plugin marketplace add linux-profile/email-profile
claude plugin install email-profile@email-profile
Set EMAIL_USERNAME and EMAIL_PASSWORD in your shell; .mcp.json reads
them, and EMAIL_MCP_ALLOW_SEND=true turns sending on.
| Skill | Leads to |
|---|---|
triage-inbox |
Unread first, grouped by sender, nothing changed |
find-message |
The most specific filter, then a pick list |
read-message |
Full body, re-read when truncated, attachments to disk |
draft-reply |
Read the thread, show the text, send on approval |
send-email |
Recipients and body confirmed verbatim before sending |
organize-mailbox |
Flags and moves first, delete last and only when allowed |
Tools¶
Every message is addressed by the pair (mailbox, uid). UIDs are unique
inside one mailbox and change when a message is moved, so take them from
a fresh search_messages call.
| Tool | Annotation | What it does |
|---|---|---|
list_mailboxes |
read-only | Server-side folder names |
search_messages |
read-only | Filter one mailbox; headers only, newest first, paginated |
read_message |
read-only | Headers, body (truncated to max_chars) and attachment metadata |
list_attachments |
read-only | Name, type and size of each attachment |
save_attachment |
reversible | Write one attachment to disk |
mark_seen / mark_unseen |
reversible | Read state |
flag_message / unflag_message |
reversible | Star |
move_message |
reversible | Move to another mailbox |
send_email |
destructive | New message over SMTP — --allow-send |
reply_message |
destructive | Reply keeping thread headers — --allow-send |
forward_message |
destructive | Forward with attachments — --allow-send |
delete_message |
destructive | Flag, or expunge with expunge=true — --allow-delete |
Bodies come back truncated; when truncated is true the model is told
to read again with a larger max_chars. Attachment bytes never cross the
wire, and save_attachment writes only under EMAIL_MCP_ATTACHMENTS_DIR.
uid is one id — digits only. IMAP sequence sets (1:*, 1,2) are
refused so a single approved call never touches more than one message.
Search filters are escaped per RFC 3501 before they reach the server.
Tools run one at a time on the IMAP connection: the server holds a lock around every command, so a client that pipelines calls gets them serialized rather than interleaved on one socket.
Prompts¶
| Prompt | Leads to |
|---|---|
triage_inbox |
search_messages(unseen=true) grouped by sender |
find_message |
The most specific filter first, then a pick list |
draft_reply |
read_message → approval → reply_message |
summarize_thread |
Every message of a subject, oldest first |
Options¶
| Flag | Env | Default |
|---|---|---|
--allow-send |
EMAIL_MCP_ALLOW_SEND |
off |
--allow-delete |
EMAIL_MCP_ALLOW_DELETE |
off |
--max-chars |
EMAIL_MCP_MAX_CHARS |
4000 |
| — | EMAIL_MCP_LIMIT |
20 |
| — | EMAIL_MCP_DEFAULT_MAILBOX |
INBOX |
| — | EMAIL_MCP_ATTACHMENTS_DIR |
. — the only place save_attachment writes |
--http --host --port |
— | stdio |
--http serves streamable HTTP for clients that connect over the network.
From Python¶
from email_profile import Email
from email_profile.mcp import Settings, build
server = build(
Settings(allow_send=True),
email_factory=lambda: Email("imap.example.com", "user", "pw"),
)
server.run()