mcpbeat Sign in

Agent Cold Email MCP Server

by ys-projectcalc Your server? Claim it
answering

Agent Cold Email is answering right now. Last checked moments ago. 80 installs a week from npm. It exposes 37 tools. Last commit 25 Sep 2026.

Coldrig — cold-email infra run by your agent: 28 MCP tools, live sending, free sandbox, from $99/mo.

Installs per day peak 165 · avg 13 · -74% w/w
a month agotoday
Uptime history 91 days of history · worst day 92%
91 days agonow
100.0%
Uptime 24h
184 of 184 checks
37
Tools
read from the server
240 ms
Response time
average over 24h
80
Installs / week
npm and PyPI

What changed 119

Every tool that appeared, vanished or quietly changed what it asks for. Recorded since 10 August 2026. No other catalogue keeps this.

28 Oct 4 tools changed the parameters they ask for add_leads, launch_campaign, list_leads and 1 more
28 Oct a tool appeared experiment
28 Oct a tool description was rewritten update_lead
28 Oct a tool changed version
27 Oct 5 tool descriptions were rewritten cancel_subscription, configure_byo_domain, create_checkout_link and 2 more
27 Oct 3 tools changed the parameters they ask for cancel_subscription, configure_byo_domain, setup_infrastructure
23 Oct 2 tools appeared cancel_subscription, create_checkout_link
23 Oct a tool description was rewritten infrastructure_status
23 Oct a tool changed the parameters it asks for launch_campaign
19 Oct 4 tool descriptions were rewritten5 times that day account, contact_operator, remove_mailboxes and 1 more
and 96 more, back to 10 August 2026

What the code does

We read the source, 5 d ago · tools taken from the live server · rules 995398eba69f

A tool parameter reaches a dangerous call

A value the model can set ends up inside a file or shell call. That is not a flaw by itself — for a terminal server it is the job — but it is where things go wrong when it is not.

A tool parameter reaches a file or shell call configure_webhook.url → pw-scripts/waitlist-form-drive.js:15
  res.end(fs.readFileSync(f));

Is this your server and something here is wrong? Tell us — corrections are free and do not require a plan.

A tool parameter here reaches a dangerous call

That is not a flaw by itself — but it is where things go wrong when it is not the job. We re-read this code on every release. Watch it and you hear from us the day another one appears.

Three servers free · no card

Connect this server

Endpoint below is the one we actually reach during checks — not the one copied from a README. Last verified 0 min ago.

run in your terminal
claude mcp add agent-cold-email --transport http https://agent-cold-email-api.yaakovscher.workers.dev/mcp
Settings → Connectors → Add custom connector, then paste this address
https://agent-cold-email-api.yaakovscher.workers.dev/mcp
~/.codex/config.toml
[mcp_servers.agent-cold-email]
url = "https://agent-cold-email-api.yaakovscher.workers.dev/mcp"
.cursor/mcp.json
{
  "mcpServers": {
    "agent-cold-email": {
      "url": "https://agent-cold-email-api.yaakovscher.workers.dev/mcp"
    }
  }
}
.vscode/mcp.json
{
  "servers": {
    "agent-cold-email": {
      "type": "http",
      "url": "https://agent-cold-email-api.yaakovscher.workers.dev/mcp"
    }
  }
}

This endpoint answered with an authorization challenge. The server is running, and it signs you in through your browser: there is no API key to paste.

This server publishes 1 more address. The block above uses the one we reach during checks; the full list is under Endpoints below, and the author may intend a particular one for your client.

Available tools 37

Read directly from the server with tools/list, grouped by what they act on. If a tool disappears, we record the date.

