npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hasna/emails

v0.4.29

Published

Email management CLI + MCP server + dashboard for Resend and AWS SES

Downloads

3,148

Readme

@hasna/emails

Email management CLI + MCP server for AI agents. Send, log, search, and analyse emails across Resend, AWS SES, and Gmail from a single terminal interface or via MCP tools that any AI agent can call.

Install

npm install -g @hasna/emails
# or
bun add -g @hasna/emails

Quick Start (5 commands to send your first email)

# 1. Add a Resend provider
emails provider add --name "My Resend" --type resend --api-key re_xxxx

# 2. Add a sending domain
emails domain add yourdomain.com --provider <provider-id>

# 3. Check DNS records to configure in your DNS registrar
emails domain dns yourdomain.com

# 4. Add a sender address
emails address add [email protected] --provider <provider-id>

# 5. Send your first email
emails send --from [email protected] --to [email protected] \
  --subject "Hello from emails CLI" --body "It works!"

Providers

Resend Setup

emails provider add \
  --name "Resend Production" \
  --type resend \
  --api-key re_YOUR_API_KEY

Get your API key at resend.com/api-keys.

AWS SES Setup

emails provider add \
  --name "SES Production" \
  --type ses \
  --region us-east-1 \
  --access-key AKIAIOSFODNN7EXAMPLE \
  --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

The IAM user needs ses:SendEmail, ses:GetIdentity, and ses:ListIdentities permissions.

Gmail OAuth Setup

emails provider add \
  --name "My Gmail" \
  --type gmail \
  --client-id YOUR_OAUTH_CLIENT_ID \
  --client-secret YOUR_OAUTH_CLIENT_SECRET

The command opens a browser for the OAuth consent flow and saves the refresh token automatically. To re-authenticate (when tokens expire):

emails provider auth <provider-id>

Sandbox Provider (Dev Mode)

The sandbox provider captures emails locally without delivering them — ideal for development and CI.

emails provider add --name dev --type sandbox

Use it like any other provider. Emails sent to it are stored in the local database and printed to stderr:

[sandbox] Email captured: Welcome, Alice → [email protected] (id: abc12345)

Inspect captured emails:

emails sandbox list                    # List all captured emails
emails sandbox show <id>               # Show full email details
emails sandbox open <id>               # Open HTML in browser
emails sandbox clear                   # Delete all captured emails
emails sandbox count                   # Show count

Inbound Email

Receive inbound emails over SMTP or via a webhook from Resend.

SMTP listener

emails inbound listen --port 2525

Starts a minimal SMTP server. Point your mail transfer agent (or test scripts) at localhost:2525.

Resend inbound webhook

In the Resend dashboard configure an inbound route pointing to POST /api/inbound on your server (or expose locally with ngrok). The endpoint accepts Resend's inbound JSON payload and stores the email.

# Expose locally for testing
ngrok http 3456
emails webhook --port 3456
# Then configure https://your-ngrok-url/api/inbound in Resend

Inspect inbound emails

emails inbound list                    # List received emails
emails inbound show <id>               # Show full details
emails inbound open <id>               # Open HTML in browser
emails inbound clear                   # Delete all received emails
emails inbound count                   # Show count

Reliability

Multi-provider failover

Configure one or more fallback providers. If the primary provider fails at send time, the CLI automatically retries each failover provider in order.

emails config set failover-providers <id1>,<id2>

The send command prints a warning when a failover is used:

⚠ Send failed on Resend Production, trying failover...
  (Used failover provider)

Idempotency keys

Pass --idempotency-key to prevent duplicate sends. If an email with the same key was already sent, the existing record is returned instead of re-sending — safe to call repeatedly on retries.

emails send --from [email protected] --to [email protected] \
  --subject "Order confirmed" --body "..." \
  --idempotency-key order-9876

Deliverability

List-Unsubscribe headers (RFC 8058)

Pass --unsubscribe-url to automatically inject List-Unsubscribe and List-Unsubscribe-Post headers conforming to RFC 8058 one-click unsubscribe — required by Gmail and Yahoo for bulk senders.

emails send --from [email protected] --to [email protected] \
  --subject "Newsletter" --body "..." \
  --unsubscribe-url "https://yourdomain.com/unsubscribe?token=abc"

Bounce and complaint alerts

Set thresholds (as percentages). After each emails pull, if the bounce or complaint rate for the last 30 days exceeds the threshold a warning is printed to stderr.

emails config set bounce-alert-threshold 5       # Alert when bounce rate > 5%
emails config set complaint-alert-threshold 0.1  # Alert when complaint rate > 0.1%

CLI Reference

All commands support --json for machine-readable output, -q for quiet mode, and -v for verbose debug output.

provider

Manage email providers.

