Customer Health MCP
Incorporates support ticket signals shaped like Intercom tickets, including severity, status, and open duration, into account health scoring.
Provides customer health and churn-risk analysis based on subscription data shaped like Stripe subscription objects, including plan, MRR, payment status, and downgrades.
Incorporates support ticket signals shaped like Zendesk tickets, including severity, status, and open duration, into account health scoring.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Customer Health MCP which accounts are most at risk of churning this week, and why?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
customer-health-mcp
Read the build log → A write-up covering the architecture, the scoring methodology, and three real bugs caught along the way.
An MCP (Model Context Protocol) server that lets an AI assistant answer customer-success questions like "which accounts are at risk of churning?" or "give me a health summary for Acme Corp", by synthesizing data shaped like three real B2B SaaS systems:
Billing/subscription data, shaped like a Stripe subscription object (plan, MRR, payment status, downgrades)
Support ticket data, shaped like an Intercom/Zendesk ticket (severity, status, open duration)
Product usage data, shaped like a product analytics tool (daily active seats over 90 days, feature adoption, login recency)
This is a portfolio project: it runs with zero external API keys or databases, using deterministic synthetic data generated from a seed at startup. The goal is to demonstrate production-grade MCP server design (tool granularity, input validation, structured output, error handling, and an inspectable scoring methodology) on top of realistic (if fictional) data.
Why this exists
Customer success teams live at the intersection of three systems that rarely talk to each other: billing, support, and product usage. A churn signal is almost never visible in just one of them: an account can have healthy MRR and no open tickets while quietly not logging in for a month. This server models that synthesis problem directly: it doesn't just expose three raw data sources as tools, it also computes and explains a single composite risk score from all three.
Related MCP server: CSM Intelligence MCP Server
Architecture
┌────────────────────────────────────────┐
│ MCP Client │
│ (Claude Desktop / Claude Code) │
└────────────────────────────────────────┘
│
│ stdio · JSON-RPC
▼
┌────────────────────────────────────────┐
│ data/generator.ts │
│ seeded PRNG → 32 fictional accounts, │
│ generated once at boot │
└────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ 32 accounts, held in memory │
│ billing + usage + support tickets │
└────────────────────────────────────────┘
│
│ re-read on every tool call
▼
┌────────────────────────────────────────┐
│ scoring/riskScore.ts │
│ weights + thresholds → score, band, │
│ drivers, never cached │
└────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ tools/ + resources/ │
│ list_accounts · get_account_details │
│ list_at_risk_accounts │
│ get_account_risk_summary │
│ methodology://risk-scoring │
└────────────────────────────────────────┘Data flows one direction: the generator builds the in-memory dataset once at startup, the scorer derives a risk score from it on demand (never cached, so it's always consistent with the underlying data), and the tools are thin adapters that filter/shape that data and the scorer's output for MCP clients.
Setup
Requires Node.js 22.12+ or 24+ (this repo pins lts/* via .nvmrc).
npm install
npm run buildRun it directly to confirm it starts (it will sit waiting for stdio input;
Ctrl+C to exit):
npm startRun the test suite:
npm testPrint a quick human-readable dump of the generated dataset without starting the MCP server:
npm run sample-dataReproducible data with --seed
The dataset is generated once at startup from a seed string, so the same seed always produces the same 32 accounts:
node dist/index.js --seed my-custom-seedOmit --seed to use the built-in default seed.
Wiring into Claude Desktop
Add an entry to your claude_desktop_config.json (on macOS:
~/Library/Application Support/Claude/claude_desktop_config.json; on
Windows: %APPDATA%\Claude\claude_desktop_config.json), pointing at the
built dist/index.js with an absolute path:
{
"mcpServers": {
"customer-health": {
"command": "node",
"args": ["/absolute/path/to/customer-health-mcp/dist/index.js"]
}
}
}Restart Claude Desktop after saving. You should see customer-health listed
among the connected MCP servers, with its four tools and one resource
available.
Example prompts
Once connected, try asking Claude things like:
"Which of our accounts are most at risk this week, and why?"
"Give me a health summary for Tidewater Insurance."
"List every enterprise account that's past due on payment."
"How is churn risk actually calculated here?" (Claude can read the
methodology://risk-scoringresource to answer this precisely, instead of guessing.)
Design decisions
Tool granularity: raw data tools + one synthesis tool
Three of the four tools (list_accounts, get_account_details,
list_at_risk_accounts) return data that's close to the underlying model:
they filter, sort, and reshape, but they don't interpret. The fourth,
get_account_risk_summary, is the "synthesis" tool: it's the only one that
combines all three data sources into a single judgment call (a score, a band,
and a plain-language explanation).
This split matters for how a calling model uses the server. Raw-data tools
let the model verify a claim or dig into specifics ("show me the actual
tickets"); the synthesis tool lets it answer a fuzzy question quickly without
re-deriving the scoring logic itself in-context (which it would likely do
inconsistently). Keeping them separate, rather than only exposing the
synthesized view, also makes the scoring auditable: a model (or a human
reading its answer) can always cross-check a risk summary against
get_account_details for the same account.
Scoring methodology
computeRiskScore (in src/scoring/riskScore.ts)
returns a 0-100 score, where higher means more risk, built from four
independently-capped categories (usage trend/dormancy, seat adoption,
support signal, and billing signal), each of which contributes plain-language
"drivers" rather than just a number. Two choices are worth calling out:
Billing is the highest-leverage category. A canceled subscription alone is enough to reach the "at_risk" band, and a past-due payment alone is enough to reach "watch," because in practice a billing failure is a much harder churn signal than a soft dip in usage or a few open tickets. Usage and support signals still matter, but they combine more gradually.
Drivers are sorted by point contribution, not by category order. The first item in
driversis always whatever single signal contributed the most to the score, so a model summarizing "why" an account is at risk leads with the actual biggest factor rather than an arbitrary category ordering.
The exact weights and thresholds are documented in code
(WEIGHTS/BAND_THRESHOLDS in riskScore.ts) and re-rendered live as the
methodology://risk-scoring MCP resource, so the two can never drift out of
sync with each other.
Project structure
src/
data/
types.ts Account/BillingRecord/Ticket/UsageProfile types
rng.ts seeded PRNG utilities
generator.ts seeded synthetic data generation
printSample.ts `npm run sample-data` entrypoint
scoring/
riskScore.ts composite health/risk scoring logic
riskScore.test.ts unit tests (4+ scenarios)
tools/
shared.ts shared accountId lookup/error helper
listAccounts.ts
getAccountDetails.ts
listAtRiskAccounts.ts
getAccountRiskSummary.ts
resources/
methodology.ts exposes scoring methodology as an MCP resource
server.ts wires up tools/resources into an McpServer
index.ts stdio entrypoint (`--seed` flag)
tests/
integration.test.ts real client/server round trip over InMemoryTransportAvailable Tools
4 toolsget_account_detailsGet full account detailA
Fetch the complete raw record for one account: its Stripe-style billing/subscription record, its full 90-day product-usage time series and feature adoption, and its full support ticket history. Use this when you need the underlying data behind a risk score, not just the summary. Returns a clear error if the accountId does not exist.
Inputs: accountId (required). Returns: usage, billing, support, accountId, +1 more
get_account_risk_summaryGet account risk summaryA
The flagship synthesis tool: computes an account's churn-risk score and band from its billing, usage, and support signals, lists the specific drivers behind the number, and generates a short human-readable summary paragraph. Use this when someone asks 'why is this account at risk?' or 'give me a health summary for '. Returns a clear error if the accountId does not exist.
Inputs: accountId (required). Returns: drivers, summary, riskBand, accountId, +2 more
list_accountsList customer accountsA
List all customer accounts with their plan, MRR, payment status, and computed churn-risk band. Supports optional filtering by plan and payment status. Use this to get an overview of the customer base before drilling into a specific account with get_account_details or get_account_risk_summary.
Inputs: plan, paymentStatus. Returns: accounts
list_at_risk_accountsList accounts at risk of churningA
List customer accounts whose computed churn-risk band is at or above a minimum threshold (defaults to 'watch', so both 'watch' and 'at_risk' accounts are included), sorted by risk score descending. This is the fastest way to answer 'which accounts are at risk this week?' For the reasoning behind a specific account's score, follow up with get_account_risk_summary.
Inputs: limit, minBand. Returns: accounts
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 4 tools
list_accounts and list_at_risk_accounts both return account lists and could be momentarily confused, but the descriptions make the filtering intent distinct (plan/payment vs risk band). get_account_details (raw data) vs get_account_risk_summary (synthesized score/drivers) are cleanly separated.
All four tools follow a consistent snake_case verb_noun pattern using list_/get_ prefixes and predictable noun phrases. No mixing of conventions or stylistic outliers.
Four read-only tools is well-scoped for a churn-risk analytics surface, with no redundant entries. It is slightly lean—no mutation or annotation tools—but each tool earns its place.
The read-only lifecycle is well covered: overview, at-risk ranking, raw detail, and risk synthesis with explicit follow-up pathways. Minor gaps exist (no way to list support tickets globally, no account-level write/annotation), but core workflows have no dead ends.
Maintenance
Related MCP Connectors
Your AI copilot for customer onboarding — projects, KPIs, tasks, and safe writes.
Sandbox workspace tools: search, file read, DB queries, integrations. Returns synthetic data.
Read MRR, churn, users, errors, AI costs and 0-100 health scores for every app you run.
Read-only account intelligence for Customer Success and Account Management teams.
Related MCP Servers
AlicenseAqualityCmaintenanceEnables AI agents to query customer churn risk, MRR at risk, and customer health profiles from Retain, and take actions like adding notes and marking alerts contacted.832 npm1MIT- FlicenseNot gradedqualityDmaintenanceExposes simulated Salesforce CRM data as MCP tools for Claude Desktop, enabling customer success managers to prioritize at-risk accounts, review opportunities, cases, and Gong notes via natural language.-
- FlicenseBqualityCmaintenanceExposes a churn risk model, a support ticket classifier, and a retrieval-augmented policy Q&A pipeline as callable MCP tools. Enables any MCP-aware AI client to orchestrate all three capabilities within a single conversation.3-
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to securely query a synthetic health-insurance claims database through read-only, parameterized tools for claims, member histories, coverage rules, and year-to-date summaries. Includes API-key authentication, per-tool authorization, rate limiting, untrusted-text sanitization, and fail-closed audit logging.MIT