configure
configure_byo_domain
Register or advance a BYO domain intake. action = register (needs domain + domainRelationship: fresh_standalone|subdomain_of_primary|is_primary — runs pre-flight scan + abuse gate + reputation ladder) | poll_dns (needs id — re-checks DNS delegation; moves to active, or to abandoned after 7 idle days) | acknowledge_consent (needs id + acknowledged:true — required before a primary domain passes pending_consent; documents informed consent; exposure remains) | request_managed_mailboxes (needs id + count — managed mailboxes on an ALREADY-ACTIVE domain; on a live account this raises the bill; every response carries a billing projection { provisionedAfter, projectedMonthlyCents, formula }; quoteOnly:true previews without adding any) | connect_mailbox (needs id + email + transport — an EXISTING connection; transport is smtp | gmail_api | ms_graph, each with its own credentials; each connected mailbox is billed at your plan's mailbox price; the quoteOnly:true preview shows it).
configure_dashboard
Write a saved dashboard view. action = create (needs name+layout) | update (needs id+rev+layout; optional name renames) | promote (id → default) | delete (id). update checks the rev: a stale rev returns { currentRev, currentLayout } so you can re-apply your change and retry. Optional note. Read the current rev+layout via get_dashboard first.
configure_webhook
Manage an outbound webhook subscription. action = create (needs url + eventTypes: reply|bounce|soft_bounce|complaint|unsubscribe; optional secret/active) | update (needs id + one changed field; active:true re-enables an auto-disabled one, active:false pauses; secret rotates) | delete (needs id). create/rotate return the HMAC signing secret ONCE. URLs must be https to a public host (private/metadata IPs rejected). Deliveries are signed X-Coldrig-Signature: sha256=HMAC-SHA256(secret, raw body).
leads
add_leads
Add contacts to a running campaign. Inputs: campaignId, leads[] (same shape as launch_campaign: email, optional firstName, company, vars), listSource (REQUIRED on a crowdfunding account). Only an active or paused campaign of this account (404 if unknown; 400 if past its end date). Each new contact starts at step 1 on the campaign's own schedule, under every launch rule: a {{firstName}} or {{key}} the sequence uses must have a value on every new contact, or the call is refused (400) and nothing is added. Skipped and counted, never an error: an address already in this campaign (unchanged), an address on your suppression list (stored as suppressed, never emailed), an address repeated in the request. Adding never resumes a paused campaign; repeating the call adds nothing; a campaign holds at most 5000 contacts. A trial account can launch up to 5 test campaigns in any 24 hours, each holding up to 100 contacts, which is enough for a small test. Both are limits of the trial, not of Coldrig: going live removes them. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting are canceled and it cannot be resumed or added to, so launch a new campaign to send real email. People who already got this email are skipped: a contact is left out, never stored or emailed, when another campaign of this account has sent them, or still has waiting for them, an email with the same subject and body as this campaign's first email, as written before {{tokens}} are filled in (spacing differences do not count). A trial's test email counts only while the account is still on the trial, and the built-in demo campaign never counts. alreadyEmailed in the answer is how many were skipped (anyone also on your suppression list is counted there, not in suppressed): tell the person in plain words, for example "skipped 12 people who already got this email". To include them, send includeAlreadyEmailed: true on the launch itself (previewOnly: true shows the count first), or after a launch add them to that same campaign with add_leads and includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. Naming a repeat with repeatOf includes them too. A launch on which everyone would be skipped is refused (400) and nothing is created; an add of contacts on which everyone is skipped adds no one, with no error. A launch, or an add of contacts, is refused with 409 duplicate_campaign when it would leave a campaign with exactly the same set of contact addresses and the same subjects and bodies in the same step order as another campaign of this account, however long ago that one was launched and whether it is running, paused, ended or finished, so the same people are not sent the same emails twice. repeatOf states a deliberate repeat, exactly as on launch_campaign. Returns { campaignId, added, suppressed, alreadyInCampaign, alreadyEmailed, duplicatesRemoved, contacts } — contacts is the campaign's total afterwards.
list_leads
List/export leads with their contact-level disposition, cursor-paginated. Returns { leads[], nextCursor }; each row: leadId, email, firstName, company, campaignId, campaignName, globalStatus, interestStatus, notes, tags, suppressed, lastEventType, lastEventTs, createdAt, threadId (that lead's conversation: pass it to thread for the lead's timeline; null if nothing was scheduled). Filters: campaign, interestStatus, suppressed, replied. This IS the export surface — paginate to dump the full book of business as JSON (no separate CSV endpoint). Use update_lead to write disposition, suppress_lead to opt an address out.
pause
pause
Pause ONE campaign: its status → 'paused' and nothing more in it sends; its remaining steps are HELD, not cancelled (already-sent mail is unaffected) — call resume to continue it. Input: campaignId. Returns { paused: true }. 404 if not found. Add leadEmails to stop ONLY those leads in this campaign instead (e.g. a donor who already gave): their remaining steps are cancelled for good (resume does not bring them back), the campaign keeps running for everyone else, and nobody is suppressed — returns { paused: false }. Matching ignores capitalisation; if any address is not a lead of this campaign, nothing is stopped and the call is refused (404) naming them. Use pause_all to pause every active campaign at once.
pause_all
Pause EVERY active campaign on the account at once (each active status → 'paused'; nothing more in them sends, and their remaining steps are held). No inputs. Returns { pausedAll: true }. Use pause to pause a single campaign by id, and resume (one call per campaign) to continue one.
account
account
Account overview: brand, plan, status, vertical ('crowdfunding' or null — set at signup), billingState, activationState, resource counts, usageCents, quota (the most domains and mailboxes a test-mode account can set up; null on a paid account, which has no such quota, though its setup_infrastructure layout still names at most 20 Coldrig-bought domains, with at most 5 mailboxes on each), deliverability (paused/throttled mailboxes, burning domains, auto-replacements, recentActions[]), teardown (reclaim summary once canceled, else null), and sendingStopsAt (after an end-of-period cancel: the ISO UTC time running campaigns stop sending; else null). Billing is per mailbox, on the live mailbox count, at the price and minimum in `pricing` (its formula); the billed quantity tracks the real mailbox count (removing mailboxes lowers it). activationState is the actual send state — trust it over 'sent' counts: 'active' = real sending live; 'pending_provisioning' = paid but infrastructure still being armed, sends shown are test-mode previews that DON'T leave; 'capacity_pending' = setup paused while Coldrig support checks recent purchases; 'screening_hold' = account under review; 'sandbox' = test mode (demo/free). Use metrics for counts, infrastructure_status for per-mailbox health.
ack
ack_message
Acknowledge a message by id (from list_messages or infrastructure_status's messages[]) — sets it read so it stops surfacing as unacked. THIS IS THE ONLY THING THAT SETS readAt: reading or listing messages never marks them, so a message stays unacked until you call this explicitly. Idempotent: acking an already-acked id returns success with no second effect, never an error. 404 if the id doesn't exist for this account.
activity
activity
Unified activity feed: campaign events (sent/reply/bounce/...) merged with deliverability actions (pause/throttle/replace-domain). Cursor-paginated → { items[], nextCursor }; each item { id, kind:'event'|'deliverability', label, ts, target, detail }. Filters: kind, limit (default 50, max 200). Use inbox for replies only.
byo
get_byo_domains
List your BYO (bring-your-own) domains, or (with id) one domain's full intake detail. No id → [{ domainId, domain, isPrimary, dnsMode, byoStatus, breakerTier, reputationBranch, mailboxCount }]. With id → adds the pre-flight scan result, abuse-gate verdict, and consent-acknowledgment status. byoStatus progresses pending_kyc|pending_consent|pending_dns → active (or rejected/abandoned). Use configure_byo_domain to register a new one or advance it.
campaign
campaign_results
Outcome counts for ONE campaign. Input: campaignId (from launch_campaign). Returns { campaignId, sent, reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — event counts, bounce = HARD only, soft_bounce separate, opens not tracked; auto_reply = automatic replies such as out-of-office, which are NOT replies (never in reply, replied, replyRate or positiveReplies; they never stop a sequence) — plus unique-contact numbers: contacts (leads in the campaign), contacted (leads sent at least once), replied (leads that replied), replyRate (replied / contacted, 0..1), positiveReplies (replied leads marked interested or meeting_booked via update_lead), and perStep[] of { step, sent, contacted, replied, bounce, unsubscribe } ordered by step (a reply, bounce or unsubscribe counts toward the lead's latest sequence step sent before it). 404 if unknown. To see each contact's own timeline use list_leads with campaign, then thread. Use metrics for account-wide totals, list_campaigns for every campaign at once.
campaigns
list_campaigns
List every campaign at once: [{ campaignId, name, status, counts{sent,reply,bounce,complaint,unsubscribe,failed,soft_bounce,auto_reply} }], newest first — no per-campaign lookup needed. Use campaign_results for one campaign's counts, metrics for account-wide totals.
cancel
cancel_subscription
Cancel this account's subscription; the same as POST /cancel. Inputs: immediate (default false), keepDomains, confirm. Without confirm:true NOTHING changes: it returns { confirmed, changed, wouldCancel, billingState, whatWouldHappen }; call again with confirm:true to cancel. immediate:false stops renewal: sending stops when the paid period ends, or sooner if the account is already paused or behind on payment. immediate:true ends it now with no refund: mailboxes added since the last invoice are billed on a final invoice where one applies, and every mailbox is released for good. Refused while a setup is running (retry when it finishes). Confirmed, it returns { alreadyCanceled, billingState, effective, teardown, periodEndsAt }; an error answer means the payment provider refused or gave no clear answer, and nothing changed.
checkout
create_checkout_link
Create the hosted checkout link that starts this account's paid plan; the same as POST /checkout. Inputs: mailboxes (3 or more; seeds the first quote only, the bill then tracks the live mailbox count), interval (month; yearly billing is closed). Returns { mode, url, sessionId, forAgents }. url opens the checkout page: pay it yourself with a browser tool, or pass url to whoever pays (forAgents says how). This tool itself never completes a payment; billingState (account tool) reads 'active' once the payment has gone through. Never put card numbers in any request. Refused (400, nothing created or charged) while a subscription is live, behind on payment or scheduled to cancel, or while an earlier payment is still clearing; the error says what to do instead.
connected
get_connected_apps
List the apps connected to this account with "Sign in with Coldrig" (for example ChatGPT or Claude): [{ id, name, redirectHost, connectedAt }]. name is what the app called itself and is not verified; redirectHost is where its sign-in returned; connectedAt is when it connected (epoch ms). API keys are not listed. Read-only.
contact
contact_operator
Reach Coldrig support for anything list_messages/infrastructure_status cannot answer (a stuck vendor issue, a billing question, an account-level ask). Inputs: body (1-2000 chars), urgency (normal|needs_human, default normal). Files a ticket, notifies Coldrig support; returns { ticketId, note, deduplicated }. Works in every account state a token authenticates in (dunning-suspended, canceling, canceled) — admin-terminated is the one exception, rejected at auth. The reply arrives as a message on THIS account (poll list_messages; no reply-fetch call). Sending the IDENTICAL body+urgency again within an hour returns the SAME ticketId, no second ticket/alert (`deduplicated: true`). TEXT MATCH, NOT AN INTENT MATCH: a genuinely new message with identical wording collapses the same way, silently — vary the wording (or raise urgency, always a new ticket). `needs_human` bypasses the ~10-min ops-email throttle. Rate-limited to 5/hour per account — a 429 names retryAfter (seconds).
dashboard
get_dashboard
Read saved dashboard views. No id → list all: [{ id, name, isDefault, rev, editedBy }]. With id → that view's full layout + rev (pass this rev as the base when you update with configure_dashboard). Views are both agent- and human-editable; write them with configure_dashboard.
disconnect
disconnect_app
Disconnect one app connected with "Sign in with Coldrig". Input: id from get_connected_apps. The app is signed out on its next call and cannot refresh its access; webhooks it created keep running until you delete them (configure_webhook). Disconnecting the app that is making this call is allowed: this call still answers, then that app is signed out. Unknown id: not found. Returns { disconnected: true }.
experiment
experiment
A/B test one part of a campaign's email at a time, on positive replies (interested, meeting_booked, referral within 14 days of the send; opens never count). action create {campaignId, element: subject|first_line|body|cta|ps|sender_name, step? (1), hypothesis (written before anything sends), variants (the challengers, each as {text, labels?}; the control 'a' is the current copy), controlLabels?, anchor (cta only: the sentence replaced), maxVersions? (default 3 incl. control), minShare? (default 0.15)}: one live test per campaign; create right after launch. labels name each version's copy choices (openerType, personalizationDepth, personalizationSource, framing, socialProof, askType) so results and shared patterns can compare choices, never text. The answer includes whatTendsToWork: the shared patterns for that element. Each contact gets one version for its whole thread, balanced within each mailbox and day. action results {campaignId}: every version's text, counts, rate, chance to beat the control and safety warnings, plus verdict: not_enough_data until 500 matured sends per version, then judged only at 500/1,000/1,500 (reading in between changes nothing): winner (at least 97.5% likely to beat the control, no safety limit crossed: conclude with it), no_clear_winner, or no_difference at 1,500 (conclude with 'a' to log the null). Spam complaints (0.3%), hard bounces (5%), and more unsubscribes or hostile replies than the control pause a version automatically. action retire {experimentId, variant}; action conclude {experimentId, winner}: new contacts get the winner; the campaign's own text is never edited. Then test the next element. action breakdown {campaignId, by (a recorded fact, e.g. audience.seniority or delivery.local_hour), experimentId?, step?}: that campaign's matured sends split by the fact, groups under 50 sends hidden; exploratory, use it to choose the next hypothesis, never to pick a winner. action patterns {element?}: what tends to earn replies across the tests customers share, each backed by at least 2,000 emails a side and 30 replies; copy facts only, never text, contacts or which customer. action sharing {share?}: read whether this account's tests feed the shared patterns (on by default for paying accounts); share false stops it and removes what it shared. Label every reply with update_lead or positive counts stay zero. Spam-folder placement is not observable.
inbox
inbox
Unified reply inbox across mailboxes. Cursor-paginated → { threads[], nextCursor }; each row: threadId, campaignName, leadEmail, subject, mailboxEmail, label, lastEventType, markStatus. Filters: mailbox, campaign, label, read, includeNonreply (threads whose last event is a bounce, complaint or automatic reply such as out-of-office; default true), archived (exclude|include|only). Use thread for one thread's history.
infrastructure
infrastructure_status
Warmup and setup progress per mailbox. Returns { domains, mailboxes, sendReady, mailboxHealth[], messages[], nextSteps }. New mailboxes ramp-limit server-side: 5/day week 1, 35/day week 4, 40 after — dailyCap/sentToday show real capped sending from day 1, never zero. Per-mailbox `sendReady` is a FULLY-RAMPED flag, NOT a send gate — below-ramp mailboxes still send, capped at their own dailyCap; read dailyCap/sentToday for capacity. Top-level `sendReady` is the AND across ALL mailboxes. Each mailbox carries status (warming/active, or resting after sitting unused) and delivStatus (healthy/throttled/paused), complaint/bounce/softBounce rates; vendorReputationScore/vendorPlacementRate are VENDOR-REPORTED, null when unmeasured. Warmup run in the mailbox provider's own pool is NOT shown here, by design. `messages[]` previews 5 unacked notices (severities: list_messages); reading never marks read. `nextSteps` names the next action (see setup_infrastructure). Use account/metrics for rollups.
label
label_thread
Set or clear a triage LABEL on an inbox thread — the same chip the dashboard shows. Inputs: threadId, label (string; pass label:null to clear). Distinct from mark (read/unread/archived state): a label is a free-form category, not a read flag. Filterable via inbox's label param.
launch
launch_campaign
Create and activate a campaign on a lead list. You supply name, offer, leads[] and sequence[] (per step: step, subject, body, delayDays); the platform does not write copy. Optional: timezone, sendWindow, stopOnReply, listSource (one sentence on where the list came from; REQUIRED on a crowdfunding account, where a missing or "unknown" source is refused 400). Step numbers must be unique. Subject is REQUIRED on step 1; a later step may omit it to reuse step 1's subject as "Re: " + that subject (recommended), or set its own, though some mail clients show a changed subject as a separate conversation; every later step is still sent In-Reply-To step 1. Subjects and bodies may use {{firstName}} and {{company}} (filled from each lead; both optional) plus your own {{key}} for any key every lead carries in its optional `vars` map (a key may not be firstName or company); values are inserted as plain text. A launch is refused (400), naming the token and how many leads lack it, if a token has no non-empty value for some lead, and also if a {{...}} is not one word, a single-brace {firstName} or {company} is used, or three or more { or } run together: each would reach the recipient unfilled. The send window is evaluated in `timezone` (IANA, e.g. America/New_York; default UTC): set it to your recipients' zone. sendWindow is { startHour, endHour, days? }: hours 0-23, endHour INCLUSIVE (the last hour a send may start in), days 0=Sunday ... 6=Saturday. Omitted fields get the RECOMMENDED default {"startHour":8,"endHour":16,"days":[1,2,3,4,5]} (Monday-Friday business hours), except on a test-mode account, where they are open every hour of every day until it goes live; the decision is yours, e.g. include 0 and 6 in days to send on weekends. Setting exactly ONE of startHour/endHour pairs it with the recommended value for the other (08/16, on a test-mode account too); a launch that would wrap past midnight is refused, so set both for an overnight window such as {startHour:22, endHour:6}. Step 1 goes out from the least-loaded mailbox; each later step goes out delayDays after the previous step ACTUALLY sent, from the SAME mailbox, In-Reply-To step 1, and waits while that mailbox is at its daily cap. A lead's remaining steps are cancelled (a 'failed' event says why) once that mailbox is paused or released, or when a paid account's thread went out from a connected BYO mailbox, which Coldrig never sends from; a step whose previous step never went out is skipped. Lead addresses are trimmed and lower-cased; a repeated address is kept once; suppressed leads are skipped. Returns { campaignId, sendWindow, timezone, contacts, suppressed, duplicatesRemoved, alreadyEmailed, endByShortfall, warning, nextSteps }: sendWindow and timezone are what actually applied; contacts is how many leads were stored, suppressed how many of those are on your suppression list (never scheduled). With an endBy, endByShortfall is about how many contacts will not get their first email before endBy on the account's current mailboxes; warning is one plain sentence when it is above 0 (a shortfall, or other waiting emails that may still make some contacts miss endBy): tell the person before they confirm. Nothing is refused for it; whatever still waits at endBy is skipped. previewOnly:true is a dry run: every launch check runs, nothing is created or sent, no idempotencyKey is recorded, and it returns { preview, sendWindow, timezone, contacts, suppressed, duplicatesRemoved, alreadyEmailed, endByShortfall, warning, samples } where samples[] = { email, steps[] } for the first three leads (each step's rendered subject and body; every real send then appends the required footer: sender identity, postal address, unsubscribe link). People who already got this email are skipped: a contact is left out, never stored or emailed, when another campaign of this account has sent them, or still has waiting for them, an email with the same subject and body as this campaign's first email, as written before {{tokens}} are filled in (spacing differences do not count). A trial's test email counts only while the account is still on the trial, and the built-in demo campaign never counts. alreadyEmailed in the answer is how many were skipped (anyone also on your suppression list is counted there, not in suppressed): tell the person in plain words, for example "skipped 12 people who already got this email". To include them, send includeAlreadyEmailed: true on the launch itself (previewOnly: true shows the count first), or after a launch add them to that same campaign with add_leads and includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. Naming a repeat with repeatOf includes them too. A launch on which everyone would be skipped is refused (400) and nothing is created; an add of contacts on which everyone is skipped adds no one, with no error. A launch, or an add of contacts, is refused with 409 duplicate_campaign when it would leave a campaign with exactly the same set of contact addresses and the same subjects and bodies in the same step order as another campaign of this account, however long ago that one was launched and whether it is running, paused, ended or finished, so the same people are not sent the same emails twice. Nothing else is compared: a different name, offer, time zone, send window, delay, date, stopOnReply, listSource, row order or contact name, company or vars is still the same campaign. The refusal creates nothing and carries existingCampaignId, existingCampaignName and existingCampaignState. To repeat that campaign on purpose, send the request again with repeatOf set to that existingCampaignId: it is accepted once, and only while it names the most recent such campaign. To retry a call whose response you lost, resend it with the same idempotency key, which replays the original result instead. A campaign launched on a trial account emailed nobody and counts only while the account is still on the trial; the built-in demo campaign never counts. A trial account can launch up to 5 test campaigns in any 24 hours, each holding up to 100 contacts, which is enough for a small test. Both are limits of the trial, not of Coldrig: going live removes them. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting are canceled and it cannot be resumed or added to, so launch a new campaign to send real email.
lead
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. Inputs: email, interestStatus (none|interested|meeting_booked|referral|not_now|question|wrong_person|not_interested|objection|bad_fit|hostile|out_of_office — a server-enforced enum; 'do not contact' is NOT a member, use suppress_lead instead), objection (with objection: has_solution|no_budget|no_need|timing|trust), until (with not_now or out_of_office: the YYYY-MM-DD to follow up or that they return), notes, tags (free-form). An interestStatus also labels the person's latest reply for experiments: interested, meeting_booked and referral are positive replies, the experiment metric; none clears the label. Label every reply. A PARTIAL patch — only the fields you pass are changed; at least one of interestStatus/notes/tags is required. Filterable via list_leads.
mailboxes
remove_mailboxes
Downgrade: release your N NEWEST live mailboxes, lowering the billed quantity. Inputs: count, acknowledged (must be true: release is immediate and irreversible; no mid-cycle credit, lower price starts next renewal, minimum: see account pricing). Returns { releasedCount, failedCount, unreleased, billing, deduplicated }. `count` is RELATIVE, not a fleet target. ALWAYS pass an idempotencyKey: the FIRST call under a key fixes WHICH mailboxes to release; a later call with the SAME key can only finish that set. `releasedCount` can be less than `count` — `failedCount` is how many the vendor refused (still live, billed; `unreleased` lists them). Repeat the call with the same key until `failedCount` is 0. `deduplicated: true` = NO new work; an earlier outcome under that key was replayed. A genuinely NEW downgrade needs a NEW key. 409 = a release is already running; check infrastructure_status first. To ADD mailboxes use setup_infrastructure or configure_byo_domain.
mark
mark
Set a thread's READ-STATE for inbox triage. Inputs: threadId, status = 'read' | 'unread' | 'archived' (archived hides it from the default inbox; refetch with inbox archived='include'/'only'). Returns { marked: true }. 404 if unknown. This is the read/archive flag ONLY — use label_thread for a triage label chip, reply to respond.
messages
list_messages
List this account's system and Coldrig support messages (a retryable setup step, a credential going live, a note from Coldrig support), cursor-paginated. Unacked messages sort first (newest within that group), then acked (also newest first). Returns { messages[], nextCursor }; each: id, kind, severity ('info' = resolves on its own | 'action_required' = act and it progresses | 'operator_pending' = platform stopped, only Coldrig support can restart it, retrying the SAME call then works | 'terminal' = platform stopped, only a human can move it, do NOT retry), body, actionHint, source (system|operator), createdAt, readAt. `readAt` is set ONLY by ack_message — listing never marks messages read, so a null readAt does not mean never seen, only not yet acknowledged. Use ack_message to stop one resurfacing. infrastructure_status inlines the newest 5 unacked messages; this is the full paginated surface.
metrics
metrics
Account-wide outcome totals across ALL campaigns: { sent, reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — the same event counts as campaign_results but summed across the whole account (no per-contact fields) (bounce = hard only, opens not tracked; auto_reply = automatic replies such as out-of-office, never counted in reply). Use campaign_results for one campaign, list_campaigns per-campaign, or account for billing/quota.
plan
plan_campaign
Plan a dated outreach campaign (e.g. a fundraising drive) and get its price. Read-only: nothing is booked, charged or changed. Inputs: contacts, campaignStart (first campaign day, YYYY-MM-DD, UTC), campaignDays, bookingDate (normally today), and optionally domainsAlreadyHeld (default 0; infrastructure_status shows `domains`) and listIsOwnOptIn (your own opted-in donors; default false). Returns { mailboxes, domainsToBuy, bookBy, advanceStartOn, safeReach, priceCents, warnings[] }. safeReach is how many contacts the warm-up ramp can safely reach if booked on bookingDate; the price is for that many. warnings: late_booking (safeReach below contacts), own_list_cheaper_on_esp (an opted-in list may cost less on an email-service provider), campaign_day_not_sent_by_default (weekend day: in reach and price, sent only if sendWindow.days at launch has 6 for Sat and/or 0 for Sun). A bookingDate after the last campaign day is refused 400. Booking is not available: contact support (contact_operator).
reply
reply
Send a reply on an existing thread, from the mailbox that sent it. Inputs: threadId, body. Returns { messageId, deduplicated }. A reply is real send volume, governed like campaign sends: counts against the mailbox daily cap, and is REFUSED (never silently dropped) when the recipient is suppressed, the mailbox is deliverability-paused, or the cap is used up — a refusal returns { error, code: send_blocked, reason: suppressed|mailbox_paused|daily_cap_reached, retryable }; retryable (cap) clears next daily rollover, non-retryable does not — stop retrying, do not loop replies to manufacture volume. Idempotent: identical retries collapse to one send — pass a stable idempotencyKey (else a body hash is used, matched only 10 minutes) so a dropped-response retry cannot double-send. `deduplicated: true` = NO new email sent; `messageId` is from an earlier matching send. Pass a key, or vary the body, if a repeat must genuinely go out. 404 if no sending mailbox is on record for the thread.
resume
resume
Resume ONE paused campaign: its status → 'active'. Input: campaignId. Returns { resumed: true }. Only a paused campaign of this account can be resumed: 404 if unknown, refused (400) if it is not paused, is past its end date, or is a trial campaign on an account that has gone live. On a paid account, resuming sends nothing by itself: the held steps go out through the normal schedule, inside the send window and the daily caps. A step whose time passed while paused goes out at the next allowed slot, one step at a time per lead: the step after it still waits its own delayDays from when that one actually sent, so a pause never puts two steps in one day. Leads stopped on their own stay stopped: stopped with pause leadEmails, replied, unsubscribed, bounced or suppressed. Steps a campaign lost while it was paused before this tool existed are not restored.
setup
setup_infrastructure
Set up sending: buy lookalike domains, create mailboxes, start warmup. On a live account this spends money and raises the monthly bill; quoteOnly:true returns the price and buys nothing. `domains`/`inboxesEach` are the setup you want to HAVE: a repeat call never buys twice; each ordinal 0..domains-1 fills to its own count, at most 5 (3-5 for a live account's new domain). Mailbox addresses are DETERMINISTIC from persona+ordinal+slot: keep persona unchanged on a retry. Buying a NEW domain also needs `registerDomains:true` (and `registrant`, unless on file); without it the call is refused 400 registrar_optin_missing and nothing is bought. NO background retry exists: a `provisioning` result of pending needs YOU to call again; capacity_pending needs contact_operator. Returns { jobId, billing, provisioning?, nextSteps }.
submit
submit_feedback
Send private feedback to the Coldrig founders about how the product is working for you. Inputs: score (integer 0-10, optional) and/or text (1-2000 chars, optional); at least one is required. It is stored privately and emailed to the founders, never published, and it opens no support ticket and changes nothing on your account (use contact_operator for a problem that needs an answer). Each call is a separate submission. Returns { feedbackId, received }.
suppress
suppress_lead
Permanently suppress an email address account-wide (every current and future campaign) — the manual/free-text 'stop emailing me' path for opt-outs the strict typed-unsubscribe matcher misses. Inputs: email, reason (fixed 'manual' — the only value this tool claims; bounce/complaint/unsubscribe are recorded automatically elsewhere), note (accepted, not persisted). Cancels every pending send + marks every campaign-lead row 'suppressed'. Last-write-wins: re-suppressing a bounce/complaint/unsubscribe row relabels its reason to 'manual'. There is no un-suppress tool.
thread
thread
Full message history for ONE thread. Input: threadId (from inbox). Returns { threadId, campaignId, leadId, leadEmail, mailboxEmail (null before first send), messages[] }, each message { type (sent/reply/auto_reply/bounce/...; auto_reply = an automatic reply such as out-of-office), step (the sequence step of a sent message, null otherwise), ts, messageId, metadata }, oldest first. 404 if unknown. Use inbox to LIST threads; reply to respond; mark/label_thread to triage.
webhooks
get_webhooks
List your outbound webhook subscriptions, or (with id) one subscription plus its recent delivery + attempt log. No id → [{ id, url, eventTypes, active, status, disabledReason, consecutiveFailures }]. With id → { subscription, recentDeliveries[], recentAttempts[] }. Secrets are never returned on reads — they are shown once at create/rotate.

Endpoints

URLTransportStateLatencyChecked
https://agent-cold-email-api.yaakovscher.workers.dev/mcp streamable-http answering 62 ms 0 min ago
https://api.coldrig.dev/mcp streamable-http sign-in 88 ms 0 min ago

Alternatives to Agent Cold Email

same job, measured the same way
EmailMCP
by setip

AI agent email — 23 tools free to read inbox, 28 paid to send. $9.99/mo. Token auto-provisioned.

56 tools answering
Commune
by shanjai-raj

Email and SMS MCP server for AI agents — 41 tools for inboxes, sending, search, and SMS.

116 installs/wk local only
Emelia
by emelia

Run Emelia cold email and LinkedIn campaigns, lists, enrichment and your Emelia inbox from AI.

answering
Campaignstack
by campaignstack

Run B2B outreach from your AI agent: 250+ tools for campaigns, leads, LinkedIn and email workflows.

answering
Email Inbox API + Sending by Sendmux
by sendmux

AI email inbox and sending tools with attachments, search, live events, and webhooks.

195 installs/wk answering
webhooks.cc
by kroqdotdev

Capture, inspect, send and assert on webhooks and inbound email from your coding agent.

914 installs/wk answering
Engagelab Email
by r1ghtherewaiting

Send, receive, monitor and reply to email from AI agents with threaded inbox context.

61 installs/wk local only
Voidmail Agent Email
by voidly-ai

Local MCP tools for server-readable agent inboxes and sending to owner-approved recipients.

133 installs/wk local only

Agent Cold Email — questions

Answers built from our own checks of this server.

What can Agent Cold Email do?
It exposes 37 tools, read directly from the server on our last check. Among them: account, ack_message, activity, add_leads, campaign_results, cancel_subscription and 31 more. The full list with descriptions is on this page — we take it from the server itself via tools/list, not from a README. How MCP servers expose tools in the first place →
What is Agent Cold Email mostly used for?
Its tools cluster around configure, pause and leads. That is what this server is built to work with — the grouping comes from the actual tool names, not from a category we assigned.
Is Agent Cold Email working right now?
We send a real MCP handshake every 15 minutes. Over the last 24 hours 184 of 184 checks got a reply (100.0%), average response time 240 ms. The bar chart above shows every period we have measured.
How do I connect Agent Cold Email?
Copy the ready config from this page. We generate it for Claude Code, Claude Desktop, Codex, Cursor and VS Code, each in the form that client actually reads. It is a remote server, so there is nothing to install — the client connects to the address. You sign in through your browser once and the client keeps the session: no key to copy, and it refuses anonymous clients.
How do you sign in to Agent Cold Email?
No API key is involved. Agent Cold Email answers our knock with an OAuth challenge, so you authorise it once in your browser and the client keeps the session. That is also why we see no tool list: it will not describe itself to an anonymous client, and that is the server working as intended, not a fault.
How fast is Agent Cold Email?
It answers our handshake in 240 ms on average, which is faster than 59% of all working MCP servers we measure. The comparison comes from our own checks across the whole registry, every 15 minutes.
How many people use Agent Cold Email?
The npm package agent-cold-email was installed 80 times in the last week. Week over week that is -74%. We show installs rather than GitHub stars on purpose: a star is a bookmark, an install is someone actually running it.
Is Agent Cold Email open source?
The code is open source: it is published under the MIT licence, written in TypeScript and 1 stars on GitHub. The licence covers the code in the repository, not the hosted service at agent-cold-email-api.yaakovscher.workers.dev, which runs on its operator's own terms.