As of 2026-07-13

What is a cold email MCP server? The cold-email MCP server for coding agents

A hosted, streamable-HTTP MCP server exposing 24 intent-level cold-email infrastructure tools, scoped by per-tenant bearer token and live in production. This page documents the exact tool surface — verified against the live tools/list response, not aspirational.

Live in production The endpoint below is reachable today and live for real sending (Gmail API, HTTPS/443) on activated accounts. New public signups default to an isolated sandbox tenant — no real domains, mailboxes, or sends — until activation. See FAQ for the full status disclosure.

What this MCP server does

Most cold-email MCP servers in the wild re-export a vendor's full API surface — a well-known example ships 100+ tools. This one is a curated, high-level facade: 24 intents that hide domain-registrar, mailbox-vendor, and sequencing-engine plumbing behind calls an agent can reason about in one context window. Content generation — the offer, the subject lines, the sequence body — stays your agent's job; this server owns infrastructure provisioning, sequencing, isolation, and compliance guardrails. Five of the 21 (get_dashboard, configure_dashboard, label_thread, list_campaigns, activity) are the same facade the optional human dashboard at /app uses — parity by design, never dashboard-only state. For how that tool count compares to larger vendor-wrapper MCP servers, see Smartlead vs Instantly MCP tool coverage.

How to add it

Streamable HTTP, JSON-RPC 2.0 (initialize, tools/list, tools/call), one config line, no OAuth dance. Get a token first via POST /signup (see the setup guide), then add:

{
  "mcpServers": {
    "agent-cold-email": {
      "url": "https://agent-cold-email-api.yaakovscher.workers.dev/mcp",
      "headers": {
        "Authorization": "Bearer <your-token>"
      }
    }
  }
}

The endpoint resolves your token fresh on every call — there is no session or cache that could leak another tenant's data. The same tool names and same per-tenant auth are also reachable as plain REST (see openapi.yaml) if your agent talks HTTP directly instead of MCP.

The 24 tools — exact schemas

Verified directly against the live tools/list response and openapi.yaml. Two additional helpers described in the design spec (write_sequence, suggest_domains) are not implemented — don't assume they exist.

setup_infrastructure

Buy branded lookalike domains, provision mailboxes, and start the warmup ramp. Returns immediately (async job); poll infrastructure_status for progress.

{
  brand: string (1-200 chars),
  primaryDomain: string (3-253 chars),
  domains: integer (1-20),
  inboxesEach: integer (1-10),
  persona: string (1-200 chars),
  physicalAddress: string (1-500 chars),   // CAN-SPAM footer address
  senderIdentity: string (1-200 chars)     // legal sender identity, captured at setup
}
// all 7 fields required

infrastructure_status

Provisioning + warmup progress, per-mailbox health, and send-readiness. No parameters.

launch_campaign

Create and activate a campaign against a lead list. The caller supplies the offer and sequence step content — this platform does not generate outreach copy.

{
  name: string (1-200 chars),
  offer: string (1-2000 chars),
  leads: [ { email: string, firstName: string (1-200 chars), company?: string } ],  // 1-5000 items
  sequence: [ { step: integer (>=1), subject: string (1-300 chars),
                body: string (1-20000 chars), delayDays: integer (0-60) } ],        // 1-10 items
  timezone: string,          // default "UTC"
  sendWindow: { startHour: integer (0-23), endHour: integer (0-23) },  // default 0-23
  stopOnReply: boolean       // default true
}

campaign_results

Sends, replies, bounces, and complaints for one campaign.

{ campaignId: string }

list_campaigns

List every campaign for the tenant with id, name, status, and event counts (sent/reply/bounce/...) — no per-campaign lookup needed. No parameters.

metrics

Account-wide deliverability + warmup health. No parameters.

activity

Unified, chronological activity feed merging campaign events (sent/reply/bounce/...) with deliverability control-loop actions (pause/throttle/...).

{
  limit?: integer (1-200),      // default 50
  cursor?: string,
  kind?: "event" | "deliverability"   // omit for every kind
}

inbox

Unified reply inbox across all mailboxes for the tenant. No parameters.

thread

Full message history for one thread.

{ threadId: string }

reply

Send a reply on an existing thread.

{ threadId: string, body: string (1-20000 chars) }

mark

Mark a thread read, unread, or archived.

{ threadId: string, status: "read" | "unread" | "archived" }

label_thread

Set (or, with label: null, clear) a triage label on an inbox thread — the same labels the dashboard UI shows as chips.

{ threadId: string, label?: string (1-100 chars) | null }   // default null

pause

Pause one campaign.

{ campaignId: string }

pause_all

Pause every campaign for the tenant. No parameters.

account

Usage, billing state, quota, and what the AI deliverability control loop has done (paused/throttled mailboxes, burning domains, auto-replacements, recent actions). No parameters.

get_dashboard

List every saved dashboard view (id, name, isDefault, rev, editedBy) or, with id, fetch one view's full layout + rev. The agent-controlled counterpart to the optional human dashboard at /app.

{ id?: string (1-200 chars) }   // omit to list every view

configure_dashboard

Create, update, promote-to-default, or delete a dashboard saved view. update requires the rev you last read; a stale rev returns a structured conflict (currentRev + currentLayout) so you can rebase and retry.

