Skip to content
Conduit

mcp server

yases

Yet Another SES Wrapper

valtlfelipe/yasescommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 26, 2026

A self-hosted email service built on AWS SES with a beautiful dashboard for managing identities, tracking deliveries, and handling bounces.

Features

  • Send Emails — Programmatic email sending via REST API or MCP
  • Domain Management — Add and verify sending domains with automatic DKIM setup
  • Suppression List — Automatic bounce and complaint handling
  • Real-time Analytics — Track delivery, bounce, open, and click rates
  • Email Timeline — View detailed status and events for each sent email

Self-Hosted Deployment Guide

Deploy on Railway

Prerequisites

  • A server with a public HTTPS URL (required for AWS SNS webhooks)
  • PostgreSQL database
  • Redis/Valkey
  • AWS account with production SES enabled

Docker

Pull the image from GitHub Container Registry:

# Run the app
docker run -d \
  -p 3000:3000 \
  -e DATABASE_URL=postgres://user:pass@host:5432/db \
  -e REDIS_URL=redis://host:6379 \
  -e BETTER_AUTH_SECRET=xxx \
  -e BETTER_AUTH_URL=https://your-domain.com \
  -e TOKEN_SECRET=xxx \
  -e CREDENTIALS_ENCRYPTION_KEY=xxx \
  ghcr.io/valtlfelipe/yases:latest

# Run the worker (separate container)
docker run -d \
  -e DATABASE_URL=postgres://user:pass@host:5432/db \
  -e REDIS_URL=redis://host:6379 \
  -e BETTER_AUTH_SECRET=xxx \
  -e BETTER_AUTH_URL=https://your-domain.com \
  -e TOKEN_SECRET=xxx \
  -e CREDENTIALS_ENCRYPTION_KEY=xxx \
  ghcr.io/valtlfelipe/yases:latest bun server/workers/index.ts

AWS IAM

Create this policy and attach them to your IAM user that will be used by YASES.

This is needed to setup SES sending and webhooks, as managing domains and sending email.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SESSetup",
            "Effect": "Allow",
            "Action": [
                "ses:CreateConfigurationSet",
                "ses:GetConfigurationSet",
                "ses:DeleteConfigurationSet",
                "ses:CreateConfigurationSetEventDestination",
                "ses:GetConfigurationSetEventDestinations",
                "ses:UpdateConfigurationSetEventDestination",
                "ses:CreateEmailIdentity",
                "ses:GetEmailIdentity",
                "ses:DeleteEmailIdentity",
                "ses:PutEmailIdentityDkimAttributes",
                "ses:PutEmailIdentityDkimSigningAttributes",
                "ses:PutEmailIdentityMailFromAttributes"
            ],
            "Resource": "*"
        },
        {
            "Sid": "SNSSetup",
            "Effect": "Allow",
            "Action": [
                "sns:CreateTopic",
                "sns:GetTopicAttributes",
                "sns:SetTopicAttributes",
                "sns:Subscribe",
                "sns:ListSubscriptionsByTopic"
            ],
            "Resource": "*"
        },
        {
            "Sid": "SESTenants",
            "Effect": "Allow",
            "Action": [
                "ses:CreateTenant",
                "ses:GetTenant",
                "ses:DeleteTenant",
                "ses:CreateTenantResourceAssociation",
                "ses:DeleteTenantResourceAssociation",
                "ses:GetReputationEntity"
            ],
            "Resource": "*"
        },
        {
            "Sid": "STSGetAccountId",
            "Effect": "Allow",
            "Action": [
                "sts:GetCallerIdentity"
            ],
            "Resource": "*"
        },
        {
            "Sid": "SESSendEmail",
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        }
    ]
}

3. Create Your Admin Account

Open your browser and navigate to /signup to create the first user. This user will have admin privileges and can access all features.

4. Configure AWS SES

