Skip to main content
Glama
iceener

Resend MCP Server

by iceener

Resend MCP Server

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

Protocol status: candidate 2026-07-28, using exact MCP server/client 2.0.0-beta.5. This is not a claim of final protocol conformance.

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.

Related MCP server: Sequenzy MCP Server

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 — Fetch-native Bun or Cloudflare Workers

  • Modern + Legacy — Candidate 2026-07-28 plus SDK stateless legacy fallback

  • Credential Separation — MCP access tokens are never forwarded to Resend

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
MCP_AUTH_MODE=static-bearer

# Generate with: openssl rand -hex 32
MCP_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-MCP_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 <MCP_BEARER_TOKEN>                 │
│      ▼                                                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  MCP Server (Node.js / Cloudflare Worker)                   ││
│  │                                                             ││
│  │  1. Validate MCP_BEARER_TOKEN or OAuth RS token             ││
│  │  2. Use RESEND_API_KEY internally                           ││
│  │                                                             ││
│  │  RESEND_API_KEY ──────────► Resend API                      ││
│  │  RESEND_DEFAULT_FROM ─────► (api.resend.com)                ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

Key points:

  • MCP_BEARER_TOKEN: Optional random MCP client token; legacy BEARER_TOKEN remains a compatibility alias

  • RESEND_API_KEY: Server-side Resend key — never exposed to clients and never sourced from an MCP bearer

  • 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 "MCP_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 MCP_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

Modern or stateless-legacy MCP exchange

/mcp

GET, DELETE

405 Method Not Allowed (no server-created sessions)

/health

GET

Candidate protocol/runtime status


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)

MCP_AUTH_MODE

none, static-bearer, or oauth (default: none)

MCP_BEARER_TOKEN

For static auth

MCP client token (legacy BEARER_TOKEN is accepted)

MCP_PUBLIC_URL

Production

Canonical MCP resource URL

MCP_ALLOWED_HOSTS

Strict Host allowlist

MCP_ALLOWED_ORIGIN_HOSTNAMES

Strict browser Origin hostname allowlist

PORT

Server port (default: 3000)

HOST

Server host (default: 127.0.0.1)

LOG_LEVEL

debug, info, warn, error (default: info)


Architecture

src/
├── index.ts                    # Bun fetch entry
├── worker.ts                   # Cloudflare Workers entry
├── config/                     # Validated runtime/provider config + metadata
├── core/                       # Fresh McpServer factory + isolate handler
├── http/                       # Auth, body bounds, Host/Origin/CORS boundary
├── services/resend/client.ts   # Preserved Resend provider client
├── schemas/outputs.ts          # Complete Zod v4 output schemas
└── shared/tools/resend/        # Preserved email/contact/campaign/template tools

Development

bun dev           # Start with hot reload
bun start         # Run the stateless HTTP server
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
# For static auth: Authorization: Bearer <your-MCP_BEARER_TOKEN>

Troubleshooting

Issue

Solution

401 Unauthorized

Check MCP_AUTH_MODE and MCP_BEARER_TOKEN, or the configured OAuth RS token

"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)

Rate limit (429)

Resend default is 2 req/s. Wait for retry-after header

Tools not showing

Reconnect the client and verify it negotiated modern 2026-07-28 or the stateless legacy fallback


License

MIT

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iceener/resend-streamable-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server