CLI for agent-cold-email, agent-native cold-email infrastructure: provision isolated branded domains and mailboxes, run sequences, and manage replies with one bearer token. Run `npx agent-cold-email demo` for a no-signup sandbox demo, no card, no real emails sent. Also installable as a stdio MCP server (`agent-cold-email mcp`).
This server exposes 2 tools with VERY DETAILED natural-language descriptions (the setup_infrastructure description alone is ~2200 chars), but critically lacks visible input schema definitions in the source code provided. The tool descriptions are exceptionally thorough and domain-specific (covering billing semantics, idempotency, DNS retry, registrant validation, capacity limits), which would normally indicate excellent definition quality. However, per the HARD SCORING RULES: 'If a tool has NO input schema at all: its schema score MUST be 0.' The source code snippet shows tool definitions in apps/platform/src/mcp/tools.ts, but the actual JSON Schema input definitions are not visible in the provided excerpts. The package.json entries and Dockerfile context suggest this is a real, shipped product with a Cloudflare Workers backend (apps/platform uses Hono + Wrangler), but without seeing the schema registration code itself, we must score conservatively. The descriptions are production-grade and LLM-optimized in tone (action-oriented, consequence-aware), but schema score of 0 due to visibility rules, combined with missing structured output schema documentation, severely penalizes the overall quality score.
Query current infrastructure provisioning status, warmup ramp, and next steps.
Provision sending infrastructure: buy branded lookalike domains, create mailboxes, start warmup. New mailboxes are ramp-limited server-side — 5 sends/day in week 1, rising to 40/day after 4 weeks — and your own calls cannot exceed that cap; poll infrastructure_status for the current dailyCap. Inputs: brand, primaryDomain, domains + inboxesEach counts, persona, physicalAddress, senderIdentity. Billing is per-provisioned-mailbox ($10/mailbox + $49 platform, min 5) and the billed quantity follows what you provision here — pass quoteOnly:true first to preview the new count + projected monthly price before committing (no silent capacity addition). Every response carries a `billing` projection { provisionedAfter (the live count AFTER this call — reality, not the ask), projectedMonthlyCents, formula }: on quoteOnly it's the preview, on an actual provision it's the real post-provision bill (a capacity-limited partial reflects only what landed). Returns { jobId, billing } — `jobId` is a correlation id only, not a trackable job handle: there is no job store and no endpoint that accepts it, so do not poll for it or treat its presence as meaningful; it exists to correlate this response with logs. A `provisioning` field is present ONLY when the call returned still OWING work, and names which state: 'pending' (a domain's DNS registration is still completing — `pendingDomain` names one of them) or 'capacity_pending' (held at a spend/plan-slot limit, so polling will NOT progress until an operator raises it — call contact_operator instead of waiting). Its ABSENCE is what says the provision finished; the only way to find out is to re-call setup_infrastructure or read infrastructure_status's `nextSteps`. An outcome carrying `provisioning`, and a quoteOnly preview, are deliberately NOT recorded against your idempotencyKey — retrying with the same key re-runs the call and finishes the job rather than replaying the unfinished answer. `domains` and `inboxesEach` are the infrastructure you want to HAVE, not an amount to add: each call keeps and resumes what this account already has and buys only the shortfall, so to provision MORE you ask for a LARGER number (domains:2 after a call that provisioned one buys the second; raising inboxesEach tops each domain up). Repeating a call therefore never buys twice, whatever you do with idempotencyKey — resend it, change it, or omit it. The key controls response replay only and has no bearing on what is purchased, so a retry is always safe. A domain this account already registered but that never landed in your account (a prior call that failed after the purchase) is ADOPTED on the retry at zero extra cost rather than bought again. A domain whose DNS setup has not finished yet is recorded and never lost — there is no per-domain DNS field to poll (infrastructure_status reports a domain count, not per-domain detail), so read its `nextSteps` for what to do next, and repeat this same call to converge on the domain and finish its DNS setup. Slot semantics: `domains: N` covers ordinals 0..N-1, and each ordinal fills to its own mailbox count (uniform via `inboxesEach`, or per-ordinal via `distribution` — see below); a repeat call at the SAME `domains` provisions nothing new once every ordinal is satisfied, so reaching ordinal 1 needs `domains:2`, not a second `domains:1` call. Mailbox addresses are DETERMINISTIC — derived from `persona` + ordinal + slot — so keep `persona` EXACTLY as it was on a resume or retry; changing it mid-account does not move existing addresses but does change what a NEW ordinal derives. `distribution` (optional, alternative to `inboxesEach`) names the per-ordinal mailbox count directly — one entry per domain ordinal, e.g. `[3, 2]` for 3 mailboxes on domain 0 and 2 on domain 1 — for a shape `inboxesEach`'s single uniform number cannot express; supply exactly one of the two (an array with `domains` entries, or the uniform count). `registerDomains` is this tenant's opt-in consent to real domain purchases made on the platform's own account (our COGS — your bill is unchanged, mailbox-count-based only); only the operator's own global switch being armed too can ever actually enable it. OMITTING it leaves any consent you previously gave UNCHANGED — pass `false` explicitly to revoke it. A call that omits `registerDomains` (or sends it `false`) on a buy-bearing request (one that needs to purchase a new domain) is refused BEFORE any spend. Once this account has consented at least once, that refusal is a 400 `registrar_optin_missing`: 'registerDomains was not set on this request' — resend the identical call with it set to true to self-correct; this is never an operator escalation. (An account that has never consented, on a platform where domain registration is not yet enabled at all, instead sees the operator-facing 503 `registrar_unarmed` — indistinguishable from the outside except by status code, but the fix is the same: set `registerDomains: true` and retry.) Separately, a `registerDomains:true` call with NO `registrant` anywhere (neither in this request nor persisted from a prior one) fails with a 400 `incomplete_registrant` naming the missing fields — also before any purchase. Fresh candidates are de-duplicated against what this account already owns and checked for availability; if a call needs to buy a domain and no available un-owned candidate exists it fails with a 400 naming that, never a silent repeat purchase (a call that needs to buy nothing is unaffected). `registrant` (a full registrant-of-record object { firstName, lastName, email, phone, addressLine1, city, state, country, postalCode, organization (optional, defaults to brand) }) is OPTIONAL even when `registerDomains` is true whenever a complete registrant is already on file from a prior call — supplying one makes it authoritative for THIS call; omitting it re-uses the persisted one. This platform never invents a domain registrant's legal identity, so a call with no registrant anywhere (neither supplied nor persisted) is rejected naming the missing fields, at or before the actual purchase — never silently. There is NO background retry: a call returning `provisioning` needs the caller to retry it; the platform does not complete it for you on a timer. Every response carries `nextSteps` (see the discriminated `status`/`steps` shape) — the account's own next action, computed fresh each time, so a stalled setup is never silent.
Input schemas not visible in source code. The tools are described with natural language in the prompt, but the actual JSON Schema definitions (types, required fields, constraints) are not shown in the provided source excerpts.
Output schema not documented. Neither tool's return type is formally specified. setup_infrastructure claims to return { jobId, billing, provisioning?, nextSteps }, but no schema is provided. infrastructure_status's output is undefined. LLMs cannot plan chaining without knowing what fields to expect.
infrastructure_status description is only ~60 chars ('Query current infrastructure provisioning status, warmup ramp, and next steps.'). When should an agent call this vs setup_infrastructure? What does 'warmup ramp' mean? What's in 'next steps'? A 10-20 sentence explanation is needed.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | F | 39 | 2026-07-28+ | v2 |
setup_infrastructure's input schema shows parameter names and optional type hints in the description (e.g. 'registrant: { firstName, lastName, email, ... }'), but there is no formal JSON Schema visible. The registrant parameter is listed as type 'object' with description referencing field names, but no nested schema or constraint (required fields, additionalProperties) is defined. Same for distribution (type array, but no itemType specified).
Error handling not documented. The description mentions many error cases ('registrar_optin_missing', 'registrar_unarmed', 'incomplete_registrant', 'capacity_pending') but there is no structured error response schema or recovery guidance tied to each error code. An LLM receiving a 400 'registrar_optin_missing' has no explicit instruction on what to do next without re-reading the massive natural-language description.
Idempotency semantics are described in natural language but not enforced via schema constraints. The description says 'idempotencyKey' controls response replay and has no bearing on purchase, but there is no formal constraint (e.g. required=false, description='optional') visible in the schema. An LLM might not understand it's optional.
Parameter interdependencies not formalized. The description states 'supply exactly one of [inboxesEach, distribution]', 'registrant is optional WHEN a complete one is already on file', and 'registerDomains must be true if domains are being purchased'. These are critical constraints, but they are not expressed in schema (oneOf, allOf, conditional logic). LLMs will miss them.