MCP Servers

A collection of Model Context Protocol servers, templates, tools and more.

R
Resend Streamable MCP Server

MCP Server for interacting with resend.com. Written in TypeScript, Node and Hono.dev

Created 12/6/2025
Updated 7 days ago
Repository documentation and setup instructions

Resend MCP Server

Streamable HTTP MCP server for email and newsletter management via Resend.

Author: overment

[!WARNING] This server gives an AI agent access to your email sending capabilities. Language models can make mistakes, misinterpret instructions, or send unintended emails. Always:

  • MAKE SURE that the client you use with this MCP requires you to REVIEW and CONFIRM the action before use.
  • Test with a small segment first
  • Broadcasts are always scheduled (minimum 5 minutes ahead) so you can cancel if needed (default is 5 minutes)
  • Set RESEND_DEFAULT_FROM to a verified sender address

Motivation

Traditional email APIs require knowing exact IDs, segment structures, and API quirks. This server is designed so AI agents can:

  • Send newsletters — broadcast to segments with proper formatting
  • Manage subscribers — add, update, remove contacts in bulk
  • Use human identifiers — segment names and emails, not UUIDs
  • Handle formatting — automatic multipart emails (HTML + plain text)
  • Track campaigns — view delivery status, cancel scheduled sends

The result: an agent that can reliably manage your newsletter without you touching the Resend dashboard.

Features

  • Contacts — Add, update, remove, search contacts (bulk-capable, up to 100 per call)
  • Segments — Create, list, delete segments and manage membership
  • Send — Individual emails or broadcast to segments
  • Campaigns — View history, check delivery status, cancel scheduled
  • Templates — List available templates with variables
  • Subscriptions — Topic opt-in/opt-out management
  • Multipart Emails — Auto-generates HTML + plain text for best deliverability
  • Dual Runtime — Node.js/Bun or Cloudflare Workers

Design Principles

  • LLM-friendly: Task-oriented tools, not 1:1 API mirrors
  • Bulk-first: Contact operations accept arrays by default
  • Human identifiers: Use emails and segment names, not UUIDs
  • Multipart by default: Plain text auto-generates HTML for proper rendering
  • Clear feedback: Results include summaries and next-step suggestions

Server Instructions (What the Model Sees)

Use these tools to manage email contacts, segments, and send emails via Resend.

WORKFLOW:
1. find_contacts or segments(list) → discover existing data
2. upsert_contacts → add/update subscribers
3. send → individual email or broadcast to segment
4. campaigns(status) → track delivery

FORMATTING (for body):
- Use \n\n between paragraphs
- Use \n between list items
- Plain text is auto-converted to multipart (HTML + text) for proper rendering

IMPORTANT:
- Segment names are case-insensitive
- Templates must be published in Resend dashboard to use
- Broadcasts use segment name, not ID

Quick Start

Prerequisites

1. Install

cd resend-mcp
bun install
cp env.example .env

2. Configure

Get your Resend API key from resend.com/api-keys.

Edit .env:

PORT=3000
AUTH_ENABLED=true
AUTH_STRATEGY=bearer

# Generate with: openssl rand -hex 32
BEARER_TOKEN=your-random-auth-token

# Resend credentials
RESEND_API_KEY=re_your_resend_api_key
RESEND_DEFAULT_FROM=newsletter@yourdomain.com

3. Run

bun dev
# MCP: http://127.0.0.1:3000/mcp

4. Connect Client

Alice App:

  • URL: http://127.0.0.1:3000/mcp
  • Type: streamable-http
  • Header: Authorization: Bearer <your-BEARER_TOKEN>

Claude Desktop / Cursor:

{
  "mcpServers": {
    "resend": {
      "command": "npx",
      "args": ["mcp-remote", "http://localhost:3000/mcp", "--transport", "http-only"],
      "env": { "NO_PROXY": "127.0.0.1,localhost" }
    }
  }
}

Tools

upsert_contacts

Add or update contacts in bulk. Creates if email doesn't exist, updates if it does.

// Input
{
  contacts: [
    { email: "john@example.com", first_name: "John", last_name: "Doe" },
    { email: "jane@example.com", properties: { plan: "pro", company: "Acme" } }
  ],
  segments?: ["Newsletter", "Premium"],  // Add to these segments
  unsubscribed?: false                   // Set subscription status
}

// Output
{
  results: [{ email, ok, id, action: "created" | "updated", error? }],
  summary: { created: 1, updated: 1, failed: 0 }
}

remove_contacts

Permanently delete contacts from your list.

// Input
{ emails: ["user@example.com", "other@example.com"] }

// Output
{ results: [{ email, ok, error? }], summary: { deleted: 2, failed: 0 } }

find_contacts

Search and filter contacts with pagination.

