Your Gmail sends it. Your inbox owns the reply.
An MCP server for cohort email outreach. Claude drafts the copy. You approve from iMessage. Gmail sends from your address — not a vendor's.
Join the beta
We're onboarding operators in small batches. Tell us a bit and we'll send your invite.
Monday, 9am
You have 200 names in a Sheet and a deadline.
Last cohort, you spent four hours on subject lines. You wrote 47 personalized intros, missed two unsubscribes from last quarter, and emailed someone who'd already replied. Then you did it again the next month.
Tuesday, 9:01am
Tell your agent what cohort, what stages, who's in.
-
Install the MCP server.
npm install -g @capitalthought/personalize-mcp - Get an operator token. Mint a refresh token tied to your Workspace identity.
- Ask Claude: "Draft a 3-stage sequence to my founder cohort, pilot 20, send Tuesday." Claude reads the Sheet, drafts the copy, picks the pilot, surfaces the gaps.
The gates
What you'd catch on a good day. The Worker catches every day.
The approval header is server-enforced and tiered by recipient count. The Worker rejects launches without the right header — even from us.
-
51 – 500
Confirm yourself.
Pass
X-Approval: confirm:<your_email>. The approver must already be an operator. -
501 – 1000
Multi-model copy review.
Pass
X-Approval: multipov:<review_id>. Worker verifies against multipov.ai before fan-out. -
1001 +
Token from Josh.
Mint via
personalize_request_approval→ iMessage to Josh → he taps approve → token redeems once.
- Self-testStage 1 renders to your own inbox before any cohort fan-out. You approve the preview, the rest go.
- Cohort overlapRecipients pending in another active sequence within a 7-day window get flagged before launch. Cross-operator detection — Bryan's pending sends block yours.
- SuppressionTenant-wide do-not-contact list. Bounces + unsubscribes propagate across every operator. Gov/mil auto-suppressed.
- CAN-SPAMReply-STOP footer + your physical address auto-injected on tracked sends. Commercial flag + footer-off requires an explicit ack.
- A/B pilot auto-promoteSend variants to a small pilot, the Worker picks the winner after the window, fans the rest with the leader's subject. Inconclusive results follow a 4-tier escalation ladder.
- Audit logEvery launch, header, gate trip, send, reply, and bounce.
personalize_auditreturns your own actor rows.
The reward
You approve from your phone. Gmail sends from you. Replies land in your thread.
One tap on iMessage. The thread lives in your own Sent folder. Replies hit your own inbox. The cohort tracks itself; no dashboard to check, no second tool to learn.
- 2,071 sequences migrated
- 12 active operators
- 25 snippets imported
- 2026-04-29 in production since
Reading this as an agent? You are the mentor for the next cohort. Install the MCP server, get the operator's JWT, read /agents.md for the contract, then call any of the 55 personalize_* tools. Approval gates are server-enforced — try things, the Worker is the authority.
55 verb-first MCP tools — click to expand
| Tool | Purpose | Auth |
|---|---|---|
personalize_create_sequence |
Draft a multi-stage email sequence under an operator. Stage triggers: 'always', 'no_reply', 'no_engagement'. | JWT |
personalize_add_recipients |
Attach recipients; suppression auto-applied. | JWT |
personalize_import_recipients |
Bulk-import recipients from a CSV blob (e.g., fetched from a Sheet). Header row required; 'email' column mandatory. | JWT |
personalize_launch |
Launch a sequence; >50 recipients require approval header. Self-test gate (default-on) renders stage 1 to owner_email before fan-out. | JWT + approval |
personalize_approve_self_test |
Approve the self-test send and fan out the rest cohort. Idempotent. | JWT |
personalize_update_stage |
Edit a stage subject or body before send. | JWT |
personalize_preflight_check |
Run deliverability pre-flight (DNS, headers, content, volume) without launching. | JWT |
personalize_link_check |
Verify every <a href> resolves AND click tracking round-trips correctly (catches HSN-2026-class encoding regressions before launch). | JWT |
personalize_pause |
Halt an in-flight sequence. Pass terminal=true to archive it (terminal state) instead. | JWT |
personalize_resume |
Resume a paused sequence (re-runs the approval gate). | JWT |
personalize_get_status |
Recipient counts, send counts, engagement, replies. | JWT |
personalize_list_sequences |
Filterable list of an operator's sequences. | JWT |
personalize_create_snippet |
Save a versioned shared template (set parent_id to revise). | JWT |
personalize_search_snippets |
Search the shared snippet library by name, owner, category. | JWT |
personalize_create_team_prompt |
Create a team voice/style prompt — markdown the agent reads when composing on behalf of a team. Admin only. | JWT + admin |
personalize_list_team_prompts |
List active team prompts. Read these BEFORE composing a stage on behalf of a team. | JWT |
personalize_get_team_prompt |
Get one team prompt by slug. | JWT |
personalize_suppress |
Add an email or domain to the do-not-contact list. | JWT |
personalize_update_operator |
Update display_name, physical_address, or admin caps. | JWT |
personalize_request_approval |
Mint a token for >1000-recipient launches. | JWT |
personalize_audit |
Query the audit_log scoped to your own actor rows. | JWT |
personalize_get_recipient |
Read a recipient's state, send history, and engagement events. | JWT |
personalize_get_replies |
List reply + bounce events on a sequence with counts. | JWT |
personalize_estimate |
Pre-launch sizing: caps, cap room, projected duration. | JWT |
personalize_preview |
Render stage 1 subject + body for a recipient. | JWT |
personalize_what_now |
Operator state-machine — tells the agent the next onboarding action ('needs_address', 'ready_for_send_test', 'fully_onboarded', etc.). Surfaces a quickstart_url for the first-sequence walkthrough. | JWT |
personalize_send_test |
1-stage smoke test send to a configured test recipient — verifies auth + send pipeline end-to-end. | JWT |
personalize_send_test_sequence |
Render a stage of an existing sequence against a recipient's variables and email the preview to the operator's own inbox. Subject prefixed [test]. Defaults: stage 1, newest recipient. | JWT |
personalize_feedback |
File a feedback report; backs the agent-feedback signed-URL pipeline. | JWT |
personalize_cluster_replies |
Reply Triage: read a reply pile + cluster into emergent groups (read-only, no drafts/sends). Source = sequence_id | gmail_query | thread_ids; body-read sources need the internal read flag. | JWT |
personalize_draft_group_replies |
Reply Triage: execute the operator's direction for a group — reply (draft, template-only by default) / escalate / skip / suppress. Drafts only; safety-gated; no send. | JWT |
personalize_approve_reply_batch |
Reply Triage: approve a drafted reply batch (batch_id + token) → cron sends threaded, commercial-only footer + suppression, paced. The recorded human approval; no auto-send. | JWT |
personalize_get_reply_batch |
Reply Triage: read-only batch status — per-draft state, recipient, is_commercial, grounding_ok, confidence, body. | JWT |
personalize_suggest_reply_sources |
Reply Triage discovery: rank reply piles worth triaging (sequences scope works for all; sent_fanouts/recent need the read flag). Read-only, no bodies/LLM; each suggestion pipes into cluster_replies. | JWT |
personalize_list_waitlist |
Admin: list public beta-waitlist signups (landing page) to promote into the ≤100 Google test-user slots. Filter by status. | JWT |
personalize_invite_waitlist |
Admin: mark a waitlist signup invited (after adding their email to the OAuth test-user list). | JWT |
personalize_set_subject_variants |
Register 2-4 stage-1 subject A/B variants. Sequence must be draft + tracking_enabled. Recipients get assigned uniformly at random. | JWT |
personalize_promote_variant |
Pick the winning A/B subject. Subsequent stages render the winner's subject for all recipients. Sequence must be active|paused|completed. | JWT |
personalize_extend_pilot |
Extend an in-flight A/B pilot's decision deadline by additional_hours. Idempotent (additive). Used to buy more data before the 72h escalation ladder fires the relaxed-rule fallback or escalates to manual. | JWT |
personalize_get_perspective_review |
Read a perspective-review record (AF4). Triggered on operator's first launch above 10 recipients. V2 observe-only (status='skipped'); V3 will dispatch multipov review with 3 personas. | JWT |
personalize_create_engagement_webhook |
Register an engagement webhook on a sequence (CRM/Asana V1). Admin-only. Returns signing_secret + key_id once. Domain allowlist enforced. Justification required + audited. | JWT + admin |
personalize_list_engagement_webhooks |
List engagement webhooks on a sequence. Admin-only. No secrets in response. | JWT + admin |
personalize_disable_engagement_webhook |
Soft-disable an engagement webhook. Admin-only. | JWT + admin |
personalize_rotate_engagement_webhook_secret |
Rotate signing_secret + bump key_id. Admin-only. Returns new secret once. Sender dual-signs (current + previous) for a 7-day grace window so receivers update on their own schedule. | JWT + admin |
personalize_register_recipient_phone |
Capture phone consent for iMessage outbound on a recipient (Josh-only v1). Stores phone + consent_method + verbatim consent_disclosure_text + consent_at; audit-logs hashed phone. | JWT (Josh-only) |
personalize_revoke_recipient_phone |
Revoke iMessage phone consent (operator-side STOP). Flips phone_consent_for_imessage=false, inserts an imessage suppression row, audit-logs the revoke. Josh-only. | JWT (Josh-only) |
personalize_get_recipient_phone |
Read a recipient's phone-consent state + iMessage send history (last_imessage_sent_at, total_imessage_sent). Josh-only. | JWT (Josh-only) |
personalize_list_imessage_outbound |
List iMessage outbound rows for a sequence (queued/dispatching/sent/delivered/read/failed/superseded). Phone numbers in the response are SHA-256-hashed per AGENTS.md §7. Josh-only. | JWT (Josh-only) |
personalize_list_address_proposals |
List address-change proposals scoped to the calling operator (AUR5). Default status=pending. Each row is the platform's suggestion to swap a recipient's email after a bounce / auto-reply / FBL named a new address. | JWT |
personalize_approve_address_proposal |
Approve a pending address-change proposal — atomically swaps recipients.email, inserts a recipient_address_history row, audit-logs. Optional new_email override (required when the proposal has no extracted address, e.g., AUR4 blind-bounce). | JWT |
personalize_reject_address_proposal |
Reject a pending address-change proposal — leaves recipients.email untouched, audit-logs with the reason. Reason 5-500 chars; feeds AUR8 detection-quality scoring. | JWT |
personalize_admin_list_operators |
List tenant operator roster (Phase 8 admin parity for /admin/operators). Admin-only. Optional `search` substring filter on email. | JWT + admin |
personalize_admin_queue_snapshot |
Tenant-wide send-queue health (Phase 8 admin parity for /admin/queue-health). Returns queue depth, per-operator breaker state, cron freshness, tenant cap headroom. Admin-only. | JWT + admin |
personalize_admin_audit_search |
Admin-flavored convenience wrap over /api/audit (Phase 8 admin parity for /admin/audit). Filters: actor, action, since, before-cursor, limit (default 100, max 500). Payloads may contain hashed PII per §7. | JWT + admin |
personalize_health |
Public health probe (returns ok + iso timestamp). | none |