Skip to content
Official MCP RegistryListed

io.github.YS-projectcalc/agent-cold-email

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

First seen 2 Oct 2026. Evidence as of 2 Oct 2026.

28
Tools
From an anonymous probe
1
Source listings
Each with its own history
0
Recorded changes
Since first seen

Tools

ToolDescriptionBehaviour
accountAccount overview: brand, plan, status, billingState, activationState, resource counts, usageCents, quota, deliverability (loop state: paused/throttled mailboxes, burning domains, auto-replacements, recentActions[]), and teardown (reclaim summary once canceled, else null). Billing is per-provisioned-mailbox on the live provisioned count: $49 platform + per mailbox, graduated: 1-5 at $10, 6-20 at $8, 21+ at $7; 5-mailbox minimum ($99 at 5); the billed quantity tracks the real provisioned count (deprovision lowers it). activationState is the HONEST send state — trust it over 'sent' counts: 'active' = real sending live; 'pending_provisioning' = paid but infrastructure still being armed, sends shown are sandbox previews that DON'T leave; 'capacity_pending' = provisioning held at a spend limit; 'screening_hold' = account under review; 'sandbox' = demo/free. Use metrics for counts, infrastructure_status for per-mailbox health.Read-only
ack_messageAcknowledge 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 tenant.Changes data
activityUnified activity feed: campaign events (sent/reply/bounce/...) merged with deliverability loop 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.Read-only
campaign_resultsOutcome counts for ONE campaign. Input: campaignId (from launch_campaign). Returns { campaignId, sent, reply, bounce, complaint, unsubscribe, failed, soft_bounce } — bounce = HARD only, soft_bounce separate, opens not tracked. 404 if unknown. Use metrics for account-wide totals, list_campaigns for every campaign at once.Read-only
configure_byo_domainRegister or advance a BYO domain/mailbox intake. action = register (needs domain + domainRelationship: fresh_standalone|subdomain_of_primary|is_primary — runs pre-flight scan + abuse gate + reputation ladder, returns starting byoStatus) | poll_dns (needs id — re-checks DNS delegation, advances pending_dns -> active, or -> abandoned after 7 idle days) | acknowledge_consent (needs id + acknowledged:true — required before a primary domain can proceed past pending_consent; documents informed consent, does not remove exposure) | request_managed_mailboxes (needs id + count — platform-provisioned mailboxes on an ALREADY-ACTIVE domain; every response carries a billing projection { provisionedAfter, projectedMonthlyCents, formula }; quoteOnly:true previews without provisioning) | connect_mailbox (needs id + email + transport — declares an EXISTING OAuth/SMTP+IMAP connection, bypassing provisioning; transport is smtp | gmail_api | ms_graph, each with its own credential fields).Destructive
configure_dashboardWrite a saved dashboard view. action = create (needs name+layout) | update (needs id+rev+layout; optional name renames) | promote (id → default) | delete (id). update is rev-CAS: a stale rev returns { currentRev, currentLayout } to rebase and retry. Optional note. Read the current rev+layout via get_dashboard first.Destructive
configure_webhookManage 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).Destructive
contact_operatorReach a human operator 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 the operator; 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/infrastructure_status — 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/tenant — a 429 names retryAfter (seconds).Changes data
get_byo_domainsList 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.Read-only
get_dashboardRead 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 CAS base to configure_dashboard update). Views are both agent- and human-editable; write them with configure_dashboard.Read-only
get_webhooksList 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.Read-only
inboxUnified reply inbox across mailboxes. Cursor-paginated → { threads[], nextCursor }; each row: threadId, campaignName, leadEmail, subject, mailboxEmail, label, lastEventType, markStatus. Filters: mailbox, campaign, label, read, includeNonreply (bounces/OOO, default true), archived (exclude|include|only). Use thread for one thread's history.Read-only
infrastructure_statusWarmup + provisioning progress per mailbox. Returns { domains, mailboxes, sendReady, mailboxHealth[], messages[], nextSteps }. New mailboxes ramp-limit server-side: 5 sends/day week 1, 40/day week 4 — 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 (true once every one is ramped) — for one mailbox's capacity use its own flag. Each mailbox carries delivStatus (healthy/throttled/paused), complaint/bounce/softBounce rates; vendorReputationScore/vendorPlacementRate are VENDOR-REPORTED, null when unmeasured. Vendor-pool warmup is FEED-INVISIBLE 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.Read-only
label_threadSet 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.Changes data
launch_campaignCreate and activate a campaign on a lead list. You supply name, offer, leads[], sequence[] (per step: subject, body, delayDays — subject is REQUIRED on step 1; a LATER step may omit it to keep step 1's subject line [recommended], which resolves at launch to "Re: " + step 1's subject (unchanged if that already starts with "Re: "), or set one explicitly to change only that step's subject line — every later step is still sent In-Reply-To step 1, though some mail clients display a changed subject as a separate conversation), and optionally timezone, sendWindow, stopOnReply — the platform does not write copy. Each step's `step` number must be unique within the sequence; a duplicate step number is refused (400) naming it. Subjects and bodies may use {{firstName}} and {{company}} (filled from each lead); a launch is refused (400) if any subject or body uses any other {{token}}, a single-brace form of a known token such as {firstName} or {company} (a typo for the double-brace form — it would otherwise reach the recipient unsubstituted), or a run of 3 or more consecutive `{` or `}` anywhere (e.g. {{{firstName}}} or a lopsided run like {{{firstName}} — it would otherwise reach the recipient with stray braces attached). 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? }: integer hours 0-23, endHour INCLUSIVE (the last hour a send may start in), days 0=Sunday … 6=Saturday. Each omitted field (or all of them, by omitting sendWindow) gets the platform's RECOMMENDED default {"startHour":8,"endHour":16,"days":[1,2,3,4,5]} — Monday-Friday business hours — except on a sandbox account, where omitted fields are open every hour of every day until it upgrades and they take that default; the decision is yours, e.g. include 0 and 6 in days to send on weekends. Setting exactly ONE of startHour/endHour combines it with the recommended default for the OTHER (08/16, including on a sandbox account, since that value re-resolves to the same default on upgrade) — if that combination would wrap the window past midnight (start > end), the launch is refused; set both explicitly, including for a deliberate 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, with In-Reply-To/References set to step 1's Message-ID — it waits while that mailbox is at its daily cap, and the lead's remaining steps are cancelled (a 'failed' event says why) once that mailbox is paused or released, since neither ever lifts on its own, or when a paid account's thread went out from a connected BYO mailbox, which this build never sends from; a step whose previous step never went out is skipped. Suppressed leads are skipped. Returns { campaignId, sendWindow, timezone, nextSteps } — sendWindow and timezone are what actually applied. Campaigns send real mail, so a launch identical to one this account made in the last 60 seconds is REFUSED with 409 { code:'duplicate_campaign', existingCampaignId } rather than contacting the same prospects twice — check that campaign instead of relaunching. Resend the same idempotencyKey to retry a call whose response you lost: that replays the original result instead of being refused. Campaigns that differ in any field, and deliberate relaunches after those 60 seconds, are never blocked.Destructive
list_campaignsList every campaign at once: [{ campaignId, name, status, counts{sent,reply,bounce,complaint,unsubscribe,failed,soft_bounce} }], newest first — no per-campaign lookup needed. Use campaign_results for one campaign's counts, metrics for account-wide totals.Read-only
list_leadsList/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. 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.Read-only
list_messagesList this tenant's system + operator messages (a retryable setup step, a credential going live, an operator notice), 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 an operator 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.Read-only
markSet 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.Changes data
metricsAccount-wide outcome totals across ALL campaigns: { sent, reply, bounce, complaint, unsubscribe, failed, soft_bounce } — same shape as campaign_results but summed tenant-wide (bounce = hard only, opens not tracked). Use campaign_results for one campaign, list_campaigns per-campaign, or account for billing/quota.Read-only
pausePause ONE campaign: its status → 'paused', so the tick schedules no further steps (already-sent mail is unaffected; there is no resume tool). Input: campaignId. Returns { paused: true }. 404 if not found. Use pause_all to pause every active campaign at once.Destructive
pause_allPause EVERY active campaign for the tenant at once (each active status → 'paused'; the tick then schedules no further sends). No inputs. Returns { pausedAll: true }. Use pause to pause a single campaign by id.Destructive
remove_mailboxesDowngrade: release your N NEWEST live mailboxes, lowering the billed quantity. Inputs: count, acknowledged (must be true — release is immediate but irreversible; no mid-cycle credit, lower price starts next renewal, minimum 5 mailboxes/$99). Returns { releasedCount, failedCount, unreleased, billing, deduplicated }. `count` is RELATIVE (that many MORE, 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` names how many the vendor refused (still live, billed; `unreleased` lists them). Resend with the same key until `failedCount` is 0. `deduplicated: true` = NO new work — an earlier recorded outcome under that key was replayed. A genuinely NEW downgrade needs a NEW key. 409 = a release already running; re-check infrastructure_status before retrying. To ADD mailboxes use setup_infrastructure or configure_byo_domain.Destructive
replySend 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.Destructive
setup_infrastructureProvision sending infra: buy lookalike domains, create mailboxes, start warmup. Inputs: brand, primaryDomain, domains + inboxesEach (or distribution), persona, physicalAddress, senderIdentity. Billing is per provisioned mailbox; quoteOnly:true previews cost first. `domains`/`inboxesEach` are the infra you want to HAVE, not an amount to add — a repeat call never buys twice; each ordinal 0..domains-1 fills to its own count. Mailbox addresses are DETERMINISTIC from persona+ordinal+slot — keep persona unchanged on a retry. `registerDomains:true` is required consent before any NEW domain purchase; omitting it on a buy call is refused 400 registrar_optin_missing (self-correct by resending true, never an operator escalation) — also needs `registrant`, unless already on file. NO background retry exists: a `provisioning` result (pending|capacity_pending) needs YOU to retry to progress; capacity_pending needs contact_operator instead. Returns { jobId, billing, provisioning?, nextSteps }.Changes data
suppress_leadPermanently 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. Inputs: email, reason (fixed 'manual' — the only value this tool honestly 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.Destructive
threadFull 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/bounce/...), ts, messageId, metadata }, oldest first. 404 if unknown. Use inbox to LIST threads; reply to respond; mark/label_thread to triage.Read-only
update_leadRecord 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|not_now|not_interested|bad_fit|out_of_office|wrong_person — a server-enforced enum; 'do not contact' is NOT a member, use suppress_lead instead), notes, tags (free-form). A PARTIAL patch — only the fields you pass are changed; at least one of interestStatus/notes/tags is required. Filterable via list_leads.Changes data

Change history

No changes since the first observation. The first snapshot is the baseline.

Source listings
SourceListingFirst seenLast seenVersions
Official MCP Registryio.github.YS-projectcalc/agent-cold-email2 Oct 20262 Oct 20261