MCP Server
Give your AI agent an email address. Bottled speaks the Model Context Protocol, so Claude Desktop, Claude Code, and any MCP client can read, search, and send email from your custom domain.
Endpoints
Streamable HTTP is the primary transport. SSE is available for clients that need it.
POST https://bottled.email/api/mcp/mcp (Streamable HTTP)
GET https://bottled.email/api/mcp/sse (SSE)Authentication
Same API key as the REST API: a btl_ key from Dashboard > API, sent as a Bearer token. Every tool is scoped to your workspace.
Authorization: Bearer btl_YOUR_API_KEYConnect Claude
Claude Code (one command):
claude mcp add --transport http bottled \
https://bottled.email/api/mcp/mcp \
--header "Authorization: Bearer btl_YOUR_API_KEY"Or any MCP client via JSON config:
{
"mcpServers": {
"bottled": {
"url": "https://bottled.email/api/mcp/mcp",
"headers": {
"Authorization": "Bearer btl_YOUR_API_KEY"
}
}
}
}Two gates before any write
Nothing changes in your workspace without clearing both of them.
1. Write access is off until you turn it on
A freshly generated key is read-only over MCP. The write tools (send_email, reply_to_email, mark_emails, add_contact_note, rotate_api_key, and the *_draft tools) require the key's mcpAllowWrites flag, toggled per key in Dashboard > API. A read-only key gets a clear write_access_disabled error. list_drafts and get_draft only read, but they sit behind the same flag, because drafts are part of write access as a whole. This gates MCP tools only; the REST API is unaffected. Sends go through the same plan quotas and rate limits as the dashboard.
2. A human approves each write, one call at a time
Turning the flag on does not hand your agent a blank cheque. Before a tool changes anything, Bottled asks the person at the MCP client to approve that one specific action, spelling out the concrete details (recipient, subject, draft id) rather than a vague "allow writes?". Your agent cannot click it for you. If the prompt is declined, cancelled, times out, or errors, the call fails closed and comes back written: false with nothing changed. It works over MCP elicitation, so clients that support it (Claude Desktop does) show the prompt; older clients without the capability fall back to gate 1 on its own.
3. Key rotation asks for one more thing
rotate_api_key rotates the very key the session is authenticated with, so on top of both gates it needs an exact typed phrase (rotate-yourdomain.com). Called without it, the tool only returns an impact report and changes nothing. A confirmed rotation shows the new key once, keeps the old one working for a 24h grace window, and resets mcpAllowWrites back to off.
Tools
list_recent_emailsRecent emails, newest firstcheck_new_emailsUnread count plus previewsget_emailOne email with bodysearch_emailsFree-text search across mailget_usagePlan tier and quota usagesend_emailWRITESend from a verified addressreply_to_emailWRITEReply to a received emailmark_emailsWRITEMark read or unreadcreate_draftWRITESave an email as a draft for review. Re-drafting replaces the open draft to that recipient instead of adding a duplicate. Does not deliver, does not touch the send quotalist_draftsSaved drafts, newest first. Read-only, but still needs write access enabled on the keyget_draftOne draft with body. Read-only, but still needs write access enabled on the keyupdate_draftWRITERevise a draft in placesend_draftWRITESend a reviewed draft. This one deliversdelete_draftWRITEDelete a saved draftlist_domainsDomains with verification stateget_domainDNS records for one domainverify_domainWRITERe-check a domain's DNS and delivery setup and save the refreshed status. Confirm-gated, but does not need the write flaglist_aliasesAddresses and forwardingcreate_aliasWRITEAdd an address on one of your domains and route it to a real inboxlist_contactsCRM contactsget_contactFull contact profileadd_contact_noteWRITEAppend a note to a contactlist_campaignsCampaigns with resultscreate_campaignWRITEDraft a campaign for a whole list in one call; deduplicates and holds back unsubscribes. Sends nothinglist_campaign_recipientsA campaign's recipients and their per-recipient statussend_campaignWRITEDeliver a draft campaign — one individual email per recipient, each with its own unsubscribe linkcancel_campaignWRITECancel a campaign that hasn't finishedget_api_key_infoKey usage, expiry, and rotation status (never the key itself)rotate_api_keyWRITERotate this key; requires typing an exact confirmation phrase, old key gets a 24h grace windowExample: send
With write access enabled, ask your agent to email someone and it calls send_email:
{
"name": "send_email",
"arguments": {
"to": "recipient@example.com",
"subject": "Meeting notes",
"html": "<p>Notes attached below…</p>"
}
}If from is omitted, your workspace's primary verified address is used. Sends count against your plan's monthly quota.
Example: draft, review, then send
To read an email before it goes out, have your agent call create_draft instead. Same arguments as send_email, but nothing is delivered:
{
"id": 42,
"to": "recipient@example.com",
"subject": "Meeting notes",
"status": "draft",
"draftUrl": "https://bottled.email/dashboard/compose?draft=42"
}Open the draftUrl to see the email exactly as the recipient will, and edit or send it there. Or keep going in the agent: update_draft to revise, then send_draft to deliver. Drafts don't count against your monthly quota until they're sent.
Ready to plug in your agent?
Generate a key in Dashboard > API. Free tier included, no credit card.
Get Your API Key