Configure SES from the dashboard:

  1. Open Settings → Providers and click Add Provider
  2. Select AWS SES, add your AWS Access Key ID, Secret Access Key, and region
  3. Test the connection, then click the setup action (Configure webhooks) for that provider
  4. Enter your public host (for example https://api.yourdomain.com) and run Setup Webhooks
  5. Go to Settings → Domains and add your sending domain
  6. Copy the DNS records shown (DKIM, MAIL FROM, DMARC) to your DNS provider
  7. After DNS propagation (can take up to 72 hours), click sync on the domain row to refresh verification status

Finally, request production access in AWS Console → SES → Account dashboard (if you're still in sandbox).

5. Generate an API Key

After creating your account:

  1. Navigate to Settings in the dashboard
  2. Click "Create API Key"
  3. Copy the generated key — it will only be shown once

Use this key in the x-api-key header to authenticate API requests.


API Reference

Send Email

Send an email via the REST API.

Endpoint: POST /api/emails/send

Authentication: Pass your API key via the x-api-key header (recommended) or Authorization: Bearer <key> header.

Request Body:

FieldTypeRequiredDescription
tostringYesRecipient email address (e.g., "User <user@example.com>" or just "user@example.com")
fromstringYesSender email address (must be a verified identity, e.g., "Sender <noreply@yourdomain.com>")
subjectstringYesEmail subject line
htmlstringYes*HTML email body (*required if text is not provided)
textstringYes*Plain text email body (*required if html is not provided)
replyTostringNoReply-to address
typestringNoEmail type: "transactional" (default) or "marketing"

Transactional vs Marketing emails:

  • transactional — password resets, receipts, notifications. Bypasses unsubscribe suppression; other suppression reasons (bounces, complaints) still block sending.
  • marketing — newsletters, promotions. Respects all suppressions including unsubscribes. Requires a {{unsubscribeUrl}} placeholder in the html or text body — the server replaces it with a signed, one-click unsubscribe link before sending.

Example — transactional:

curl -X POST https://your-domain.com/api/emails/send \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key-here" \
  -d '{
    "to": "recipient@example.com",
    "from": "Sender <noreply@yourdomain.com>",
    "subject": "Your receipt",
    "html": "<p>Thanks for your purchase!</p>",
    "text": "Thanks for your purchase!"
  }'

Example — marketing:

curl -X POST https://your-domain.com/api/emails/send \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key-here" \
  -d '{
    "to": "recipient@example.com",
    "from": "Sender <noreply@yourdomain.com>",
    "subject": "This month'\''s newsletter",
    "type": "marketing",
    "html": "<p>Hello!</p><p><a href=\"{{unsubscribeUrl}}\">Unsubscribe</a></p>",
    "text": "Hello!\n\nUnsubscribe: {{unsubscribeUrl}}"
  }'

Success Response (202 Accepted):

{
  "id": "cmq123abc456",
  "status": "queued"
}

When the recipient is suppressed, the email is recorded but not sent:

{
  "id": "cmq123abc456",
  "status": "suppressed",
  "reason": "unsubscribed"
}

Error Responses:

StatusErrorDescription
400Validation failedInvalid request body
400UNVERIFIED_IDENTITYSender domain is not verified in SES
400INVALID_EMAILRecipient email is invalid or disposable
400MISSING_UNSUBSCRIBE_URLMarketing email is missing the {{unsubscribeUrl}} placeholder
401Invalid API keyMissing or invalid API key

MCP Server

You can also send emails via the Model Context Protocol (MCP), allowing AI agents to send emails on your behalf.

Configuration

Add the MCP server to your AI client (Claude Desktop, Cursor, etc.):

{
  "mcpServers": {
    "yases-email": {
      "url": "https://your-domain.com/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Replace YOUR_API_KEY with an API key from Settings → API Keys.

Available Tools

send_email

Send an email to a recipient.

Parameters:

FieldTypeRequiredDescription
tostringYesRecipient email address
fromstringYesSender email address (must be a verified identity)
subjectstringYesEmail subject line
htmlstringYes*HTML email body (*required if text is not provided)
textstringYes*Plain text email body (*required if html is not provided)
replyTostringNoReply-to address
typestringNo"transactional" (default) or "marketing"

License

MIT License — see LICENSE for details.

↑↓ navigate openesc close