The Churnkey MCP server (@churnkey/mcp) exposes Churnkey data and operations as Model Context Protocol tools. MCP-aware clients—Claude, Cursor, Claude Code, and others—call these tools to query sessions, payment-recovery analytics, GDPR endpoints, and cancel flow configuration without custom integration code.
The server is open source and authenticates per user via OAuth: every action runs as your Churnkey user, inherits your workspace role, and is recorded in the audit log under your name.
Setup
The server runs locally via npx. Sign in once with the bundled OAuth flow, then configure your MCP client to spawn the server on demand — no credentials go in the client config.
A workspace admin must enable MCP Access for your user first (Churnkey → Team). MCP access is off by default for every user.
Sign in from a terminal:
npx @churnkey/mcp auth login
Your browser opens the Churnkey consent screen, where you review and approve the permissions the MCP server may use (see Authentication). Tokens are stored in ~/.churnkey/mcp-auth.json with owner-only permissions. npx @churnkey/mcp auth status shows your session; npx @churnkey/mcp auth logout revokes it.
Claude Desktop
Add the following to your Claude Desktop config:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"churnkey": {
"command": "npx",
"args": ["-y", "@churnkey/mcp"]
}
}
}
Fully quit and reopen Claude Desktop.
Claude Code
Register the server with the Claude Code CLI:
npx @churnkey/mcp auth login
claude mcp add churnkey -- npx -y @churnkey/mcp
Or configure it in a file. Use ~/.claude.json for a global server (available in every project) or .mcp.json in your project root for a project-scoped server (can be checked into source control):
{
"mcpServers": {
"churnkey": {
"command": "npx",
"args": ["-y", "@churnkey/mcp"]
}
}
}
Restart your Claude Code session to load the server.
Cursor
Add the following to ~/.cursor/mcp.json and reload the Cursor window:
{
"mcpServers": {
"churnkey": {
"command": "npx",
"args": ["-y", "@churnkey/mcp"]
}
}
}
Other clients
Any MCP client that supports stdio transport works. Run npx @churnkey/mcp auth login once, then spawn npx -y @churnkey/mcp.
Authentication
MCP authentication is per user (OAuth 2.1 authorization code with PKCE), not workspace-shared. That has three practical consequences:
- Attribution — every configuration change made through MCP is recorded in the audit log with your user, the MCP client, and the permission used.
- Role ceiling — an MCP session can never do more than your dashboard role allows. Viewers get read-only tools; publishing and segment changes require a role with Cancel Flow write access.
- Instant revocation — sessions can be revoked at any time, by you or by an admin, and tokens stop working immediately.
Scopes and consent
During auth login, the consent screen lists the permissions (scopes) the MCP server requests. Everything within your role's ceiling is pre-checked; you can uncheck any scope before approving, and scopes that expose customer personal data or make live changes are flagged. The granted set is exactly what you approve — nothing more. If you belong to multiple workspaces, the consent screen lets you pick which one to authorize (the role ceiling follows your role in that workspace). Every consent decision (granted scopes, denials) and session revocation is recorded in the audit log.
| Scope | Grants |
|---|---|
cancel_flows.blueprints.read | List/inspect cancel flow blueprints and segments |
cancel_flows.blueprints.write | Create/edit drafts, manage segments, publish |
cancel_flows.metrics.read | Aggregated session metrics |
cancel_flows.sessions.read | Session-level records |
cancel_flows.sessions.read_pii | Session records including customer personal data |
payment_recovery.metrics.read | Recovery campaigns and aggregated amounts |
payment_recovery.campaigns.write | Unsubscribe customers from recovery emails |
cancel_flows.adaptive_offers.read / .write | Adaptive discount configuration (attach-to-step also needs cancel_flows.blueprints.write) |
payment_recovery.blueprints.read / .write | Recovery campaign configs: sequences, cadence, publish |
payment_recovery.campaigns.read / .read_pii | Running recovery sequences (identity redacted without read_pii) |
ab_test.read / ab_test.write | A/B tests incl. pick-winner (write promotes a variant to 100% of matched traffic) |
dns.read / dns.write | Hosted-page subdomain/custom-domain setup |
stripe_settings.read / stripe_settings.write | Billing provider settings (write is owner/admin only — direct revenue impact) |
account.api_usage.read | Data API usage |
account.audit_log.read | Workspace audit trail (owner/admin only) |
dsr.read / dsr.write | GDPR/CCPA access and deletion requests (owner/admin only) |
To request a narrower set up front: npx @churnkey/mcp auth login --scopes cancel_flows.blueprints.read,cancel_flows.metrics.read.
Personal data redaction
Session reads with cancel_flows.sessions.read (without read_pii) return customer records with personal fields redacted in place: customer.email, customer.id, and custom attribute values come back as "[redacted]" (numbers/dates as null), while non-personal fields (plan, subscription, outcome) are untouched — so tools keep working, just without the identity data. Grant cancel_flows.sessions.read_pii on the consent screen to see the real values.
Rate limits
Configuration writes over MCP are capped at 120 writes per hour per user per scope to contain runaway agents. The limit applies per write permission (e.g. blueprint edits), returns 429 with standard rate-limit headers when exceeded, and does not affect reads.
Sessions and revocation
Your own active MCP sessions are listed under Settings → Account (Your MCP Sessions), each with client, granted scopes, last activity, and a per-session Revoke. Admins see every session in the workspace on Churnkey → Team; disabling a user's MCP access revokes all of their sessions at once. npx @churnkey/mcp auth logout revokes your own session from the terminal. Access tokens are short-lived (~1 hour) and refresh automatically with rotating refresh tokens — a reused (leaked) refresh token revokes the whole session. Revocation takes effect immediately.
Live vs test mode
Mode defaults to live. To work in test mode, set CHURNKEY_MODE=test in the MCP server's environment (stdio), or send x-ck-mode: test per request (Streamable HTTP). A single OAuth login spans both modes — mode is chosen per session, not baked into the credential. get_account reports the effective mode for the current session.
Mode applies to session analytics only — list_sessions and aggregate_sessions. Those two tools also echo the active mode (Mode: LIVE / Mode: TEST) in their results, so an agent never mistakes empty test-mode data for "no activity." Everything else is mode-independent:
- Configuration (blueprints, segments, surveys, recovery campaigns, settings) is shared across live and test mode — an edit applies to both.
- Payment-recovery analytics (
list_payment_recoveries,aggregate_payment_recoveries, campaigns, engagement) come from real provider failed-payment events and carry no test/live distinction. get_flow_metricsis live-mode by definition (it joins real invoices).- DSR (
dsr_access,dsr_delete) looks up a customer by email across the whole workspace, regardless of mode.
Data API keys (deprecated for MCP)
Earlier versions of @churnkey/mcp authenticated with the workspace Data API key (CHURNKEY_APP_ID + CHURNKEY_API_KEY). That still works for read-only tools, but configuration writes (blueprint/segment edits, publish) are rejected without an OAuth user session, and the server prints a deprecation warning. The Data API key itself is unaffected for server-to-server /v1/data/* calls from your own backend — only its use as an MCP credential is deprecated.
Tools
| Tool | Description |
|---|---|
get_account | Identity and session context — best called first. Returns the acting workspace (org), the authenticated user, coarse entitlements (active subscription, Intelligence access), the granted OAuth scopes, and the effective live/test mode. Requires no scope. |
list_sessions | Returns cancel and dunning sessions, filterable by date range, customer, outcome, plan, segment, A/B test, and more. Up to 500 sessions per call. |
aggregate_sessions | Returns session counts grouped by one or more dimensions (saveType, offerType, month, planId, others). |
list_payment_recoveries | Returns individual failed-payment recovery campaigns. |
aggregate_payment_recoveries | Returns counts and amounts (invoice, recovered, pending, lost) for failed-payment recoveries, in original currency and USD. |
list_blueprints | Lists the current cancel flow inventory for the authenticated organization: the default flow plus every non-deleted segment flow (including disabled ones), each with status (active, setup_pending, or inactive), published and hasUnpublishedChanges booleans, editableBlueprintId / publishedBlueprintId, and compact draft / publishedBlueprint metadata. |
get_blueprint | Returns one full cancel flow blueprint by ID, including the steps array. Each step/offer/survey-choice carries its guid for use with the targeted edit tools. |
create_blueprint | Creates the default organization-level cancel flow draft when the organization does not already have one. Requires confirm: "create_blueprint". Supports template: "empty", "BASIC", "B2B", or "MERGEFIELDS" for prepopulated draft structure. |
update_blueprint_draft | Updates top-level fields (name, brandImage, primaryColor, translatedLanguages) on a draft working copy. Passing a published blueprint ID updates the corresponding working copy. Does not publish. |
update_blueprint_step | Updates one draft step by stepGuid or stepIndex without sending the full steps array: step/offer copy, enabled, survey behavior (randomize, followupRequired, minLength), and freeform/confirm config. Copy edits clear stale translations; behavioral flags do not. |
update_blueprint_offer | Updates one offer's type and functional config (discount, pause, trial extension, redirect, plan change, contact, rebate) on a draft, addressable on an offer step, a survey choice, or a structured follow-up option by guid. If the target has no offer yet, passing offerType adds one (seeded with default config). contact takes no config; rebate config supports amountType, customAmount, percentAmount, mbgWindowDays, and invoiceScope. Discount autoOptimize (adaptive discounts) requires Churnkey Intelligence — the API rejects it (403) for organizations without access. |
edit_survey_structure | Adds, removes, or reorders survey response choices, or configures a choice's follow-up (add_choice / remove_choice / reorder_choices / set_followup) on a draft survey step. |
add_blueprint_step / remove_blueprint_step | Adds a step at a canonical place (the server builds the base step) or removes a step by stepGuid on a draft. |
publish_blueprint | Publishes a draft working copy after explicit confirmation (confirm: "publish"). Passing a published blueprint ID publishes the corresponding working copy. Destructive; clients prompt for confirmation. |
list_segments | Lists cancel flow segment metadata in priority order (priority index, enabled flag, and the audience filter rules). Includes disabled segments. |
list_segment_attributes | Lists the audience-filter attributes you can target for cancel flow segments: builtIn (the cancel-flow attribute palette, each with valueType, applicable operands, and — for fixed-enum attributes — the allowed values as { value, label }) and custom (your organization's own custom customer attributes). |
create_segment_flow | Creates a cancel flow segment and an editable draft blueprint linked to it. Requires confirm: "create_segment_flow". New segments are enabled by default (mirroring the dashboard); pass segment.enabled: false only to create a paused segment. Supports the same blueprint templates as create_blueprint; the new draft remains setup-pending until published. |
reorder_segments | Reorders cancel flow segment priority after explicit confirmation (confirm: "reorder_segments"). Live-impacting; clients prompt for confirmation. |
set_segment_enabled | Enables or disables a segment's live targeting after explicit confirmation (confirm: "set_segment_enabled"). Live-impacting. |
update_segment_filter | Replaces a segment's audience filter rules (whole-array replace) after explicit confirmation (confirm: "update_segment_filter"). Live-impacting. Editing a live (enabled + published) segment additionally requires confirmLiveChange: true, since it changes targeting immediately. |
archive_segment | Soft-deletes a cancel flow segment after explicit confirmation (confirm: "archive_segment"). Useful for cleaning up disposable test flows. Destructive; clients prompt for confirmation. |
get_flow_metrics | Cancel flow performance: sessions, customers saved, save rate, boosted revenue (USD + per-currency), outcomes breakdown, and Feedback AI themes (Intelligence plans). Scope by segment, published blueprint version, or A/B test + date range; includes a sample-size warning and a quotable summary. |
get_stripe_settings / update_stripe_settings | Read (with per-setting descriptions + recommendations) and change billing provider settings: proration, cancellation timing, pause behavior, invoice handling on pause, coupon stacking, session recording. Writes require confirm: "update_stripe_settings", explain conflicting combinations, and audit-log before/after values. Direct revenue impact. |
get_adaptive_offers / update_adaptive_offers | Read and configure adaptive (auto-optimized) discounts: strategy presets, percent/duration ranges with guardrails (5–95% in 5% steps), Intelligence gating, learning-period warnings. Attaching to a flow step (via update_blueprint_offer config.autoOptimize) requires both the adaptive-offers and blueprints write scopes. |
get_dns_config / set_hosted_subdomain / add_custom_domain / check_domain_status / remove_custom_domain | Hosted-page domain setup: read state, set the churnkey.co subdomain (live instantly), register custom domains idempotently with the exact DNS records to add on your side, poll propagation/SSL, deregister. |
list_recovery_blueprints / get_recovery_blueprint / clone_recovery_blueprint / update_recovery_email / publish_recovery_blueprint | Payment recovery campaign configs: full email sequences (cadence, sender, copy), clone as a starting point, per-email edits with CTA/merge-tag validation and dashboard length limits, confirm-gated publish (rebuilds pending sends on in-flight sequences). |
update_recovery_email_offers / update_recovery_sms / add_recovery_email / remove_recovery_email / update_recovery_audience / list_recovery_audience_attributes / set_recovery_blueprint_enabled | Full recovery campaign editing parity with the dashboard: per-email offers (provider coupon + one-time invoice discount; the first offer auto-fills the CTA text and offers mirror to the paired SMS), SMS editing (org feature gate, one-SMS-segment cap, merge-field validation), add/remove sequence steps (email+SMS pairs), audience rename/filters against the dunning attribute palette (locked during an active A/B test), and the confirm-gated on/off toggle — which saves and publishes in one step, exactly like the dashboard. Exclusion lists and one-time sendOnDate sends remain dashboard-only. |
list_recovery_campaigns / get_recovery_campaign_messages / get_recovery_engagement / stop_recovery_campaign | Running per-customer recovery sequences: who's in which step, per-message sends/opens/clicks, per-email engagement rates, and the irreversible confirm-gated interrupt. Customer identity requires the read_pii scope. |
list_ab_tests / create_ab_test / start_ab_test / pause_ab_test / complete_ab_test / get_ab_test_metrics / pick_ab_test_winner | Full A/B test lifecycle: clone a segment flow as the variant, edit it with the blueprint tools, run the test, read per-arm metrics with statistical significance, pick the winner (commits the variant to 100% of matched traffic; early decisions require explicit acknowledgement). Two-arm, 50/50 split. |
get_audit_log | The workspace audit trail: who changed what and when, with human-readable summaries (before → after diffs), the acting user/client/scope for MCP actions, and source filtering (AI agent vs Data API vs dashboard). Owner/admin role required. The same trail powers the dashboard's Settings → Activity page. |
dsr_access | GDPR/CCPA right-to-know lookup by email. |
dsr_delete | GDPR/CCPA right-to-delete. Destructive; clients prompt for confirmation. |
Tools accept structured input. Filters use enums for known values (saveType, offerType, billingInterval), accept negation through a not object, and accept arrays of dimensions for grouping. The full schema for each tool is exposed through the standard MCP tools/list response.
Configuration writes are granular and split between draft edits, creation, and live-impacting actions. Blueprint edits (update_blueprint_draft, update_blueprint_step, update_blueprint_offer, edit_survey_structure, add_blueprint_step, remove_blueprint_step) mutate the unlocked working copy and send only the targeted fields—never the full steps array. If a client passes a published blueprint ID to any of these (or to publish_blueprint), Churnkey resolves the matching draft working copy for that org or segment.
Because blueprint edits only touch the draft, they do not require a confirm—publish_blueprint is the single live gate and is the one that requires confirm: "publish". Creation tools require their own confirmation literals: create_blueprint creates only the default organization-level draft when one does not already exist, while create_segment_flow is the repeatable path for new segmented cancel flows and smoke tests. Segment tools (create_segment_flow, reorder_segments, set_segment_enabled, update_segment_filter, archive_segment) act on live segment configuration directly, so each requires its own confirm literal. Churnkey records an audit log for every configuration write — attributed to the authenticated user, MCP client, and scope — including whether a published ID was resolved to a working copy. Blueprint configuration is shared across live and test mode.
Segment audience filters
update_segment_filter (and the segment.filter field on create_segment_flow) take an array of rules, each { attribute, operand, value, type? }. The operand and value must match the attribute's value type. Call list_segment_attributes for the supported list plus your organization's own custom attributes. These are the built-in attributes available for cancel flow segments (the cancel-flow palette, matching the dashboard builder):
| Value type | Operands | Attributes |
|---|---|---|
| Text / ID (value is a list) | INCLUDES, NOT_INCLUDES | CUSTOMER_EMAIL, PLAN_ID, PRODUCT_ID, CURRENCY, BILLING_INTERVAL (DAY/WEEK/MONTH/YEAR), SUBSCRIPTION_STATUS (ACTIVE/TRIALING/PAST_DUE), SUBSCRIPTION_DISCOUNT (ONCE/REPEATING/FOREVER) |
| Number | GTE, LTE, BETWEEN, NOT_BETWEEN | PRICE (major currency units, e.g. dollars — not cents), BILLING_INTERVAL_COUNT, SUBSCRIPTION_AGE_MONTHS |
| Date (ISO-8601 string) | GTE, LTE, BETWEEN, NOT_BETWEEN | SUBSCRIPTION_START_DATE |
Boolean (value [true] or [false]) | INCLUDES | CANCEL_FLOW_WILL_SHOW_CLICK_TO_CANCEL |
A few rules govern how these filters behave:
- The
typefield is for custom attributes only. Set it (STRING/NUMBER/BOOLEAN/DATE) only on custom attributes. Omit it for built-in attributes — they derive their type automatically, and a storedtypemakes the dashboard render a duplicate value input, so the API strips it. - Fixed-enum attributes need their exact dashboard value.
BILLING_INTERVAL,SUBSCRIPTION_STATUS, andSUBSCRIPTION_DISCOUNTmust use the exact casing — e.g.MONTH, notmonth— or the cancel-flow builder shows the value as an unrecognized chip. The allowed set can also be provider-specific (e.g.PAST_DUEonly on Stripe/Paddle,DAYonly on Chargebee/Paddle). Calllist_segment_attributesto get each enum attribute's allowedvaluesas{ value, label }for your organization. - Some built-in attributes aren't allowed here. Attributes scoped to other products (e.g.
CUSTOMER_HAS_PHONE,INVOICE_AMOUNT_DUE,SURVEY_CHOICE) are rejected for cancel flow segments, since the cancel-flow builder can't render them. - You can also use your own custom attributes. Any custom customer attribute your organization has defined is supported — it's returned in the
customgroup oflist_segment_attributes. - Operands depend on the value type. Text and boolean use
INCLUDES/NOT_INCLUDES; numbers and dates useGTE/LTE/BETWEEN/NOT_BETWEEN.GT/LTandEQUAL/NOT_EQUALare not supported — useGTE/LTEorINCLUDES/NOT_INCLUDESinstead. - Value counts depend on the operand.
BETWEENandNOT_BETWEENtake exactly two values[low, high];GTE/LTEtake one;INCLUDES/NOT_INCLUDEStake the list of matching values. - A segment flow needs at least one rule to publish. Its audience filter can't be empty.
- Publishing doesn't change a segment's enabled state (same as the dashboard). Segments are enabled by default when created, and
set_segment_enabledtoggles targeting independently. - Editing a live segment's audience takes effect immediately. For a segment that is enabled and published,
update_segment_filterchanges which customers see the flow right away, so it requires an extraconfirmLiveChange: trueacknowledgment.
Data freshness
list_sessions, aggregate_sessions, list_payment_recoveries, and aggregate_payment_recoveries read from the Churnkey analytics warehouse, which refreshes roughly every 3 hours for sessions and every 20 minutes for payment recoveries. Data more recent than the refresh window does not appear in these responses.
For real-time session reads, query the Data API directly: GET /v1/data/sessions and GET /v1/data/session-aggregation are backed by the live operational store with no lag. Payment-recovery analytics are warehouse-only — there is no real-time Data API equivalent.
dsr_access and dsr_delete read and write the operational store with no lag.
The cancel flow configuration tools—list_blueprints, get_blueprint, create_blueprint, update_blueprint_draft, update_blueprint_step, update_blueprint_offer, edit_survey_structure, add_blueprint_step, remove_blueprint_step, publish_blueprint, list_segments, list_segment_attributes, create_segment_flow, reorder_segments, set_segment_enabled, update_segment_filter, and archive_segment—read and write the operational store with no warehouse lag. Draft blueprint edits are not customer-facing until the draft is published. Copy edits clear stale translations for the affected content, and publishing refreshes translations before the draft becomes live. Segment changes (creation, priority, enabled state, audience filter, archive) take effect immediately after confirmation.
Examples
Sample prompts an MCP client can translate into tool calls:
What was our save rate by month for 2025?
Show the top decline reasons we recovered from last quarter.
Which plans had the most cancellations in March, and what offers were accepted?
Look up all sessions for
[email protected].
List my cancel flow blueprints and show me which ones have unpublished drafts.
Create a new segmented cancel flow called "Enterprise save flow" with the basic template, but do not publish it yet.
Update the draft primary color for this blueprint, but do not publish it yet.
Update the header for this cancel flow step without publishing yet.
Change the discount on the final offer to a $15-off coupon for this flow's draft.
Add a "Too expensive" survey choice with a follow-up question, then reorder the choices.
Reorder my cancel flow segments so enterprise customers are evaluated first.
Pause the "Annual plan" segment flow without deleting it.
Archive the disposable test segment flow I created earlier.
References
- Source:
@churnkey/mcpon GitHub - Package:
@churnkey/mcpon npm