← Files UserflowARCHIVED FILE
skills/userflow-announcement-creator/references/mcp-reference.md
6.08 KB · Sep 30, 2026 · 22:56 UTC
# Userflow MCP reference — announcements
Exact tool calls and JSON shapes for this skill. The running session also has the live Userflow
resources — read `flow-draft-dsl` and `predicate-dsl` via `read_resource` if you need the full
spec. This file captures the specific decisions this skill makes.
## Environment
Most Userflow tools take an optional `env_id`. Resolve it once via `describe_session` and reuse it.
If the user only has one environment, you can often omit `env_id` and the authenticated env is used
— but passing it explicitly avoids ambiguity when multiple environments exist.
## Notification level mapping
The announcement's prominence is the `level` field on `draft.announcement`:
| User-facing term | `level` value | Behavior |
|------------------|---------------|----------|
| Silent | `silent` | No unread badge; only seen when the user opens the Resource Center |
| Badge (default) | `badge` | Unread counter on the Resource Center launcher |
| Boosted → Pop-out | `popout` | Speech bubble from the launcher; moderate attention |
| Boosted → Modal | `modal` | Center-screen overlay, dims background; highest attention |
| Boosted → Notification | `toast` | Slides in from a corner; brief, low-friction |
Optional level-specific fields (only set if the user asks): `toast_auto_dismiss` (seconds),
`toast_width` (200–600), `modal_width` (100–1000).
## Content: rich2 (never HTML)
`draft.announcement.content` (and `more_content`) must be a **rich2** document. Do **not** pass an
HTML string like `"<p>…</p>"`.
```json
{
"type": "rich2",
"children": [
{ "type": "heading2", "children": [{ "text": "New dashboard filters" }] },
{ "type": "paragraph", "children": [{ "text": "You can now filter by region and plan." }] }
]
}
```
Block types: `paragraph`, `heading1`–`heading3`, `quote`, `ordered-list`, `unordered-list`,
`list-item`, `link`, `button-group`, `button`. Inline marks: `bold`, `italic`, `underline`, `code`,
etc. (`image`/`video` blocks exist but image handling is out of scope for this skill.)
For long copy, you can split with `more_enabled: true` + `more_button_text` + `more_content`.
## Creating the announcement
```json
{
"name": "New dashboard filters",
"type": "announcement",
"env_id": "<env-uuid>",
"draft": {
"announcement": {
"title": "New dashboard filters",
"level": "modal",
"content": {
"type": "rich2",
"children": [
{ "type": "paragraph", "children": [{ "text": "Filter dashboards by region and plan." }] }
]
}
}
},
"filter_condition": {
"predicates": [
{ "type": "attribute", "fqn": "group/subscription_state", "op": "eq", "value": "active" }
]
}
}
```
- Omit `filter_condition` entirely for an "everyone" audience.
- `create_flow` **never publishes** — it saves a draft and returns a Builder URL. Surface that URL
to the user.
- Optional CTA buttons go under `draft.announcement.buttons` (max 10), e.g.
`{ "text": "Open dashboard", "appearance": "primary", "actions": [{ "type": "navigate", "url": "/dashboards", "navigate_target": "same_tab" }] }`.
## Targeting (`filter_condition`)
Predicates are a non-empty JSON array; root entries are AND-combined. Use a `clause` object with
`"operator": "or"` for OR logic.
**Always resolve real identifiers first:**
- `list_attribute_definitions` → valid attribute FQNs and their `data_type`. Company attributes use
the `group/` prefix (e.g. `group/plan`); company-membership uses `group_membership/`.
- `list_event_definitions` → valid `event_name` values.
- `list_segments` (`subject_type: user`) → existing segment IDs for `{ "type": "segment", "segment_id": "…" }`.
Operators by data type: string → `eq, ne, contains, starts_with, ends_with`; number →
`eq, ne, gt, gte, lt, lte`; boolean → `eq, ne`; datetime → comparisons plus `within_days` /
`older_than_days` (value = positive integer days).
A friendly label the user gives you ("paid", "active", "enterprise") almost never maps 1:1 to a
stored value. Resolve it: e.g. "active customers" is usually the company attribute
`group/subscription_state = active` (not a made-up `group/plan`); "enterprise" is usually a specific
`group/plan_id` value you may need to discover via `list_companies`. Look up the real FQN and value,
then echo the interpreted audience back before finalizing.
Common patterns:
```json
// Active (subscribed) customers — company attribute, evaluated per user's company
[{ "type": "attribute", "fqn": "group/subscription_state", "op": "eq", "value": "active" }]
// Inactive users (no session in 30 days)
[{ "type": "attribute", "fqn": "last_seen_at", "op": "older_than_days", "value": 30 }]
// Haven't completed a specific flow, ever
[{ "type": "not_event", "event_name": "flow_completed", "time_op": "any" }]
// EU region OR US region (OR group)
[{
"type": "clause", "operator": "or",
"predicates": [
{ "type": "attribute", "fqn": "group/region", "op": "eq", "value": "eu" },
{ "type": "attribute", "fqn": "group/region", "op": "eq", "value": "us" }
]
}]
```
Mind data types — string `"true"` ≠ boolean `true`, and that mismatch silently matches no one.
Always echo the interpreted audience back to the user in plain English before finalizing.
## Resource Center readiness check (informational)
```
list_flows(types: "resource_center", state: "published", env_id: <env>)
```
If this returns nothing, no published Resource Center exists — announcements (even Modal/Toast) won't
surface. This is informational only; mention it once and let the user proceed.
## Publishing (two-step confirm)
`set_flow_publication` requires an explicit confirm handshake:
1. First call — **omit** `confirm` (or `false`). You get a `confirmation_required` payload
describing what will change.
2. Second call — `confirm: true` to actually publish.
```json
// step 1
{ "action": "publish", "flow_id": "<flow-uuid>", "env_id": "<env-uuid>" }
// step 2
{ "action": "publish", "flow_id": "<flow-uuid>", "env_id": "<env-uuid>", "confirm": true }
```
Only do step 2 after the user has said yes to publishing. Use `action: "unpublish"` (same handshake)
to take it back down.
SHA-256: 3b072468e2b75629aa58333c6a76a57dd964a17d55c2d6a690403186352f1ff8