MCP tools
Every tool below is available at POST /api/mcp once a client is
connected. "Scope" is the API-key scope the call requires; tools
marked none need a valid key but no particular scope.
Campaigns
A campaign is a one-time send to a list, segment, tag, or set of named addresses.
| Tool | Scope | What it does |
|---|---|---|
list_campaigns | campaigns:write | The project's campaigns, newest first, with delivery and engagement counters. |
get_campaign | campaigns:write | One campaign by id, including per-arm stats for an A/B send. |
count_audience | campaigns:write | How many contacts an audience would actually reach. Read-only. |
create_campaign | campaigns:write | Create and immediately enqueue a campaign. |
save_campaign_draft | campaigns:write | Create a campaign as a draft. Sends nothing. |
update_campaign_draft | campaigns:write | Edit a draft, or a scheduled campaign that has not started sending. |
launch_campaign | campaigns:write | Enqueue a saved draft. Sends email. |
render_campaign_preview | campaigns:write | Render a campaign's real content and subject. Read-only. |
cancel_campaign | campaigns:write | Halt a campaign's still-pending sends. Idempotent. |
The reviewable workflow
create_campaign enqueues the moment it returns. Unless a scheduleAt is set,
mail starts going out immediately and there is no state in between where a human
could look at it. That is rarely what you want from an agent.
Prefer this instead:
count_audience— confirm the audience is the size you expected. Sends nothing, so this is always safe to call first.save_campaign_draft— persist the campaign as a draft. Still sends nothing; it now appears in the console where a person can open it.render_campaign_preview— see the actual subject and rendered body, resolved through the same path the dispatcher uses.update_campaign_draft— fix anything that looks wrong, as many times as needed.launch_campaign— send it, once a human has approved.
Steps 1–4 are all reversible. Only step 5 is not.
Editing rules
update_campaign_draft replaces a campaign's settings rather than patching
them: an optional field you omit is cleared. Call get_campaign first if you
only mean to change one thing. Omitting data is the single exception — that
leaves the campaign's pinned content untouched.
Editing a scheduled campaign returns it to a draft and drops its pending
sends; the result carries reverted: true to say so. Call launch_campaign
again to re-arm it.
A campaign that has started sending cannot be edited. update_campaign_draft
refuses with reason: "not_editable", and a scheduled campaign within a minute
of its send time refuses with reason: "too_close_to_send" rather than risk
racing the dispatcher.
Templates — design
Templates are stored as an EmailDocument tree. The model authors the document;
these tools are the ground truth it checks itself against.
| Tool | Scope | What it does |
|---|---|---|
get_document_schema | none | The EmailDocument JSON Schema. |
validate_document | none | Validate a document, with per-path errors. |
render_preview | none | Render a document to HTML without saving it. |
import_html_to_document | none | Best-effort conversion of existing HTML into a document tree. |
get_template_document | none | The stored document for a template key. |
save_template_document | templates:write | Persist a document, optionally publishing it. |
send_test_email | send | Send a rendered document to one address. |
The usual loop: import_html_to_document → refine → validate_document until it
passes → render_preview to inspect → save_template_document.
Templates — management
| Tool | Scope | What it does |
|---|---|---|
list_templates | none | Template keys, types, and enablement for a brand. |
set_template_enabled | templates:write | Enable or disable a template, and set its default sender. |
rename_template | templates:write | Change a template's display name. |
Contacts
| Tool | Scope | What it does |
|---|---|---|
list_contacts | contacts:read | Filter by status, tag, or free-text query. |
get_contact | contacts:read | One contact by id or email. |
upsert_contacts | contacts:write | Create or update names and profile fields for up to 500 contacts, idempotent on email. |
unsubscribe_contact | contacts:write | Unsubscribe one contact. |
Sequences
| Tool | Scope | What it does |
|---|---|---|
describe_sequence_nodes | sequences:write | The authoring contract: every node kind's config and out-ports, which triggers fire, the condition language. |
list_sequences | sequences:write | Sequences, optionally filtered by status. |
create_sequence_draft | sequences:write | An empty draft with the minimal trigger → exit graph, ready for update_sequence. |
create_sequence | sequences:write | Create a sequence from an ordered list of steps. |
get_sequence | sequences:write | A sequence as a portable flow graph. |
update_sequence | sequences:write | Replace a sequence's graph atomically. |
simulate_sequence | sequences:write | Dry-run a contact through a sequence. Nothing is sent or persisted. |
set_sequence_status | sequences:write | Flip a sequence between draft, active, paused, and archived. |
enroll_contact | sequences:write | Enroll one contact in a named sequence. |
enroll_by_trigger | sequences:write | Enroll a contact into whatever sequence a trigger arms. |
bulk_enroll | sequences:write | Enroll up to 5000 contacts at once. |
record_event | sequences:write | Record a product event, which may enroll or exit sequences. |
Authoring a flow
A sequence is a graph, not a list, and a graph that saves cleanly can still
enroll nobody — a split whose variants have no edges, or a trigger kind the
engine never reads. So start with describe_sequence_nodes (or the
mailroom://schema/sequence-nodes resource), then:
create_sequence_draft → a draft enrolls nobody, so it is safe to iterate on
update_sequence → send the whole graph; it replaces what is stored
simulate_sequence → endedBy "dead_end" means a port has no edge
set_sequence_status → "active" is what finally makes it live
simulate_sequence is the sequence equivalent of count_audience — use it to
see where a contact would end up before enrolling anyone for real.
Only event triggers are evaluated. segment_enter, segment_exit, schedule
and manual are accepted and stored, and nothing ever reads them.
Resources
| URI | What it is |
|---|---|
mailroom://schema/email-document | JSON Schema for the EmailDocument tree |
mailroom://schema/sequence-nodes | the flow-graph authoring contract, in markdown |
mailroom://templates/registry | the project's template keys, types, and default subjects |
mailroom://examples/seeds | preset documents to use as references |
mailroom://brand/theme | the resolved brand render theme |