// Input
{
  segment?: "Newsletter",      // Filter by segment name
  email?: "user@example.com",  // Find specific contact
  unsubscribed?: false,        // Filter by status
  limit?: 50,                  // Max 100
  cursor?: "..."               // From previous response
}

// Output
{
  items: [{ id, email, first_name, last_name, unsubscribed, created_at, properties }],
  has_more: boolean,
  cursor?: string
}

segments

Manage segments and their members.

// List all segments
{ action: "list" }
→ { items: [{ id, name, contact_count, created_at }] }

// Create segment
{ action: "create", name: "VIP Customers" }
→ { id, name, ok: true }

// Delete segment (contacts remain in system)
{ action: "delete", name: "Old Segment" }

// Add contacts to segment
{ action: "add_contacts", name: "Newsletter", contacts: ["a@x.com", "b@x.com"] }

// Remove contacts from segment
{ action: "remove_contacts", name: "Newsletter", contacts: ["a@x.com"] }

send

Send individual emails or broadcast to segments.

// Individual email
{
  to: "user@example.com",        // or array up to 50
  subject: "Welcome!",
  body: "Hello {{{FIRST_NAME}}}!\n\nWelcome to our newsletter.",
  from_name?: "Alice Newsletter",
  reply_to?: "support@example.com",
  schedule_for?: "2024-12-25T10:00:00Z"
}

// Broadcast to segment
{
  segment: "Newsletter",         // Segment name (case-insensitive)
  subject: "Weekly Update",
  name?: "Week 42 Newsletter",   // Dashboard display name
  body: "HEADLINE\n\nFirst paragraph.\n\nSecond paragraph.",
  schedule_for?: "in 2 hours"    // Natural language supported
}

// Using template
{
  to: "user@example.com",
  template: "welcome-email",     // Template alias or ID
  variables: { CTA_TEXT: "Get Started", DISCOUNT: "20%" }
}

Formatting tips:

  • Use \n\n between paragraphs
  • Use \n between list items
  • Plain text is auto-converted to HTML with proper line breaks
  • Personalization: {{{FIRST_NAME}}} or {{{FIRST_NAME|Friend}}}

campaigns

View and manage broadcast campaigns.

// List recent campaigns
{ action: "list", limit?: 20 }
→ { items: [{ id, name, subject, segment_name, status, sent_at, recipients_count }] }

// Get detailed status
{ action: "status", campaign_id: "bc123..." }
→ { id, status, sent_count, delivered_count, opened_count, failed_count }

// Cancel scheduled campaign
{ action: "cancel", campaign_id: "bc123..." }
→ { id, cancelled: true }

subscriptions

Manage topic preferences for contacts.

// Subscribe to topic
{ emails: "user@example.com", action: "subscribe", topic: "Product Updates" }

// Unsubscribe from topic
{ emails: ["a@x.com", "b@x.com"], action: "unsubscribe", topic: "Newsletter" }

// Global unsubscribe (from all emails)
{ emails: "user@example.com", action: "unsubscribe_all" }

templates

List available email templates.

// Input
{ limit?: 50, cursor?: "..." }

// Output
{
  items: [{ id, alias, name, subject, from, variables: [{ key, type, fallback }] }],
  has_more: boolean
}

Examples

1. Import subscribers and send welcome email

// Add contacts to Newsletter segment
{
  "name": "upsert_contacts",
  "arguments": {
    "contacts": [
      { "email": "alice@example.com", "first_name": "Alice" },
      { "email": "bob@example.com", "first_name": "Bob" }
    ],
    "segments": ["Newsletter"]
  }
}

Response:

Added 2 contact(s). Created: 2, Updated: 0, Failed: 0.
Next: Use 'find_contacts' to verify or 'send' to email them.

2. Send a broadcast newsletter

{
  "name": "send",
  "arguments": {
    "segment": "Newsletter",
    "subject": "This Week in AI",
    "name": "Week 42 Newsletter",
    "body": "HIGHLIGHTS\n\nClaude 4 was released this week with impressive new capabilities.\n\nKEY FEATURES\n\n• Extended context window\n• Improved reasoning\n• Better code generation\n\nREAD MORE\n\nCheck out the full announcement on our blog."
  }
}

Response:

Broadcast to segment "Newsletter" queued for sending (Campaign ID: abc123).
Use 'campaigns' tool to track delivery.

3. Check campaign delivery

{
  "name": "campaigns",
  "arguments": { "action": "status", "campaign_id": "abc123" }
}

Response:

Campaign abc123: sent
- Sent: 1,234
- Delivered: 1,198
- Opened: 456 (38%)
- Failed: 36

4. Unsubscribe a user

{
  "name": "subscriptions",
  "arguments": {
    "emails": "unhappy@example.com",
    "action": "unsubscribe_all"
  }
}

Response:

Updated 1 contact(s). Success: 1, Failed: 0.

