Cursor + Claude Code guide
Let your coding agent
wire email, SMS and WhatsApp.
Connect the Unlimited Messaging MCP server to Cursor, Claude Code or Windsurf, paste one prompt, and the agent writes a notify module, sends a real test from your own accounts and checks that it arrived.
In a hurry? Summarize this article with an AI
The short answer
How do Cursor and Claude Code add email, SMS and WhatsApp to an app?
Give the agent two things: the Unlimited Messaging MCP server at https://mcp.unlimitedmessaging.app, so it can see your accounts and send a test message, and an API key in your .env, which the code it writes reads to call POST /message. One request then sends email, WhatsApp and SMS from your own accounts, on a free plan.
Coding agents are good at writing a fetch call and bad at guessing whether it worked. The MCP connection closes that gap: while it builds, the agent can list the mailboxes, WhatsApp numbers and Android phones you connected, send a test after you confirm, and read the delivery status, all through OAuth with no key pasted into the chat.
In your app itself, the code uses the REST API or the TypeScript and Python SDKs with an API key from the environment. Unlimited Messaging sends from accounts you already own: email from your Gmail or domain mailbox over IMAP/SMTP, WhatsApp from your number linked like WhatsApp Web, SMS from your Android phone's SIM. The free plan has no per-message fee; messages end with a short "Sent with UnlimitedMessaging.app" line.
It works the same in Cursor, Claude Code and Windsurf, and in any MCP client that supports remote servers with OAuth. None of them has an official partnership with Unlimited Messaging: they speak the open MCP protocol, and that is enough.
The MCP server in detail: MCP guide. In the Claude apps: Claude setup. Typed client for your code: the TypeScript SDK.