{
  action: "create" | "update" | "promote" | "delete",
  id?: string,       // required for update/promote/delete
  name?: string (1-200 chars),   // required for create; optional rename on update
  rev?: integer,     // required for update (rev-CAS)
  layout?: { schemaVersion: 1, widgets: [...] },  // required for create/update
  note?: string (0-2000 chars)
}

get_webhooks

List every outbound webhook subscription, or, with id, fetch one subscription plus its recent delivery and attempt log. Secrets are never returned on reads — shown once at create/rotate.

{ id?: string (1-200 chars) }   // omit to list every subscription

configure_webhook

Create, update, or delete an outbound webhook subscription — push reply, bounce, soft_bounce, and complaint events (HMAC-SHA256 signed, header X-Coldrig-Signature) to your own HTTPS endpoint. URLs must be https and resolve to a public host.

{
  action: "create" | "update" | "delete",
  id?: string,                    // required for update/delete
  url?: string,                   // required for create; optional on update
  eventTypes?: ("reply" | "bounce" | "soft_bounce" | "complaint")[],  // required for create; 1-4 items
  secret?: string (16-200 chars), // omit on create to auto-generate; set to rotate
  active?: boolean                // default true
}

get_byo_domains

List your bring-your-own domains, or, with id, fetch one domain's full intake detail (pre-flight scan, abuse verdict, consent status). byoStatus progresses pending_kyc|pending_consent|pending_dnsactive (or rejected/abandoned).

{ id?: string (1-200 chars) }   // omit to list every domain

configure_byo_domain

Register or advance a BYO domain intake (SPEC.md §20). Registration, the pre-flight scan, and consent tracking are live today; real DNS delegation, live reputation checks, and platform-provisioned mailboxes on your own domain activate via the same concierge step as real sending generally.

{
  action: "register" | "poll_dns" | "acknowledge_consent" | "request_managed_mailboxes" | "connect_mailbox",
  domain?: string (3-253 chars),               // required for register
  domainRelationship?: "fresh_standalone" | "subdomain_of_primary" | "is_primary",  // required for register
  id?: string,                                 // required for poll_dns/acknowledge_consent/request_managed_mailboxes/connect_mailbox
  acknowledged?: true,                         // required (must be true) for acknowledge_consent
  count?: integer (1-10),                      // required for request_managed_mailboxes
  personaSlug?: string (1-50 chars),           // optional for request_managed_mailboxes
  email?: string,                              // required for connect_mailbox
  transport?: { kind: "smtp", host, port, secure, user, pass }
            | { kind: "gmail_api", clientId, clientSecret, refreshToken }
            | { kind: "ms_graph", mode: "delegated" | "app_only", tenantId, clientId, clientSecret, refreshToken? }
            // required for connect_mailbox — declares a mailbox connection YOU already hold; not this platform's own send transport
}

No pricing is published yet for BYO-connected mailboxes specifically, and a burned BYO domain hard-pauses rather than auto-replacing (unlike a burned lookalike domain) — see the BYO domain page for the full risk model.

suppress_lead

Permanently suppress an email address tenant-wide — every current and future campaign — the manual/free-text "stop emailing me" path for opt-outs the strict typed-unsubscribe matcher misses. Cancels every pending send and marks every campaign-lead row for this email suppressed. There is no un-suppress tool.

{
  email: string (email),
  reason?: "manual",              // the only value this tool honestly claims — bounce/complaint/unsubscribe are recorded automatically elsewhere
  note?: string (0-2000 chars)    // accepted for schema symmetry, not persisted
}

update_lead

Record what you learned about a contact — their reply, your triage — as a durable, contact-level disposition keyed by email, visible across every campaign that lists them. A partial patch: only the fields you pass are changed.

{
  email: string (email),
  interestStatus?: "none" | "interested" | "meeting_booked" | "not_now" | "not_interested" | "bad_fit" | "out_of_office" | "wrong_person",
  notes?: string (0-5000 chars),
  tags?: string[] (0-50 items, each 1-100 chars)
}
// at least one of interestStatus, notes, tags is required

list_leads

List/export leads with their contact-level disposition, cursor-paginated. This is the export surface — paginate to dump the full book of business as JSON (no separate CSV endpoint).

{
  campaign?: string (1-200 chars),
  interestStatus?: "none" | "interested" | "meeting_booked" | "not_now" | "not_interested" | "bad_fit" | "out_of_office" | "wrong_person",
  suppressed?: boolean,
  replied?: boolean,
  cursor?: string (1-500 chars),
  limit?: integer (1-200)   // default 50
}

Returns { leads[], nextCursor }; each row: leadId, email, firstName, company, campaignId, campaignName, globalStatus, interestStatus, notes, tags, suppressed, lastEventType, lastEventTs, createdAt.

The no-signup CLI demo

npx agent-cold-email demo mints a demo tenant, provisions sample infrastructure, and runs the accelerated sandbox pipeline end to end (warmup, sends, replies, bounces, stop-on-reply) in one command — no signup or token-wrangling required. The npm CLI is live at v0.2.0 (registry-verified 2026-07-15). The same 24 tools are available over the hosted MCP endpoint or directly over HTTP (quickstart). The demo path stays sandboxed; real sending is live in production for activated accounts, via a short concierge activation step.

Auth model

One bearer token per tenant, minted by the unauthenticated POST /signup bootstrap call. Every other tool call requires Authorization: Bearer <token> and resolves server-side to exactly one tenant's isolated state — there is no cross-tenant data access at any layer.

Related