Skip to main content
Glama

setup_infrastructure

Provision cold-email sending infrastructure by buying branded lookalike domains, creating mailboxes, and starting warmup. Use quoteOnly to preview the projected monthly cost before committing.

Instructions

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. Changed4 schema fields changedv0.2.3
    • addedInput schema / properties / distribution
      Added value: +{
      +  "items": {
      +    "maximum": 10,
      +    "minimum": 1,
      +    "type": "integer"
      +  },
      +  "maxItems": 20,
      +  "minItems": 1,
      +  "type": "array"
      +}
    • addedInput schema / properties / registerDomains
      Added value: +{
      +  "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"
      +}
    • changedInput schema / required
      Previous value: -[
      -  "brand",
      -  "primaryDomain",
      -  "domains",
      -  "inboxesEach",
      -  "persona",
      -  "physicalAddress",
      -  "senderIdentity"
      -]New value: +[
      +  "brand",
      +  "primaryDomain",
      +  "domains",
      +  "persona",
      +  "physicalAddress",
      +  "senderIdentity"
      +]
  2. First observedv0.1.0

TDQS

A4.6/5.0
Behavior5/5

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

With only a destructiveHint:false annotation, the description carries the full behavioral burden and fully delivers: it discloses that idempotencyKey controls response replay only and has no bearing on purchases, that jobId is a correlation id with no store or endpoint, that a result carrying provisioning is deliberately NOT recorded against the key, and that there is NO background retry. It candidly explains the refusal paths (registrar_optin_missing, registrar_unarmed, incomplete_registrant) and that billing reflects what actually landed. The description's credit for creating rather than destroying state is consistent with the destructive=false annotation — no contradiction.

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?

The front-loading is correct — the purpose is stated before details — and nearly every sentence carries semantic value. However, the prose is enormous (~700 words) with dense semicolon and parenthetical chains, and a few behaviors (no background retry, retry-to-converge) surface in more than one place, which hurts scannability relative to the information density.

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

Completeness4/5

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

For a high complexity tool with no output schema, the description almost fully ties out the contract: it defines billing projection fields, the provisioning field presence/absence state machine, jobId's non-usability, error codes, and the pointer to infrastructure_status's nextSteps. The only real gap is that the discriminated status/steps shape is referenced but not enumerated, and primaryDomain's role in the domain acquisition stays ambiguous.

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

Parameters4/5

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

Schema coverage is only 8% (one param has a description), so the description must compensate — and it largely does: the slot semantics for domains with ordinals 0..N-1, the inboxesEach vs distribution alternative with an example array, determinism of mailbox addresses with persona, and registerDomains/registrant/quoteOnly consent flows. The remaining gap is primaryDomain, which is name-dropped in the inputs list but never distinguished from the lookalike domains being purchased; the schema entry is just a string field, so the agent gains no clarity there.

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?

The description opens with a precise verb and resource — 'Provision sending infrastructure: buy branded lookalike domains, create mailboxes, start warmup' — which immediately distinguishes it from read-only siblings like infrastructure_status and from teardown tools like remove_mailboxes. An agent can tell what this tool does and what it is not without reading any other schema.

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?

The description routes the agent to the right action for each state: poll infrastructure_status for dailyCap, call contact_operator instead of waiting when capacity is held, re-call setup_infrastructure to converge DNS after 'pending', and pass quoteOnly:true first to preview billing before committing. Sibling tools and alternative behaviors are named explicitly with the exact conditions that select them.

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

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/YS-projectcalc/agent-cold-email'

If you have feedback or need assistance with the MCP directory API, please join our Discord server