I wanted to manage my Mailchimp campaigns from Claude without copy-pasting data between tabs. The existing MCP servers for Mailchimp were either read-only, incomplete, or abandoned weekend projects with 3-5 tools.
So I built one with 33 tools that actually covers the full API — campaigns, audiences, members, segments, templates, automations, and reports. Read and write.
What It Does
MCP lets AI assistants interact with external tools directly. With this server installed, instead of tab-switching between Claude and Mailchimp, you just ask:
Before (manual):
- Open Mailchimp
- Navigate to Campaigns → Reports
- Find last campaign
- Copy the stats
- Paste into Claude
- Ask for analysis
After (with mcp-mailchimp):
"How did my last campaign perform? Compare the open rate to my previous 5 campaigns."
Claude calls list_campaigns, then get_campaign_report for each, and gives you the analysis — all in one shot.
33 Tools, Full Read/Write
Not just listing data — you can actually do things:
- Campaigns (8): Create, send, schedule, replicate, test, update
- Content (2): Get and set campaign HTML/templates
- Reports (3): Performance stats, click-level details, open tracking
- Audiences (3): List, get details, create new lists
- Members (6): Add, update, upsert, archive, search, activity history
- Tags (2): List and manage member tags
- Segments (3): List, view members, create from emails
- Templates (2): List and view template HTML
- Automations (3): List, pause, start classic workflows
- Account (1): Validate API key and get account info
Technical Decisions Worth Sharing
The Subscriber Hash Gotcha
Mailchimp doesn't identify members by email or UUID. It uses an MD5 hash of the lowercased email. Get this wrong and you get silent 404s — no error message, just empty responses.
@staticmethod
def subscriber_hash(email: str) -> str:
return hashlib.md5(email.lower().strip().encode()).hexdigest()
Every Mailchimp integration learns this the hard way. My client handles it automatically so the tools just accept plain email addresses.
Upsert Over Separate Create/Update
Instead of separate "add" and "update" tools, I use Mailchimp's PUT endpoint for member management (add_or_update_member). It's a safe upsert — exists? update. New? create.
The critical detail: it uses status_if_new so it won't accidentally resubscribe someone who previously unsubscribed. That's a compliance landmine most implementations miss.
Formatted Responses, Not API Dumps
Mailchimp's API responses are verbose — nested _links, redundant metadata, fields nobody needs. Every tool extracts just the useful fields:
@mcp.tool()
async def get_campaign_report(campaign_id: str) -> str:
"""Get performance report for a sent campaign."""
mc = get_client()
r = await mc.get(f"/reports/{campaign_id}")
return _fmt({
"emails_sent": r.get("emails_sent", 0),
"opens": {
"unique": r.get("opens", {}).get("unique_opens", 0),
"rate": r.get("opens", {}).get("open_rate", 0),
},
"clicks": {
"unique": r.get("clicks", {}).get("unique_clicks", 0),
"rate": r.get("clicks", {}).get("click_rate", 0),
},
"bounces": {
"hard": r.get("bounces", {}).get("hard_bounces", 0),
"soft": r.get("bounces", {}).get("soft_bounces", 0),
},
"unsubscribes": r.get("unsubscribed", 0),
})
The AI gets clean data it can reason about. No parsing gymnastics.
FastMCP Makes Tool Definition Trivial
The official mcp Python SDK with FastMCP generates JSON Schema from type hints and docstrings automatically. A tool is just an async function:
@mcp.tool()
async def list_campaigns(
status: str = "",
list_id: str = "",
count: int = 20,
offset: int = 0,
) -> str:
"""List email campaigns. Filter by status or audience."""
# ... implementation
No schema files, no registration boilerplate. Type hints become parameter descriptions. Docstrings become tool descriptions. It just works.
Get Started in 2 Minutes
Install
pip install mcp-mailchimp
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"mailchimp": {
"command": "mcp-mailchimp",
"env": {
"MAILCHIMP_API_KEY": "your-key-us21"
}
}
}
}
Claude Code
claude mcp add mailchimp -- env MAILCHIMP_API_KEY=your-key mcp-mailchimp
Cursor
Same JSON config as Claude Desktop in .cursor/mcp.json.
What I'd Do Differently
Start with fewer tools. 33 tools gives comprehensive coverage, but 15-20 would handle 95% of use cases. The automation and segment tools are niche — campaigns and members are where the value is.
Test with real data sooner. Mailchimp's free tier is now just 250 contacts and 500 sends/month. I developed mostly against the API docs, which meant some edge cases only showed up during real testing.
Lessons for MCP Server Builders
If you're building MCP servers, here's what Mailchimp specifically taught me:
- Watch for silent identity schemes. Mailchimp uses MD5 hashes of lowercased emails as member IDs — no error if you get it wrong, just empty responses. If your target API has non-obvious ID formats, abstract them away.
-
Prefer upsert over create+update. Mailchimp's PUT endpoint does a safe upsert with
status_if_new, so it won't accidentally resubscribe someone. Idempotent operations are always safer when an AI is making the calls. -
Strip verbose responses. Mailchimp returns
_links, nested metadata, and redundant fields in every response. Extract only what the AI needs to reason about — the fewer tokens, the better the analysis. - Write copy-paste configs. Claude Desktop, Claude Code, Cursor — give people the exact JSON block. Every friction point between install and first use loses a user.
Links
- GitHub: AlexlaGuardia/mcp-mailchimp
- PyPI: mcp-mailchimp
- License: MIT
This is part of a series of production-grade MCP servers I'm building for underserved SaaS platforms. Also available: WooCommerce, FreshBooks, ActiveCampaign. Follow me here or on GitHub to catch the next one.
Top comments (0)