MCP server
@saaspro/mcp puts SaaS Pro Max inside Claude Code, Claude Desktop, Cursor
or any other Model Context Protocol host. The
agent can then read your analytics, triage your errors, roll out a flag, and
install the SDK into a codebase — with the same permissions the person holding
the token has in the console.
It is a thin shell over the Management API: no state, no cache, no stored credential. Every authorization decision is made by the platform, so nothing about your security posture depends on the agent.
Install
claude mcp add saaspro --env SPM_TOKEN=spm_pat_xxxxxxxx -- npx -y @saaspro/mcp
Or write the configuration yourself. Claude Desktop
(~/Library/Application Support/Claude/claude_desktop_config.json on macOS,
%APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"saaspromax": {
"command": "npx",
"args": ["-y", "@saaspro/mcp"],
"env": {
"SPM_TOKEN": "spm_pat_xxxxxxxx",
"SPM_HOST": "https://saaspro.dev"
}
}
}
}
Cursor takes the same object in .cursor/mcp.json (per project) or
~/.cursor/mcp.json (global). VS Code uses .vscode/mcp.json with
"type": "stdio".
| Variable | Required | Default | Meaning |
|---|---|---|---|
SPM_TOKEN |
yes | — | spm_pat_… personal or spm_org_… organization token |
SPM_HOST |
no | https://saaspro.dev |
API origin; only preview environments change it |
Get a token from Organization → Access tokens (belongs to the team) or Account → Personal access tokens (acts as you).
Tools
| Tool | Scope | What it does |
|---|---|---|
list_organizations |
read | Organizations this token reaches, with its role in each |
list_apps |
read | Applications in one organization, with modules and environments |
create_app |
write | Create an application and its three environments |
get_app |
read | One application's settings and enabled modules |
list_api_keys |
admin | Ingest keys by prefix and last4 — never the value |
create_api_key |
admin | Mint a public or secret ingest key, shown once |
get_install_instructions |
read | Framework snippets with the real key prefix |
get_analytics_overview |
read | Visitors, pageviews, sessions, bounce rate, conversions, with deltas |
get_timeseries |
read | One metric bucketed over time |
get_breakdown |
read | Top pages, sources, campaigns, geo, devices, event names |
get_retention |
read | Weekly or monthly cohort retention |
get_active_users |
read | DAU, WAU, MAU and stickiness |
list_events |
read | The raw event feed, filtered |
list_event_names |
read | Event names with counts and unique users |
list_funnels |
read | Saved funnels and their steps |
create_funnel |
write | Save an ordered funnel |
get_funnel_results |
read | Per-step conversion and drop-off |
search_people |
read | Person search by text, stage, tag or segment |
get_person |
read | One person's dossier |
update_person |
write | Stage, tags and CRM fields |
list_errors |
read | Error issues by status |
get_error |
read | One issue with daily counts, releases and tags; with eventId, one occurrence in full (frames, breadcrumbs, tags, extra, request) |
update_error_status |
write | Resolve, ignore or reopen an issue |
list_metrics |
read | Metric names, kinds and units |
get_metric_series |
read | One metric aggregated, with label filters |
list_flags |
read | Feature flags and their rollouts |
upsert_flag |
write | Create or replace a flag definition |
create_alert_rule |
write | Alert on a metric, error rate, uptime, vital or event volume |
list_incidents |
read | Alert incidents, open first |
list_campaigns |
read | Campaigns with status counts and delivery statistics |
get_campaign_stats |
read | One campaign with sent, delivered, opened and clicked |
get_revenue_overview |
read | MRR, ARR, movement, payments |
ask_saaspromax |
write | Ask the platform assistant in natural language; changes come back as proposals to approve |
Every app-scoped tool takes app, which accepts an application id or the
orgSlug/appSlug form list_apps returns.
ask_saaspromax is the escape hatch: it runs the platform's own tool loop and
returns a written answer with the tools it used. Prefer a specific tool when you
know what you need — it is cheaper, and the raw data is easier to reason about.
When the assistant wants to change something
The platform assistant can act, not only read, so ask_saaspromax has two kinds
of outcome. A question that only needs reads comes back as an answer. A question
that would change something comes back as an answer plus one or more proposed
actions, each with an id, the tool it would call, its arguments, its effect
(write, destructive or external) and a human-readable preview of the
change. Nothing has been written at that point.
Approving is a separate call, against the assistant endpoints:
curl -X POST -H "Authorization: Bearer $SPM_TOKEN" -H 'content-type: application/json' \
-d '{"reason":"Agreed in the release channel"}' \
"$SPM_HOST/api/v1/apps/acme/site/ai/actions/act_1/approve"
GET /apps/{app}/ai/actions lists what is waiting, and /reject declines one
with a reason. Approving executes the action, writes the audit row and resumes
the conversation, so a later ask_saaspromax in the same conversation can report
the outcome.
Two limits apply to an MCP host in particular:
- A token that could not perform the action itself cannot approve it.
readscope can ask, and can watch proposals appear, but not approve them. - A
destructiveaction — a deletion, a revocation, a production write, minting a key — cannot be approved through the API at all. It needs a fresh passkey, which only a console session has, so the endpoint answers403withpasskey_requiredand a person finishes it in the console.
The design is deliberate: an agent may propose, a person disposes. If your host
wants direct writes instead, use the specific tool (upsert_flag,
update_error_status, update_person) with a write-scoped token — those act
as the token's owner and need no approval.
Resources
saaspro://docs/{slug} serves the deployment's own documentation as
markdown: SDK guides, module references, integration setup, the API reference.
The list comes from GET /api/v1/docs, so it always matches the platform the
token points at rather than a snapshot bundled with the server.
Prompts
| Prompt | Arguments | What it does |
|---|---|---|
integrate_app |
org?, app? |
Detect the framework, pick or create an application, install the SDK, wire identify, track, errors, metrics and a flag, then verify events arrive |
weekly_report |
app, range? |
Traffic, acquisition, funnels, reliability and revenue for a week, against the previous week |
investigate_error |
app, issueId? |
Read an issue, judge blast radius, find the code, propose a fix, resolve it |
The agent skills go further: they carry per-framework reference snippets and an output template, and they trigger on what you ask for rather than needing to be picked from a menu.
Security
- Scope the token to the job. A read-only token is enough for reporting and
error triage.
writeadds module data.adminadds ingest keys — give it only when the agent is genuinely installing an SDK. - Tokens are organization-scoped. An organization token reaches exactly one
organization with the
developerrole. A personal token acts as its owner and reaches that owner's organizations, each with its own role permissions. Neither can delete an organization or an application, change members, or mint further tokens: those need a console session with a fresh passkey. - Nothing is stored on your machine. The token is read from the environment and forwarded as a bearer credential. It is never written to disk, never logged, and never included in a tool result. The server logs to stderr only — stdout carries JSON-RPC.
- Personal data stays gated. Emails and names are masked unless the token's
bearer holds
people.pii.read. - Ingest keys are shown once.
create_api_keyreturns the value in that one response; only a hash is stored. Put it in an environment file, never in source and never in a chat transcript. - Every write is audited as an action by the person the token belongs to,
tagged
via: management-apiwith the token's id — so a leaked token's blast radius is always answerable. - Revocation is immediate. Revoke in the console and the next tool call fails; a personal token also dies the moment its owner leaves the organization.
Troubleshooting
| Symptom | Cause |
|---|---|
SPM_TOKEN is not set on start |
The host did not pass the environment; check env in the config |
Every tool answers unauthorized |
The token is revoked, expired, or from another deployment — check SPM_HOST |
insufficient_scope |
The token's scope is too narrow; mint one with write or admin |
forbidden naming a permission |
The bearer's role lacks it, or the application has the module off |
not_found for an application you can see in the console |
This token's organization does not contain it |
Debug against a real deployment with the inspector:
SPM_TOKEN=spm_pat_xxxxxxxx npx @modelcontextprotocol/inspector npx -y @saaspro/mcp