{
  "name": "getscaled",
  "title": "GetScaled",
  "description": "Model Context Protocol server for GetScaled — drive email/SMS/voice campaigns, contacts, journeys, sequences, deliverability, and analytics through a scoped API key.",
  "version": "0.3.2",
  "serverUrl": "https://mcp.getscaled.com/v1",
  "transport": "streamable-http",
  "protocolVersion": "2025-06-18",
  "authentication": {
    "type": "oauth2",
    "scheme": "Bearer",
    "tokenPrefix": "gsk_",
    "authorizationServer": "https://mcp.getscaled.com/.well-known/oauth-authorization-server",
    "protectedResourceMetadata": "https://mcp.getscaled.com/.well-known/oauth-protected-resource",
    "apiKeyInstructions": "https://portal.getscaled.com/admin/api-keys",
    "docs": "https://getscaled.com/auth.md"
  },
  "documentation": "https://getscaled.com/developers",
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "toolCount": 227,
  "tools": [
    {
      "name": "list_clients",
      "description": "List all clients (tenants) accessible to your API key. Platform-wide keys see every client; tenant-scoped keys see only their own. Use this to discover client UUIDs that other tools require when called with a platform-wide key. Returns id, name, slug, plan, max_daily_volume, active flag, created_at.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_client",
      "description": "Fetch a single client (tenant) — name, slug, plan, send quota, settings overview, status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_campaigns",
      "description": "List campaigns for the current client (scope is determined by your API key). Returns id, name, status, subject, scheduled_at, and engagement counters. Use this to find a campaign by name before calling get_campaign_stats.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_campaign",
      "description": "Get full details for a single campaign — type, status, sender, template, list, segment_filter, ip_pool_strategy, daily_limit, scheduled_at, all timestamps. Use this when list_campaigns showed a candidate and you need to inspect what is configured before deciding to launch, clone, or update.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_campaign_stats",
      "description": "Get engagement stats for a single campaign: sent / delivered / opened / clicked / replied / bounced / unsubscribed counts and rates. Use live=true while a campaign is sending; use live=false for completed campaigns. Stats include first-touch dedup, so opens reflect unique recipients, not pixel loads.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_campaign_links",
      "description": "List trackable links in this campaign with click counts and unique clickers — useful for content optimization.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_campaign_live_stats",
      "description": "Real-time campaign stats while sending — sent/queued/failed counts, current send rate, per-IP throughput. Use for monitoring an active launch. For final stats use get_campaign_stats.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_contacts",
      "description": "List contacts for the current client. Returns id, email, first_name, last_name, list memberships, suppressed flag, and last activity timestamps. Useful for: (a) finding a contact before enrolling them in a journey, (b) auditing list size before launching a campaign, (c) checking who is suppressed and why.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_contact",
      "description": "Get full details for one contact — email, name, company, title, custom_fields, tags, validation_status, engagement_score, engagement_tier, suppressed flag, list memberships, recent activity. Use after list_contacts narrows the candidate set; gives you the full record for one person before enrolling them, suppressing them, or analyzing their history.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_contact_activity",
      "description": "Full activity feed for a single contact: sends, opens, clicks, replies, bounces, unsubscribes, journey enrollments, list adds/removes. Use to debug engagement or attribution questions.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "search_contacts",
      "description": "Advanced contact search with structured filters. Supports tag, engagement, validation status, custom_field filters. Returns paginated results.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "detect_duplicates",
      "description": "Scan contact base for duplicates. Returns groups of likely-same contacts. Pair with merge_contacts to consolidate.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "list_custom_fields",
      "description": "List custom contact fields configured for this client. Used for templating + filter UIs.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_lists",
      "description": "List contact lists for the current client. Each list groups contacts for sending campaigns to. Returns id, name, contact_count, description, created_at. Use this to find a list_id before creating a campaign — campaigns require a list to send to.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_list_members",
      "description": "Page through contacts in a list. Returns id, email, first/last name, company, engagement_score, validation_status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_list_health",
      "description": "List health snapshot — bounce rate, complaint rate, engagement, suppression count, duplicate rate, validation status. Use before launching to a new/old list to flag risks.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_senders",
      "description": "List sender addresses (the \"from\" addresses campaigns can send from). Returns id, from_name, from_email, domain_id, verification_status, daily_limit. Use this to find a sender_id before creating a campaign. Only senders with verification_status \"verified\" can actually send.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_sender",
      "description": "Fetch a sender — verification status, daily limit, warmup state, today's send count.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_templates",
      "description": "List email templates. Each template is a reusable email body (HTML + plain text + subject) with merge tags for personalization. Returns id, name, subject, last_modified, status. Use this to find a template_id before creating a campaign.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_template",
      "description": "Fetch a single template — full body_html, body_text, subject, metadata.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "preview_template",
      "description": "Render a template with merge tags resolved. Returns subject, body_html, body_text. Use to verify merge-tag substitution before sending.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_domains",
      "description": "List sending domains for the client. Each domain has SPF/DKIM/DMARC records and one or more dedicated IPs assigned. Returns id, name, status (verified/pending/failed), cloudflare_zone_id, dkim_status, created_at. Use this to discover domain UUIDs for create_campaign domain_ids — campaigns can be limited to specific sending domains.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_domain",
      "description": "Fetch a domain — DNS state (DKIM/SPF/DMARC verified flags), bound IPs, BIMI status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_dns_records",
      "description": "Get the full set of DNS records the tenant must set for this domain — DKIM (one per active key), SPF, DMARC, optional MX (for inbound replies), optional BIMI. Each row includes type, host, value, and verification status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_dkim_keys",
      "description": "List all DKIM keys for a domain — selector, status (active|rotating|inactive|retired), created/rotated timestamps.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_journeys",
      "description": "List journeys (multi-step automation flows). A journey is a DAG of nodes — emails, waits, branches, goals — that contacts move through over time. Returns id, name, status, contact_count, created_at, last_activated_at. Use this to find journey_id before enrolling contacts or attaching campaigns to a journey.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_journey",
      "description": "Fetch a journey — full graph, status, triggers, goals, top-line stats.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "reset_journey",
      "description": "Reset a paused/draft journey — clears journey_contacts + journey_events. Refuses to reset 'active' journeys (pause first). Use to clear test traffic before re-launching, or to start a paused journey from a clean slate. Pass clear_dedup=true to ALSO wipe CCH rows, dedup30 Redis keys, AND per-contact rate-limit counters (contact_daily/weekly/monthly) for affected contacts (recovery from \"earlier sends never actually delivered\").",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "journey_stats",
      "description": "Top-line journey metrics — enrolled, in-progress, completed, exited (goal-met or removed), conversion rate.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "journey_node_analytics",
      "description": "Per-node metrics for a journey — entered/exited counts, dwell time, send-node engagement (open/click/reply/bounce).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "list_journey_contacts",
      "description": "Page through contacts enrolled in a journey with their current node + state.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_journey_templates",
      "description": "List the platform-shipped journey graph templates (welcome series, abandoned cart, re-engagement, ...).",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_sequences",
      "description": "List email sequences (linear N-step drips). Lighter-weight than journeys — a sequence is just a list of emails with delays between them. Returns id, name, step_count, enrolled_count, status. Use list_journeys for branching/conditional flows; use sequences for \"send email A on day 0, B on day 3, C on day 7.\"",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_sequence",
      "description": "Fetch a sequence with its steps + bound list + status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sequence_stats",
      "description": "Sequence-level stats — enrolled/active/completed/replied counts, step-level open/click/reply rates.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_segments",
      "description": "List segments (saved filter expressions). Returns id, name, contact_count, refreshed_at.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_segment",
      "description": "Fetch a segment — full rule_groups, contact count, refresh status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "preview_segment",
      "description": "Test a segment filter — returns total match count and a sample of matching contacts. Use before create_segment.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_dashboard",
      "description": "Get a one-shot health and activity overview for the client: total contacts, active campaigns, sends in last 24h/7d, open/click/bounce/reply rates, top performing campaigns, suppression count, current send velocity. Best as the first call when you (an agent) are starting a new task — gives you situational awareness in one request before drilling into specifics.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_imports",
      "description": "List CSV import jobs for the current client. Returns id, filename, status, row counts, target list_id, created_at.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_import",
      "description": "Get import job state — status (pending|validating|ready|processing|complete|failed), counts, errors. Poll while processing.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_ips",
      "description": "List sending IPs (admin scope). Returns address, rDNS, status, current daily cap, usage today, MTA node, pool assignment, dedicated client (if any), reputation snapshot.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_ip",
      "description": "Detailed IP record — warmup schedule progress, today's daily counter, last 7d sends, blacklist hits, bound domains.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_ip_pools",
      "description": "List IP pools — name, purpose tag (transactional|cold_outreach|warmup|nurture), member count, total daily capacity.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_ip_cost_summary",
      "description": "Monthly IP cost breakdown by provider/region. Includes active vs retiring counts and projected next-month cost. Cost source-of-truth for the cost dashboard.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_warmup_schedule",
      "description": "Today's expected warmup-tier daily limits across all warming IPs. Useful for capacity planning.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_mta_nodes",
      "description": "List Hetzner KumoMTA nodes — id, name, IP, last-heartbeat, status, hosted IP count, total daily capacity.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_mta_queue_stats",
      "description": "Real-time queue depth across all KumoMTA nodes — ready/active/scheduled/throttled counts. Use for ops dashboards.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_mta_features",
      "description": "List KumoMTA feature flags — TLS preferences, ARC signing, DKIM bypass, throttling tiers, TSA flags.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "fleet_capacity",
      "description": "Today's fleet capacity vs projected demand: aggregate daily IP cap, per-pool breakdown, headroom %, scale recommendation.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "reputation_overview",
      "description": "Fleet reputation summary — IP count by tier (excellent/good/warning/critical), with deltas vs 7d/30d.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "reputation_ips",
      "description": "Per-IP reputation snapshot — bounce rate, complaint rate, blacklist hits, Google/Outlook PMS.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_replies",
      "description": "List inbound replies. Filter by classification — human_reply (worth attention), auto_reply / out_of_office (often safe to skip), bounce/unsubscribe (handled automatically). Returns id, from, subject, snippet, classification, campaign_id, contact_id, received_at.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "reply_stats",
      "description": "Reply summary — counts by classification + status, response rate, median time-to-reply.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_conversation",
      "description": "Fetch the full thread for a reply — original outbound message + all subsequent replies + agent forwards. Provide reply_id (preferred) or contact_id+campaign_id.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_funnel",
      "description": "Funnel: sent → delivered → opened → clicked → replied → converted. Counts and stage conversion rates.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_trends",
      "description": "Time-series trend for a metric. Default: daily sends over the last 30 days.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_by_isp",
      "description": "Performance broken down by recipient ISP (gmail / outlook / yahoo / aol / other). Critical for diagnosing inbox-placement.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_geo",
      "description": "Open/click activity geographically (country + city) — derived from open-tracking pixel IPs.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_devices",
      "description": "Open activity by device type (mobile/desktop/tablet) and email client (Gmail web, iOS Mail, Outlook desktop, ...).",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_send_time",
      "description": "Open/click rate by hour-of-day + day-of-week. Use to choose best send windows.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_revenue",
      "description": "Revenue attributed to email (requires CRM/Stripe integration). Returns total $, conversions, RPE, attribution model.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_cohort",
      "description": "Cohort retention/engagement curves grouped by signup or first-send month.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_list_growth",
      "description": "List growth (subs added vs churned vs unsubs) over time.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_compare",
      "description": "Side-by-side comparison of campaigns — sent, delivered, open/click/reply rates, ROI.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "analytics_heatmap",
      "description": "Click heatmap for a campaign — per-link click counts. Use to identify high-engagement copy.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_suppressions",
      "description": "List the per-client suppression list. Suppressed addresses receive zero sends regardless of any list/segment/journey membership.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_consent",
      "description": "List consent records (express/implied/B2B-LI) per contact. Required for CASL/CCPA audit.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "compliance_stats",
      "description": "Compliance posture summary — total contacts, with-consent breakdown by basis, suppressed counts, GDPR erasure count, expiring CASL implied consents in next 30d.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_frequency_cap",
      "description": "Get this client's frequency-cap config — max sends per contact per day/week/month, plus exempt categories.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_deliverability_score",
      "description": "Composite deliverability score (0-100) with the underlying factor breakdown — bounce/complaint/inbox-placement/auth.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_blacklist_status",
      "description": "Latest RBL/blacklist scan results across all client IPs and domains.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_ab_tests",
      "description": "List A/B tests for a campaign — variants, allocations, current winner status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_ab_significance",
      "description": "Statistical significance for an A/B test — p-value, winning variant, confidence intervals.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_forms",
      "description": "List embeddable signup forms — id, name, fields, target list, submission count.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_integrations",
      "description": "List connected CRM/data integrations (HubSpot, Salesforce, Stripe, ...). Returns provider, status, last_sync.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_webhooks",
      "description": "List outbound webhooks configured for this client.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_api_keys",
      "description": "List API keys (id, name, scopes, last_used_at, expires_at). Plaintext tokens are NEVER returned — only on issue.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_users",
      "description": "List users with role, last login, MFA status. Tenant-scoped or platform-scoped depending on caller.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "system_health",
      "description": "Cross-platform health check — Aurora, Redis, Kafka lag, ECS service rollouts, MTA fleet liveness, Lambda error rates, queue depths, alarm states. Use as a one-call ops dashboard.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "audit_log",
      "description": "Query the platform audit log. Tracks all mutating actions across clients, users, and API tokens.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "audit_log_actions",
      "description": "List all action names that can be filtered against in audit_log queries.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_export_jobs",
      "description": "List recent export jobs (contacts, analytics, audit). Returns id, type, status, S3 download URL when complete.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_scheduled_jobs",
      "description": "List EventBridge cron rules + ECS scheduled tasks: name, schedule, last invocation status, next-fire time.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "admin_analytics",
      "description": "All-clients analytics rollup (PLATFORM scope only). Sends, opens, replies, top clients, top campaigns.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "client_scorecard",
      "description": "Per-client deliverability + engagement health grid (PLATFORM scope). Use to spot at-risk tenants.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_notifications",
      "description": "Platform notifications for the current actor — alerts, threshold trips, scheduled report digests.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "lookup_trace",
      "description": "End-to-end trace of a single message — enrolment → render → send-pipeline → IP+MTA → remote MTA response → engagement events. Indispensable for \"why did this email fail\" debugging.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_settings",
      "description": "Get tenant or platform settings (depending on key scope). Returns visible settings; secret values are masked.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_mcp_overview",
      "description": "MCP server self-introspection — registered tools, scopes, recent invocation rate, current API key status.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_onboarding_status",
      "description": "Per-step onboarding progress for a client. Identifies remaining manual steps.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "create_client",
      "description": "Provision a new tenant. UI safeguard: confirm=true required. Slug uniqueness checked. If trigger_onboarding=true, then DKIM/SPF/pool/onboarding email all run as part of the response. Returns the new client_id (use this for subsequent tenant-scoped calls).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_client",
      "description": "Update client settings — quota, contact info, active flag. Setting is_active=false stops all sends platform-wide for this client.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_contact",
      "description": "Create a single contact. UI safeguards: email format validated, suppression list checked (suppressed contacts return 409), per-client uniqueness enforced. Use bulk_import_contacts for >100 contacts at once. Use import_csv_* tools for CSV files.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_contact",
      "description": "Update a contact. Email changes trigger suppression-list re-check + uniqueness validation.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "suppress_contact",
      "description": "Suppress (soft-delete) a contact — adds to the per-client suppression list and stops all future sends. UI safeguard: requires confirm=true. The contact record is retained for audit trail; only sending is blocked. Use rightToErasure for full GDPR deletion.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "bulk_import_contacts",
      "description": "Insert up to 5,000 contacts in a single call. Suppression list is checked per-row. Returns counts of inserted/updated/skipped/suppressed. For larger imports use the import_csv_* flow which streams via S3.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "bulk_contact_action",
      "description": "Apply an action to many contacts at once — tag changes, list membership changes, suppression, consent updates. UI safeguard: confirm=true required. Cross-tenant contact_ids are silently filtered.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "merge_contacts",
      "description": "Merge duplicate contacts into a primary record. CCH (campaign sends), engagement events, list memberships, and journey states are all reassigned to the primary. Duplicates are soft-deleted. UI safeguard: confirm=true required. NOT reversible.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "export_contacts",
      "description": "Trigger an async contact export. Use trigger_export + get_export_status + get_export_download for the full flow.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_list",
      "description": "Create a new contact list. Static lists hold a manual membership set; dynamic lists auto-update from a filter. UI safeguard: dynamic_filter is REQUIRED when type=dynamic. Returns the new list record. Idempotency-key is supported for safe retries.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_list",
      "description": "Update a contact list — name, description, or (for dynamic lists) the filter.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_list",
      "description": "Permanently delete a contact list. UI-equivalent safeguard: requires confirm=true. This removes all contact_list_members entries and sets campaigns.list_id to NULL for any campaigns referencing this list. Contacts themselves are NOT deleted.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "add_contacts_to_list",
      "description": "Add contacts to a static list. UI safeguard: cannot be used on dynamic lists (returns INVALID_STATE). Cross-tenant contacts are filtered out (skipped_cross_tenant in response). Idempotent — duplicates are deduped via ON CONFLICT.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "remove_contacts_from_list",
      "description": "Remove contacts from a static list. Only the membership row is deleted; the contacts themselves remain in the platform. Cross-tenant IDs are silently filtered.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "import_csv_presign",
      "description": "Step 1 of CSV import: get a presigned S3 upload URL. The agent (or its caller) PUTs the CSV bytes to the returned `upload_url`. Returns import_id (use in subsequent steps), upload_url, fields. Flow: import_csv_presign → upload bytes → import_csv_validate → import_csv_assign_list → import_csv_process.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "import_csv_inline",
      "description": "Single-step CSV upload for sandboxed agents that cannot reach S3 directly. Server-side proxy: API receives the CSV bytes, uploads to S3, creates the import job. Use INSTEAD of import_csv_presign when your sandbox blocks egress to S3. Returns job_id — proceed with import_csv_preview / import_csv_validate / import_csv_assign_list / import_csv_process as usual. For CSVs larger than ~7MB, the presign flow is required.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "import_csv_validate",
      "description": "Step 2 of CSV import: scan the uploaded file, detect column mapping, and run a dry-run validation against the contacts schema. Returns row counts (valid/invalid/duplicates), detected mapping, and a sample of error rows. Always preview before processing — process is non-reversible.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "import_csv_preview",
      "description": "Preview the first N rows of an uploaded import (after validate). Helps confirm column mapping is correct before processing.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "import_csv_assign_list",
      "description": "Step 3 of CSV import: bind the import to a target list. Once processed, all imported contacts will be added to this list. The list must be static (dynamic lists derive membership from filters).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "import_csv_process",
      "description": "Step 4 of CSV import: kick off async processing. UI safeguard: requires confirm=true. Once started, contacts are inserted/updated and added to the assigned list. Returns the import job state — poll get_import for progress. NOT reversible — run import_csv_validate and import_csv_preview first.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "mark_contacts_validated",
      "description": "Bulk-set validation_status for contacts WITHOUT running the email verifier. Use this when contacts were imported without skip_validation=true and are stuck in 'pending' (or any other status the user wants to override). Selects by list_id, contact_ids, or emails. Stamps validated_at=NOW() and clears validation_stale. Requires confirm=true.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_template",
      "description": "Create a reusable email template. UI safeguards mirrored: subject required, body_html required, must include {{unsubscribe_url}} merge tag (compliance). For cold_outreach category, the system will additionally enforce CAN-SPAM physical-address footer at send time. Always run spam_check_template before associating with a campaign.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_template",
      "description": "Update an existing template. Edits to a template DO NOT auto-update in-flight campaigns (those snapshot the body at launch time). Affects new campaigns only.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_template",
      "description": "Delete a template. UI safeguard: requires confirm=true. Templates referenced by in-flight campaigns will fail to delete (FK constraint). Pause/finish those campaigns first.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "send_template_test",
      "description": "Send a one-off test email of this template to a single address. Routed through normal send pipeline (so DKIM, footer, tracking pixels all match production). Does NOT count against campaign quotas or trigger journeys.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "clone_template",
      "description": "Duplicate a template (handy for A/B variants or category swaps). Returns the new template id.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "spam_check_template",
      "description": "Run SpamAssassin/postmaster-style heuristics against a template. Returns score, rule hits, and flagged phrases. Recommended before launching ANY new template.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_campaign",
      "description": "Create a new campaign in draft state. Returns the campaign id and full record. For safety, ALWAYS dry-run first (set dry_run=true) so the validation chain catches missing fields before persistence. After successful create, set list_id, sender_id, template_id (if not provided here), then call campaign preflight, then launch. Includes idempotency-key support: re-sending the same key + body returns the original campaign without creating a duplicate.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_campaign",
      "description": "Update an existing campaign. UI safeguard: only DRAFT or SCHEDULED campaigns can be edited — campaigns in launching/sending/complete states reject most edits (the API returns INVALID_STATE). After editing list_id/template_id/sender_id, run campaign_preflight again to validate before launch.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_campaign",
      "description": "Delete a DRAFT or PAUSED campaign. UI safeguard: requires confirm=true. Campaigns that have already started sending cannot be deleted — pause + complete them first. CCH rows (historical sends) are retained for compliance even after campaign delete.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "clone_campaign",
      "description": "Duplicate a campaign as a fresh DRAFT. The clone copies template/sender/list/strategy but starts with no enrolled contacts and stat counters at zero.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "send_campaign_test",
      "description": "Send the campaign as a one-off test to a single address. Validates the full pipeline — sender, template, footer, DKIM, tracking. Does NOT modify campaign state.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "send_campaign_sample",
      "description": "Send the campaign to a sampled subset of the list (deliverability/inbox-placement check). UI safeguard: requires confirm=true. Real emails are dispatched. Use this for staged rollouts of large campaigns — review engagement before sending to the rest.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "campaign_preflight",
      "description": "Run preflight validation on a campaign: checks the list is non-empty, sender is verified, template renders cleanly, daily volume is within IP capacity, no compliance gaps (physical address, unsubscribe link, suppression overlap). Returns { success, errors[], warnings[], estimated_recipients, daily_headroom }. AGENT WORKFLOW: always preflight before launching. If errors[] is non-empty, fix them and re-preflight. Warnings are advisory but worth reporting to the user.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "launch_campaign",
      "description": "Launch a campaign — actually start sending email to recipients. IRREVERSIBLE. Requires the campaigns:launch permission (separate from campaigns:write — a \"drafting\" agent can compose campaigns but cannot fire them). AGENT WORKFLOW: ALWAYS run campaign_preflight first; refuse to launch if preflight returned errors. Show the user a summary (recipient count, sender, template name, scheduled time) and get explicit human confirmation before calling this tool. The `confirm: true` field is a forcing function — if the agent constructed this call without thinking, the schema rejects it.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "pause_campaign",
      "description": "Pause a campaign that is currently sending. Transitions status from \"sending\" to \"pausing\" (drains in-flight messages) then \"paused\". Already-queued messages may still send before the pause takes effect — typically within a few seconds. Resumable via update_campaign with status=\"sending\". Use this when: deliverability is degrading, the user reported a wrong list/template, or a problem was discovered after launch. Requires campaigns:write (a runaway-stop capability that any write-capable agent should have).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_domain",
      "description": "Register a new sending domain for this client. Auto-generates DKIM keys + SPF/DMARC recommendations. Returns the domain id and the DNS records that must be set in the tenant's zone (Cloudflare or whoever they use). After DNS publish, call verify_domain.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_domain",
      "description": "Update a sending domain — toggle primary, activate/deactivate, change reply-to.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_domain",
      "description": "Remove a sending domain. UI safeguard: confirm=true required. Senders bound to this domain become invalid; in-flight campaigns may fail. DKIM keys are retained for audit.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "verify_domain",
      "description": "Run a DNS verification check — DKIM, SPF, DMARC, MX (for inbound). Returns per-record pass/fail. Domain becomes send-eligible only when DKIM and SPF pass.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "sync_dns",
      "description": "For domains where the tenant has linked their Cloudflare API (or similar), push the expected DNS records directly. Idempotent. Returns the result for each record.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "verify_dns",
      "description": "Live DNS lookup against authoritative resolvers. Returns per-record verified flag + observed value.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "generate_dkim",
      "description": "Generate a fresh DKIM key for this domain. Returns the public key DNS record. The key is added in inactive state until you publish DNS — then call verify_dns + rotate_dkim to make it the active signing key.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "rotate_dkim",
      "description": "Promote the most recently verified DKIM key to \"active\" (the one used for signing) and demote the previous active key to \"rotating\" (kept around for ~7 days while cached signatures still validate). UI safeguard: confirm=true required, and the pending key must already be verified by DNS.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_sender",
      "description": "Create a sender (from-address). UI safeguards: from_email must be at a verified domain (else returns DOMAIN_NOT_VERIFIED), per-mailbox warmup tracking is initialized, and a verification email is sent to the address (mailbox owner must click confirm before sender becomes send-eligible).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_sender",
      "description": "Update sender display name, reply-to, signature, or daily limit. from_email is immutable (delete + recreate to change).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_sender",
      "description": "Delete a sender. UI safeguard: confirm=true required. Campaigns referencing this sender must be paused/completed first (FK constraint).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "resend_sender_verification",
      "description": "Re-send the mailbox verification email. New token replaces previous. Useful if the first email was missed/expired. Token TTL is 7 days.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "purchase_ips",
      "description": "Purchase fresh IPv4 addresses from the configured provider. UI safeguard: confirm=true required (real cost). Allocates VMs (or assigns from a Hetzner Floating IP pool), updates rDNS to a sending domain, registers in DB, and pushes config to the relevant MTA nodes. Returns the new IPs once they're reachable.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "release_ip",
      "description": "Mark an IP as retiring + return to provider. UI safeguard: confirm=true. Will fail if the IP is the only active IP in any binding (would leave a domain un-sendable).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "start_warmup",
      "description": "Begin (or restart) IP warmup with a curve. Once started, ipSelector enforces the daily cap automatically. UI safeguard: only IPs in warming state accept this; for active/paused IPs use update_ip first.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_ip_pool",
      "description": "Create an IP pool to logically group sending IPs by purpose. Pools are referenced from campaigns/journeys for routing.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "assign_ip_to_pool",
      "description": "Move an IP into a pool (or out, by passing null). One IP belongs to at most one pool.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "auto_scale_fleet",
      "description": "Compute fleet capacity gap vs target headroom and purchase the difference (capped). UI safeguard: confirm=true. Real money is spent. Use fleet_capacity first to inspect.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_mta_features",
      "description": "Update MTA feature flags. UI safeguard: confirm=true. Triggers config push to all nodes.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "retire_ip",
      "description": "Retire an IP end-to-end: OS unbind on its node, mark retired in DB, drop all ip_domain_bindings, resync SPF chains in Cloudflare, push refreshed KumoMTA POOLS, and (for OVH IPs) detach + terminate at the provider so monthly billing stops. Idempotent — re-retiring a retired IP is a no-op.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "assign_ip_to_client",
      "description": "Flip an IP between dedicated (one client) and shared (all tenants). Dedicated IPs only bind to that client's domains; the server triggers ip_domain_bindings cleanup + SPF resync on every flip. Pass client_id=null to return an IP to the shared pool.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "order_ovh_ips",
      "description": "Order N OVH failover IPs (ARIN/US block, $2.40/mo each) via /order/cart. Returns the OVH order id + invoice URL. After OVH provisions (5-15 min), the gs-ip-claim-ovh-failover cron picks them up, runs a pre-attach DNSBL check (Quad9 → Spamhaus + Barracuda), moves clean IPs into the project, attaches them to nodes round-robin, binds to all tenant domains, and starts warmup. Dirty IPs get auto-terminated.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "claim_ovh_failover",
      "description": "Manually trigger the claim cron now instead of waiting for the 10-min schedule. Handles three scenarios: (1) account-level orphan IPs from /order/cart deliveries (moves into project + attaches), (2) project IPs not yet routed to a node (classic attach), (3) routed-but-not-in-DB IPs (post-attach wiring: OS bind, PTR, DB row, domain bindings). DNSBL-listed orphans are auto-terminated.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "provision_mta_node",
      "description": "Provision a new OVH Public Cloud MTA node end-to-end: create the VPS, install WireGuard + KumoMTA + heartbeat cron, register the WireGuard peer on the AWS gateway, set OVH PTR, insert mta_nodes row, and mark active once heartbeat lands. Returns the node id + WireGuard IP.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "push_mta_config",
      "description": "Force-push KumoMTA config to a node. Regenerates init.lua with the current POOLS/SOURCES from the DB and triggers kumod hot-reload. Useful after manually rewiring IP bindings or when the 10-min auto-push cron is too slow. Returns the push result + a diff summary.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "terminate_mta_node",
      "description": "Terminate an MTA node and free all its IPs back to the project. Steps: drain in-flight queue (optional), detach all failover IPs from the instance at OVH, delete the OVH instance, remove the WireGuard peer from the AWS gateway, and mark the mta_nodes row terminated. IPs return to the project pool — they'll be re-attached to other nodes by the claim cron unless you retire them separately.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "fleet_audit",
      "description": "Snapshot of every active MTA node + every non-retired IP in the DB. Returns node-level fields (hostname, status, last_heartbeat, ip_count, wireguard_ip) and per-IP fields (address, warmup_day, reputation_score, blacklist_status, domain_count, daily_limit_today, sent_today, assigned_client_id). Use this to verify a freshly-ordered batch of IPs has landed fully wired up.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "fleet_recover",
      "description": "Run a fleet-wide recovery sweep. Reconciles OVH project state against ip_addresses, re-binds any IPs that lost their domain bindings, resyncs SPF for affected tenants, and pushes refreshed KumoMTA config to every active outbound node. Slower than fleet_audit (~30-60s) but corrects drift; safe to run.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "trigger_blacklist_check",
      "description": "Force an immediate DNSBL check across all of the calling client's IPs. Normally the reputation engine runs every 30 min — use this when you suspect an IP just landed on a blacklist and want confirmation before the next scheduled scan. Uses Quad9 for Spamhaus (avoids the open-resolver block) + Barracuda.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "generate_bimi_record",
      "description": "Generate + publish a BIMI DNS record (default._bimi.<domain> TXT) for a domain. Stores the logo/VMC URLs in the domains row and pushes the record to Cloudflare. Gmail/Yahoo/Apple Mail will start showing the brand logo in the inbox once DMARC is at p=quarantine+ AND (for Gmail) VMC is verified.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "check_bimi_setup",
      "description": "Validate a domain's BIMI setup: DNS record present, logo URL reachable + SVG-compliant, DMARC policy at p=quarantine or stricter, VMC certificate (if configured) issued by a trusted CA. Returns each check's pass/fail status so you can fix the failing pieces before relying on logo display.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "list_fbl_registrations",
      "description": "List all Feedback Loop (FBL) registrations for the calling client. ISPs like Yahoo (CFL), Comcast, Microsoft (JMRP), and AOL post a webhook to a registered email address every time a recipient hits \"Mark as spam\" — these are how complaint rate gets attributed to the right sender.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "create_fbl_registration",
      "description": "Register for an ISP Feedback Loop (FBL). After creation you still need to apply through the ISP's web form using the same registered_email — this row tracks the application + complaint volume. Once the ISP approves, set status=active so the FBL webhook handler attributes complaints correctly.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_fbl_registration",
      "description": "Update an FBL registration — typically used to flip status to active once the ISP confirms. Also use to rotate the registered_email or pause the FBL if it's producing false positives.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "get_sunset_policy",
      "description": "Get the calling client's sunset policy — the rules for suppressing or removing contacts who haven't engaged in N days. Keeps lists healthy and avoids burning IP reputation on dead addresses.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "update_sunset_policy",
      "description": "Update the sunset policy. Always run preview_sunset_contacts first to see how many contacts the new thresholds will affect — moving from 365d to 180d can suppress 30%+ of a list in one shot.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "preview_sunset_contacts",
      "description": "Preview how many contacts WOULD be suppressed or removed if the current sunset policy executed right now. Returns the count + a sample. Always run this before update_sunset_policy or execute_sunset to avoid accidentally torching a large chunk of an active list.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "execute_sunset",
      "description": "Apply the configured sunset policy NOW (otherwise it runs on a daily cron). Suppresses or removes contacts that match the no-open/no-click thresholds. Idempotent — running twice in a row is safe.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "get_engagement_tiers",
      "description": "Get the count of contacts in each engagement tier (highly-engaged / engaged / dormant / inactive) for the calling client. Used to prioritize warmup sends — high-engagement subscribers protect IP reputation while a new IP builds trust with ISPs.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_engagement_policy",
      "description": "Get the thresholds that define each engagement tier (e.g. \"highly-engaged = opened in last 30 days AND clicked in last 60 days\"). These thresholds drive tier classification + sunset decisions.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "update_engagement_policy",
      "description": "Update the engagement tier thresholds. Tighter thresholds (e.g. highly-engaged = 14d instead of 30d) shrink the highly-engaged tier and shift more contacts to dormant — useful during warmup to focus on a smaller, hotter audience.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "run_content_preflight",
      "description": "Multi-check content preflight: spam heuristics, link safety, image-to-text ratio, ALL CAPS detection, merge-tag validation, and rendering checks across major clients. Returns a score + ordered list of issues to fix. More comprehensive than run_spam_check (which only does heuristic word analysis).",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "set_mxtoolbox_config",
      "description": "Store the MXToolbox API key for the calling client. Required to use the Blacklist Monitor page — without a key the page shows \"not configured\". Free tier = 32 queries/day; paid starts ~$129/mo.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "set_glockapps_config",
      "description": "Store GlockApps credentials. Required for the Inbox Testing page to actually run placement tests against real seed inboxes (without it, tests get queued but never execute).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "set_google_postmaster_config",
      "description": "Store Google Postmaster Tools credentials (service-account JSON + domain list). Required for the ISP Insights page to populate Google data (domain reputation, spam rate, auth pass rates, FBL volume). Get the service account at console.cloud.google.com — create one, enable Postmaster Tools API, verify each domain at postmaster.google.com.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "set_microsoft_snds_config",
      "description": "Store Microsoft Smart Network Data Services (SNDS) credentials. Required for the ISP Insights page to populate Microsoft data (per-IP trap hits, complaint rate, sample volumes). Register IPs at sendersupport.olc.protection.outlook.com/snds and grab the access token from your dashboard.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "fetch_google_postmaster",
      "description": "Force-pull the latest Google Postmaster Tools data for the configured domains. Stores the result so get_google_postmaster_report returns fresh data. Normally runs on a daily cron — use this when you just rotated DKIM or made a major sending change and want immediate signal.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "fetch_microsoft_snds",
      "description": "Force-pull the latest Microsoft SNDS data for the configured IPs. Stores the result so the ISP Insights page shows current trap hits + complaint volume. Microsoft updates SNDS once per day; use this to verify a registered IP started reporting.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "get_cost_dashboard",
      "description": "Get the current monthly cost rollup: OVH instances, OVH failover IPs, AWS Lambda/RDS/Kafka, SES, Cloudflare, Cognito, etc. Useful for spotting waste — e.g. IPs you forgot to retire or oversized RDS.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_dmarc_summary",
      "description": "Get an aggregated DMARC report summary across all of the calling client's domains — pass/fail rates for SPF + DKIM + DMARC alignment broken down by sending IP and receiving ISP. Pulled from inbound DMARC aggregate reports (RUA mailbox at dmarc-reports@<domain>).",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_dmarc_reports",
      "description": "List individual DMARC aggregate reports received from ISPs (Google, Yahoo, Microsoft, etc.) with per-report pass/fail counts. Useful for forensic auditing when something is failing alignment.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "trigger_dkim_rotation_check",
      "description": "Force the DKIM rotation cron to run now. Checks every active DKIM key, rotates any older than the policy threshold (default 180 days), generates the new keypair, publishes new CNAMEs in Cloudflare, and marks the old key for grace-period decommission. Normally runs daily at 07:00 UTC.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "trigger_domain_dns_sync",
      "description": "Force a full DNS re-sync for every Cloudflare-managed domain: SPF chain (rebuilt from current IP fleet), DKIM CNAMEs, DMARC TXT, MX (apex + bounce subdomain), tracking CNAME, BIMI, TLS-RPT, and SES verification records (_amazonses TXT + 3 SES DKIM CNAMEs). Normally runs every 10 min on a cron — trigger this after adding/removing IPs to push the new SPF immediately.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "resync_dmarc",
      "description": "Re-publish DMARC records for every domain using the configured per-domain policy (p=none / quarantine / reject) plus the global RUA mailbox. Useful after tightening policy across many domains at once.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "resend_user_invite",
      "description": "Issue a fresh 7-day invite link for a user who hasn't accepted yet, invalidate any prior tokens, and re-send the branded invite email. Refuses if the user already accepted (Cognito CONFIRMED) — use the standard password-reset flow in that case. Returns the new accept_url so it can be relayed manually if the email bounces.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_journey",
      "description": "Create a journey (multi-step automation). UI safeguards: graph is validated for cycles, unreachable nodes, and missing required fields per node-type. Always start as draft, run preflight_journey, then activate. Node types: entry, send_email, wait_for_event, ab_split, update_contact, end.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_journey",
      "description": "Update a journey. UI safeguards: graph edits to ACTIVE journeys are restricted (only wait-time tweaks allowed; structural changes require pause first). Status transitions are validated.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_journey",
      "description": "Delete a journey. UI safeguard: confirm=true required. Cannot delete journeys with currently-enrolled contacts (returns INVALID_STATE) — archive or remove enrollments first.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "preflight_journey",
      "description": "Validate a journey before activation. Checks: graph structure, unreachable nodes, cycle detection, send_email nodes have valid template+sender, wait nodes have valid durations, compliance footer present, suppression list freshness. Returns blockers + warnings.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "enroll_contacts",
      "description": "Enroll contacts into a journey via list, segment, or explicit IDs (one of the three is required). UI safeguard: confirm=true required because this triggers real sends. The journey must be ACTIVE — the API rejects enrollment to draft/paused/archived. Suppressed contacts are skipped silently.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "remove_journey_contact",
      "description": "Hard-exit a contact from a journey. Their enrollment row gets state=exited and pending sends are cancelled.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "clone_journey",
      "description": "Duplicate a journey graph as a new draft (no contacts carried over). Useful for creating client variants.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "apply_journey_template",
      "description": "Apply a built-in journey template to an existing draft journey. Overwrites graph.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_journey_triggers",
      "description": "Set the auto-enrollment triggers for a journey. Replaces existing triggers.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_journey_goals",
      "description": "Set goals (success criteria) for a journey. Goals can optionally cause auto-exit when met.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "start_journey_test",
      "description": "Run a journey end-to-end against test contacts in a sandbox mode (compresses waits). Email is dispatched but to test-friendly seed addresses. Use to validate before activate.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_sequence",
      "description": "Create an outreach sequence (linear multi-step send). UI safeguards: each step requires a valid template, delay_days enforces a minimum gap, skip_on_reply respects RFC reply detection. Sequences are simpler than journeys — for branching logic use create_journey.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_sequence",
      "description": "Update top-level sequence fields. Use update_sequence_steps for the step graph.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_sequence_steps",
      "description": "Replace the step graph of a sequence. UI safeguard: edits to ACTIVE sequences are restricted — only steps not yet reached by enrolled contacts can change.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "launch_sequence",
      "description": "Activate a sequence. UI safeguard: confirm=true. Validates list, templates, senders before activation. After this, contacts in the bound list start receiving step 1.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "pause_sequence",
      "description": "Pause an active sequence. In-flight contacts stay where they are; new sends are blocked. Resume by relaunching.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_sequence",
      "description": "Delete a paused or draft sequence. UI safeguard: confirm=true.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_segment",
      "description": "Create a contact segment. Always preview_segment first to confirm rule_groups matches the expected contacts.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_segment",
      "description": "Update a segment definition. rule_groups changes invalidate any cached contact list.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_segment",
      "description": "Delete a segment. UI safeguard: confirm=true. Campaigns/journeys referencing this segment will need to be re-pointed first.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "snapshot_segment_to_list",
      "description": "Materialize the contacts matching rule_groups (or a saved segment_id) into a STATIC list as real members — the \"segment → campaign-sendable list\" primitive. Campaigns target a list_id and a campaign's segment_filter is NOT applied at send, so to send to a cohort you: create_list (static) → snapshot_segment_to_list (intersect the segment with the source list via a list_membership rule) → point the campaign's list_id at the new list. Idempotent (re-running adds only new matches). Returns { list_id, added, list_total }.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "send_reply",
      "description": "Reply to an inbound message. Threading headers (In-Reply-To, References) are set automatically so the response lands in the same thread. UI safeguard: confirm=true. Sender is auto-resolved from the original campaign — cannot be overridden via this tool.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "forward_reply",
      "description": "Forward an inbound reply to an internal address with optional context note. UI safeguard: confirm=true.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "add_suppression",
      "description": "Add an address to the suppression list. Idempotent — duplicates are deduped.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "remove_suppression",
      "description": "Remove an address from the suppression list (allow sending again). UI safeguard: confirm=true. Removing unsubscribe/complaint suppressions can violate CAN-SPAM/GDPR — only do this with documented re-consent.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "import_suppressions",
      "description": "Bulk-add to suppression list. Use for migrating from another platform or absorbing partner DNS-block lists.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_consent",
      "description": "Record a consent grant for a contact. Required for B2C campaigns under CASL/GDPR.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "revoke_consent",
      "description": "Revoke an existing consent record (auto-suppresses contact if it was their only basis).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "right_to_erasure",
      "description": "GDPR/CCPA right-to-be-forgotten. UI safeguard: confirm=true REQUIRED. Hard-deletes the contact + all PII from contacts, contact_lists, journey_state. Activity events become anonymized (kept for compliance audit but no PII). Adds a permanent suppression hash so the same email cannot be re-added without explicit re-consent.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_frequency_cap",
      "description": "Update frequency-cap policy. Set 0 to disable a window. Transactional category typically exempt.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_ab_test",
      "description": "Create an A/B test on a campaign. Allocations must sum to 100. Statistical significance auto-monitored.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "declare_ab_winner",
      "description": "Manually declare a winner. UI safeguard: confirm=true. Remaining hold-out audience receives the winning variant.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "run_spam_check",
      "description": "Run SpamAssassin against an inline template or template_id. Returns score, rule hits.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "start_inbox_test",
      "description": "Send template through configured seedlist (Glockapps/MailGenius). UI safeguard: confirm=true. Costs ~$2 per test.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_form",
      "description": "Create a hosted signup form. Returns form id + embed JS snippet. Double opt-in is on by default.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "trigger_integration_sync",
      "description": "Force a sync run for an integration (CRM contact pull, revenue attribution, etc.).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "create_webhook",
      "description": "Create an outbound webhook subscription. Payloads are HMAC-SHA256-signed with the signing_secret.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "test_webhook",
      "description": "POST a test event to a webhook URL with realistic sample data and proper HMAC signature. Use to validate endpoint setup.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "issue_api_key",
      "description": "Issue a new API key (gsk_...). UI safeguard: confirm=true. The plaintext token is returned ONCE in the response — store it immediately, the API stores only the hash.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "revoke_api_key",
      "description": "Revoke an API key — takes effect immediately, all in-flight requests using this key fail.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "invite_user",
      "description": "Invite a new user via magic-link. UI safeguard: invite token is single-use, 7-day TTL. The invitee must complete onboarding (set MFA, accept terms) before account is active.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_settings",
      "description": "Update tenant/platform settings. UI safeguard: confirm=true required. Secret-value keys (API keys etc.) are stored encrypted in client_settings. Returns the post-update visible state (secrets masked).",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "trigger_onboarding",
      "description": "Kick off the automated client-onboarding flow — DKIM keys, SPF/DMARC recommendations, default IP pool assignment, owner invite. UI safeguard: confirm=true required.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "get_send_quotas",
      "description": "Inspect a tenant's Tier 3 quota state. Returns the resolved recipient-category quotas (tenant override merged onto platform default) PLUS the current hour and day Redis counters so an operator can see how close each category (gmail/outlook/yahoo/apple/gov/edu/corporate/other) is to its cap. Also includes per-sending-domain quotas for the tenant's domains. Use before update_recipient_quota to confirm the current value and check counter pressure.",
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "update_recipient_quota",
      "description": "Upsert the recipient-category quota for a tenant (or the platform default). UI safeguard: confirm=true required. The new caps take effect within 5 min — send-pipeline's _quotaCache TTL — without a service restart. Use get_send_quotas first to see current state and counter pressure. Setting hourly_cap or daily_cap to null removes the bound for that key.",
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    }
  ]
}