mail-mcp v0.1 — Rust MCP server for Sulkta email
Phase A: mail_send + mail_inbox_list + mail_inbox_read. Replaces scripts/kayos_mail.py with a typed MCP server. Outbound guarantees Date, Message-ID (own-domain), User-Agent, MIME-Version, multipart/alternative for HTML+text, multipart/mixed for attachments, In-Reply-To + References for threading. Single account in v0.1 (default_account from config). Phase B adds multi-account + threading + search; Phase C adds mark + attachments + reply helper. Stack: rmcp 0.1 (matches aldabra), lettre 0.11 + tokio-rustls, async-imap 0.10, mail-parser 0.9. Stderr-only logging (stdout is the MCP transport). Smoke verified 2026-05-21: send -> land -> read kayos@sulkta.com round trip, DKIM-Signature + Authentication-Results pass at the rspamd relay.
This commit is contained in:
commit
2240bf745e
11 changed files with 3552 additions and 0 deletions
73
README.md
Normal file
73
README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# mail-mcp
|
||||
|
||||
Rust MCP server for Sulkta-hosted email. SMTP send + IMAP read with RFC-correct headers, multipart/alternative when HTML is included, multipart/mixed for attachments, threading via `In-Reply-To`/`References`.
|
||||
|
||||
Replaces the `scripts/kayos_mail.py` CLI path that lived in `kayos/openclaw-workspace` since 2026-04-23.
|
||||
|
||||
## Why a server, not a CLI
|
||||
|
||||
`kayos_mail.py` shipped without `Date` or `Message-ID` headers until a 2026-05-18 patch — exactly the kind of header-discipline regression a typed Rust server prevents at compile time. The "no spam bin" framing is mostly upstream of any client (Rackham postfix + rspamd DKIM-sign at the relay; mail-tester scored 10/10 and port25 SpamAssassin −7.31 on 2026-05-20), but a correct client doesn't trip filters with bad MIME structure, broken threading, or missing headers.
|
||||
|
||||
## Tools (v0.1)
|
||||
|
||||
- `mail_send` — send mail. Args: `account?`, `to`, `cc[]?`, `bcc[]?`, `subject`, `body`, `body_html?`, `attachments[]?`, `in_reply_to?`, `references[]?`. Returns `{message_id, sent_at}`.
|
||||
- `mail_inbox_list` — list folder messages newest-first. Args: `account?`, `since?` (YYYY-MM-DD), `unread_only?`, `limit?` (default 50, max 500), `folder?` (default INBOX). Uses `BODY.PEEK` so it does not toggle `\Seen`.
|
||||
- `mail_inbox_read` — fetch one message by UID. Args: `account?`, `uid`, `folder?`, `format?` (`text`|`html`|`raw_eml`). Attachment payloads are not inlined — only filename/mime_type/size metadata.
|
||||
|
||||
## Headers we guarantee on outbound
|
||||
|
||||
- `Date` — UTC, RFC 5322 (lettre auto)
|
||||
- `Message-ID` — `<UUIDv4@<from_addr_domain>>` — own-domain, never 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 the body shape (text-only / alternative / mixed)
|
||||
|
||||
DKIM-Signature is applied by the relay (rspamd on Rackham), not the client.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Binary lands at `target/release/mail-mcp`.
|
||||
|
||||
## Config
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/mail-mcp
|
||||
cp config.example.toml ~/.config/mail-mcp/config.toml
|
||||
chmod 600 ~/.config/mail-mcp/config.toml
|
||||
```
|
||||
|
||||
Edit accounts as needed. Passwords are NEVER inline:
|
||||
|
||||
1. Looked up from the env var named in `password_env`
|
||||
2. Falling back to `password_file` (shell-format: `KEY=VALUE` per line)
|
||||
3. Hard-failing with a vault-pointer hint if neither resolves
|
||||
|
||||
Vault canonical: `bw.sulkta.com` → `kayos@sulkta.com — IMAP/SMTP`.
|
||||
|
||||
## MCP wiring (Claude Code / kayos-house)
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"mail-mcp": {
|
||||
"command": "/usr/local/bin/mail-mcp",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Logging is stderr-only — stdout is the JSON-RPC transport.
|
||||
|
||||
## Future phases
|
||||
|
||||
- **Phase B** (~200 LOC): multi-account routing across all configured `[accounts.*]`, plus `mail_thread` and `mail_search`.
|
||||
- **Phase C** (~150 LOC): `mail_mark` (read/unread/flag/trash/archive), `mail_attachment_get`, `mail_reply` helper.
|
||||
|
||||
Full locked spec: `kayos/openclaw-workspace` → `memory/spec-mail-mcp.md`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue