Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Expect

### Added

- Restored blueprint/segment tools dropped during the MCP reslice (their Data API endpoints have been live on the server the whole time): `create_blueprint` (default org flow with `empty`/`BASIC`/`B2B`/`MERGEFIELDS` templates), `create_segment_flow` (segment + editable draft in one call — the preferred isolated/test-flow path), `archive_segment` (soft-delete disposable segments), and `list_segment_attributes` (targetable audience attributes with their `valueType`, valid `operands`, and fixed-enum `values`). `update_segment_filter` regains `confirmLiveChange` — editing a live (enabled + published) segment's audience requires `confirmLiveChange: true` (enforced by the API), which the tool previously could not send.
- Segment audience operands corrected to match the Data API: only `INCLUDES`/`NOT_INCLUDES` (STRING/BOOLEAN) and `GTE`/`LTE`/`BETWEEN`/`NOT_BETWEEN` (NUMBER/DATE). `GT`/`LT` are removed — the API rejects them as unrecognized operands, so advertising them produced guaranteed-fail calls.
- `get_account` tool (no scope required): identity & session-context preflight — the acting workspace (org), authenticated user, coarse entitlements (active subscription, Intelligence access), granted OAuth scopes, and the **effective live/test mode**. Agents are nudged to call it first to confirm which org and mode they're operating in (and to read their scopes before hitting a guaranteed 403). Backed by the new `GET /v1/data/account`.
- Effective-mode visibility: `list_sessions` / `aggregate_sessions` now append the active mode (`Mode: LIVE|TEST`) to their results and carry a mode-sensitivity note in their descriptions — they're the only mode-partitioned surface, so an agent never has to infer live-vs-test from empty data. Configuration, payment-recovery analytics, flow metrics, and DSR are mode-independent and are documented as such.

