mailcheck
Check whether an email address is likely to bounce before you send to it. It never sends a message. It is one 600 KB program with no dependencies, written in Rust, and it doubles as an MCP server so an AI assistant can use it.
How it works
For each address, mailcheck runs these checks in order and stops at the first one that settles the answer:
- Syntax. Malformed addresses are rejected.
- DNS. It looks up the domain's mail servers (MX). A domain that doesn't exist, has no mail server, or publishes a "null MX" cannot receive mail.
- Mailbox check. It connects to the mail server on port 25 and starts an SMTP conversation: it says hello, names a sender, then names your address as recipient. The server's answer says whether the mailbox exists. mailcheck then quits before any message is sent.
- Catch-all test. It asks the same server about a random address that cannot exist. If that is accepted too, the domain accepts everything, so a "yes" for your address means nothing.
- Forwarding flag. Domains that use a mail-forwarding service are flagged, because the real inbox behind the forwarder can't be checked.
| Status | Meaning |
|---|---|
valid | The server accepted this mailbox and rejected a random one. Strong evidence, not a guarantee. |
invalid | Bad syntax, no such domain, no mail server, or the server says the mailbox does not exist or refuses mail for the domain. It will bounce. |
catch_all | The domain accepts every address. The mailbox cannot be confirmed. |
unknown | The server blocked the check, asked to retry later, or gave an unclear answer. Each result carries a reason. |
Each result also has flags (role_address such as info@ or sales@, forwarding_mx), the mail server used, the SMTP reply code and a UTC timestamp.
Install
You need a Linux machine with outbound port 25 open (many cloud and home networks block it) and Rust. The full source is on this page: main.rs, Cargo.toml, .cargo/config.toml.
mkdir mailcheck && cd mailcheck
mkdir -p src .cargo
# save main.rs.txt as src/main.rs, Cargo.toml.txt as Cargo.toml,
# cargo-config.toml.txt as .cargo/config.toml
rustup target add x86_64-unknown-linux-musl
cargo build --release
# binary: target/x86_64-unknown-linux-musl/release/mailcheck
The included config builds a static musl binary with Rust's own linker, so no C compiler is needed. If you have a normal toolchain, delete .cargo/config.toml and run cargo build --release.
Command line
# a few addresses, one JSON line each
mailcheck check alice@example.com bob@example.org
# a list, one address per line, results as CSV
mailcheck file emails.txt results.csv 16
Example output (made-up address):
{"email":"someone@example.com","status":"invalid","reason":"mail server says the mailbox does not exist","catch_all":null,"mx":"mx.example.com","smtp_code":550,"flags":[],"checked_at":"2026-11-08T18:11:25Z"}
Settings, as environment variables:
| Variable | What it does |
|---|---|
MAILCHECK_FROM | The sender address used in the conversation, for example verify@yourdomain.com. Default check@example.com. Use an address on a domain you own. |
MAILCHECK_HELO | The hostname mailcheck announces. Use a real fully qualified name such as mail.yourdomain.com. Some servers refuse bare hostnames, and this one setting fixed many "unknown" results. |
MAILCHECK_DNS | Comma-separated DNS servers. Default 1.1.1.1,8.8.8.8. |
Connect it as an MCP server
Run mailcheck mcp and it speaks the Model Context Protocol over standard input and output. Add it to any MCP client that supports local (stdio) servers.
Claude Code:
claude mcp add mailcheck -e MAILCHECK_FROM=verify@yourdomain.com -e MAILCHECK_HELO=mail.yourdomain.com -- /path/to/mailcheck mcp
Claude Desktop and other clients (JSON config):
{
"mcpServers": {
"mailcheck": {
"command": "/path/to/mailcheck",
"args": ["mcp"],
"env": {
"MAILCHECK_FROM": "verify@yourdomain.com",
"MAILCHECK_HELO": "mail.yourdomain.com"
}
}
}
}
| Tool | Input |
|---|---|
verify_email | email: one address |
verify_emails | emails: up to 200 addresses. Addresses on the same domain are checked one at a time, 1.5 seconds apart. Different domains run in parallel. |
Then ask your assistant something like: "Check these 50 addresses with mailcheck and tell me which will bounce."
The server runs on the machine where you install it. It is not a web service, and this site does not run it.
Hosted version (HTTP API and remote MCP)
If someone runs mailcheck as a service for you, it also offers a web API and a remote MCP endpoint. Both need a secret bearer token from whoever runs it. Without the token every request is refused, so nobody else can use it to probe mailboxes.
curl -s https://mailcheck.thinginn.com/api/verify \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"emails":["alice@example.com","bob@example.org"]}'
Up to 50 addresses per request. A health check is open at /api/health. To connect an AI client over HTTP:
claude mcp add --transport http mailcheck https://mailcheck.thinginn.com/mcp \
--header "Authorization: Bearer YOUR_TOKEN"
Run your own with MAILCHECK_TOKEN=a-long-random-secret mailcheck serve 127.0.0.1:8088, behind a reverse proxy such as nginx that adds HTTPS and rate limits.
Limits you should know
- No tool can guarantee delivery.
validmeans the server accepted the mailbox when asked. It does not mean the inbox is monitored, not full, or that your message avoids spam filters. - Catch-all domains can't be confirmed. Some servers accept everything and bounce later. mailcheck reports
catch_allwhere it can detect this. - Servers may block the check. Big providers and security gateways often refuse connections from unknown or low-reputation IP addresses, which gives
unknown. A well-configured sending server with a proper hostname helps. - Be polite. Checking thousands of addresses against one provider quickly can get your IP blocked. Keep volumes moderate.
- Know the rules for mailing. Verifying an address does not give you permission to email it. Laws such as CAN-SPAM and GDPR/ePrivacy apply to the messages you send.
It is early software. The plan is to keep improving it by studying the real bounces it fails to predict.