Skip to help content
RightMessage
MCP referenceReference

MCP tools for campaigns and page elements

MCP campaign arguments, Smart Match targeting, safe change updates, live performance results, errors, and draft boundaries.

Available on the Grow, Scale, and Pro plans.

Updated

View as Markdown

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.

ArgumentType and behavior
nameString. Internal campaign name.
is_activeBoolean. Creation default: true.
base_urlString. Campaign base URL. Creation trims it and adds https:// if no HTTP scheme is present. Creation default: null. Update does not perform that normalization.
testingObject. 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_idsArray of goal-ID strings, default [] on creation. Order matters for primary_goal.
outcomeany_goal or primary_goal. Creation default any_goal. Legacy campaigns without the field use primary_goal.
changesArray 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

PropertyType and behavior
idExisting action ID, used on update to merge into that action. Omit for a new action.
variant_idExisting variant ID returned with id. Keep it to distinguish actions whose IDs repeat across variants.
segment_idsComma-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.
selectorCSS-selector string. Use unless supplying targeting.
targetingSmart Match object. Prefer the complete object returned by get-page-elements.
pageComma-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.
contentReplacement string, or newline-separated /find/replace/ expressions for partial text changes.
visibilityshow or hide.
styleObject mapping CSS properties to overrides.
attrObject mapping HTML attributes to overrides.
add_classes, remove_classesComma-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 changes returned by get-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 id plus variant_id merges supplied properties into the stored action. Omitted action properties survive. Explicit null removes targeting or modification values; null selector becomes an empty string, and null page resets 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 id creates 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:

FieldSchema
textRequired content string; optional matchType: exact, contains, or startsWith, default exact.
elementtagName string and candidateTags string array, default [].
structureInteger siblingIndex, siblingCount, and depth; string parentChain.
semanticsNullable strings id, ariaRole, ariaLabel; nullable object dataAttributes; string array meaningfulClasses, default [].
contentInteger charCount and wordCount; string textPrefix.
visualfontSize: {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, and testing.
  • Configuration fields recipe_code, dimension_id, and condition; new campaigns use null, null, and true respectively.
  • variants: each has id, name, rules, and actions. Segment rules use rules.all entries with dimensionId and segment; a default variant has no segment restrictions.
  • Each action includes its id, target/selector, page-rule array, and modifications. Modifications can contain text, findReplace entries {find, replace}, visibility, style, attr, and classes with add/remove arrays.

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:

JSON
{"name":"get-page-elements","arguments":{"url":"https://example.com/pricing","no_cache":false}}
JSON
{"url":"https://example.com/pricing","title":"WorkForge pricing","element_count":1,"elements_capped":false,"elements":[{"type":"H1","content":"WorkForge pricing","selector_stable":true,"text_unique":true,"targeting":{"mode":"fingerprint","fingerprint":{"text":{"content":"WorkForge pricing","matchType":"exact"},"element":{"tagName":"h1","candidateTags":["h1","div","span","p"]},"structure":{"siblingIndex":0,"siblingCount":1,"parentChain":"BODY > MAIN","depth":2},"semantics":{"id":"pricing-title"},"content":{"charCount":17,"wordCount":2,"textPrefix":"WorkForge pricing"}},"fallbackSelector":"#pricing-title"}}]}

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:

JSON
{"name":"list-campaigns","arguments":{}}
JSON
[{"id":"cpn_workforge","name":"WorkForge pricing","is_active":true,"variantCount":1,"goalCount":1,"outcome":"any_goal","abTestSummary":"10% control group"}]

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:

JSON
{"name":"get-campaign","arguments":{"campaign_id":"cpn_workforge"}}
JSON
{"id":"cpn_workforge","name":"WorkForge pricing","changes":[{"id":"act_title001","variant_id":"var_default1","segment_ids":"","selector":"#pricing-title","targeting":null,"page":"example.com/pricing","content":"Choose your WorkForge plan","visibility":null,"style":null,"attr":null,"add_classes":null,"remove_classes":null}]}

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:

JSON
{"name":"create-campaign","arguments":{"name":"WorkForge pricing","base_url":"https://example.com/pricing","goal_ids":["gol_workdemo"],"outcome":"any_goal","testing":{"is_enabled":true,"withhold":10},"changes":[{"selector":"#pricing-title","segment_ids":"","page":"example.com/pricing","content":"Choose your WorkForge plan"}]}}
JSON
{"id":"cpn_workforge","name":"WorkForge pricing","is_active":true,"base_url":"https://example.com/pricing","goal_ids":["gol_workdemo"],"outcome":"any_goal","testing":{"is_enabled":true,"withhold":10}}

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:

JSON
{"name":"update-campaign","arguments":{"campaign_id":"cpn_workforge","name":"WorkForge pricing headline"}}
JSON
{"id":"cpn_workforge","name":"WorkForge pricing headline","is_active":true,"base_url":"https://example.com/pricing","goal_ids":["gol_workdemo"],"outcome":"any_goal","testing":{"is_enabled":true,"withhold":10}}

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:

JSON
{"name":"duplicate-campaign","arguments":{"campaign_id":"cpn_workforge","new_name":"WorkForge pricing alternative"}}
JSON
{"id":"cpn_alternate","name":"WorkForge pricing alternative","is_active":true,"goal_ids":["gol_workdemo"],"outcome":"any_goal","testing":{"is_enabled":true,"withhold":10}}

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:

JSON
{"name":"delete-campaign","arguments":{"campaign_id":"cpn_alternate"}}
JSON
{"deleted":true,"campaign_id":"cpn_alternate"}

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.

ArgumentType and behavior
date_fromRequired string: YYYY-MM-DD or ISO 8601 timestamp. The date is taken in timezone.
date_toRequired string in the same format. Includes that local date. Must not precede date_from; maximum 90 inclusive days.
timezoneIANA timezone string, default UTC.
campaign_idOptional string to filter returned campaign rows.
project_idOptional 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:

JSON
{"status":"computing","retry_after_seconds":5,"message":"Campaign results are computing. Call get-campaign-performance again with the same arguments after 5 seconds."}

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:

FieldContract
headlinePortfolio 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.
experimentCountsCounts keyed by experiment state. This is separate from the date-range measurement.
campaignsArray of campaign result objects described below.
notLiveArray of {id, name, reason} for draft_only or inactive campaigns. A draft_goals entry also contains goalIds.
totalsvisitorsReached 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}:

  • holdback compares personalized visitors with the control group in the selected range.
  • proven is a prior A/B result retained after rollout, not a remeasurement of this range. Its rate and reference are null.
  • baseline compares 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 numeric value in 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:

JSON
{"name":"get-campaign-performance","arguments":{"campaign_id":"cpn_workforge","date_from":"2026-10-01","date_to":"2026-10-07","timezone":"UTC"}}
JSON
{"status":"ready","period":{"start":"2026-10-01","end":"2026-10-07","timezone":"UTC"},"computedAt":"2026-10-08T09:00:00Z","stale":false,"campaigns":[{"id":"cpn_workforge","name":"WorkForge pricing","testingEnabled":true,"holdbackPercent":10,"visitors":{"personalized":900,"holdback":100},"excludedBothArmVisitors":0,"comparison":{"basis":"holdback","label":"vs holdback","lift":"+20%","relativeLift":0.2,"rate":0.12,"reference":{"visitors":100,"converters":10,"conversionRate":0.1}}}],"notLive":[]}

Still need a hand?

Tell us what you’re trying to do and where you’re getting stuck.

support@rightmessage.com