Why not hand-code it
Why is it slow to let an agent hand-code SMTP, Twilio and Meta?
The agent writes the integration in a minute. Then the real work starts: accounts, verification, platform rules and silent failures it cannot see.
Email: the SMTP snippet breaks in production
Agents love nodemailer, but Vercel blocks outgoing SMTP on port 25 and Supabase Edge Functions refuse ports 25 and 587. Switching to an email API means a verified domain and DNS records before anyone but you gets mail.
SMS: a number, a registration, a fee per segment
Twilio bills $0.0083 per segment in the US plus carrier fees, on a number you rent. Sending to US numbers from a regular long code also requires A2P 10DLC registration, with a $4.50 brand fee and vetting.
WhatsApp: templates and a review queue
Meta's Cloud API needs an approved template for any message outside the 24-hour customer service window, with review taking up to 24 hours and a price per template message. New accounts start at 250 unique recipients per 24 hours.
The agent cannot check its own work
Without a way to see the provider's side, the agent declares victory when the code compiles. Three providers also mean three keys in .env, three error formats and three webhook handlers to review.
Two ways in
MCP or API: which does the agent use, and when?
The MCP server is for the agent while it builds. The API key is for your app once it runs. Most projects use both.
| Hand-coded providersSMTP, Twilio, Meta | Unlimited Messaging MCPIn the coding agent | Unlimited Messaging APIIn your app's code | |
|---|---|---|---|
| Who sends | Your app, through three providers | The agent, after you confirm | Your app, through one endpoint |
| Authentication | Three sets of credentials | OAuth in the browser, no key in files | One API key from the environment |
| Channels | One provider per channel | Email, WhatsApp and SMS | Email, WhatsApp and SMS in one request |
| Can check delivery | Only through each provider's dashboard | Yes, list-messages with statuses | Yes, GET /message and webhooks on paid plans |
| Cost to test | Trials, then per message | Free plan | Free plan, footer line on messages |
Provider fees from Twilio's and Meta's own pages, checked on 9 November 2026.
Step by step
From zero to a tested integration in five steps
- 01
Connect the MCP server
Claude Code: run the one-line claude mcp add command below. Cursor: add the block to .cursor/mcp.json. Windsurf: add it to mcp_config.json with serverUrl.
- 02
Sign in through OAuth
On the first tool call, your browser opens the Unlimited Messaging sign-in. No account yet? It is created on the free plan, with no credit card. In Claude Code, /mcp starts the sign-in.
- 03
Connect your channels
Ask the agent to connect your mailbox or your WhatsApp (it shows the QR code), and install the Android app on a phone with a SIM for SMS.
- 04
Create an API key for the app
In the dashboard's API section, create a key and put it in .env as UM_API_KEY yourself. The agent only needs the variable name.
- 05
Paste the prompt
The agent writes the module, wires it to your trigger, then sends a test to you through MCP and confirms the status.
Copy, paste
What prompt do you paste into Cursor or Claude Code?
Paste this into the agent chat, in Agent mode. It works in Cursor, Claude Code and Windsurf. Name the event that should trigger the message.
Add Unlimited Messaging to this project so it can send email, WhatsApp and SMS (docs: https://docs.unlimitedmessaging.app).
1. Create a server-side module, src/lib/notify.ts (or notify.py), that calls POST https://api.unlimitedmessaging.app/message with "Authorization: Bearer ${UM_API_KEY}" and the body { "to": { "phone": "<E.164>", "email": "<email>" }, "subject": "...", "text": "...", "channels": [{ "channel": "EMAIL" }, { "channel": "WHATSAPP" }, { "channel": "SMS" }] }. Throw on a non-2xx response with the body in the error.
2. Read UM_API_KEY from the environment. Add it to .env.example with an empty value, check that .env is in .gitignore, and never read, print or hardcode the real key.
3. Call notify() when <your event: signup, new order, failed payment...>, only from server code.
4. Use the unlimited-messaging MCP tools: list-accounts to see which channels I connected (drop the others from the call), then, after asking me, send-message to my own contact as a test, and list-messages to confirm its status.# Claude Code
claude mcp add --transport http unlimited-messaging https://mcp.unlimitedmessaging.app
# Cursor: ~/.cursor/mcp.json or .cursor/mcp.json
{ "mcpServers": { "unlimited-messaging": { "url": "https://mcp.unlimitedmessaging.app" } } }
# Windsurf: mcp_config.json
{ "mcpServers": { "unlimited-messaging": { "serverUrl": "https://mcp.unlimitedmessaging.app" } } }// src/lib/notify.ts (server-side only)
type Channel = 'EMAIL' | 'WHATSAPP' | 'SMS';
export async function notify(
to: { phone?: string; email?: string },
text: string,
channels: Channel[],
subject?: string,
) {
const res = await fetch('https://api.unlimitedmessaging.app/message', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.UM_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
to, text, subject,
channels: channels.map((channel) => ({ channel })),
}),
});
if (!res.ok) throw new Error(`notify failed: ${res.status} ${await res.text()}`);
return res.json(); // one message per channel, sharing a dispatchId
}Safety
How do you keep the API key away from the agent and the repo?
The key belongs in .env, read by server code. Each tool gives you a way to keep the agent out of that file.
MCP needs no key at all
The agent's connection uses OAuth: tokens are stored by the client and can be revoked, in Claude Code from the /mcp menu. Nothing secret goes into .cursor/mcp.json or .mcp.json, so those files are safe to commit.
Claude Code: deny reading .env
Anthropic's settings docs show permission rules such as Read(./.env) and Read(./.env.*) under deny. Add them to .claude/settings.json and Claude Code cannot open the file.
Cursor: .cursorignore, with its limits
Files listed in .cursorignore are blocked from Agent, Tab, Inline Edit and @ mentions. Cursor's docs warn that terminal and MCP tools used by the agent are not covered, so do not rely on it alone.
Server code only
Never prefix the key with NEXT_PUBLIC_ or VITE_, and never import the notify module from client components. Review the agent's diff for that before you commit.
Keep the confirmation on
send-message sends real messages from your accounts. The server tells assistants to confirm each send, and your editor can ask before each tool call: do not auto-approve this one.
Replies
How does your app receive replies?
Replies always show up in the Unlimited Messaging inbox, and the agent can read them through list-messages while you build. For your app to react to them in production, you need webhooks, which come with the paid plans: Medium at €19 a month and Max at €29 a month.
Ask the agent for a webhook route, register its public URL on the Webhooks page of the dashboard, and subscribe to message.received (an inbound message) and message.status (sent, delivered, read, failed). Store the whsec_ secret as UM_WEBHOOK_SECRET in .env.
Deliveries are signed per the Standard Webhooks spec, with an HMAC-SHA256 over the raw body in the webhook-signature header. Answer 2xx within 10 seconds and process afterwards. Retries run for a little more than 3 days, so deduplicate on eventId.
{
"type": "message.received",
"eventId": "whd_...",
"occurredAt": "2026-11-09T10:00:01.000Z",
"data": {
"channel": "WHATSAPP",
"message": {
"text": "Yes, see you Monday!"
}
}
}import { createHmac } from 'node:crypto';
import { Buffer } from 'node:buffer';
// Standard Webhooks signature check, before trusting the body.
export function isValid(rawBody: string, h: Headers, secret: string) {
const id = h.get('webhook-id');
const ts = h.get('webhook-timestamp');
const sig = h.get('webhook-signature') ?? '';
if (!id || !ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = createHmac('sha256', key)
.update(`${id}.${ts}.${rawBody}`)
.digest('base64');
return sig.split(' ').some((s) => s === `v1,${expected}`);
}Limits
What are the limits, honestly?
Unlimited Messaging sends from personal accounts at a human pace: right for notifications and conversations, wrong for blasts.
WhatsApp is unofficial
Your number is linked as a device, like WhatsApp Web, outside Meta's Business Platform. Unsolicited messages get numbers banned. For official WhatsApp at scale, use Meta's Cloud API.
Email follows your mailbox
A personal Gmail account sends up to 500 emails a day. Newsletters and large volumes belong with an email service provider.
SMS at Android's pace
Android asks for confirmation beyond 30 SMS in 30 minutes, so the app spaces messages out. Texts count against your carrier plan, and the sending phone must be an Android.
Free plan trade-offs
Messages on Free end with "Sent with UnlimitedMessaging.app". Media, voice messages and webhooks need Medium or Max, which also remove the footer.
FAQ
Questions about messaging with coding agents
Sources
Sources & references
To check the information in this article for yourself:
- Model Context Protocol - official documentationModel Context Protocol
- Connect Claude Code to tools via MCP (claude mcp add --transport http, /mcp OAuth)Claude Code documentation - Anthropic
- Settings (permissions deny Read(./.env))Claude Code documentation - Anthropic
- Model Context Protocol (mcp.json, remote url, OAuth)Cursor documentation
- Ignore files (.cursorignore and what it does not cover)Cursor documentation
- Cascade MCP integration (serverUrl, OAuth for remote servers)Windsurf documentation
- Unlimited Messaging MCP server - client configsGitHub - UnlimitedMessaging
- Can I use SMTP with Vercel? (port 25 blocked)Vercel Knowledge Base
- Edge Function limits (ports 25 and 587 not allowed)Supabase documentation
- SMS pricing in the United States ($0.0083 per segment)Twilio
- A2P 10DLC Brand Registration ($4.50 brand fee)Twilio
- Message template review (decision within 24 hours)Meta for Developers
- WhatsApp messaging limits (250 unique recipients per 24 hours to start)Meta for Developers
- Limits for sending and getting mail (500 emails a day)Gmail Help
- SmsUsageMonitor (30 SMS in 30 minutes threshold)Android Open Source Project
- Standard Webhooks specificationStandard Webhooks

Written by
Ilian · Senior Software Engineer
A senior software engineer, he founds and runs several tech companies. He builds products around messaging APIs, automation and AI integration, and shares here what he learns while building.
Give your agent a mailbox, a number and a phone.
Create a free account, connect the MCP server in your editor, and paste the prompt.