@diskd-ai/email-client-mcp
v0.1.34
Published
Drive-backed multi-account email MCP with reliable IMAP-to-mailboxes sync.
Readme
Email Client MCP
Multi-account email MCP server with reliable IMAP-to-mailboxes sync into a Drive-backed messagesStore (@diskd-ai/sdk). Read tools hit IMAP live; the watcher mirrors every configured account into the workspace store on an interval (>= 60s).
Address: end:comm/email-client-mcp.
Install
npx -y @diskd-ai/email-client-mcp stdioThe server speaks Model Context Protocol over stdio. JSON-RPC on stdout; logs to stderr.
Tools
All read tools fetch from IMAP live (BODY.PEEK, never silently flips \Seen):
list_accounts-- enumerate configured accounts (config-only; no IMAP).list_mailbox_folder-- folders for an account, with optional unread counts and special-use flags.find_mailbox_folder-- resolve real folders containing a given Message-Id (use beforemove_emailfor Gmail virtuals).list_emails-- paginated metadata listing with filters (unread, flagged, from/to/subject, since).get_email-- single message by UID. Formats:raw,text(HTML stripped),stripped(also drops quoted replies + signatures).markRead=trueopt-in.get_emails-- bulk variant ofget_email(max 20).move_email-- IMAP MOVE between folders. Refuses virtual mailboxes (e.g.[Gmail]/All Mail).set_email_attributes-- set read/unread and flagged/unflagged state for IMAP messages, then patch the Drive mirror.bulk_action--mark_read | mark_unread | flag | unflag | move | deleteover up to 100 UIDs.get_watcher_status-- in-memory snapshot of the sync watcher.
Watcher reliability
The watcher synchronizes IMAP into the workspace messagesStore on every tick. Invariants:
lastSyncedUidadvances only after a successful idempotentupsertBatch.externalId = "${UIDVALIDITY}:${UID}"so a UIDVALIDITY rollover never collides with the previous generation.- UIDVALIDITY mismatch on a folder triggers a clean drop + resync.
- Per-account in-flight lock: an overlapping tick is skipped, never queued.
- Folder metadata is the durable checkpoint -- restart resumes from there, never from in-memory state.
- Folders deleted in IMAP are pruned from Drive at the end of a tick.
- Sliding-window flag reconciliation bounds drift to
flag_reconcile_windowUIDs per tick.
Tick interval is clamped to >= 60s.
Configuration
Default path: ~/.config/email-client-mcp/config.toml (override via EMAIL_CLIENT_MCP_CONFIG). When deployed by mcp-hub the file is mounted by the diskd-ai/email-client-mcp vault assembler.
[sdk]
api_key = "..."
base_url = "https://apis.example/"
# workspace_id falls back to MCP_HUB_WORKSPACE_ID env when omitted.
[watcher]
enabled = true
interval_ms = 60000
flag_reconcile_window = 500
# folders = ["INBOX", "Sent"] # optional allowlist; empty = all
[[accounts]]
name = "work"
email = "[email protected]"
full_name = "You"
password = "..."
[accounts.imap]
host = "imap.example.com"
port = 993
tls = true
verify_ssl = falseFor Gmail OAuth accounts, replace the password field with an [accounts.oauth2] block:
[[accounts]]
name = "gmail-work"
email = "[email protected]"
full_name = "You"
[accounts.oauth2]
provider = "google"
client_id = "..."
client_secret = "..."
refresh_token = "..."
[accounts.imap]
host = "imap.gmail.com"
port = 993
tls = true
verify_ssl = falseDevelopment
bun install
bun run typecheck
bun run lint
bun run test # vitest unit tests
bun run build # tsc -> dist/
bun run start # node dist/server.js stdio (needs config TOML)
bun run dev # tsx src/server.ts stdio (live reload)
# stdio smoke test (initialize + tools/list + tool calls):
node scripts/smoke.mjsReleasing
Auto-publish via .github/workflows/release.yml:
- Tag-driven (preferred):
npm version patch && git push --follow-tagsfrom main; thev*.*.*tag triggers the workflow which typechecks, lints, tests, builds, andnpm publishes. - One-click: in the GitHub Actions UI run the Release workflow with
bump = patch | minor | major. The job bumpspackage.json, commits, tags, pushes, and publishes -- all in one job.
Both paths run bun install --frozen-lockfile, bun run typecheck, bun run lint, bun run test, and bun run build before publishing, with provenance attestation enabled.
Required repo secret:
| Secret | Purpose |
| --- | --- |
| NPM_TOKEN | npmjs.com granular token, scope @diskd-ai/*, Bypass 2FA enabled. |
License
LGPL-3.0-or-later. See LICENSE.
