mcp-simple-mail-sender
v1.1.0
Published
MCP server for sending email via SMTP
Maintainers
Readme
mcp-simple-mail-sender
A minimal MCP server that enables AI agents and automated pipelines to send email via any standard SMTP server.
Three tools, one purpose — start with send_email:
| Tool | What it does | When to use instead |
|---|---|---|
| send_email ⭐ | Send an email with structured arguments — recommended for most use cases | — |
| send_email_json_arg | Send an email by passing the entire payload as a single JSON string | Your tooling can only template a single top-level string value |
| send_email_flat_args | Send an email with a fully flat argument structure — no nested objects | Your tooling does not support nested arguments or nested templating |
For AI agents and standard MCP clients, use
send_email. The other two tools exist for specific edge cases in pipeline tooling that imposes restrictions on argument structure.
MCP Configuration
{
"mcpServers": {
"mail": {
"command": "npx",
"args": ["-y", "mcp-simple-mail-sender"],
"env": {
"SMTP_HOST": "smtp.example.com",
"SMTP_PORT": "587",
"EMAIL_USER": "[email protected]",
"EMAIL_PASS": "your-password"
}
}
}
}Or via Claude Code CLI:
claude mcp add simple-mail-sender \
-e SMTP_HOST=smtp.example.com \
-e [email protected] \
-e EMAIL_PASS=your-password \
-- npx -y mcp-simple-mail-senderEnvironment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
| EMAIL_USER | yes | — | SMTP auth username |
| EMAIL_PASS | yes | — | SMTP auth password |
| SMTP_HOST | yes | — | SMTP server hostname |
| SMTP_PORT | no | 587 | SMTP server port |
| SMTP_SECURE | no | false | true = force STARTTLS |
| SMTP_IMPLICIT_TLS | no | false | true = wrap connection in TLS from the start (port 465 style) |
| SMTP_EHLO_HOST | no | os.hostname() | Hostname sent in the EHLO greeting (client identity) |
| SMTP_TRIGGER_FIRST | no | false | true = send wake bytes before waiting for the SMTP greeting (see below) |
| SMTP_EHLO_FIRST | no | false | true = send EHLO immediately without waiting for a greeting, skipping non-250 responses |
| SMTP_SEND_TIMEOUT | no | 60000 | Overall send timeout in milliseconds |
| EMAIL_FROM | no | EMAIL_USER | Envelope From address |
Tool: send_email ⭐
Send an email via SMTP. Accepts standard structured arguments — this is the recommended tool for AI agents and MCP clients.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| to | string | yes | Recipient(s), comma-separated |
| subject | string | yes | Email subject |
| body | string | yes | Email body |
| html | boolean | no | Set true if body is HTML |
| cc | string | no | CC recipients, comma-separated |
| attachments | array | no | List of attachments (see below) |
Attachments
Each attachment object:
| Field | Type | Required | Description |
|---|---|---|---|
| filename | string | yes | Display name. If content is omitted, also used as the filesystem path to read from. |
| content | string | no | Base64-encoded file content. When provided, filename is only the display name. |
| contentType | string | no | MIME type, e.g. application/pdf |
Attach from base64 content:
{ "filename": "report.pdf", "content": "<base64>", "contentType": "application/pdf" }Attach from filesystem path:
{ "filename": "/tmp/report.pdf" }Example tool call
{
"to": "[email protected]",
"subject": "Important question",
"body": "<h1>Where was Gondor when the Westfold fell?</h1>",
"html": true,
"attachments": [{ "filename": "/users/theoden/map.pdf" }]
}Tool: send_email_json_arg
Send an email by passing the entire argument structure as a single JSON string.
Same functionality as send_email — the difference is only in how arguments are passed. Use this when your tooling can only apply templating or dynamic substitution to a single top-level string value, not to individual structured fields.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| payload | string | yes | Full email definition as a JSON string (same fields as send_email) |
Example tool call
{
"payload": "{\"to\":\"[email protected]\",\"subject\":\"Hello\",\"body\":\"Hi there.\",\"attachments\":[{\"filename\":\"/tmp/report.pdf\"}]}"
}Example — Jinja pipeline where the whole payload is a single templated value
tool: send_email_json_arg
arguments:
payload: >
{
"to": "{{ recipient }}",
"subject": "{{ subject }}",
"body": "{{ body }}",
"attachments": [
{ "filename": "report.pdf", "content": "{{ report_b64 }}", "contentType": "application/pdf" },
{ "filename": "{{ log_path }}" }
]
}Tool: send_email_flat_args
Send an email using a fully flat argument structure — no nested objects.
Same functionality as send_email — the difference is only in how arguments are passed. Attachments are passed as individual top-level fields (attachment_0_filename, attachment_0_content, attachment_0_content_type, … up to attachment_8_*). Use this when your tooling does not support nested arguments or cannot template nested keys.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| to | string | yes | Recipient(s), comma-separated |
| subject | string | yes | Email subject |
| body | string | yes | Email body (plain text or HTML) |
| html | boolean | no | Set true if body is HTML |
| cc | string | no | CC recipients, comma-separated |
| attachment_N_filename | string | no | Display filename. If attachment_N_content is omitted, used as filesystem path. |
| attachment_N_content | string | no | Base64-encoded file content. |
| attachment_N_content_type | string | no | MIME type, e.g. application/pdf |
(N = 0–8, up to 9 attachments)
Example tool call
{
"to": "[email protected]",
"subject": "Monthly report",
"body": "See attached.",
"attachment_0_filename": "report.pdf",
"attachment_0_content": "JVBERi0xLj...",
"attachment_0_content_type": "application/pdf"
}Example — Jinja YAML pipeline where only top-level keys are templated
# Every value is a top-level key — Jinja resolves all {{ }} tokens correctly,
# including attachment content that would be inaccessible inside send_email's nested array.
tool: send_email_flat_args
arguments:
to: [email protected]
subject: "Pipeline report {{ run_id }}"
body: "Run {{ run_id }} completed."
attachment_0_filename: report.pdf
attachment_0_content: "{{ report_b64 }}"
attachment_0_content_type: application/pdf
attachment_1_filename: "{{ log_path }}"Non-standard SMTP proxies
SMTP_TRIGGER_FIRST
Some SMTP relays use a "client-first" TCP proxy that stays silent after the TCP handshake, waiting for the client to send data before forwarding to the real SMTP backend. Standard SMTP clients (which wait for the server's 220 greeting) will hang indefinitely.
Setting SMTP_TRIGGER_FIRST=true sends a small burst of bytes immediately on connect to wake the proxy. A trailing \r\n is appended so the SMTP backend treats the bytes as a complete (invalid) command, flushes its 500 error response, and is in a clean state when EHLO arrives. Without the \r\n, the binary bytes would linger in the server's command buffer and get prepended to EHLO, causing a 500 5.5.2 bad chars in command error.
Use SMTP_TRIGGER_FIRST=true when connections hang silently and never receive a 220 greeting.
The default EHLO hostname is os.hostname() (your machine's hostname), which works for most servers. Override with SMTP_EHLO_HOST if the server rejects it — common reasons are loop protection (server's own hostname detected) or blocklisting of generic names.
SMTP_IMPLICIT_TLS
Use SMTP_IMPLICIT_TLS=true for servers that expect a TLS handshake immediately on connect (typically port 465). This is distinct from STARTTLS (SMTP_SECURE), which upgrades a plain connection mid-session.
SMTP_EHLO_FIRST
Use SMTP_EHLO_FIRST=true for proxies that need the client to speak first but where EHLO itself is the right wake signal (rather than garbage bytes). The client sends EHLO immediately without waiting for 220, and skips any non-250 responses until the EHLO acknowledgement arrives.
Troubleshooting
To test your SMTP configuration without going through the MCP layer, clone the repo and run test.js directly:
git clone https://github.com/xstast24/mcp-simple-mail-sender.git
cd mcp-simple-mail-sender
npm install
SMTP_HOST=smtp.example.com \
[email protected] \
EMAIL_PASS=your-password \
node test.js [email protected] "Test subject" "Test body"Add any extra flags (SMTP_TRIGGER_FIRST=true, SMTP_EHLO_HOST=myhostname, etc.) to the same command.
Note:
test.jsis not included in the published npm package — cloning the repository is required to use it.
Running locally
npm install
node index.js # starts the MCP server on stdio