MCP tools for campaigns and page elements
MCP campaign arguments, Smart Match targeting, safe change updates, live performance results, errors, and draft boundaries.
Use these tools to inspect page elements, build audience-specific website changes, and read campaign results. Configuration reads can show an unpublished draft. Performance reports measure the live campaign.
MCP campaign access and draft changes
All tools accept optional project_id (string). Omission uses the connection's active project or an accessible project the server can resolve. Pass it explicitly when working across projects.
The tools follow the shared MCP authorization and project-selection rules. Grow includes MCP access. Campaign personalization and goals require Scale or above to publish. Creating a draft through MCP does not override that publishing requirement.
Create, update, duplicate, and delete operate on the draft. They save a configuration snapshot, validate the result, and update the draft. None publishes or changes a live page immediately. Resource reads use the draft first, otherwise the published configuration. Shared failures include missing or inaccessible projects, missing entitlement, and configuration validation errors. See publication, backups, and undo before making changes live.
Examples are illustrative, not executed. Requests show the tool-call name and arguments. JSON results show the content, not the MCP transport envelope. Excerpts are labeled.
Campaign write arguments
Creation and update accept the following fields. Creation requires name; update requires campaign_id and at least one changed field.
| Argument | Type and behavior |
|---|---|
name | String. Internal campaign name. |
is_active | Boolean. Creation default: true. |
base_url | String. Campaign base URL. Creation trims it and adds https:// if no HTTP scheme is present. Creation default: null. Update does not perform that normalization. |
testing | Object. is_enabled is boolean; creation default true. withhold is a numeric control-group percentage; use 0–100. Creation defaults it to 10, converts numeric input to an integer, and replaces nonnumeric input with 10. The saved schema does not enforce a 0–100 bound. |
goal_ids | Array of goal-ID strings, default [] on creation. Order matters for primary_goal. |
outcome | any_goal or primary_goal. Creation default any_goal. Legacy campaigns without the field use primary_goal. |
changes | Array of change objects described below. Omission creates an empty campaign or preserves an existing campaign's variants. |
any_goal counts a visitor once when they convert on any attached goal. primary_goal uses the first ID in goal_ids. Use goal tools to create or inspect those goals.
On update, supplied top-level fields replace the corresponding stored fields; omitted fields remain. testing replaces the object rather than deep-merging it, although an omitted or nonnumeric withhold keeps the stored percentage. Save-time repair resets an omitted or nonboolean is_enabled to true, even if testing was previously off. Send both is_enabled and withhold when changing testing. Top-level null arguments are ignored, not a way to clear a field.
Each changes item
| Property | Type and behavior |
|---|---|
id | Existing action ID, used on update to merge into that action. Omit for a new action. |
variant_id | Existing variant ID returned with id. Keep it to distinguish actions whose IDs repeat across variants. |
segment_ids | Comma-separated string, not an array. Keep IDs in the same order across new changes that should share a variant. Empty string selects the default/fallback variant. |
selector | CSS-selector string. Use unless supplying targeting. |
targeting | Smart Match object. Prefer the complete object returned by get-page-elements. |
page | Comma-separated domain/path patterns, such as example.com/pricing, example.com/demo. No URL scheme. Omission on a new action means all pages; omission on an existing action preserves its page rules. |
content | Replacement string, or newline-separated /find/replace/ expressions for partial text changes. |
visibility | show or hide. |
style | Object mapping CSS properties to overrides. |
attr | Object mapping HTML attributes to overrides. |
add_classes, remove_classes | Comma-separated CSS-class strings. |
A domain and its www. form match each other. Other subdomains must be targeted explicitly. */blog* targets matching paths on any domain. Each page token becomes {domain, path}; a token without a slash gets path *.
The changes array is a replacement list, not an append operation. On update:
- Start with the complete
changesreturned byget-campaign. - Keep every action you want to retain. Unrepresented actions are removed; variants emptied by that removal are removed too. Variants that already had no actions are retained.
- An existing
idplusvariant_idmerges supplied properties into the stored action. Omitted action properties survive. Explicit null removes targeting or modification values; nullselectorbecomes an empty string, and nullpageresets targeting to all domains and paths. - Matching segment sets retain the existing variant ID and name. An unchanged round-trip retains the stored campaign content.
- A change without
idcreates a new action.
Do not reconstruct the update from the presentation-oriented variants field. Its long text can be truncated.
Resolve every segment ID before saving. New-variant construction silently omits unknown segment IDs from its rules; it does not reliably reject them. A variant whose IDs are all unknown can end up unrestricted. New variants group by the supplied ID order, while matching an existing variant compares the segment set without regard to order.
Smart Match object
targeting requires mode: fingerprint or selector. It can contain fallbackSelector and selector strings. A fingerprint object requires text; its nested fields are:
| Field | Schema |
|---|---|
text | Required content string; optional matchType: exact, contains, or startsWith, default exact. |
element | tagName string and candidateTags string array, default []. |
structure | Integer siblingIndex, siblingCount, and depth; string parentChain. |
semantics | Nullable strings id, ariaRole, ariaLabel; nullable object dataAttributes; string array meaningfulClasses, default []. |
content | Integer charCount and wordCount; string textPrefix. |
visual | fontSize: {min, max}, fontWeight: {min}, and positionY: {min, max}, with numeric bounds; boolean isAboveFold. These signals are not returned by the server-side page extractor. |
Preserve the returned fingerprint instead of guessing it. Identical text on two elements still requires two actions, because their structure signals distinguish the elements.
Returned campaign object
Create, update, and duplicate return the saved campaign, including:
id(cpn_…),name,is_active,base_url,goal_ids,outcome, andtesting.- Configuration fields
recipe_code,dimension_id, andcondition; new campaigns use null, null, and true respectively. variants: each hasid,name,rules, andactions. Segment rules userules.allentries withdimensionIdandsegment; a default variant has no segment restrictions.- Each action includes its
id, target/selector, page-rule array, andmodifications. Modifications can containtext,findReplaceentries{find, replace},visibility,style,attr, andclasseswithadd/removearrays.
Create and update also add unique_variant_count when it is greater than zero. This is the number of segment combinations across the dimensions used in variant rules, not simply the length of variants.
get-page-elements
Fetch a page's rendered HTML and identify elements available for personalization. Requires url (full HTTP or HTTPS URL). Optional no_cache (boolean, default false) bypasses the 24-hour HTML cache. Optional project_id uses the shared contract.
Returns {url, title, element_count, elements_capped, elements}. element_count is the total found before the 200-element cap; elements_capped indicates truncation. Each element contains uppercase type, content, selector_stable, text_unique, and targeting.
The extractor includes headings, paragraphs, buttons, links, and list items with at least three bytes of text. It skips scripts, styles, noscript, SVG, head, navigation, and footer subtrees. A header can still contain eligible content. Fingerprint parent chains contain at most five ancestors, while depth describes the full depth. Semantic hints omit empty values, skip internal data-rm-* attributes, and keep at most three meaningful classes.
This is read-only with respect to RightMessage configuration, but it makes an external page-fetch request through Firecrawl and can populate its cache. It does not install a snippet or edit the page.
Invalid URLs return Invalid URL format. Provide a full URL including https://. Fetch failures return Failed to fetch page HTML: …, or an error saying the page may be unreachable or Firecrawl may not be configured.
Illustrative request and result:
list-campaigns
List compact campaign summaries. The only argument is optional project_id. No configuration changes occur.
Returns an array of {id, name, is_active, variantCount, goalCount, abTestSummary} and outcome when that field exists. abTestSummary is a string such as 10% control group, or null when testing is off. variantCount counts saved variants, not all possible combinations. Shared access errors apply; an empty configuration returns [].
Illustrative request and result:
get-campaign
Read a campaign and its lossless editable changes. Requires campaign_id (string); accepts optional project_id.
Returns the campaign object, plus changes in the write format above. Presentation fields add isDefault to variants, pageFormatted to actions, and resolved names alongside known IDs. Modification strings in variants are shortened after 100 bytes and can include phrase annotations. The separate changes array is untruncated and preserves empty JSON objects.
Missing ID returns campaign_id is required. Unknown ID returns Campaign '…' not found. No configuration changes occur.
Illustrative request and result excerpt:
create-campaign
Create a campaign in the draft. Requires name and accepts the campaign write arguments plus optional project_id.
Returns the saved campaign object. Without changes, it creates no variants. Invalid stored values can reject the save, but unknown segment IDs can be dropped rather than rejected, as described above. Missing name returns name is required. Shared save and plan boundaries apply.
Illustrative request and result excerpt:
Use the Smart Match targeting from get-page-elements when available; the shorter selector example assumes a known stable ID.
update-campaign
Change a draft campaign. Requires campaign_id (string) and at least one campaign write argument. Optional project_id uses the shared contract.
Returns the saved campaign object. A supplied changes list can remove actions and variants, so retain the complete list you intend to keep. To rename a campaign, omit changes entirely. Unknown ID returns Campaign '…' not found. Missing fields return At least one field to update must be provided. Invalid configuration returns an error rather than a saved result.
Illustrative request and result excerpt:
duplicate-campaign
Copy a campaign into the draft. Requires campaign_id (string). Optional new_name defaults to the original name plus (Copy); optional project_id uses the shared contract.
Returns the copied campaign with a fresh campaign ID and fresh variant/action IDs. Goals and segment references remain references to the existing resources. The copy retains the campaign's configuration; it is not a copy of historical performance. Nothing is published.
Missing ID, an unknown campaign, or invalid copied configuration returns an error.
Illustrative request and result excerpt:
delete-campaign
Remove a campaign from the draft. Requires campaign_id (string); accepts optional project_id.
Returns {deleted: true, campaign_id}. It removes the campaign configuration, not the goal resources or previously recorded events. The live campaign is unaffected until publication. Missing ID returns campaign_id is required. Unknown ID returns Campaign '…' not found.
Illustrative request and result:
get-campaign-performance
Read live campaign results for an inclusive local-date range. This tool does not measure draft-only changes and does not alter a test.
| Argument | Type and behavior |
|---|---|
date_from | Required string: YYYY-MM-DD or ISO 8601 timestamp. The date is taken in timezone. |
date_to | Required string in the same format. Includes that local date. Must not precede date_from; maximum 90 inclusive days. |
timezone | IANA timezone string, default UTC. |
campaign_id | Optional string to filter returned campaign rows. |
project_id | Optional string under the shared access contract. |
Range figures count unique visitors. A conversion must follow exposure in the same arm. Visitors exposed to both arms are excluded. The selected range's descriptive lift is not a fresh experiment decision.
Response states
A cold or stale computation can return:
Call again with the same arguments after the stated delay. This queues or reads reporting work; it does not publish.
Other responses have status, period ({start, end, timezone} or null), computedAt (timestamp or null), and stale (boolean). Non-ready states include capped, not_live, and unavailable; they add reason and totals. Missing data is not zero.
A ready response adds:
| Field | Contract |
|---|---|
headline | Portfolio comparison: status, basis, relativeLift, observedLift, campaignCount, basisCounts, visitors, reason, display, and text. basis is holdback, proven, baseline, mixed, or null. basisCounts maps the first three bases to counts. display has value and label strings. The headline is visitor-weighted across measured campaigns. |
experimentCounts | Counts keyed by experiment state. This is separate from the date-range measurement. |
campaigns | Array of campaign result objects described below. |
notLive | Array of {id, name, reason} for draft_only or inactive campaigns. A draft_goals entry also contains goalIds. |
totals | visitorsReached and goalConversions measurement objects, not bare numbers. |
Filtering by campaign_id filters campaigns, notLive, and the totals; the portfolio headline and experiment counts are assembled before that filter.
Each campaign result has id, name, testingEnabled, holdbackPercent, daysOfData, exposedVisitorsPerDay, visitors: {personalized, holdback}, excludedBothArmVisitors, status, statusLabel, statusReason, measuredFrom, goals, outcome, comparison, and experiment.
goals is an array; outcome is a goal-shaped object or null. Each contains id, name, primary, personalized, holdback, lift, relativeLift, ci95, pValue, status, statusLabel, and statusReason. Both arms contain visitors, converters, and nullable conversionRate. Rates and relativeLift are proportions, not percentages. ci95 is {lower, upper} or null. Null lift means it cannot be measured, not no effect.
comparison is null or {basis, label, lift, relativeLift, rate, reference}:
holdbackcompares personalized visitors with the control group in the selected range.provenis a prior A/B result retained after rollout, not a remeasurement of this range. Itsrateandreferenceare null.baselinecompares same-page visitors who did not see the campaign, weighted to the personalized group's engagement and known-contact mix. It is not a controlled test.
When present, reference has visitors, converters, and conversionRate. Per-goal status text still describes holdback diagnostics even when the campaign comparison uses another basis.
experiment is null or the separately persisted decision: state, label, versionNumber, startedAt, seriesStartWeek, provenance, metricId, lookNumber, cutoffAt, decidedAt, arms, observedLift, decidedLift, decidedVisitors, haltReason, and proven. Timestamps, metrics, arm results, and lift values can be null when there is not enough decision-grade data. Experiment arms use enrollment and converter counts rather than the range's exposed-visitor totals. proven, when present, contains state, label, versionNumber, startedAt, endedAt, decidedAt, metricId, observedLift, decidedLift, decidedVisitors, and unchanged.
Experiment state is collecting, winner, loser, inconclusive, halted, not_decision_grade, or not_measurable; those are also the experimentCounts keys. When available, arms.personalized and arms.holdback contain enrolled, converters, and nullable pageviews. decidedVisitors is the combined enrolled-visitor count at the decision, not a visitor list. haltReason is srm, contamination, or null.
Each totals measurement uses one of three shapes:
- Ready:
{status: "ready", value: number, computedAt: string, stale: boolean}. - Computing:
{status: "computing", retryAfterSeconds: number}. - Unavailable:
{status: "unavailable", reason: string, explanation: string, availableFrom: string|null}. There is no numericvaluein this state.
Errors and example
Invalid timezone returns timezone must be a valid IANA timezone. Too many days returns The date range must be no more than 90 inclusive days. Invalid or reversed dates return an error describing the accepted date formats. A specific campaign absent from both live and draft/inactive entries returns a not-found error. Other reporting failures return Failed to fetch campaign performance for project ….
Illustrative request and result excerpt from a ready response: