Summary
Add a way to test that the stored credentials actually authenticate against the configured IMAP and SMTP servers, without standing up the MCP transport. Either an opt-in flag on --dry-run (--with-auth) or a dedicated subcommand (verify-credentials). Same code paths as the production server up through LOGIN / SMTP AUTH; reports per-protocol success/failure to stdout and exits.
Motivation
--dry-run today validates: config parse, audit/credential-store wiring, TCP reach, TLS handshake (with pin), and CAPABILITY preflight. It deliberately stops before LOGIN — see crates/rimap-imap/src/preflight.rs:
//! PreflightInfo.tls_fingerprint). Does NOT perform LOGIN and does NOT
SMTP is not touched at all. That's a reasonable boundary for preflight, but it leaves a real onboarding hole on both protocols: the first time the credential is exercised is when the MCP client launches the server (IMAP) or when the agent first asks for send_email (SMTP). If the password is wrong (typo at login prompt, wrong username form, server requires a SASL mechanism the client isn't offering, separate keychain entry needed for SMTP on Gmail-style split-host providers), the failure surfaces as ERR_AUTH inside the MCP client's stderr — the worst place to debug it from.
Users currently work around this with raw IMAP via openssl s_client + manual LOGIN, and SMTP via swaks --quit-after AUTH (both documented in the quickstarts). That works but it:
- Bypasses the keyring (re-enters the password by hand), so it doesn't actually validate the same credential the server will use.
- Doesn't exercise SASL negotiation, OAuth flows, or any auth path beyond plain LOGIN / AUTH LOGIN.
- Requires the user to construct the right tool invocation per server (implicit TLS vs STARTTLS, separate recipes for IMAP vs SMTP).
- For Gmail-style split-host setups, doesn't help confirm that the SMTP-specific keychain entry (separate
--host from the IMAP one) is actually present and correct.
A built-in verification step would close the gap and exercise exactly the production code path through to LOGIN (IMAP) and AUTH (SMTP) success/failure, using the keychain-resolved credential the production server will use.
Sketch
rusty-imap-mcp --dry-run --with-auth
# or
rusty-imap-mcp verify-credentials
Same preflight output as today, plus a per-protocol trailing block:
Authentication:
IMAP:
[ok ] keyring lookup matched (service=rusty-imap-mcp, account=default/drc@linux.ibm.com@imap.linux.ibm.com)
[ok ] LOGIN succeeded as drc@linux.ibm.com (mechanism=LOGIN)
SMTP:
[ok ] keyring lookup matched (service=rusty-imap-mcp, account=default/drc@linux.ibm.com@smtp.example.com)
[ok ] AUTH succeeded as drc@linux.ibm.com (mechanism=LOGIN, STARTTLS)
Or on failure (typical Gmail split-host SMTP case where the user only stored the IMAP keyring entry):
Authentication:
IMAP:
[ok ] LOGIN succeeded as you@gmail.com (mechanism=LOGIN)
SMTP:
[err] keyring lookup failed: no entry for host smtp.gmail.com
Suggested fixes:
- Run `rusty-imap-mcp login --host smtp.gmail.com --username you@gmail.com`
- Reuse the same App Password — Gmail accepts it for both IMAP and SMTP
SMTP is skipped (not an error) if [smtp] is not configured.
In scope
- IMAP
LOGIN (PLAIN, LOGIN, future SASL mechanisms as they're added).
- SMTP
AUTH using the existing lettre client path. Reads the SMTP-specific keychain key (<account>/<smtp_username>@<smtp_host>), which on split-host providers (Gmail) is distinct from the IMAP entry.
- Both transport modes per protocol: implicit TLS and STARTTLS.
- TLS fingerprint pinning honored on both: a pinned IMAP cert that mismatches still fails; SMTP cert validation uses the configured trust path.
- Audit emission for the verification attempts, marked so audit consumers can distinguish them from real server-mode auth records.
Open design questions
- Flag vs subcommand.
--dry-run --with-auth keeps everything under one umbrella but adds combinatorial flag complexity. A separate verify-credentials subcommand is more discoverable.
- Multi-account behavior. Iterate every
[[accounts]] and report per-account, or require --account <id>? Iterating risks rate-limit hits against shared providers (Gmail throttles repeated AUTH attempts).
- Audit source kind. Should the emitted
Auth records carry a source = "verify" tag (or equivalent) so audit consumers can filter, or should they be indistinguishable from real server auth? Indistinguishable preserves audit fidelity; tagged keeps verification noise out of usage analytics.
- Failure-mode coverage in error suggestions. Where to draw the line between "diagnostic suggestion" and "support article"? E.g., for SMTP
535 on Gmail, suggest checking App Password / 2FA / "Less secure apps"?
Out of scope
- Token-exchange flows (OAuth refresh, etc.) — those go elsewhere.
- Folder-listing or message-fetch validation — separate concern. Could be a later
verify-permissions mode.
- Actually sending a test email — the
--quit-after AUTH equivalent is what's wanted; transacting a message has different blast radius.
Related docs
The quickstart workarounds using openssl s_client (IMAP) and swaks (SMTP) landed alongside this issue:
docs/quickstart-gmail.md → "Optional: verify the credential authenticates" (IMAP) and the SMTP verify step inside "Optional: enable sending"
docs/quickstart-proton-bridge.md → "Optional: verify the credential authenticates" (IMAP) and the SMTP verify subsection inside "Optional: enable sending"
docs/troubleshooting.md → credential verification section, cross-linking both quickstart subsections
Once this feature ships, all four quickstart subsections (and the troubleshooting cross-link) should be retitled to point at the new flag/subcommand, with the raw-IMAP and swaks recipes demoted to "manual alternative" footnotes.
Summary
Add a way to test that the stored credentials actually authenticate against the configured IMAP and SMTP servers, without standing up the MCP transport. Either an opt-in flag on
--dry-run(--with-auth) or a dedicated subcommand (verify-credentials). Same code paths as the production server up throughLOGIN/ SMTPAUTH; reports per-protocol success/failure to stdout and exits.Motivation
--dry-runtoday validates: config parse, audit/credential-store wiring, TCP reach, TLS handshake (with pin), and CAPABILITY preflight. It deliberately stops beforeLOGIN— seecrates/rimap-imap/src/preflight.rs:SMTP is not touched at all. That's a reasonable boundary for preflight, but it leaves a real onboarding hole on both protocols: the first time the credential is exercised is when the MCP client launches the server (IMAP) or when the agent first asks for
send_email(SMTP). If the password is wrong (typo atloginprompt, wrong username form, server requires a SASL mechanism the client isn't offering, separate keychain entry needed for SMTP on Gmail-style split-host providers), the failure surfaces asERR_AUTHinside the MCP client's stderr — the worst place to debug it from.Users currently work around this with raw IMAP via
openssl s_client+ manualLOGIN, and SMTP viaswaks --quit-after AUTH(both documented in the quickstarts). That works but it:--hostfrom the IMAP one) is actually present and correct.A built-in verification step would close the gap and exercise exactly the production code path through to
LOGIN(IMAP) andAUTH(SMTP) success/failure, using the keychain-resolved credential the production server will use.Sketch
rusty-imap-mcp --dry-run --with-auth # or rusty-imap-mcp verify-credentialsSame preflight output as today, plus a per-protocol trailing block:
Or on failure (typical Gmail split-host SMTP case where the user only stored the IMAP keyring entry):
SMTP is skipped (not an error) if
[smtp]is not configured.In scope
LOGIN(PLAIN, LOGIN, future SASL mechanisms as they're added).AUTHusing the existinglettreclient path. Reads the SMTP-specific keychain key (<account>/<smtp_username>@<smtp_host>), which on split-host providers (Gmail) is distinct from the IMAP entry.Open design questions
--dry-run --with-authkeeps everything under one umbrella but adds combinatorial flag complexity. A separateverify-credentialssubcommand is more discoverable.[[accounts]]and report per-account, or require--account <id>? Iterating risks rate-limit hits against shared providers (Gmail throttles repeated AUTH attempts).Authrecords carry asource = "verify"tag (or equivalent) so audit consumers can filter, or should they be indistinguishable from real server auth? Indistinguishable preserves audit fidelity; tagged keeps verification noise out of usage analytics.535on Gmail, suggest checking App Password / 2FA / "Less secure apps"?Out of scope
verify-permissionsmode.--quit-after AUTHequivalent is what's wanted; transacting a message has different blast radius.Related docs
The quickstart workarounds using
openssl s_client(IMAP) andswaks(SMTP) landed alongside this issue:docs/quickstart-gmail.md→ "Optional: verify the credential authenticates" (IMAP) and the SMTP verify step inside "Optional: enable sending"docs/quickstart-proton-bridge.md→ "Optional: verify the credential authenticates" (IMAP) and the SMTP verify subsection inside "Optional: enable sending"docs/troubleshooting.md→ credential verification section, cross-linking both quickstart subsectionsOnce this feature ships, all four quickstart subsections (and the troubleshooting cross-link) should be retitled to point at the new flag/subcommand, with the raw-IMAP and swaks recipes demoted to "manual alternative" footnotes.