Repository URL → git.sulkta.com. Drop Lucy Browserless IPs from tool doc-strings (replaced with abstract 'sandboxed headless browser' guidance). Drop sibling-repo cross-references, kayos@/cobb@ mailbox examples in tool descriptions, vault pointers. Generalize config.example.toml + README to neutral hosts. Add LICENSE (MIT — Cargo.toml already declared it). Tests still green. No behavior change.
80 lines
2.3 KiB
Markdown
80 lines
2.3 KiB
Markdown
# mail-mcp
|
|
|
|
Rust MCP server for IMAP/SMTP. SMTP send + IMAP read with RFC-correct headers,
|
|
multipart/alternative when HTML is included, multipart/mixed for attachments,
|
|
threading via `In-Reply-To` / `References`.
|
|
|
|
## Tools (v0.1)
|
|
|
|
- `mail_send` — `account?`, `to`, `cc[]?`, `bcc[]?`, `subject`, `body`,
|
|
`body_html?`, `attachments[]?`, `in_reply_to?`, `references[]?`.
|
|
Returns `{message_id, sent_at}`.
|
|
- `mail_inbox_list` — newest-first listing. `account?`, `since?` (YYYY-MM-DD),
|
|
`unread_only?`, `limit?` (default 50, max 500), `folder?` (default INBOX).
|
|
Uses `BODY.PEEK` — does not toggle `\Seen`.
|
|
- `mail_inbox_read` — fetch one message by UID. `account?`, `uid`, `folder?`,
|
|
`format?` (`text` | `html` | `raw_eml`). Attachment payloads NOT inlined —
|
|
only `{filename, mime_type, size}` metadata.
|
|
|
|
## Outbound headers
|
|
|
|
- `Date` — UTC, RFC 5322 (lettre auto)
|
|
- `Message-ID` — `<UUIDv4@<from_addr_domain>>` — own-domain, not the
|
|
container hostname
|
|
- `From` — `name <addr>`
|
|
- `MIME-Version: 1.0`
|
|
- `User-Agent: mail-mcp/<version>`
|
|
- `In-Reply-To` + `References` when threading args present
|
|
- `Content-Type` correct for body shape (text-only / alternative / mixed)
|
|
|
|
DKIM-Signature is applied by the relay, not the client.
|
|
|
|
## Build
|
|
|
|
```
|
|
cargo build --release
|
|
```
|
|
|
|
Binary at `target/release/mail-mcp`.
|
|
|
|
## Config
|
|
|
|
```
|
|
mkdir -p ~/.config/mail-mcp
|
|
cp config.example.toml ~/.config/mail-mcp/config.toml
|
|
chmod 600 ~/.config/mail-mcp/config.toml
|
|
```
|
|
|
|
Passwords are never inline. Per account:
|
|
|
|
1. read from the env var named in `password_env`
|
|
2. otherwise from `password_file` (shell-format `KEY=VALUE` per line)
|
|
3. hard fail if neither resolves
|
|
|
|
## MCP wiring
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"mail-mcp": {
|
|
"command": "/usr/local/bin/mail-mcp",
|
|
"args": []
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Logging is stderr-only — stdout is the JSON-RPC transport.
|
|
|
|
## Safety
|
|
|
|
`mail_inbox_read` returns attacker-controlled bytes. Do NOT auto-fetch URLs
|
|
found in inbound mail — web beacons confirm read and links may be phishing.
|
|
Default deny on every URL; surface as text and wait for explicit per-link
|
|
authorization. If a fetch is authorized, route through a sandboxed headless
|
|
browser rather than `curl` / `WebFetch` from the production host. Attachment
|
|
bytes get the same treatment.
|
|
|
|
## License
|
|
|
|
MIT — see `LICENSE`.
|