Skip to main content
Glama
Haydebug

roblox-analytics-mcp

by Haydebug
README.md
# roblox-analytics-mcp

A local MCP server that gives an AI agent read access to your Roblox experience
analytics through the [Open Cloud Analytics Query API](https://create.roblox.com/docs/cloud/guides/analytics).

It exposes all **168 metrics** the API supports across 16 categories — retention,
engagement, monetization, acquisition, performance, economy, funnels, custom
events, thumbnails, matchmaking, data stores, safety, and ads — plus a layer of
analysis tools that turn "how is my game doing?" into a single call.

## Why not just call the API directly

The raw API is awkward to drive:

- Metric names, granularities, and dimensions are case-sensitive, undiscoverable
  at runtime, and every invalid combination returns the same opaque `400 / 2001`.
- You get 30 queries per minute **per account**, so a wasted call is expensive.
- Large queries return `202` and a path you have to poll.
- Buckets with no activity are omitted rather than returned as zero, so gaps are
  ambiguous.
- Funnel steps and product IDs must be discovered before they can be filtered on.

This server handles all of that: it validates requests against a bundled catalog
before spending a query, paces itself under the rate limit, follows long-running
operations to completion, reports missing buckets explicitly instead of inventing
zeros, and does the two-stage funnel lookup for you.

## Setup

```bash
npm install
npm run build
npm link          # optional, puts `roblox-analytics-mcp` on your PATH
```

Create an API key at the [Creator Dashboard](https://create.roblox.com/dashboard/credentials):

1. Add each experience you want to query under **Access Permissions**.
2. Grant the `universe.analytics:read` operation under the `universe-analytics` system.

Then store it:

```bash
roblox-analytics-mcp setup     # prompts for the key and an optional default universe
roblox-analytics-mcp test      # runs a live DAU query to confirm it works
```

A stored key takes precedence over the `ROBLOX_API_KEY` environment variable.

### Register with Claude Code

```bash
claude mcp add roblox-analytics -- node /absolute/path/to/AnalyticsMcp/dist/cli.js
```

## Tools

### Discovering what is queryable

| Tool | Purpose |
| --- | --- |
| `list_metrics` | Browse all 168 metrics; filter by search, category, supported dimension, or granularity. |
| `describe_metric` | Full definition: granularities, retention window, every valid dimension. |
| `list_dimensions` | All 69 dimensions and which metrics support each. |
| `list_dimension_values` | The actual countries, product IDs, funnel names, or place versions in your data. |

### Reading data

| Tool | Purpose |
| --- | --- |
| `query_metric` | One metric as a time series, with summary stats, outliers, and coverage. |
| `query_metrics` | Several metrics over the same range, paced under the rate limit. |
| `get_analytics_operation` | Escape hatch for an operation that timed out. |

### Analysis

| Tool | Purpose |
| --- | --- |
| `get_experience_overview` | 17 headline KPIs vs the previous period, with sharp movers flagged. |
| `get_metric_report` | A themed pack — monetization, performance, acquisition, economy, safety, ads… |
| `breakdown_metric_by_segments` | One metric sliced by *every* dimension it supports, ranked. |
| `compare_periods` | Two arbitrary windows, per-series absolute and percent change. |
| `analyze_funnel` | Discovers funnels and step IDs, then reports churn and completion per step. |

### Context

| Tool | Purpose |
| --- | --- |
| `get_universe_info` | Experience metadata plus public stats (likes, favourites, live CCU). |
| `get_public_game_stats` | Up to 50 universes at once. No API key — works for competitors too. |
| `whoami` | Who the API key belongs to, what it can read, and the owner's groups. |
| `find_universes` | Universe IDs by name or creator — own account **and** all groups. |
| `get_place_info` | Place metadata, for when performance metrics point at a place ID. |
| `set_default_universe` | Store a default universe and an optional alias. |
| `get_server_status` | Key source, default universe, cache state, catalog size, rate limits. |

## Identity: how the server knows whose games these are

Open Cloud API keys are **anonymous to the endpoints they call** — no analytics
response reveals the caller, and no additional key permission changes that. The
server resolves identity through a separate endpoint instead:

```
POST https://apis.roblox.com/api-keys/v1/introspect   { "apiKey": "..." }
```

The key travels in the *body*, and the call needs no scope of its own — any
valid key can introspect itself. It returns `authorizedUserId`, the key's
scopes, whether it is enabled and unexpired, and which universes it covers
(`*` meaning every experience the owner can access).

That user ID unlocks the rest through public endpoints: the owner's username,
their personal experiences, and every group they belong to. **Group scanning
matters** — studio titles usually live under a group the developer merely
belongs to, so a personal-account-only lookup misses the real games entirely.

```
whoami                                  → who owns this key, what can it read
find_universes { search: "party" }      → ranked matches across account + groups
```

Group listings are cached for 6 hours (`ROBLOX_ANALYTICS_CACHE_TTL_MINUTES` to
change, `refresh: true` to bypass). A cold scan of 40 groups takes about a
minute; warm lookups are instant. Caching is **per owner**, so a group listing
that gets rate-limited is the only thing retried next time.

Any source that could not be read is reported in `errors` rather than dropped —
an empty result with errors present means the scan was throttled, not that the
creator has no games. Those two states look identical otherwise, and conflating
them produces confidently wrong answers.

## Things worth knowing

**Aggregation is reported, not assumed.** Summing daily active users across a
month does not give you monthly actives — it double-counts anyone who played
twice. Where the API supports it, period totals come from a single whole-range
query (`aggregation: "api-period-total"`); where it does not, the fallback is
labelled `mean-of-buckets` or `sum-of-buckets` so the number is never anonymous.

**`sum` is not always meaningful.** Every result carries an `aggregationHint`.
For rates, averages, and percentiles (`ForwardD1Retention`, `ClientFpsP90`,
`PayingUsersCVR`) it reads `average-only` — the `sum` field is arithmetic, not
information.

**Gaps are not zeros.** The API omits buckets with no activity. Results report a
`missingBuckets` list rather than filling in zeros that would drag averages down.

**Time is UTC.** `endTime` is exclusive and defaults to today's UTC midnight, so
you get only complete buckets. Pass `endTime: "now"` to include the partial
current day. Ranges accept RFC 3339, plain dates, or relative shorthand
(`-30d`, `-12w`, `-6mo`, `today`, `yesterday`), or use `lastDays`.

**Retention windows differ.** Standard metrics keep 4 years; performance and
stability metrics keep only 28 days. Queries beyond the window are rejected
locally with the earliest queryable timestamp.

**Beta API.** The Analytics Query API is in beta and its schema may change. The
metric catalog is generated from the official docs — refresh it with
`npm run generate:catalog` if Roblox adds metrics.

## Development

```bash
npm run build             # compile TypeScript
npm test                  # 6 suites: time, validation, query pipeline, reports, packs, MCP protocol
npm run generate:catalog  # regenerate src/catalog.ts from the Roblox docs
```

Tests mock `fetch` for the API-facing suites, so only the MCP protocol suite
touches the network (public endpoints, no key needed).

## License

MIT

TDQS

A4.2/5.0

Scored across 19 tools

Disambiguation4/5

The query-related tools (query_metric, query_metrics, get_metric_report, get_experience_overview, compare_periods, breakdown_metric_by_segments) all read analytics data, but each description carves a distinct usage: single series, batch, themed packs, health check, period comparison, exhaustive segment scan. A couple of metadata/auth tools (get_server_status vs whoami, get_universe_info vs get_public_game_stats) are adjacent but their descriptions separate them.

Naming Consistency4/5

Names are consistently snake_case and mostly verb-first: list_*, get_*, query_*, compare_periods, analyze_funel, find_universes. Minor deviations: 'whoami' is an idiom rather than verb_noun, and 'breakdown_metric_by_segments' uses 'breakdown' as a verb-ish noun instead of 'break_down_metric_by_segments'.

Tool Count4/5

At 19 tools this is above the ideal 3-15 range, but the server spans analytics querying, metric/dimension discovery, reporting wrappers, funnel analysis, universe/place lookup, and account/config diagnostics, so the count is defensible. A few convenience tools could theoretically be folded into query_metric, but none feel disposable.

Completeness4/5

Core analytics workflows are well covered: discover metrics/dimensions, query single/bulk metrics, break down, compare periods, analyze funnels, and retrieve async results. Minor gaps: no direct place-list endpoint (place IDs must be discovered via dimension values then resolved with get_place_info) and alias configuration can be set but not explicitly removed.

Maintenance

ActivitySlowing
ResponsivenessNo issues