About mailchimp-mcp-server
mailchimp-mcp-server lets teams manage Mailchimp campaigns, audiences, and subscribers from an MCP client. It combines guarded campaign sending with subscriber operations and post-send reporting.
Key features
- 01Draft, test, send, or schedule campaigns
- 02Manage audiences and subscriber records
- 03Create and update segments and merge fields
- 04Review campaign reports and engagement
- 05Use local assets and templates optionally
Use cases
- 01Prepare and test a newsletter before sending
- 02Import or update audience subscribers
- 03Review clicks, opens, and unsubscribes
- 04Author campaigns with local templates
MCP support
Exposes Mailchimp account, audience, subscriber, campaign, template, and reporting operations through MCP tools, resources, and a newsletter prompt. Supports STDIO and Streamable HTTP.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
npx -y @cyanheads/mailchimp-mcp-serverMAILCHIMP_API_KEYSecretMailchimp Marketing API key, including the '-dc' data-center suffix (e.g. 'abc...-us22'). Generate at Mailchimp → Account → Extras → API keys.MAILCHIMP_BASE_URLOverride the API base URL (for mock servers or testing). Defaults to 'https://{dc}.api.mailchimp.com/3.0' derived from the API key suffix.MAILCHIMP_TIMEOUT_MSPer-request timeout in milliseconds.MAILCHIMP_MAX_RETRIESMax retry attempts for transient upstream failures (0-10).MAILCHIMP_CONCURRENCY_LIMITMax in-flight upstream requests across this process (1-10).MAILCHIMP_ASSETS_DIRAbsolute path to a local assets directory. When set (Node-only — Workers ignored), enables the `mailchimp_assets` tool and auto-uploads `@assets/<path>` references in campaign HTML to Mailchimp File Manager at send time. Cache at <dir>/.mailchimp-cache.json.MAILCHIMP_TEMPLATES_DIRAbsolute path to a local templates directory. When set (Node-only), enables the `mailchimp_local_templates` tool and support for `content.localTemplate` on campaign tools. Templates are `.eta` files with optional YAML frontmatter; legacy `<name>.meta.yaml` sidecars are read when frontmatter is absent. Canonical write path on free-tier Mailchimp.MCP_LOG_LEVELMinimum log level for output: debug, info, notice, warning, or error.MCP_TRANSPORT_TYPESelects the HTTP transport.MCP_HTTP_HOSTThe hostname for the HTTP server.MCP_HTTP_PORTThe port to run the HTTP server on.MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.MCP_AUTH_MODESecretAuthentication mode to use: 'none', 'jwt', or 'oauth'.Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.
Tools
Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.
Details
From the project README@cyanheads/mailchimp-mcp-server
Draft, test, and send Mailchimp campaigns straight from your MCP client — with audience management, subscriber CRUD, and post-send analytics behind safe-by-default send gates. STDIO or Streamable HTTP.
Overview
Mailchimp campaign management. Draft, test, and send email campaigns, manage audiences and subscribers, and review post-send analytics, with optional local image and template directories for authoring.
Tools
| Tool | Description |
|---|---|
mailchimp_account | Account profile, plan, data center, and the Chimp Chatter activity feed |
mailchimp_audiences | Read, create, and update audiences (lists), with per-audience analytics and signup-form config |
mailchimp_audience_overview | One-call audience digest: info, stats, growth history, top email clients, merge-field schema |
mailchimp_subscribers | Subscriber reads, updates, archive, tags, notes, and engagement history |
mailchimp_upsert_subscriber | Add or update one subscriber idempotently, with status, merge fields, tags, and a note |
mailchimp_find_subscriber | Locate a subscriber by email in one audience or across the account |
mailchimp_import_subscribers | Batch add or update up to 500 subscribers per call, pending by default |
mailchimp_segments | Segment CRUD (saved, static, fuzzy), member listing, and batch add/remove |
mailchimp_merge_fields | Read, create, and update custom subscriber attributes |
mailchimp_campaigns | Campaign records, content, send checklist, and RSS/resend controls |
mailchimp_send_campaign | Compose a campaign and leave it as a draft, test it, send it, or schedule it in one call |
mailchimp_replicate_campaign | Duplicate a campaign with overrides, then draft, test, send, or schedule |
mailchimp_reports | Campaign reports: the index, one report, or one of ten report dimensions |
mailchimp_campaign_report | Post-send digest: headline metrics plus top links, locations, and unsubscribes |
mailchimp_templates | Mailchimp-hosted templates; reads work on free plans, writes need a paid plan |
mailchimp_files | File Manager uploads, listing, renames, and deletes on Mailchimp's CDN |
mailchimp_search | Search members or campaigns across the account |
mailchimp_assets (conditional: MAILCHIMP_ASSETS_DIR) | Inspect and pre-warm uploads for @assets/<path> references in campaign HTML |
mailchimp_local_templates (conditional: MAILCHIMP_TEMPLATES_DIR) | Author and render local .eta templates, the template write path on free plans |
mailchimp_playbook | Procedural playbooks merged with live account state; advice only |
Resources
| Resource | Description |
|---|---|
mailchimp://account | Account snapshot: profile, plan, data center, total subscribers |
mailchimp://audiences/{audienceId} | Audience snapshot: name, contact, stats, double opt-in status |
mailchimp://campaigns/{campaignId} | Campaign snapshot: status, settings, recipients |
mailchimp://campaigns/{campaignId}/report | Post-send headline metrics for a campaign |
All resource data is also reachable through tools; audiences and campaigns are listed with each tool's list operation.
Prompts
| Prompt | Description |
|---|---|
newsletter_from_source | Compose an editorial newsletter from a URL or brief, then walk it through draft, test, and send |
Design reference: docs/email-design-playbook.md.
Capability reference
mailchimp_account tool
operation: inforeturns profile, plan, data center, total subscribers, and industry benchmarks;operation: activity-feedreturns Chimp Chatter events, paged bycount(max 100, default 20) andoffset- An empty activity feed carries a
notenaming the likely causes
mailchimp_audiences tool
- Operations:
list,get,create,update, the analytics readslist-activity,list-growth,list-clients,list-abuse-reports,list-locations, andget-signup-forms/customize-signup-forms(takessignupFormConfig); every operation exceptlistandcreateneedsaudienceId createrequiresname,contact(company,address1,city,state,zip,country),permissionReminder, andcampaignDefaults(fromName,fromEmail,language)
mailchimp_audience_overview tool
audienceIdplusgrowthMonths(1–36, default 12); returns info, stats, contact, campaign defaults, growth history, top email clients, and the merge-field schemanotes[]explains empty growth or email-client arrays, separating "no data yet" from zero engagement
mailchimp_subscribers tool
audienceIdon every call andemailon every operation exceptlist(filterable bystatus); operations:list,get,update,archive,list-tags,set-tags,list-notes/add-note/update-note/delete-note(bynoteId), andlist-activity/list-events/list-goals- Addresses are trimmed and rejected if the local or domain part is missing; an empty
listemail stays unfiltered.updatetosubscribedasks for confirmation when resuming a subscription; already-subscribed members are left as they are set-tagsis declarative:tagsbecomes the full active set and every other tag is removed unless named inpreserveTags(static-segment membership is stored as a tag). ReturnstagsAdded,tagsRemoved, andtagsActive
mailchimp_upsert_subscriber tool
audienceId,email, andstatusrequired; optionalmergeFields,tags+preserveTags(same declarative sync asset-tags),note,vip,language;updateExistingStatus: falseappliesstatusto new records only- Returns
isNew, the resultingstatus,tagsAdded/tagsRemoved/tagsFinal,noteAttached, andwebUrl status: "pending"uses Mailchimp's double opt-in flow. New or resumedsubscribedstatus asks for confirmation of documented consent before anything is written; declining writes nothing. Already-subscribed updates andupdateExistingStatus: falsekeep the existing status without asking
mailchimp_find_subscriber tool
emailaccepts an address or free-form search fragment, including a fragment without@, scoped to oneaudienceIdor, when omitted, every audience on the account (echoed assearchedAcross)- Returns
exactMatchesandfuzzyMatcheswith merge fields and stats;includeTags(defaulttrue) adds each match's tags at one extra call per match
mailchimp_import_subscribers tool
audienceIdand 1–500subscribersrows per call;statusdefaults topending(double opt-in) and a per-rowstatusoverrides it;updateExistingdefaults tofalse, so existing records are skipped- Any row that would be
subscribedneeds one confirmation before the whole batch writes; declining writes nothing. Pending-only imports need none - Returns
totalCreated,totalUpdated,errorCount, and per-rowsucceededandfailedentries; incomplete addresses fail asinvalid_email, and a batch with no valid rows makes no upstream request
mailchimp_segments tool
audienceIdon every call andsegmentIdon every operation exceptlist(filterable bytype:saved,static,fuzzy) andcreate;create/updatetakename,staticEmails, or saved-segmentoptionsbatch-update-membersappliesmembersToAdd/membersToRemoveto a static segment and returnsadded,removed, and per-emailerrors- Free plans get static and basic saved segments; advanced conditions need Premium and fail as
mailchimp_forbidden
mailchimp_merge_fields tool
list,get,create,update;createrequiresname,tag(10 characters max), andtype;get/updatetakemergeIdoptionscarries type-specific config:choicesfordropdown/radio,date_formatfordate/birthday,phone_formatforphone
mailchimp_campaigns tool
- Operations:
list(filter bystatus,listId,sinceSendTime/beforeSendTime),get,create(typeofregular/plaintext/rss, plusrecipientsandsettings),update,replicate,get-content,set-content,get-checklist,cancel-send,create-resend,pause-rss,resume-rss; all butlistandcreatetakecampaignId set-contentacceptshtml,plainText,templateId+templateSections,archiveContent,url, orlocalTemplate(exclusive withhtmlandtemplateId);get-checklistreturnsisReadyand typed itemsget-contentacceptsvariant: html | plainText | archiveHtml | all(defaultall); empty bodies are omitted- No send, test, schedule, or delete here; dispatch goes through
mailchimp_send_campaignormailchimp_replicate_campaign
mailchimp_send_campaign tool
audienceId,subject,fromName,replyTo, andcontent(at least one ofhtml,plainText,templateId,localTemplate) required;modeisdraft(default),test(needstestEmails, max 50),send, orschedule(needsscheduleTimeat least 15 minutes out)- Returns
campaignId, the effectivemode,status,recipientCount,webUrl, and non-blockingchecklistWarnings; blocking checklist errors throwpre_send_checklist_failedoutsidedraft, andcleanupOnError(defaulttrue) deletes the draft after a mid-flow failure sendandschedulerequireconfirmSend: true, then a single-use confirmation prompt before any campaign is created; declining leaves a draft and setscancelledByUser. Changed content or a replay asks again, and creation is never retried after an ambiguous upstream failure
mailchimp_replicate_campaign tool
sourceCampaignIdplus optionalsubjectOverride,previewTextOverride,fromNameOverride,replyToOverride,titleOverride,audienceOverride,segmentOverride, andcontentOverride;mode,testEmails,scheduleTime, andcleanupOnErrorwork as inmailchimp_send_campaign- Returns the new
campaignId,overridesApplied[], and the same status, checklist, andcancelledByUserfields - Same
confirmSend: trueplus confirmation-prompt gate forsendandschedule
mailchimp_reports tool
list(filter bytype,sinceSendTime/beforeSendTime),get, orslicewith acampaignIdand adimension:abuse-reports,advice,click-details(one URL vialinkId),open-details(one member viasubscriberHash),domain-performance,eepurl,email-activity,locations,sent-to,unsubscribedslicereturnsrowsshaped by the dimension plustotalItems;getthrowscampaign_not_sentfor a campaign with no send
mailchimp_campaign_report tool
campaignIdplusincludeTopN(1–100, default 10) rows per slice- Returns
delivery(delivered, bounces by type, abuse reports),engagement(opens, clicks, unsubscribes),topClickedLinks,topLocations,recentUnsubscribes, andindustryBenchmarkswhen Mailchimp has them; throwscampaign_not_sentfor a campaign with no send
mailchimp_templates tool
list(filter bytypeofuser/base/gallery,category,folderId),get,get-default-content,create,update, anddelete;updateaccepts onlyname,html, andfolderId, so per-section edits go through a campaign'stemplateSections- Reads of
baseandusertemplates work on free plans; writes andgalleryreads need a paid plan and fail asmailchimp_forbidden.mailchimp_local_templatesis the authoring path on every plan
mailchimp_files tool
list(filter bytype,folderId,sinceCreatedAt/beforeCreatedAt),get,upload(namewith extension plus base64fileData, nodata:prefix),update(rename, or move withfolderId,0for root),delete, and read-onlylist-folders/get-folder; uploads cap at 1 MB per image and 10 MB per other file, and.webp/.avifare not accepteduploadandgetreturnfullSizeUrl, the public CDN URL to embed in campaign HTML;listaddstotalFileSizeInBytes
mailchimp_search tool
scope(membersorcampaigns) andquery;audienceIdnarrows a member search;includeTopN(1–100, default 10) caps each result array- Members return as
exactandfuzzywith upstreamtotalExact/totalFuzzy; campaigns asmatcheswith asnippetandtotalMatches; an empty result carries anote
mailchimp_assets tool
list,info(byrelPath; returnssha256and anycachedupload),sync(uploads uncached files ahead of a send; returnsuploaded,cached, andskippedwith reasons), andclear-cache@assets/<path>references in content passed tomailchimp_send_campaign,mailchimp_replicate_campaign, ormailchimp_campaignsset-contentupload to File Manager and are rewritten to CDN URLs. Uploads are cached by SHA-256 in<assetsDir>/.mailchimp-cache.json; oversize files and paths outside the directory are rejected before upload- Listed only when
MAILCHIMP_ASSETS_DIRis set
mailchimp_local_templates tool
list,get(source plus parsed metadata),render-preview(nameplusvars; returns HTML, sends nothing), andseed-from-mailchimp(writes Mailchimp templatemailchimpTemplateIdto disk asname)- Templates are
.etafiles with optional YAML frontmatter forsubject,previewText, andvars; a declared var left unsupplied fails the render. Campaign tools render one viacontent.localTemplate+content.localTemplateVars, exclusive withhtmlandtemplateId. Example templates - Listed only when
MAILCHIMP_TEMPLATES_DIRis set
mailchimp_playbook tool
topic:send,post-send-review,deliverability,list-hygiene,onboarding,subscriber-triage, ordesign-campaign;audienceIdforsend/list-hygiene/design-campaign,campaignIdforpost-send-review,emailforsubscriber-triage- Returns markdown
instructionstuned to live numbers, aliveStatesnapshot, andnextToolSuggestionswith pre-filled inputs; makes no writes
mailchimp://account resource
- No parameters;
application/jsonwith profile, plan, data center, total subscribers, andfetchedAt - Same data as
mailchimp_accountoperation: info
mailchimp://audiences/{audienceId} resource
- Name, contact, stats, campaign defaults, permission reminder, double opt-in status, and subscribe URL
audienceIdcomes frommailchimp_audiencesoperation: list
mailchimp://campaigns/{campaignId} resource
- Status, settings, recipients, tracking, and report summary
campaignIdcomes frommailchimp_campaignsoperation: list
mailchimp://campaigns/{campaignId}/report resource
- Emails sent, bounces, opens, clicks, unsubscribes, abuse reports, and industry stats
- Fails with a validation error until the campaign has been sent
newsletter_from_source prompt
- Arguments:
source(URL or free-form brief) required;audienceIdandseasonalContextoptional - Returns one user message that chains
mailchimp_playbook(topic: design-campaign) into an HTML draft, then draft, test, and send viamailchimp_send_campaign
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Mailchimp-specific:
- Marketing API v3 client that derives the API host and data center from the key's
-<dc>suffix, with retries, a process-wide concurrency limit (MAILCHIMP_CONCURRENCY_LIMIT), and a cooldown after a 429 - Send gates: campaigns default to
draft,send/scheduleneedconfirmSend: trueplus a confirmation prompt, and new or resumedsubscribedsubscriptions need confirmation too; imports default topending - No irreversible deletes: removing audiences, merge fields, or campaigns, and permanently deleting subscribers, stay in the Mailchimp UI;
archiveis the strongest subscriber removal - Local authoring:
@assets/<path>files upload and rewrite at send time, and.etatemplates render locally, which covers free plans where the Templates API is read-only - List operations page with
count(max 1000) andoffset
Agent-friendly output:
- Explained empties:
note/notes[]on empty activity feeds, audience overviews, and searches say whether data is missing or genuinely zero - Partial failure:
mailchimp_import_subscribersandmailchimp_segmentsbatch-update-membersreturn per-row successes and errors instead of failing the batch - Discriminated outputs:
operation,dimension, andmodesay which fields are populated;isNewandcancelledByUserreport what actually happened - Typed errors: every declared reason (
campaign_not_sent,pre_send_checklist_failed,mailchimp_forbidden, …) carries a recovery hint for the caller's next move
Getting started
Add the following to your MCP client configuration file. See docs/api-key.md for how to generate a Mailchimp API key.
{
"mcpServers": {
"mailchimp-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/mailchimp-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"MAILCHIMP_API_KEY": "your-key-with-dc-suffix-e.g.-us22"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"mailchimp-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/mailchimp-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"MAILCHIMP_API_KEY": "your-key-with-dc-suffix-e.g.-us22"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"mailchimp-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "MAILCHIMP_API_KEY=your-key-with-dc-suffix-e.g.-us22",
"ghcr.io/cyanheads/mailchimp-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 MAILCHIMP_API_KEY=... bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- A Mailchimp Marketing API key. Its
-dcsuffix (e.g.-us22) identifies your data center and is parsed at startup.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/mailchimp-mcp-server.git
- Navigate into the directory:
cd mailchimp-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# edit .env and set MAILCHIMP_API_KEY
Configuration
| Variable | Description | Default |
|---|---|---|
MAILCHIMP_API_KEY | Required. Marketing API key, including the -dc suffix (e.g. abc…-us22). | none |
MAILCHIMP_BASE_URL | API base URL override, for mock servers or tests. | https://{dc}.api.mailchimp.com/3.0 |
MAILCHIMP_TIMEOUT_MS | Per-request timeout, in ms. | 60000 |
MAILCHIMP_MAX_RETRIES | Retry attempts for transient upstream failures (0–10). | 3 |
MAILCHIMP_CONCURRENCY_LIMIT | Max in-flight upstream requests across the process (1–10). | 4 |
MAILCHIMP_ASSETS_DIR | Absolute path to a local assets directory. Enables mailchimp_assets and @assets/<path> uploads in campaign HTML. | none |
MAILCHIMP_TEMPLATES_DIR | Absolute path to a local templates directory. Enables mailchimp_local_templates and content.localTemplate on campaign tools. | none |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | Session mode; confirmations need stateful, and stateless refuses HTTP startup (stdio is unaffected). | stateful |
MCP_REQUEST_STATE_KEY | Optional sealing key, at least 32 bytes. Confirmations are single-use and tied to one process, so a restart or another instance asks again. | none |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOGS_DIR | Directory for log files (Node.js only). | <app-root>/logs |
OTEL_ENABLED | Enable OpenTelemetry. | false |
OTEL_EXPORTER_OTLP_ENDPOINT | Base URL for traces and metrics. | none |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | OTLP log endpoint; log export is off unless this is set. | none |
LOG_TOOL_FAILURE_PAYLOADS | Log failed-call arguments and results, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). Secrets inside free-form values remain visible. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
-
Watch mode (transport via
MCP_TRANSPORT_TYPE):bun run dev # stdio (default) MCP_TRANSPORT_TYPE=http bun run dev # http -
Build and run:
bun run rebuild bun run start:stdio # or bun run start:http -
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t mailchimp-mcp-server .
docker run --rm -e MAILCHIMP_API_KEY=your-key-us22 -p 3010:3010 mailchimp-mcp-server
The Dockerfile defaults to HTTP transport, stateful session mode, and logs to /var/log/mailchimp-mcp-server. OpenTelemetry peer dependencies are installed by default; build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point: registers tools, resources, and the prompt, and initializes services. |
src/config | MAILCHIMP_* environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts) plus shared helpers for template rendering, asset rewriting, and send confirmation. |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts). |
src/services/mailchimp | Mailchimp API client: HTTP, request pacing, retries, response normalization. |
src/services/assets | Local assets: discovery, hashing, upload cache, @assets/ rewriting. |
src/services/templates | Local templates: Eta rendering, frontmatter parsing, seeding from Mailchimp. |
templates/ | Example .eta templates; point MAILCHIMP_TEMPLATES_DIR here to try them. |
docs/ | API key guide and the email design playbook. |
tests/ | Vitest suites for config, services, tools, and the prompt. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging - Register new tools and resources via the barrels in
src/mcp-server/*/definitions/index.ts - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.