mail-mcp
AGENTS.md112 starsChanged 18 days agoMITView on GitHub
Guidance for coding agents working in mail-mcp. No repository-specific Cursor or Copilot instruction files were found: If these are added later, treat them as higher-priority local policy and merge them into this file. Run commands from repo root. Use a test name substring: Run a single test and show stdout: Run tests in one module/file scope: Before finalizing changes, run: Do not skip clippy or tests for code changes. The repository includes a minimal multi-stage Dockerfile for running the MCP…
- Reads credentials
- Installs packages
We split this AGENTS.md into the rules it gives an agent
51 rules. About 1 is common, 32 are distinctive, and 18 are very specific to this project.
≈1
Common · estimate
Many files like this have a rule of this kind.
≈32
Distinctive · estimate
This author's own choices. Worth a look.
18
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 16
No repository-specific Cursor or Copilot instruction files were found:
- Distinctive: line 22
If these are added later, treat them as higher-priority local policy and merge them into this file.
- Distinctive: line 26
Run commands from repo root.
- Very specific: line 44
Run all tests: cargo test
- Distinctive: line 50
Use a test name substring:
- Distinctive: line 55
Run a single test and show stdout:
- Distinctive: line 60
Run tests in one module/file scope:
- Distinctive: line 68
Before finalizing changes, run:
- Distinctive: line 74
Do not skip clippy or tests for code changes.
- Very specific: line 83
Run over stdio: docker run --rm -i --env-file .env mail-mcp
- Very specific: line 87
Keep MCP transport as stdio by default.
- Very specific: line 88
Keep runtime image minimal (current pattern: builder image + scratch).
- Distinctive: line 89
If dependencies require a non-scratch runtime, document why in the PR/commit message.
- Very specific: line 90
Keep .dockerignore aligned with repo layout to avoid leaking local files and reduce context size.
- Very specific: line 112
Do not change package scope (@bradsjm) unless explicitly requested.
- Very specific: line 113
Prefer CI-driven publishing via git tags (v*.*.*).
- Distinctive: line 114
Prefer npm trusted publishing (GitHub OIDC + provenance) over token-based npm publish.
- Distinctive: line 130
Keep installer generation managed by dist config.
- Very specific: line 131
Keep Linux x64 target as x86_64-unknown-linux-gnu unless explicitly requested otherwise.
- Distinctive: line 142
Keep package versions, dist artifacts, and release tags aligned.
- Distinctive: line 143
If release automation changes, update README.md install commands and this file together.
- Distinctive: line 166
Keep imports explicit; avoid wildcard imports.
- Distinctive: line 167
Remove unused imports rather than allowing warnings.
- Distinctive: line 171
Use rustfmt defaults; do not hand-format against rustfmt.
- Distinctive: line 172
Keep line wrapping and chaining style consistent with existing files.
- Distinctive: line 173
Prefer small helper functions over deeply nested expressions.
- Distinctive: line 177
Prefer concrete types and explicit bounds over implicit conversions.
- Very specific: line 178
Use AppResult<T> for fallible internal APIs.
- Very specific: line 179
Use struct types in models.rs for tool input/output contracts.
- Very specific: line 181
Keep schema-annotated types (JsonSchema) aligned with runtime behavior.
- Very specific: line 188
Use domain names that match IMAP/MCP vocabulary (uidvalidity, mailbox, cursor).
- Distinctive: line 189
Avoid abbreviations unless they are protocol-standard.
- Very specific: line 193
Never use unwrap() or expect() in production code paths.
- Very specific: line 195
Prefer specific variants (InvalidInput, NotFound, Conflict, etc.) over Internal when possible.
- Distinctive: line 196
Keep user-facing error messages precise and bounded.
- Distinctive: line 204
Keep pagination and cursor invariants intact.
- Distinctive: line 208
All network/IMAP operations should be timeout-bounded.
- Distinctive: line 210
Keep async functions cancellation-safe and side effects clear.
- Very specific: line 214
Never log or return secrets (*_PASS, tokens, auth headers).
- Distinctive: line 217
Keep attachment extraction bounded and failure-tolerant.
- Distinctive: line 221
Keep tool names stable unless a breaking change is explicitly intended.
- Very specific: line 223
Keep summary concise and useful.
- Very specific: line 224
Ensure meta.duration_ms and meta.now_utc remain populated.
- Distinctive: line 228
Add unit tests for pure logic changes (parsing, validation, pagination, ids).
- Distinctive: line 234
Prefer minimal, focused diffs.
- Distinctive: line 235
Do not introduce compatibility shims unless requested.
- Distinctive: line 236
Do not silently alter public tool contracts.
- Distinctive: line 237
Update docs when behavior or bounds change.
- Very specific: line 238
If adding env vars, document them in docs/tool-contract.md and this file.
- Distinctive: line 257
Read and write failures are logged, never fatal.
- Common: line 268
No secrets in code/log output
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md
Guidance for coding agents working in mail-mcp.
Project Snapshot
- Language: Rust (edition
2024) - Runtime:
tokio - Protocol framework:
rmcp - Domain: IMAP-backed MCP server over stdio
- Entry point:
src/main.rs - Main server implementation:
src/server.rs
Cursor/Copilot Rules
No repository-specific Cursor or Copilot instruction files were found:
.cursorrules: not present.cursor/rules/: not present.github/copilot-instructions.md: not present
If these are added later, treat them as higher-priority local policy and merge them into this file.
Build, Lint, and Test Commands
Run commands from repo root.
Build
- Debug build:
cargo build - Release build:
cargo build --release
Format
- Check formatting:
cargo fmt -- --check - Apply formatting:
cargo fmt
Lint
- Strict clippy (all targets):
cargo clippy --all-targets -- -D warnings
Test
- Run all tests:
cargo test - List tests:
cargo test -- --list - GreenMail integration smoke test:
scripts/test-greenmail.sh
Run a Single Test
Use a test name substring:
cargo test parses_mailbox_with_colonscargo test pagination::tests::evicts_to_max_entries
Run a single test and show stdout:
cargo test parses_mailbox_with_colons -- --exact --nocapturecargo test greenmail_imap_smoke_test -- --exact --ignored --nocapture
Run tests in one module/file scope:
cargo test message_id::testscargo test mime::testscargo test pagination::tests
Suggested Local Validation Sequence
Before finalizing changes, run:
cargo fmt -- --checkcargo clippy --all-targets -- -D warningscargo test
Do not skip clippy or tests for code changes.
Docker
The repository includes a minimal multi-stage Dockerfile for running the MCP server.
Build and Run
- Build image:
docker build -t mail-mcp . - Run over stdio:
docker run --rm -i --env-file .env mail-mcp
Docker Notes for Agents
- Keep MCP transport as stdio by default. An opt-in streamable HTTP transport exists (
MAIL_MCP_TRANSPORT=http, seesrc/http.rsanddocs/advanced-configuration.md); never make it the default or enable it implicitly in Docker. - Keep runtime image minimal (current pattern: builder image +
scratch). - If dependencies require a non-scratch runtime, document why in the PR/commit message.
- Keep
.dockerignorealigned with repo layout to avoid leaking local files and reduce context size. - Docker publish workflow:
.github/workflows/publish-docker.yml. - Docker publish trigger: manual only (
workflow_dispatch); after tagging a release, run it against the tag (e.g.gh workflow run "Publish Docker Image" --ref vX.Y.Z). - Published image tags include
latest,vX.Y.Z, andX.Y.Zon GHCR.
NPM / NPX Distribution
The project is distributed as an npm package published by cargo-dist.
Package and Publish Source
- Package name:
@bradsjm/mail-mcp - Dist config source:
dist-workspace.toml(installers = ["npm"],publish-jobs = ["npm"]) - Publish workflow:
.github/workflows/release.yml
Runtime Behavior
- End users run via
npx @bradsjm/mail-mcp@latest. - The npm package is generated by cargo-dist during release and published from CI.
Publishing Rules for Agents
- Do not change package scope (
@bradsjm) unless explicitly requested. - Prefer CI-driven publishing via git tags (
v*.*.*). - Prefer npm trusted publishing (GitHub OIDC + provenance) over token-based npm publish.
Release Assets (Curl Installer)
The repository publishes GitHub Release archives/installers via cargo-dist.
Release Workflow
- Release workflow:
.github/workflows/release.yml - Trigger: git tags matching
v*.*.* - Dist generates archives/checksums/installers from
dist-workspace.tomltarget and installer settings.
Installer Behavior and Guardrails
- Shell/Powershell installers are generated by cargo-dist and uploaded to GitHub Releases.
install-updater = true: those installers also installmail-mcp-update(axoupdater). The updater artifacts come fromdist build; no CI change was needed.- Keep installer generation managed by dist config; do not hand-maintain custom installer scripts.
- Keep Linux x64 target as
x86_64-unknown-linux-gnuunless explicitly requested otherwise.
Homebrew Tap
- Formula is published to
tecnologicachile/homebrew-tap(brew install tecnologicachile/tap/mail-mcp) by thepublish-homebrew-formulajob inrelease.yml. - That job is copied from what
dist generateemits, except the tap checkout usesssh-key: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}(a write deploy key on the tap repo only) instead of aHOMEBREW_TAP_TOKENPAT. Keep that change if the job is ever regenerated. - Formula
descandhomepagecome fromCargo.toml(description,homepage).
Tag Publish Flow
release.ymlruns onv*.*.*tags;publish-docker.ymlis manual (workflow_dispatch) and must be run against the tag afterwards.- Keep package versions, dist artifacts, and release tags aligned.
- If release automation changes, update
README.mdinstall commands and this file together.
Architecture and File Ownership
src/main.rs: process bootstrap, env loading, tracing init, stdio serving.src/config.rs: env-driven account/server config parsing.src/errors.rs: app-level error model and MCP error mapping.src/imap.rs: IMAP transport/session operations with timeout wrappers.src/server.rs: MCP tool handlers, validation, business orchestration.src/models.rs: input/output DTOs and schema-bearing types.src/mime.rs: message parsing, header/body extraction, sanitization.src/message_id.rs: stable message id parse/encode logic.src/pagination.rs: cursor storage and TTL/eviction behavior.src/server/drafts.rs: draft tools and their IMAP/EWS/Graph routing.
Code Style Guidelines
Imports
- Group imports in this order:
- standard library (
std::...) - external crates
- internal crate imports (
crate::...)
- standard library (
- Keep imports explicit; avoid wildcard imports.
- Remove unused imports rather than allowing warnings.
Formatting
- Use rustfmt defaults; do not hand-format against rustfmt.
- Keep line wrapping and chaining style consistent with existing files.
- Prefer small helper functions over deeply nested expressions.
Types and Data Modeling
- Prefer concrete types and explicit bounds over implicit conversions.
- Use
AppResult<T>for fallible internal APIs. - Use
structtypes inmodels.rsfor tool input/output contracts. - Preserve serde defaults and validation bounds when extending inputs.
- Keep schema-annotated types (
JsonSchema) aligned with runtime behavior.
Naming
- Types:
UpperCamelCase. - Functions/methods:
snake_case. - Constants:
UPPER_SNAKE_CASE. - Use domain names that match IMAP/MCP vocabulary (
uidvalidity,mailbox,cursor). - Avoid abbreviations unless they are protocol-standard.
Error Handling
- Never use
unwrap()orexpect()in production code paths. - Convert external errors into
AppErrorwith actionable context. - Prefer specific variants (
InvalidInput,NotFound,Conflict, etc.) overInternalwhen possible. - Keep user-facing error messages precise and bounded.
- Preserve mapping from
AppErrortoErrorDatainerrors.rs.
Validation and Bounds
- Validate all user input before IMAP operations.
- Enforce existing numeric/string bounds (limit, chars, dates, bytes).
- Reject conflicting parameter combinations explicitly.
- Keep pagination and cursor invariants intact.
Async and Timeouts
- All network/IMAP operations should be timeout-bounded.
- Reuse server-configured timeout settings; do not hardcode new timeout values.
- Keep async functions cancellation-safe and side effects clear.
Security and Privacy
- Never log or return secrets (
*_PASS, tokens, auth headers). - Preserve TLS-only behavior unless a deliberate security design change is requested.
- Maintain HTML sanitization for message HTML (
ammonia). - Keep attachment extraction bounded and failure-tolerant.
MCP Tool Behavior
- Keep tool names stable unless a breaking change is explicitly intended.
- Maintain
ToolEnvelopeshape (summary,data,meta). - Keep
summaryconcise and useful. - Ensure
meta.duration_msandmeta.now_utcremain populated.
Testing Expectations
- Add unit tests for pure logic changes (parsing, validation, pagination, ids).
- Extend existing test modules near changed code.
- For bug fixes, add regression tests reproducing prior failure mode.
Change Discipline for Agents
- Prefer minimal, focused diffs.
- Do not introduce compatibility shims unless requested.
- Do not silently alter public tool contracts.
- Update docs when behavior or bounds change.
- If adding env vars, document them in
docs/tool-contract.mdand this file. MAIL_SMTP_<ID>_FROM_EMAIL: optional sender address override. When the SMTP auth username (_USER) differs from the desired From address (e.g. shared/group mailboxes where auth uses a personal account but the From should be the group address), set this to the group address. When unset, the From address defaults to_USER. Affects send, reply, and forward.MAIL_IMAP_MAX_MAILBOXES: cap on how many mailboxesimap_list_mailboxesreturns (default200, clamped to1..=10000). The response always carriestotalandtruncated, so a client can tell a capped list from a complete one.MAIL_DRAFTS_ENABLED: enables thedraft_*tools (defaultfalse). Independent ofMAIL_IMAP_WRITE_ENABLEDby design: drafts-only must not grant move/delete/flag on other mail, so every draft operation on an existingdraft_idchecks the target is a draft (IMAP Drafts folder, EWSIsDraft, GraphisDraft).draft_sendalso requiresMAIL_SMTP_WRITE_ENABLED. Backend per account: EWS config, else Graph config (MAIL_GRAPH_*only, never the genericMAIL_OAUTH2_*), else IMAP.MAIL_OAUTH2_TOKEN_STORE: optional file where rotated OAuth2 refresh tokens are persisted (unset = in-memory rotation only). Entries carry a fingerprint of the env token they came from, so a changed env token wins. Read and write failures are logged, never fatal.MAIL_IMAP_SEARCH_CONCURRENCY: parallel IMAP connections used byimap_search_all_mailboxes(default4, clamped to1..=8). Each connection walks its share of the mailboxes with EXAMINE + UID SEARCH; providers cap concurrent connections per account, so stay well below their limit.
Quick Pre-Commit Checklist
- Code formatted
- Clippy clean with warnings denied
- Tests passing
- No secrets in code/log output
- Contract and validation invariants preserved
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository2 other files it gives its agents.
- site/llms-full.txtllms.txt
- site/llms.txtllms.txt
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