Authentication Flow

┌─────────────────────────────────────────────────────────────────┐
│  Client (Alice App, Claude Desktop)                             │
│      │                                                          │
│      │ Authorization: Bearer <BEARER_TOKEN>                     │
│      ▼                                                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  MCP Server (Node.js / Cloudflare Worker)                   ││
│  │                                                             ││
│  │  1. Validate BEARER_TOKEN (client auth)                     ││
│  │  2. Use RESEND_API_KEY internally                           ││
│  │                                                             ││
│  │  RESEND_API_KEY ──────────► Resend API                      ││
│  │  RESEND_DEFAULT_FROM ─────► (api.resend.com)                ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

Key points:

  • BEARER_TOKEN: Random token you generate — authenticates MCP clients
  • RESEND_API_KEY: Your Resend API key — never exposed to clients
  • RESEND_DEFAULT_FROM: Verified sender address — required

Deployment Options

Local Development (Node.js/Bun)

bun dev
# Endpoint: http://127.0.0.1:3000/mcp

Cloudflare Worker (Local Dev)

# Create .dev.vars with secrets
echo "BEARER_TOKEN=your_token" >> .dev.vars
echo "RESEND_API_KEY=re_xxx" >> .dev.vars
echo "RESEND_DEFAULT_FROM=newsletter@yourdomain.com" >> .dev.vars

bun x wrangler dev --local | cat
# Endpoint: http://127.0.0.1:8787/mcp

Cloudflare Worker (Production)

# Set secrets
bun x wrangler secret put BEARER_TOKEN
bun x wrangler secret put RESEND_API_KEY
bun x wrangler secret put RESEND_DEFAULT_FROM

# Deploy
bun x wrangler deploy
# Endpoint: https://<worker>.<account>.workers.dev/mcp

HTTP Endpoints

| Endpoint | Method | Purpose | |----------|--------|---------| | /mcp | POST | MCP JSON-RPC 2.0 | | /mcp | GET | SSE stream (for notifications) | | /health | GET | Health check |


Environment Variables

| Variable | Required | Description | |----------|----------|-------------| | RESEND_API_KEY | ✓ | Resend API key from resend.com/api-keys | | RESEND_DEFAULT_FROM | ✓ | Verified sender address (e.g., newsletter@yourdomain.com) | | BEARER_TOKEN | ✓ | Auth token for MCP clients (generate with openssl rand -hex 32) | | PORT | | Server port (default: 3000) | | HOST | | Server host (default: 127.0.0.1) | | AUTH_ENABLED | | Enable auth (default: true) | | AUTH_STRATEGY | | Auth strategy (default: bearer) | | LOG_LEVEL | | debug, info, warn, error (default: info) |


Architecture

src/
├── index.ts                    # Node.js entry point
├── worker.ts                   # Cloudflare Workers entry point
├── config/
│   ├── env.ts                  # Environment parsing
│   └── metadata.ts             # Server & tool descriptions
├── core/
│   └── mcp.ts                  # McpServer builder
├── shared/
│   └── tools/
│       └── resend/             # Tool definitions
│           ├── upsert-contacts.ts
│           ├── remove-contacts.ts
│           ├── find-contacts.ts
│           ├── segments.ts
│           ├── send.ts
│           ├── campaigns.ts
│           ├── subscriptions.ts
│           └── templates.ts
├── services/
│   └── resend/
│       └── client.ts           # Resend API client
├── schemas/
│   └── outputs.ts              # Zod output schemas
└── http/
    ├── app.ts                  # Hono HTTP app
    └── routes/
        └── mcp.ts              # MCP endpoint handler

Development

bun dev           # Start with hot reload (note: sessions clear on reload)
bun start         # Production mode (stable sessions)
bun run typecheck # TypeScript check
bun run lint      # Lint code
bun run build     # Production build

Testing with MCP Inspector:

bunx @modelcontextprotocol/inspector
# Connect to: http://localhost:3000/mcp
# Add header: Authorization: Bearer <your-BEARER_TOKEN>

Troubleshooting

| Issue | Solution | |-------|----------| | 401 Unauthorized | Check BEARER_TOKEN matches in server and client | | "RESEND_API_KEY not configured" | Set RESEND_API_KEY in .env or secrets | | "RESEND_DEFAULT_FROM not configured" | Set RESEND_DEFAULT_FROM to a verified sender | | "Segment not found" | Use segments(action='list') to see available segments | | "Template not found" | Ensure template is published in Resend dashboard | | No newlines in email | Already fixed — emails are multipart (HTML + text) | | Stale session after restart | Disconnect and reconnect client (hot reload clears sessions) | | Rate limit (429) | Resend default is 2 req/s. Wait for retry-after header | | Tools not showing | Reconnect client — session may be stale |


License

MIT