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:

  1. Syntax. Malformed addresses are rejected.
  2. 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.
  3. 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.
  4. 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.
  5. Forwarding flag. Domains that use a mail-forwarding service are flagged, because the real inbox behind the forwarder can't be checked.
StatusMeaning
validThe server accepted this mailbox and rejected a random one. Strong evidence, not a guarantee.
invalidBad 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_allThe domain accepts every address. The mailbox cannot be confirmed.
unknownThe 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:

VariableWhat it does
MAILCHECK_FROMThe sender address used in the conversation, for example verify@yourdomain.com. Default check@example.com. Use an address on a domain you own.
MAILCHECK_HELOThe 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_DNSComma-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"
      }
    }
  }
}
ToolInput
verify_emailemail: one address
verify_emailsemails: 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

It is early software. The plan is to keep improving it by studying the real bounces it fails to predict.