6 min read
An MCP server for SaaS metrics: ask Claude about your revenue
What an MCP server for SaaS metrics should return: fixed definitions, as-of dates, measured vs modeled labels and attributed share, plus how to connect it to Claude.
An MCP server for SaaS metrics lets you ask Claude "why did MRR drop last week?" and get an answer built from your actual numbers, not a guess. Whether that answer is any good depends less on the model than on what the server gives it. This post explains the difference between servers that hand the model raw data and servers that hand it finished metrics, what a trustworthy answer should contain, and how to connect one to Claude.
What is an MCP server?
The Model Context Protocol is an open standard for connecting AI assistants to tools and data. A server publishes a list of tools, each with a name, a description and a schema for its inputs. During a conversation, the assistant decides which tool to call, sends the arguments, reads the structured result and writes its answer from it. Remote MCP servers run over HTTP, so there is nothing to install, and they typically use OAuth: you sign in once in a browser window instead of pasting an API key into a chat app.
Why do single-source MCP servers give different answers?
Many products now ship their own official MCP server, billing and analytics tools included. They are useful for what they are built for: looking up a customer, checking an invoice, doing tasks inside that one product.
They are less useful for metrics, for two reasons.
The model has to compute the metric itself. A billing server returns subscriptions, prices and invoices. To answer "what is my MRR?", the model has to page through subscriptions, divide annual plans by 12, decide whether trials count, apply or ignore discounts, leave out tax, and convert currencies. Each of those is a decision, and the model makes it again, from scratch, in every conversation. Ask the same question on Monday and Thursday and you can get two numbers that differ for reasons nobody wrote down.
The interesting questions cross sources. "Which blog posts brought paying customers?" needs the landing page from your analytics joined to the subscription in your billing system through a user ID. Neither single-source server has that join, and asking the model to rebuild it from two raw exports in a chat window is asking for a confident wrong answer.
What changes with pre-computed metrics?
The alternative is a server that does the computing before the model ever sees the data. Metrics are calculated in code, once, against written definitions, and the tools return finished results. The model's job shrinks to choosing the right tool and explaining what came back.
That has a few consequences that matter in practice:
- The same question gives the same number. The definition of MRR lives in the server, not in the model's reasoning for that conversation.
- The chat matches the dashboard. If both read the same metric layer, there is no second version of the truth to reconcile.
- Causes come from code too. For "why did this change?", the server can rank segments by their contribution to the change and return that ranking. The model describes the result instead of improvising an analysis.
- Uncertainty is labeled at the source. Whether a figure is measured or modeled is decided by the server and passed through, rather than left for the model to infer.
What should a good answer contain?
Three things, whatever the question.
- An as-of date. Data has a sync time, and some sources lag. Search Console data, for example, typically arrives two to three days late. An answer about "last week" should say how fresh its inputs were.
- Measured or modeled. Revenue linked to the page a customer first landed on is measured. Revenue split across search queries by their share of clicks is modeled, because Google never reveals which query a specific visitor typed. The answer should keep that distinction.
- The attributed share. Any ranking of pages or channels by revenue covers only the customers who could be linked to a first touch. If that is 60% of new MRR, the answer should say so, and treat the remaining 40% as unknown, not zero.
Here is how that plays out for a few typical questions against Joinsight's tools:
| Question | Tool | A good answer includes |
|---|---|---|
| Why did MRR drop last week? | explain_change | Churned or net new MRR against the baseline, the top contributing segments, any annotations, as-of date |
| Which blog posts brought paying customers last month? | get_page_revenue | Pages ranked by paying customers, labeled measured, with the attributed share |
| Which keywords make money? | get_keyword_revenue | Labeled modeled, with the share left in hidden queries and the Search Console lag |
| How much of my MRR is mobile? | get_overview | The Stripe and RevenueCat split, counted once per subscription |
Every Joinsight response carries the fields that answer relies on. A trimmed example, with hypothetical values:
{
"as_of": "2026-10-19T07:12:00Z",
"currency": "EUR",
"modeled": false,
"period": { "start": "2026-09-20", "end": "2026-10-19", "label": "Last 30 days" },
"mrr": { "total": { "minor": 642000, "formatted": "€6,420" } },
"attributed_share": {
"share": 0.62,
"note": "Share of new MRR in the period linked to a first touch. The rest is unattributed (unknown source), not zero."
},
"freshness": [
{ "source": "stripe", "status": "active", "last_synced_at": "2026-10-19T07:00:00Z" },
{ "source": "google", "status": "active", "last_synced_at": "2026-10-19T06:00:00Z" }
]
}
The server's instructions tell the model to quote these numbers rather than recompute them, to call modeled figures estimates, and to mention the attributed share when it is present.
Is it safe to connect revenue data to an assistant?
It depends on what the server allows. These are the properties worth checking in any MCP server that touches revenue, and how Joinsight's handles each:
- Read-only. The tools read pre-computed metrics. Nothing is passed through to Stripe or Google, and nothing can be changed.
- OAuth 2.1 with PKCE. You sign in with your Joinsight account in a browser. No API keys are pasted anywhere.
- Tokens bound to one workspace. You choose the workspace on the consent screen, and the token can't see any other.
- Refresh token rotation. Refresh tokens rotate, and reuse of an old one revokes the chain.
- Bounded output. Customer emails are always masked, and no response returns more than 50 rows. Longer lists are paged with a cursor.
- Audit log. Every tool call is logged, and you can revoke a client at any time from settings.
The transport is Streamable HTTP, so any client that supports remote MCP servers with OAuth can connect, not only Claude.
How do you add a custom connector in Claude?
The menu names may shift between versions, but the flow is the same:
- In Claude, open Settings, then Connectors.
- Choose Add custom connector and paste the server URL:
https://joinsight.app/mcp. - Sign in when the browser window opens, and pick the workspace to share.
- In a new conversation, make sure the connector is enabled, then ask a question.
A good first question is one you already know the answer to, such as last month's MRR. If it matches your billing dashboard, you have a baseline for trusting the less obvious answers.
What the Joinsight server covers
Joinsight's MCP server has 12 tools over revenue from Stripe and RevenueCat, Search Console, GA4 or Plausible, App Store Connect and its first-party snippet: overview, MRR over time, movements, customers, a single customer's journey, page revenue, keyword revenue, keyword opportunities, the App Store funnel, Instagram posts (Instagram is coming soon), explain_change, and alerts with data health. The full list and the security details are on the MCP server page, and the definitions every tool quotes are on metric definitions. It is included in every plan, with a 14-day trial and no card required, as described on pricing.