Skip to content

Getting started ​

Requirements ​

  • Node.js 22 or newer
  • An SMTP account you may send from — a submission server on port 587 or 465, with a username and password. Providers with two-factor authentication generally need an app-specific password rather than the account password.

Try it without sending anything ​

The server starts perfectly well with sending switched off, which is the default. That state is useful on its own: you can compose messages, see exactly what would go out, and check who the server would be allowed to write to — without any possibility of a message escaping while you are still working out the configuration.

sh
SMTP_HOST=smtp.example.net \
SMTP_USER=person@example.net \
SMTP_PASSWORD=app-specific-password \
SMTP_FROM='Your Name <person@example.net>' \
npx -y @ni-c/smtp-mcp

The server writes a line to stderr telling you which state it is in:

smtp-mcp: SMTP_ALLOW_SEND is not "true" — this server can compose and preview
messages but cannot send any. Set SMTP_ALLOW_SEND=true and SMTP_ALLOWED_RECIPIENTS
to enable sending.

Ask your client to call get_server_info. It answers can_send: false and names the variable that would change that.

Turn sending on ​

Two variables, and the second one is not optional:

sh
SMTP_ALLOW_SEND=true
SMTP_ALLOWED_RECIPIENTS='@example.net,partner@example.org'

SMTP_ALLOWED_RECIPIENTS accepts:

EntryMatches
@example.netany address at that domain — not at mail.example.net
person@example.netexactly that address
*anyone

Setting SMTP_ALLOW_SEND=true without an allowlist is a startup error, not a silent "anyone". That is deliberate: an unset allowlist reads as a line somebody forgot, and the failure it causes is a message already delivered. If you genuinely want no restriction, write * and own the choice.

Send the first message ​

Ask your client to send something, and watch what happens: the server does not send on the first call. It asks.

With a client that supports MCP elicitation — Claude Desktop and Claude Code do — a dialog appears with the recipients, the subject and any Bcc on their own lines. Nothing goes out until you tick it.

With a client that cannot show a dialog, the first call returns a confirmation token instead, and the tool has to be called a second time carrying it. The result says plainly that this only proves the call was made twice with the same arguments — it is not the same thing as a human saying yes, and it does not pretend to be.

Either way, a confirmation is bound to that exact message. Adding a recipient or changing the body invalidates it, and the server asks again.

See it first ​

preview_mail builds exactly the message send_mail would build — same code path, same headers — and returns it without connecting to anything. Every check a send runs happens here too: the allowlist, the recipient limit, the attachment policy, the size limit. It is the fastest way to find out whether a message is acceptable before putting a question in front of a person.

Develop against a sandbox ​

If you are changing the server rather than using it, do not point it at a real mailbox. The repository ships a throwaway Mailpit instance that speaks real SMTP and delivers nowhere:

sh
docker compose -f test/integration/compose.yml up -d --wait
npm run build && npm run test:integration

Released under the MIT License.