Skip to main content
Glama

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

-
security - not tested
F
license - not found
-
quality - not tested

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