emails provider add --name <name> --type <resend|ses|gmail|sandbox> [credentials...]
emails provider list
emails provider update <id> [--name <name>] [credentials...]
emails provider remove <id>
emails provider auth <id>          # Re-run OAuth flow (Gmail only)
emails provider status             # Health-check all active providers

domain

Manage sending domains.

emails domain add <domain> --provider <id>
emails domain list [--provider <id>]
emails domain dns <domain>         # Show required DNS records
emails domain verify <domain>      # Re-check DNS verification status
emails domain status [--provider <id>]
emails domain remove <id>

address

Manage sender email addresses.

emails address add <email> --provider <id> [--name "Display Name"]
emails address list [--provider <id>]
emails address verify <email>      # Check verification status
emails address remove <id>

send

Send an email. Supports templates, groups, scheduling, attachments, failover, idempotency keys, and List-Unsubscribe headers.

emails send \
  --from [email protected] \
  --to [email protected] \
  --subject "Subject line" \
  --body "Plain text body"

# HTML email
emails send --from [email protected] --to [email protected] \
  --subject "Hello" --body "<h1>Hi</h1>" --html

# Send to a group
emails send --from [email protected] --to-group newsletter \
  --subject "Weekly update" --body "..."

# Send using a template with variables
emails send --from [email protected] --to [email protected] \
  --template welcome --vars '{"name":"Alice","link":"https://example.com"}'

# Schedule for later
emails send --from [email protected] --to [email protected] \
  --subject "Scheduled" --body "Hi" --schedule "2025-06-01T09:00:00Z"

# With attachments
emails send --from [email protected] --to [email protected] \
  --subject "Report" --body "See attached" --attachment report.pdf

# With List-Unsubscribe (RFC 8058)
emails send --from [email protected] --to [email protected] \
  --subject "Newsletter" --body "..." \
  --unsubscribe-url "https://yourdomain.com/unsubscribe?token=abc"

# With idempotency key (safe to retry)
emails send --from [email protected] --to [email protected] \
  --subject "Order confirmed" --body "..." \
  --idempotency-key order-9876

# Pipe body from stdin
cat message.txt | emails send --from [email protected] --to [email protected] \
  --subject "From pipe"

# Override provider
emails send --from [email protected] --to [email protected] \
  --subject "Test" --body "Hi" --provider <provider-id>

batch

Send bulk emails to a list of recipients from a CSV file, using a template for the body.

emails batch \
  --from [email protected] \
  --template welcome \
  --csv recipients.csv

The CSV must have an email column. Any additional columns are available as template variables (e.g. {{name}}).

email,name
[email protected],Alice
[email protected],Bob

template

Manage reusable email templates. Subjects and bodies support {{variable}} placeholders.

emails template add welcome \
  --subject "Welcome, {{name}}!" \
  --html "<h1>Hi {{name}}</h1><p><a href='{{link}}'>Get started</a></p>" \
  --text "Hi {{name}}! Get started: {{link}}"

emails template list
emails template show <name>
emails template remove <name>

Load templates from files:

emails template add newsletter \
  --subject "{{title}}" \
  --html-file templates/newsletter.html \
  --text-file templates/newsletter.txt

group

Manage recipient groups for bulk sending.

emails group create <name> [--description "..."]
emails group list
emails group show <name>
emails group add <name> <email> [--name "Display Name"] [--vars '{"k":"v"}']
emails group delete <name> <email>           # Remove a member
emails group delete <name>                   # Remove the group

Send to a group:

emails send --from [email protected] --to-group newsletter \
  --subject "Weekly update" --template newsletter --vars '{}'

sandbox

Inspect emails captured by sandbox providers (see Sandbox Provider).

emails sandbox list [--provider <id>] [--limit <n>]
emails sandbox show <id>
emails sandbox open <id>
emails sandbox clear [--provider <id>]
emails sandbox count [--provider <id>]

inbound

Receive and inspect inbound emails (see Inbound Email).

emails inbound listen [--port 2525] [--provider <id>]
emails inbound list [--provider <id>] [--limit <n>]
emails inbound show <id>
emails inbound open <id>
emails inbound clear [--provider <id>]
emails inbound count [--provider <id>]

log

View the sent email log.

emails log
emails log --provider <id>
emails log --limit 50
emails log --status delivered
emails log --from [email protected]

search

Full-text search across sent emails.

emails search "keyword"
emails search "alice" --provider <id> --limit 20

test

Run a connectivity test against a provider.

emails test
emails test --provider <id>

contacts

Manage the contact list. Suppressed contacts are skipped on send (unless --force is used).

emails contacts list
emails contacts list --suppressed       # Show only suppressed contacts
emails contacts suppress <email>
emails contacts unsuppress <email>

scheduled

Manage scheduled emails.

emails scheduled list
emails scheduled list --status pending
emails scheduled cancel <id>

scheduler