Expand Down
8 changes: 6 additions & 2 deletions packages/mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ Model Context Protocol server for [Churnkey](https://churnkey.co). Lets AI agent
| `get_flow_metrics` | Cancel flow performance: sessions, customers saved, save rate, boosted revenue (USD + per-currency), outcomes breakdown, sample-size warning, quotable `summary`, and Feedback AI themes (Intelligence plans). Scope by segment, published blueprint version (compare versions with two calls), or A/B test + date range. |
| `list_blueprints` | Current cancel flow inventory for the org: the default flow plus every non-deleted segment flow. Each flow has `status` (`active` / `setup_pending` / `inactive`), `published` and `hasUnpublishedChanges` booleans, `editableBlueprintId`, `publishedBlueprintId`, and compact `draft` / `publishedBlueprint` metadata. Status is a coarse subset of the dashboard badges; the "Unpublished Changes" badge is `status: "active"` + `hasUnpublishedChanges: true`. |
| `get_blueprint` | Full cancel flow blueprint by ID, including the `steps` array (each step/offer/survey-choice carries its `guid` for use with `update_blueprint_step`). Large for translated blueprints. |
| `create_blueprint` | Create the default org-level cancel flow draft when the org has none (mirrors dashboard setup). `template: "empty"` makes a blank draft; `BASIC`/`B2B`/`MERGEFIELDS` prepopulate steps and survey choices. Requires `confirm: "create_blueprint"` and writes an audit log. Use `create_segment_flow` for isolated/test flows. |
| `update_blueprint_draft` | Draft-only updates for top-level blueprint fields (`name`, `brandImage`, `primaryColor`, `translatedLanguages`). Passing a published blueprint ID edits the corresponding working copy. Writes an audit log. |
| `update_blueprint_step` | Sparse draft step edits by `stepGuid` or `stepIndex`, without sending the full `steps` array — step/offer copy, `enabled`, and behavioral config (survey `randomize`/`followupRequired`/`minLength`, freeform/confirm config). Copy edits clear stale translations; publish refreshes translations. Writes an audit log. |
| `update_blueprint_offer` | Edit one offer's type and functional config (discount/pause/trial/redirect/plan-change) on a draft, by `stepGuid` (+ optional `choiceGuid`/`optionGuid`). Writes an audit log. |
| `edit_survey_structure` | Add/remove/reorder survey choices and configure follow-ups (`add_choice`/`remove_choice`/`reorder_choices`/`set_followup`) on a draft survey step. Writes an audit log. |
| `add_blueprint_step` / `remove_blueprint_step` | Add a step at a canonical `place` (server builds the base step) or remove one by `stepGuid` on a draft. Writes an audit log. |
| `publish_blueprint` | Publish a draft blueprint as the live org/segment version. Requires `confirm: "publish"` and writes an audit log. |
| `list_segments` | Cancel flow segment metadata in priority order (response order = priority; `priority` is the 0-based index). Includes disabled segments (`enabled` flag) and each segment's audience `filter` rules. A/B variant segments appear as separate entries. |
| `list_segment_attributes` | The audience-filter attributes you can target: `builtIn` (the cancel-flow palette, each with `valueType` and applicable `operands`; fixed-enum attributes also list their allowed `values`) plus your org's `custom` attributes. Call before `create_segment_flow` / `update_segment_filter` to target real attributes with valid operands. |
| `create_segment_flow` | Create a new cancel flow segment and its editable draft blueprint in one call (the preferred path for isolated setup/test flows). Optional `blueprint.template` (`empty`/`BASIC`/`B2B`/`MERGEFIELDS`) and initial `segment.filter`. Requires `confirm: "create_segment_flow"`. New flow is `setup_pending` until `publish_blueprint`. |
| `reorder_segments` | Reorder cancel flow segment priority. Requires `confirm: "reorder_segments"` and writes an audit log. |
| `archive_segment` | Soft-delete/archive a segment so it leaves the non-deleted inventory (clean up disposable test flows). Segments in unfinished A/B tests can't be archived. Requires `confirm: "archive_segment"` and writes an audit log. |
| `set_segment_enabled` | Enable/disable a segment (live targeting on/off). Requires `confirm: "set_segment_enabled"` and writes an audit log. |
| `update_segment_filter` | Replace a segment's audience filter rules (whole-array replace). Requires `confirm: "update_segment_filter"` and writes an audit log. |
| `update_segment_filter` | Replace a segment's audience filter rules (whole-array replace). Requires `confirm: "update_segment_filter"`; editing a **live** (enabled + published) segment additionally needs `confirmLiveChange: true` since it changes targeting immediately. Writes an audit log. |
| `get_stripe_settings` / `update_stripe_settings` | Read (with descriptions + recommendations) and change workspace billing settings: proration, cancellation timing, pause behavior, invoice handling on pause, coupon stacking, session recording. Writes require `confirm: "update_stripe_settings"`, validate conflicting combinations with explanations, and are audit-logged with before/after values. *Direct revenue impact.* |
| `get_adaptive_offers` / `update_adaptive_offers` | Read and configure adaptive (auto-optimized) discounts: strategy, percent/duration ranges, Intelligence access state. Changes reset the optimizer's learning period; writes require `confirm: "update_adaptive_offers"`. Attaching to a flow step (via `update_blueprint_offer` `config.autoOptimize`) requires both the adaptive-offers and blueprints write scopes. |
| `list_recovery_blueprints` / `get_recovery_blueprint` / `clone_recovery_blueprint` / `update_recovery_email` / `publish_recovery_blueprint` | Payment recovery campaign config CRUD: list/read full email sequences (cadence, sender, copy), clone as the template-library path, patch single emails (CTA + merge-tag validation, dashboard length limits, deliverability warnings on sender changes), publish (rebuilds pending sends on in-flight sequences; `confirm` required). Scopes `payment_recovery.blueprints.read/.write`. |
Expand All @@ -39,7 +43,7 @@ Session and recovery tools read from the Churnkey analytics warehouse — sessio

Blueprint edits are **granular and draft-only**: each tool mutates the unlocked working copy and sends only the targeted fields (never the full `steps` array with translations). `update_blueprint_step` handles copy + behavioral flags, `update_blueprint_offer` handles offer config, `edit_survey_structure` handles survey choices/follow-ups, and `add_blueprint_step`/`remove_blueprint_step` handle step structure. Copy edits clear stale translations for the affected content; `publish_blueprint` refreshes translations before making the draft live and is the only live-impacting blueprint action (so it's the one that requires a `confirm`).

Segment tools (`reorder_segments`, `set_segment_enabled`, `update_segment_filter`) act on **live** config directly (segments have no draft cycle), so each requires an explicit `confirm` literal and writes an audit log.
Segment tools (`reorder_segments`, `set_segment_enabled`, `update_segment_filter`, `archive_segment`) act on **live** config directly (segments have no draft cycle), so each requires an explicit `confirm` literal and writes an audit log. Editing the audience of a live (enabled + published) segment additionally requires `confirmLiveChange: true` so the immediate targeting impact is acknowledged. `create_segment_flow` creates a segment plus a fresh draft blueprint (which does follow the draft/`publish_blueprint` cycle), and `list_segment_attributes` enumerates the targetable audience attributes and their valid operands. Note only `INCLUDES`/`NOT_INCLUDES` (STRING/BOOLEAN) and `GTE`/`LTE`/`BETWEEN`/`NOT_BETWEEN` (NUMBER/DATE) are supported — `GT`/`LT` are not.

Each tool's input schema is fully described to the MCP client — enums for `saveType` / `offerType` / `billingInterval` / breakdown dimensions, `not` object for exclusions, structured types for booleans and numbers.

Expand Down
30 changes: 30 additions & 0 deletions packages/mcp/src/tools/blueprints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ const updateDraftInput = blueprintIdInput.extend({
updates: draftUpdates,
})

const blueprintTemplate = z
.enum(['empty', 'BASIC', 'B2B', 'MERGEFIELDS'])
.optional()
.describe(
'Initial draft template. "empty" creates the same blank draft the dashboard creates before a template is picked; BASIC/B2B/MERGEFIELDS prepopulate steps and survey choices.',
)

const createBlueprintInput = draftUpdates.extend({
template: blueprintTemplate,
confirm: z
.literal('create_blueprint')
.describe(
'Required confirmation. Creating the default org flow changes org configuration; pass exactly "create_blueprint".',
),
})

const surveyChoicePatch = z
.object({
choiceGuid: z.string().min(1).optional().describe('Preferred stable survey choice identifier.'),
Expand Down Expand Up @@ -281,6 +297,20 @@ export function blueprintTools(client: ChurnkeyClient): ToolDefinition[] {
annotations: { readOnlyHint: true, openWorldHint: true },
handler: async (args) => client.get(`/data/blueprints/${args.blueprintId}`),
},
{
name: 'create_blueprint',
title: 'Create the default org cancel flow',
description: [
'Create the default org-level cancel flow draft when the org does not already have one. This mirrors the dashboard setup flow: `template: "empty"` creates a blank draft, while BASIC/B2B/MERGEFIELDS prepopulate draft steps and survey choices.',
'',
'Use create_segment_flow instead when you need an isolated test flow on an archived/disposable segment. If the default org flow already exists, this tool rejects rather than creating an extra org blueprint that the dashboard/Data API inventory would not surface.',
'',
WRITE_NOTE,
].join('\n'),
inputSchema: createBlueprintInput,
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
handler: async (args) => client.post('/data/blueprints', { body: args }),
},
{
name: 'update_blueprint_draft',
title: 'Update a draft cancel flow blueprint',
Expand Down
Loading
Loading