# MCP tools for widgets

Exact MCP widget arguments, targeting rules, design options, response fields, errors, and draft-only changes.

Available on the Grow, Scale, and Pro plans.

Use these tools to connect a flow to a widget, set its audience and placement, and edit its design. A widget controls where and how a flow appears; it does not contain the flow's questions or branches. See [flow tools](https://rightmessage.com/help/mcp-tools-flows) for those resources.

## MCP widget access and draft changes

Every tool accepts optional `project_id` (string). If omitted, the server uses the connection's active project or an accessible project it can resolve. Supply the ID explicitly when operating across projects.

The tools follow the [shared MCP authorization and project-selection rules](https://rightmessage.com/help/mcp-authorization-reference). There is no additional per-tool plan requirement. A missing project, inaccessible project, or missing entitlement returns an error rather than another project's data.

Configuration reads use the current draft when one exists, otherwise the published configuration. Create, update, duplicate, and delete save the draft and a configuration snapshot. They do not publish, display a widget, or execute its flow. Invalid configuration returns a validation error. Lookup and mutation errors are MCP errors, not successful resource objects. See [publication tools](https://rightmessage.com/help/mcp-tools-publishing) before making the widget live.

Examples below are illustrative, not executed. Requests show MCP tool names and arguments; results show the JSON content, not the surrounding MCP transport envelope. Short result examples are explicitly labeled excerpts.

## Widget arguments

`create-widget` and `update-widget` share these fields. Only creation requires `name`. Update requires `widget_id` and at least one changed field.

| Argument     | Type and behavior                                                                                                               |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `name`       | String. Internal widget name.                                                                                                   |
| `type`       | String: `modal`, `bar`, `toaster`, `inline`, or `takeover`. Creation default: `modal`.                                          |
| `is_enabled` | Boolean. Creation default: `true`.                                                                                              |
| `flow_id`    | String identifying the flow. Creation default: no linked flow. On update, explicit `null` detaches it; omission preserves it.   |
| `frequency`  | String: `startover`, `immediately`, `pageview`, `session`, or `none`. Creation default: `session`. Returned as `whenToAskNext`. |
| `visibility` | Object described below.                                                                                                         |
| `options`    | Design object described below. Nested updates preserve omitted keys.                                                            |

### `visibility`

| Property             | Type and behavior                                                                                                                                                                                                                                                                                |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `conditions`         | Recursive logic object or `null`. A leaf, `{"and":[...]}`, `{"or":[...]}`, or `{"not":...}`. `null` means all pages; `{"or":[]}` means none. Creation default: `null`. See [condition tools](https://rightmessage.com/help/mcp-tools-conditions) for leaf arguments and validation.              |
| `placements`         | Array of ordered inline-placement objects. Each requires nonblank string `path`, nonblank string `selector`, and `position`: `before`, `after`, or `inside`. Optional `domain` is a bare hostname, at most 253 characters, with no scheme, port, path, or wildcard. Omit it to match any domain. |
| `areas`              | String identifying an inline embed area, such as `workforge-survey`.                                                                                                                                                                                                                             |
| `trigger`            | String. Creation default: `exitIntent`. The runtime values and default mismatch are described below.                                                                                                                                                                                             |
| `dismissRule`        | Object with `type`: `session`, `duration`, or `forever`. Duration rules also use numeric `duration` and `unit`: `minutes`, `hours`, `days`, or `weeks`. Creation default: `{"type":"session"}`.                                                                                                  |
| `instantDismiss`     | Boolean.                                                                                                                                                                                                                                                                                         |
| `formSubmitSelector` | Selector used by the modal `formsubmit` trigger. The stored schema does not impose a dedicated type rule.                                                                                                                                                                                        |

Placement paths can be exact (`/welcome`) or wildcard (`/blog/*`). Exact paths take precedence over wildcard paths, then list order resolves ties. `inside` appends to the target's children. Domains are saved lowercase.

Audience conditions and visual placement are separate. Preserve returned `visibility.conditions` unchanged if you are not changing targeting. On update, omitted visibility properties survive; a supplied property replaces that property. In particular, `conditions`, `placements`, and `dismissRule` are not recursively merged. Send `placements: []` to clear placements. Inline widgets must retain an embed area or at least one complete placement. Non-inline widgets cannot have nonempty placements; clear them when changing type.

Legacy page-targeting properties (`show_on_specific_pages`, `show_pages`, `hide_on_specific_pages`, `hide_pages`, `isWhitelistEnabled`, `whitelist`, `isBlacklistEnabled`, `blacklist`) are rejected. Use `conditions` instead.

### Trigger values and the creation default

The tool's description names `exitIntent`, `scroll`, and `timed`, but the visitor runtime does not recognize those as complete trigger values. New widgets nevertheless default to `exitIntent`. For a non-inline widget, set a supported trigger explicitly. Saving the default alone does not establish that it will appear.

| Runtime value | Behavior                                                                                                                   |
| ------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `immediate`   | Shows when the widget is eligible.                                                                                         |
| `exit`        | Exit intent on desktop; idle-based intent on mobile.                                                                       |
| `time_5s`     | Shows after five seconds. Replace `5` with an integer number of seconds.                                                   |
| `scroll_50pc` | Shows at 50% page scroll. Replace `50` with the intended integer percentage. A page too short to scroll shows immediately. |
| `manual`      | Waits for the widget's manual link or JavaScript trigger.                                                                  |
| `formsubmit`  | Modal only. Observes website form submission, optionally limited by `formSubmitSelector`.                                  |
| `barstatic`   | Bar only. Shows as a static bar.                                                                                           |

Inline widgets show through their placement rather than waiting for `visibility.trigger`. A valid trigger still does not bypass audience, dismissal, frequency, or device settings.

### `options`

All properties are optional. Returned options use camelCase. `custom_css` and `display_targets` are accepted aliases for `customCss` and `displayTargets`. Explicit `null` resets a nullable override; omission preserves it.

Some older design properties have no type restriction in the production schema. They are marked **unconstrained** below rather than given an invented enum. Copy their existing values when you are not changing them.

| Property                               | Schema and purpose                                                                                                                   |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `theme`                                | Unconstrained; creation default `plain`.                                                                                             |
| `palette`                              | Light-palette object; fields below.                                                                                                  |
| `paletteDark`                          | Object or `null`. Same palette fields. With `darkMode: auto`, overrides light colors for the visitor's dark color scheme.            |
| `darkMode`                             | `off` or `auto`. `off` uses the light palette.                                                                                       |
| `font`                                 | Unconstrained body-font setting.                                                                                                     |
| `headingFont`                          | String or `null`; null inherits the body font.                                                                                       |
| `typeScale`                            | `s`, `m`, `l`, or `custom`. Send actual `fontSize` values when changing scale.                                                       |
| `fontSize`                             | Typography object; fields below.                                                                                                     |
| `structure`                            | Sizing and spacing object; fields below.                                                                                             |
| `look`                                 | Appearance object; fields below.                                                                                                     |
| `overrides`                            | Object containing only `phone` and `tablet`. Device-specific fields are described below.                                             |
| `customCss`                            | Unconstrained advanced CSS setting; creation default empty string.                                                                   |
| `placement`                            | Unconstrained placement setting.                                                                                                     |
| `headerInfo`                           | Object with unconstrained `isEnabled`, `style`, `imageUrl`, `title`, and `subtitle`.                                                 |
| `progressBar`                          | Object with unconstrained `isEnabled` and `baseColor`.                                                                               |
| `displayTargets`                       | Object with `phones`, `desktop`, and `tablets`; all default to `true` on creation. These properties are unconstrained in the schema. |
| `style`                                | Object with unconstrained `shadowSize`.                                                                                              |
| `backButtonLabel`, `backButtonEnabled` | Unconstrained settings.                                                                                                              |
| `nextButtonLabel`, `skipButtonLabel`   | Strings; defaults `Next` and `Skip`.                                                                                                 |
| `buttonAnimationTrigger`               | Unconstrained setting.                                                                                                               |

#### Palette fields

`palette` accepts these unconstrained base properties: `borderColor`, `contrastColor`, `backgroundColor`, `textOnContrastColor`, and `textOnBackgroundColor`. Creation defaults are `#e2e8f0`, `#3b82f6`, `#ffffff`, `#ffffff`, and `#1e293b`, respectively. In `paletteDark`, those five properties must be strings.

Both palettes also accept nullable strings: `answerButtonColor`, `textOnAnswerButtonColor`, `formFieldColor`, `formFieldTextColor`, `formFieldBorderColor`, `placeholderColor`, `linkColor`, `backdropColor`, `backgroundGradientFrom`, `backgroundGradientTo`, `questionHeadingColor`, `questionTextColor`, `offerHeadingColor`, `offerTextColor`, `headerTitleColor`, and `headerSubtitleColor`. `backdropOpacity` is a number from 0 to 1 or `null`.

`contrastColor` and `textOnContrastColor` style submit buttons. `answerButtonColor` and `textOnAnswerButtonColor` style answers. Null question, offer, and header text-color overrides inherit `textOnBackgroundColor`.

#### Typography fields

`fontSize` accepts unconstrained `lineHeight`, `headlineFontSize`, `formFieldFontSize`, `descriptionFontSize`, `answerButtonFontSize`, and `submitButtonFontSize`.

`headingFontWeight`, `bodyFontWeight`, and `buttonFontWeight` are numbers from 400 to 900 or `null`. `submitButtonFontWeight` also accepts `theme`. `headerTitleFontSize` and `headerSubtitleFontSize` are nonnegative pixel strings such as `24px` or `null`; null retains the theme size.

#### Structure fields

These `structure` properties are unconstrained: `formFieldSpacing`, `buttonBorderRadius`, `answerButtonSpacing`, `containerBorderWidth`, `containerBorderRadius`, and `widgetWidth`.

These properties accept a nonnegative pixel string or `null`: `submitButtonBorderRadius`, `fieldBorderRadius`, `imageHeight`, `backdropBlur`, `containerPaddingY`, `containerPaddingX`, `titleSpacing`, `contentSpacing`, `submitSpacing`, and `headerSpacing`.

`buttonLetterSpacing` and `submitButtonLetterSpacing` accept an `em` string, including negative values, or `null`. Examples: `0.05em`, `-0.02em`.

Custom padding and content spacing apply with `look.density: custom`, except on bars. Other density presets ignore them.

#### Appearance fields

| `look` property                    | Accepted values                                                             |
| ---------------------------------- | --------------------------------------------------------------------------- |
| `density`                          | `compact`, `comfortable`, `spacious`, `custom`, or `null`                   |
| `buttonStyle`, `submitButtonStyle` | `solid`, `outline`, `ghost`, `gradient`, or `null`                          |
| `buttonSize`, `submitButtonSize`   | `s`, `m`, `l`, or `null`                                                    |
| `buttonFullWidth`                  | Boolean or `null`                                                           |
| `submitButtonFullWidth`            | Boolean, `theme`, or `null`                                                 |
| `buttonUppercase`                  | Boolean                                                                     |
| `submitButtonUppercase`            | Boolean or `null`                                                           |
| `buttonHover`                      | `darken`, `lighten`, `lift`, `none`, or `null`                              |
| `submitButtonHover`                | The same values, plus `theme`                                               |
| `fieldStyle`                       | `boxed`, `underlined`, `filled`, or `null`                                  |
| `labelPosition`                    | `above`, `inside`, `hidden`, or `null`                                      |
| `borderStyle`                      | `solid`, `dashed`, or `dotted`                                              |
| `backgroundType`                   | `color`, `gradient`, or `image`                                             |
| `backgroundGradientAngle`          | Number from 0 to 360 or `null`                                              |
| `backgroundImageUrl`               | String or `null`                                                            |
| `imagePosition`                    | `top`, `left`, `background`, or `null`                                      |
| `entrance`                         | `none`, `fade`, `slide`, `scale`, or `null`                                 |
| `entranceSpeed`                    | `slow`, `normal`, or `fast`                                                 |
| `stepTransition`                   | `slideLeft`, `slideUp`, `slideDown`, `crossfade`, `zoom`, `none`, or `null` |
| `stepTransitionFade`               | Boolean or `null`                                                           |
| `closeButtonStyle`                 | `icon`, `circle`, `text`, or `null`                                         |
| `closeButtonPosition`              | `left`, `right`, or `null`                                                  |
| `backButtonStyle`                  | `link`, `button`, `icon`, or `null`                                         |
| `skipButtonStyle`                  | `link`, `button`, or `null`                                                 |

Answer-button settings and submit-button settings are independent. Nullable submit overrides inherit answer settings; `theme` selects the theme value where allowed.

#### Device overrides

`overrides.phone` and `overrides.tablet` each accept only `fontSize`, `structure`, and `look`; unknown properties are rejected. Base values apply on desktop.

- Device `fontSize` accepts all typography fields above. Unlike the base object, `lineHeight` must be a number, and `headlineFontSize`, `formFieldFontSize`, `descriptionFontSize`, `answerButtonFontSize`, and `submitButtonFontSize` must be strings. Weight and header-size constraints are unchanged.
- Device `structure` accepts all structure fields above. The otherwise unconstrained properties must be strings, except `widgetWidth`, which also accepts null. Pixel and `em` constraints are unchanged.
- Device `look` accepts only `density`, `buttonSize`, `buttonFullWidth`, `submitButtonSize`, and `submitButtonFullWidth`, with the same constraints as the base object.

A generated-design preview does not rebuild for CSS-only changes. Changes to `theme`, `placement`, `headerInfo`, `progressBar`, `displayTargets`, `backButtonEnabled`, `backButtonLabel`, or `look.labelPosition`, `look.imagePosition`, and `look.closeButtonStyle` require a rebuilt preview rather than an in-place change to an open generated-design preview.

## Returned widget object

Create, update, and duplicate return the saved object: `id`, `name`, `type`, `isEnabled`, `contentId` (flow ID or null), `whenToAskNext`, `visibility`, and `options`, with schema defaults and any existing configuration properties. IDs start with `wdg_`. Arguments use `flow_id`, but the saved object uses `contentId`.

Get adds presentation fields. List returns a compact summary instead. Do not copy a whole response into an update request; map the writable arguments above.

Configuration repair fills additional defaults beyond the widget constructor. Missing structure values become `containerBorderRadius: "8px"`, `containerBorderWidth: "0px"`, `buttonBorderRadius: "0px"`, `answerButtonSpacing: "8px"`, and `formFieldSpacing: "8px"`. Missing headline size is `18px` for bars/toasters and `24px` otherwise; description size is `14px` for toasters and `16px` otherwise. Answer, form-field, and submit sizes default to `13px`, with `lineHeight: 1.5`. Missing header/progress controls are disabled, header style is `left`, progress base color is `#60a5fa`, and shadow size is `lg`. A toaster with no placement receives `right`. Supplied options survive repair, so read the saved object rather than assuming it contains only the fields sent.

Newer repair also supplies `darkMode: "off"`, `paletteDark: null`, `headingFont: null`, and `typeScale: "custom"` when missing. Optional palette colors, typography weight/header-size overrides, and advanced spacing overrides default to null. Missing `look.density`, `look.buttonStyle`, `look.buttonSize`, and `look.fieldStyle` are null rather than an explicit preset; `borderStyle`, `backgroundType`, and `entranceSpeed` default to `solid`, `color`, and `normal`.

## `list-widgets`

List widgets without changing the configuration. The only argument is optional `project_id`.

Returns an array of `{id, name, type, isEnabled, trigger, flowId, flowName, pageTargeting, visibility}`. `flowId` and `flowName` can be null. `pageTargeting` is `all`, `none`, or `conditions`; the authoritative rule remains `visibility.conditions`.

Inline entries also include `placements`, the same array at `visibility.placements`, and `embedCode` when an embed area exists. `embedCode` is a string to place on your site, not an indication that the tool installed anything. Shared access errors apply; an empty project returns an empty array.

Illustrative request and result:

```json
{"name":"list-widgets","arguments":{}}
```

```json
[{"id":"wdg_workforge","name":"WorkForge welcome","type":"modal","isEnabled":true,"trigger":"exitIntent","flowId":"ofn_workforge","flowName":"WorkForge intake","pageTargeting":"all","visibility":{"conditions":null}}]
```

## `get-widget`

Read the full widget. Requires `widget_id` (string); optional `project_id` uses the shared contract.

Returns the widget object plus `flowName` when a `contentId` exists, `dismissRuleSummary`, and resolved names alongside recognized resource IDs. Inline widgets can add `embedCode`, `embedInstructions`, and `placements`. Executable conditions and placement rules are returned unchanged; `options` is the saved design object, not a presentation summary.

Missing ID returns `widget_id is required.` An unknown ID returns `Widget '…' not found.` No draft or live changes occur.

Illustrative request and result excerpt:

```json
{"name":"get-widget","arguments":{"widget_id":"wdg_workforge"}}
```

```json
{"id":"wdg_workforge","name":"WorkForge welcome","type":"modal","isEnabled":true,"contentId":"ofn_workforge","flowName":"WorkForge intake","whenToAskNext":"session","visibility":{"conditions":null,"trigger":"exitIntent","dismissRule":{"type":"session"}}}
```

## `create-widget`

Create a widget in the draft. Requires `name` (string). Accepts all shared widget arguments and optional `project_id`.

Returns the saved widget object. It does not create a flow. An inline widget without an embed area or complete placement fails validation. Invalid options or visibility return `Validation error: …`; missing name returns `name is required.` The shared configuration validation also applies.

Illustrative request and result excerpt:

```json
{"name":"create-widget","arguments":{"name":"WorkForge welcome","type":"inline","flow_id":"ofn_workforge","visibility":{"conditions":null,"placements":[{"domain":"example.com","path":"/welcome","selector":"#intake","position":"inside"}]},"options":{"look":{"density":"comfortable"}}}}
```

```json
{"id":"wdg_workforge","name":"WorkForge welcome","type":"inline","isEnabled":true,"contentId":"ofn_workforge","whenToAskNext":"session","visibility":{"conditions":null,"trigger":"exitIntent","dismissRule":{"type":"session"},"placements":[{"domain":"example.com","path":"/welcome","selector":"#intake","position":"inside"}]}}
```

## `update-widget`

Change a widget in the draft. Requires `widget_id` (string) plus one or more shared widget arguments. Optional `project_id` uses the shared contract. All omitted values survive. Top-level null values are ignored except `flow_id: null`, which detaches the flow. Nested nullable option properties retain their reset behavior.

Returns the complete saved widget. A supplied nonempty `flow_id` must resolve to an existing flow. Unknown IDs return `Widget '…' not found.` or `Flow '…' not found. Use list-flows to see available flows, or create one with create-flow.` No changed fields returns `No fields provided to update.` Visibility and design validation errors use the shared rules.

Illustrative request and result excerpt:

```json
{"name":"update-widget","arguments":{"widget_id":"wdg_workforge","options":{"palette":{"contrastColor":"#1d4ed8"},"look":{"submitButtonSize":"l"}}}}
```

```json
{"id":"wdg_workforge","options":{"theme":"plain","palette":{"borderColor":"#e2e8f0","contrastColor":"#1d4ed8","backgroundColor":"#ffffff","textOnContrastColor":"#ffffff","textOnBackgroundColor":"#1e293b"},"look":{"density":"comfortable","submitButtonSize":"l"},"customCss":"","nextButtonLabel":"Next","skipButtonLabel":"Skip","displayTargets":{"phones":true,"tablets":true,"desktop":true}}}
```

## `duplicate-widget`

Copy a widget into the draft. Requires `widget_id` (string). Optional `new_name` (string) defaults to the original name followed by ` (Copy)`; optional `project_id` uses the shared contract.

Returns the copied widget with a fresh `wdg_` ID. The flow link, audience rules, placements, and design are copied, not duplicated into new resources. The copy keeps the original enablement setting but does not become live until published.

Missing ID or an unknown widget returns an error. Shared save validation applies.

Illustrative request and result excerpt:

```json
{"name":"duplicate-widget","arguments":{"widget_id":"wdg_workforge","new_name":"WorkForge pricing intake"}}
```

```json
{"id":"wdg_pricing1","name":"WorkForge pricing intake","type":"inline","isEnabled":true,"contentId":"ofn_workforge","whenToAskNext":"session"}
```

## `delete-widget`

Remove a widget from the draft. Requires `widget_id` (string); optional `project_id` uses the shared contract.

Returns `{deleted: true, widget_id}`. It does not delete the linked flow, questions, or offers. The published widget remains live until the draft deletion is published. Missing ID returns `widget_id is required.`; an unknown ID returns `Widget '…' not found.`

Illustrative request and result:

```json
{"name":"delete-widget","arguments":{"widget_id":"wdg_pricing1"}}
```

```json
{"deleted":true,"widget_id":"wdg_pricing1"}
```