Run the background scheduler that sends due emails.

emails scheduler start                  # Start the scheduler daemon
emails scheduler run                    # Process due emails once and exit

pull

Pull delivery events from a provider into the local database.

emails pull
emails pull --provider <id>
emails pull --since 2025-01-01

stats

View delivery statistics.

emails stats
emails stats --provider <id>
emails stats --period 7d              # 1d, 7d, 30d, 90d

analytics

Detailed analytics with ASCII charts: daily send volume, delivery and bounce trends, top recipients, and busiest hours.

emails analytics
emails analytics --provider <id>
emails analytics --period 30d         # 7d, 30d, 90d

monitor

Live dashboard in the terminal (auto-refreshes).

emails monitor
emails monitor --provider <id>
emails monitor --interval 30          # Refresh interval in seconds

doctor

Run diagnostics: checks provider connectivity, DNS configuration, database integrity, and scheduled-email backlog.

emails doctor

completion

Generate shell completion scripts.

emails completion bash   >> ~/.bashrc
emails completion zsh    >> ~/.zshrc
emails completion fish   > ~/.config/fish/completions/emails.fish

webhook

Start a local webhook server to receive delivery events from Resend or AWS SES.

emails webhook --port 3456
emails webhook --port 3456 --provider <id>

Expose the server with a tunnel (e.g. ngrok):

ngrok http 3456
# Then configure https://your-ngrok-url/webhook/resend in the Resend dashboard
# or  https://your-ngrok-url/webhook/ses   in the SNS subscription

Supported paths:

  • POST /webhook/resend — Resend event payloads
  • POST /webhook/ses — AWS SNS notification payloads

export

Export emails and events to CSV or JSON.

emails export emails --format csv --output emails.csv
emails export emails --format json --output emails.json
emails export events --format csv --output events.csv
emails export events --format json

config

Manage CLI configuration stored at ~/.emails/config.json.

emails config get default_provider
emails config set default_provider <provider-id>
emails config set failover-providers <id1>,<id2>
emails config set bounce-alert-threshold 5
emails config set complaint-alert-threshold 0.1
emails config list

MCP Setup

Install the MCP server into Claude Code (or any MCP-compatible agent):

emails mcp --claude

This registers emails-mcp as a user-scoped MCP server in ~/.claude.json.

For other agents:

emails mcp --codex
emails mcp --gemini
emails mcp --all

Available MCP tools:

| Tool | Description | |------|-------------| | send_email | Send an email (supports idempotency key, unsubscribe URL) | | list_emails | List sent emails with filters | | search_emails | Full-text search across sent emails | | list_providers | List configured providers | | get_stats | Delivery statistics for a provider | | get_analytics | Detailed analytics with daily breakdown | | run_doctor | Run system diagnostics | | pull_events | Pull delivery events from a provider | | export_emails | Export emails to CSV or JSON | | list_contacts | List contacts and suppression status | | manage_templates | Create, list, and remove templates | | list_sandbox_emails | List emails captured by sandbox providers | | list_inbound_emails | List received inbound emails |

Dashboard

Start the web dashboard at http://localhost:3000 (or a custom port):

emails serve
emails serve --port 8080

The dashboard shows real-time delivery metrics, recent emails, bounce rates, and provider health across all configured providers.

Configuration

| File | Description | |------|-------------| | ~/.emails/config.json | CLI configuration (default provider, failover providers, alert thresholds, log level) | | ~/.emails/emails.db | SQLite database — all emails, events, contacts, templates, sandbox and inbound emails |

Override the database path:

EMAILS_DB_PATH=/path/to/custom.db emails send ...
# Use an in-memory database (for testing):
EMAILS_DB_PATH=:memory: emails ...

Library Usage

@hasna/emails can also be imported as a library in your own Bun or Node project.

import {
  getDatabase,
  createEmail,
  listEmails,
  searchEmails,
  upsertEvent,
  listEvents,
  createProvider,
  listProviders,
  getAdapter,
  parseResendWebhook,
  parseSesWebhook,
  createWebhookServer,
  getLocalStats,
  getAnalytics,
  batchSend,
} from "@hasna/emails";

// Get an adapter for a provider and send an email
const db = getDatabase();
const [provider] = listProviders(db).filter(p => p.active);
const adapter = getAdapter(provider);

const messageId = await adapter.sendEmail({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Hello",
  text: "Hello world",
  unsubscribe_url: "https://yourdomain.com/unsubscribe?token=abc",
  idempotency_key: "order-9876",
});

// Start a webhook server
const server = createWebhookServer(3456, provider.id);
// server.stop() when done

// Parse incoming webhook payloads yourself
const event = parseResendWebhook(incomingBody);
if (event) {
  upsertEvent({ provider_id: provider.id, ...event }, db);
}

License

Apache-2.0 — © Andrei Hasna