Pine Mail — Tiny SMTP Mail Catcher for Developers & AI Agents
Pine Mail
A tiny, single-binary SMTP mail catcher for developers and AI agents. Catch every email your app sends — locally, instantly, no cloud required.
The absurd problem with testing email
In 2026, we have solved distributed consensus, edge computing, and neural code generation. But testing email verification is still fundamentally broken.
-
Cloud sandbox SaaS
Mailtrap, SendGrid Sandbox, and others solve the basics — but your local test suite now depends on external network connections, third-party API keys, webhook configurations, and monthly subscription tiers.
-
Classic local catchers
MailHog and Mailpit are great for human eyes looking at a browser tab. For automated tests you get polling loops, flaky timing, and unparsed MIME payloads that need hours of regex debugging.
-
The AI agent wall
Autonomous agents driving your test suites can't interact with email cleanly. Asking an LLM to navigate a webmail UI via headless browser is slow, fragile, and expensive.
What Pine Mail actually is
A lightweight, single-binary SMTP mail catcher built specifically for local development, automated test suites, and autonomous AI agents. Point your app's SMTP at localhost:1025. It intercepts every email. View them in a web UI, query via REST API, or connect an AI agent with its native MCP server.
-
<15 MB
Single binary
-
<5 ms
Startup time
-
~12 MB
RAM usage
-
Zero
Cloud accounts needed
Quick start
$ docker run -d --name pinemail -p 1025:1025 -p 8025:8025 yoosuf/pinemail:latest
$ git clone https://github.com/yoosuf/pinemail.git && cd pinemail && cargo build --release
No JVM. No Node.js. No Docker required. One file on disk does everything.
Two features that change everything
Pine Mail was built around two specific capabilities designed to eliminate test flakiness and agent friction.
Long-polling wait endpoint
The HTTP request stays open. The instant the SMTP server finishes processing an incoming message, it pushes the payload back and closes the connection. Zero sleep() calls. Zero polling loops. Your assertions run the millisecond the email exists.
Automatic signal extraction
No more parsing MIME boundaries in your test scripts. Pine Mail extracts verification codes, magic links, and password reset URLs automatically. You get clean, structured JSON with exactly what you need.
Long-polling: /api/wait
Instead of juggling sleep timers or polling loops in your test suite, use a single blocking request that resolves the instant the email lands.
curl "http://localhost:8025/api/[email protected]&subject=Verify&timeout_ms=15000"
Signal extraction: /api/messages/:id/extract
In 95% of automated test scenarios you don't care about MIME boundaries or CSS styling. You care about the verification code and the reset URL. Pine Mail extracts them for you.
{
"codes": ["849201"],
"links": [
"http://localhost:3000/auth/verify?token=d8f1e09a8b2c4d5e"
],
"action_urls": [
"http://localhost:3000/auth/verify?token=d8f1e09a8b2c4d5e"
]
}
What this looks like in an automated test
Fast, robust, and completely deterministic. If the email delivery fails inside your app, the test fails immediately on the timeout without hanging.
import requests
# 1. Trigger the signup in your application
signup_resp = requests.post("http://localhost:3000/api/signup", json={
"email": "[email protected]",
"password": "CorrectHorseBatteryStaple!"
})
assert signup_resp.status_code == 201
# 2. Block until Pine Mail receives the verification email (no sleep needed)
email = requests.get(
"http://localhost:8025/api/wait",
params={
"to": "[email protected]",
"subject": "Confirm your account",
"timeout_ms": 10000
}
).json()
# 3. Pull the OTP code straight out of the parsed signals
extracted = requests.get(f"http://localhost:8025/api/messages/{email['id']}/extract").json()
otp_code = extracted["codes"][0]
# 4. Submit the verification code
verify_resp = requests.post("http://localhost:3000/api/verify-email", json={
"email": "[email protected]",
"code": otp_code
})
assert verify_resp.status_code == 200
Native MCP server for AI agents
Model Context Protocol has rapidly become the standard way AI agents interact with local developer tools. Pine Mail ships with a dedicated MCP server binary that exposes the entire inbox as native callable tools.
{
"mcpServers": {
"pinemail": {
"command": "pinemail-mcp",
"env": {
"PINEMAIL_URL": "http://localhost:8025"
}
}
}
}
Once wired up, your agent has access to seven targeted tools:
-
wait_for_email
Blocks until a matching email arrives. Zero polling.
-
extract_signals
Pulls OTP codes and URLs from parsed email content.
-
list_messages
Queries the inbox with pagination and filters.
-
get_message
Full HTML, plain text, and MIME data for any message.
-
delete_message
Clean up a specific message by ID.
-
clear_inbox
Reset state between test runs. Clean slate, every time.
-
send_test_email
Injects a simulated test message into the inbox.
Compatible with Claude Desktop, Cursor, Windsurf, Claude Code, and any MCP-capable autonomous framework. No brittle webmail scraping. No custom bash glue. The agent just does the job.
Why Rust?
For developer tooling that sits in the background of your daily workflow, three constraints matter — and Rust nails all three.
-
Zero runtime overhead
Compiles to a single, statically linked binary. Starts instantly and uses barely 12MB of resident memory. No Docker containers, no language runtimes eating battery.
-
Single-file distribution
The React web frontend is embedded directly into the binary at compile time via rust-embed. No node_modules, no static asset directories. One file does everything.
-
Resilience under load
Tokio's async runtime handles hundreds of simultaneous SMTP connections effortlessly. SQLite in WAL mode guarantees fast, reliable persistence without UI freezes.
The codebase is organized as a clean cargo workspace with three focused crates: pinemail-core (SQLite storage, MIME parsing, signal extraction), pinemail-server (SMTP + HTTP API + embedded dashboard), and pinemail-mcp (MCP stdio server). Pass --memory for CI to run with an in-memory database — tests fly, zero temp files left on disk.
What's on the roadmap
Currently at v0.1.0 and actively developed. A few capabilities in progress.
-
Incoming mail webhooks
Register a webhook endpoint so Pine Mail pushes an HTTP POST payload to your local server the moment a matching message arrives.
-
Rate-limiting simulation
Simulate real-world email provider throttling and temporary greylisting errors (421/450 responses) to test your application's email queue backoff logic.
-
IMAP read-only interface
For legacy apps or desktop email clients that insist on fetching mail via IMAP rather than checking a REST API.
Ready to stop fighting your test suite?
Pine Mail is free, open source under the MIT license. Get running in under 30 seconds.