Skip to main content
Glama

agent-cold-email

setup_infrastructure

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 Schema

TableJSON Schema
NameRequiredDescriptionDefault
brandYes
domainsYes
personaYes
quoteOnlyNo
registrantNo
inboxesEachNo
distributionNo
primaryDomainYes
idempotencyKeyNoOptional idempotency key: resend the SAME key when retrying this call so a dropped-response retry is not applied twice (no duplicate campaign/provision/send).
senderIdentityYes
physicalAddressYes
registerDomainsNo

Schema Changelog

Changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. Changed2 schema fields changed
    • addedInput schema / properties / distribution
      Added value: +{
      +  "items": {
      +    "maximum": 10,
      +    "minimum": 1,
      +    "type": "integer"
      +  },
      +  "maxItems": 20,
      +  "minItems": 1,
      +  "type": "array"
      +}
    • changedInput schema / required
      Previous value: -[
      -  "brand",
      -  "primaryDomain",
      -  "domains",
      -  "inboxesEach",
      -  "persona",
      -  "physicalAddress",
      -  "senderIdentity"
      -]New value: +[
      +  "brand",
      +  "primaryDomain",
      +  "domains",
      +  "persona",
      +  "physicalAddress",
      +  "senderIdentity"
      +]
  2. Changed1 schema field changed
    • removedInput schema / properties / registerDomains / default
      Removed value: -false
  3. Changed2 schema fields changed
    • addedInput schema / properties / registerDomains
      Added value: +{
      +  "default": false,
      +  "type": "boolean"
      +}
    • addedInput schema / properties / registrant
      Added value: +{
      +  "properties": {
      +    "addressLine1": {
      +      "maxLength": 500,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "city": {
      +      "maxLength": 200,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "country": {
      +      "maxLength": 100,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "email": {
      +      "format": "email",
      +      "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      +      "type": "string"
      +    },
      +    "firstName": {
      +      "maxLength": 200,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "lastName": {
      +      "maxLength": 200,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "organization": {
      +      "maxLength": 200,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "phone": {
      +      "maxLength": 50,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "postalCode": {
      +      "maxLength": 20,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    "state": {
      +      "maxLength": 200,
      +      "minLength": 1,
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "firstName",
      +    "lastName",
      +    "email",
      +    "phone",
      +    "addressLine1",
      +    "city",
      +    "state",
      +    "country",
      +    "postalCode"
      +  ],
      +  "type": "object"
      +}
  4. Changed1 schema field changed
    • addedInput schema / properties / quoteOnly
      Added value: +{
      +  "default": false,
      +  "type": "boolean"
      +}
  5. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations are minimal (destructiveHint:false only), so the description carries the full burden. It discloses billing semantics, idempotency behavior, no background retry, no job store, deterministic addressing, domain adoption, ramp limits, and refusal-before-spend conditions. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded and information-dense, but written as one massive paragraph with repeated caveats around idempotency, billing, and retry behavior. Every detail matters, but the presentation is heavier and more redundant than needed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description compensates by specifying the return shape ({ jobId, billing }), provisioning state meanings, nextSteps behavior, partial-capacity billing, and error codes such as 400 registrar_optin_missing and 503 registrar_unarmed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 8%, but the description compensates richly. It explains quoteOnly, slots semantics for domains/inboxesEach, distribution as an alternative, registerDomains consent/revocation, registrant persistence/defaults, persona determinism, and idempotencyKey replay-only behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description leads with a specific action: 'Provision sending infrastructure: buy branded lookalike domains, create mailboxes, start warmup.' This clearly scopes the tool and distinguishes it from siblings like infrastructure_status and configure_byo_domain.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit guidance: preview with quoteOnly:true, poll infrastructure_status for dailyCap/nextSteps, call contact_operator for capacity_pending, and do not poll jobId. It also explains when retries are safe and when an error is self-correctable versus operator-facing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

TDQS

A4.4/5.0
Disambiguation4/5

Most tools have distinct purposes, but pause/pause_all and metrics/campaign_results/list_campaigns have overlapping scopes. Descriptions clarify the differences, so ambiguity is low.

Naming Consistency4/5

Uses snake_case with a mix of verb_noun (list_campaigns) and noun_verb (infrastructure_status), but patterns are predictable. Minor inconsistency between setup_infrastructure and infrastructure_status.

Tool Count4/5

25 tools is at the upper bound for a focused server, but the domain (cold email automation) justifies the number. Each tool covers a distinct aspect of the workflow.

Completeness4/5

Covers account management, campaigns, leads, threads, infrastructure, webhooks, and dashboards. Missing resume for paused campaigns and delete for campaigns, but core operations are present.