R1 Dash Master
by alekm
README.md
# R1 Dash Master
An MCP server that builds **importable RUCKUS One Data Studio dashboards** from a
simple declarative spec. Output is a `.zip` you import via **Data Studio โ Settings
โ Import Dashboard**. Pure offline generation โ no R1 API credentials needed.
๐บ **Setup & usage in Claude Desktop:** https://youtu.be/-gU7yu6liOw
Data Studio is Apache Superset on an Apache Druid backend (`deployment: ALTO`). This
tool encodes the dataset catalog and the chart/query grammar so you
(or an agent) can build valid dashboards without learning Superset internals or guessing
field names.
## Tools
- **`list_datasets()`** โ all 18 R1 datasets (internal name, cube name, id, counts).
- **`describe_dataset(name)`** โ exact metric + dimension names for one dataset.
- **`validate_spec(spec)`** โ check a spec against the catalog before building.
- **`build_dashboard(spec, filename?)`** โ emit an importable `.zip` (written to `out/`).
## Spec format
```jsonc
{
"title": "Network Intelligence", // generic โ NEVER tenant-specific (bundles are portable across ECs)
// tenant_id: OPTIONAL โ omit it. Import auto-rescopes to the target EC (tenant). Only include to hard-pin a tenant.
"time_range": "Last week", // default for all charts (Last day/week/month/quarter, previous calendar week/month, or explicit range)
"grain": "day", // OPTIONAL trend time grain: 30 second/minute/3ยท5ยท10ยท15ยท30 minute/hour/day/week/month/quarter (default hour); charts can override
"rows": [ // each row = list of charts; widths in a row sum to <= 12
[ {chart}, {chart} ]
]
}
```
Chart:
```jsonc
{
"type": "bignum" | "bignum_trend" | "line" | "bar" | "area" | "scatter" | "pie" | "table"
| "gauge" | "heatmap" | "funnel" | "pivot" | "mixed" | "tree" | "bubble",
"stacked": true, // bar/area only: stack the series
"x": "apMac", // line/bar/area/scatter: optional DIMENSION x-axis (default __time)
// pivot: "rows": ["zoneName"], "columns": ["radio"], "metrics": [...]
// mixed: "metrics": [...] (bars) + "metrics_b": [...] (line) + optional "groupby"/"groupby_b","format_b"
// tree: "id": "apName", "parent": "apModel", "name": "apName", "metric": "..."
// bubble: "entity": "apName", "x": <metric>, "y": <metric>, "size": <metric> (x/y/size are METRICS here)
// funnel/gauge/heatmap: "metric" (singular) + "groupby" ([dim]; heatmap uses first dim as Y)
"dataset": "binnedSessions", // internal name from list_datasets
"title": "...", "width": 1-12,
"metric": "User Traffic (Total)" // bignum/pie; string = saved metric
| {"sql": "1.0*SUM(a)/SUM(b)", "label": "Rate"}, // or custom-SQL (ratios/%)
"metrics": [ ... ], // line/table (list of the same forms)
"groupby": ["radio"],
"filter": ["radio","5"] | [["radio","5"],["zoneName","X"]],
"time_range": "Last day", // optional per-chart override
"format": ".1%", // d3 number format (rates -> ".1%")
"percent_of_total": ["Traffic (Total)"], // table: share-of-column-total column
"row_limit": 25
}
```
## Layout & cross-filtering (design convention)
Data Studio dashboards are **cross-filterable**: clicking a value in any chart (e.g. a
venue in a venue table) filters the *entire* dashboard to that value; clearing it up top
removes the filter. So **put venue and AP tables/charts near the TOP** โ they double as
interactive filter controls. Recommended order: KPI row โ venue (and AP) table โ detail
charts below. The builder preserves row order from the spec, so order your `rows` that way.
## Grammar notes baked in (gotchas)
- **Field names are exact & dataset-specific.** `radio` not `Radio`; `Unique Client MAC Count`
not "Unique Client Count"; `User Traffic(Total)` (no space) in `sessionsSummary` vs
`User Traffic (Total)` (space) in `binnedSessions`. `validate_spec` catches saved-metric/dim typos.
- **Custom-SQL metrics reference RAW columns** (e.g. `successCount`), not display metric names,
and integer division floors โ always `1.0 *` (or `100.0 *`). See `raw_columns` in the catalog.
- **Rate vs share:** a true rate = SQL metric + `.1%` format. `percent_of_total` (table
`percent_metrics`) means "% of the column total" (contribution), not "format as %".
- **Band values are tri-band:** the `radio`/`band` dimensions take `"2.4"`, `"5"`, and
`"6(5)"` โ the 6 GHz band's literal value is the string `"6(5)"`, NOT `"6"` (confirmed from
the per-band metric SQL). Per-band metrics label it `6(5) GHz`. Don't hardcode just 2.4/5.
- Dashboards are **transmutable across ECs** โ keep titles generic, swap `tenant_id`.
## Why a panel imports empty (troubleshooting)
Bundles are **tenant-less**: there is no `datasets/` folder. Charts bind to a dataset
**by UUID** and reference metrics **by name string**, and both must already exist in the
**target EC**. (Multi-dataset dashboards are fully supported โ see
`examples/network_intelligence.json`; if a board looks limited to one dataset, that's not
a tool limitation.) A panel that imports but renders empty almost always traces to one of:
1. **Saved-metric name not present on the target cube.** A metric like `"Client Throughput"`
only resolves if that EC's cube defines it. **Fix:** use a self-contained custom-SQL metric
(`{"sql": "...", "label": "..."}`) instead of a saved-metric name โ it carries its own
definition and doesn't depend on the target.
2. **Dataset not provisioned in that EC.** The UUID resolves to nothing. Confirm the dataset
exists in the target Data Studio before importing.
3. **Overwrite-orphaning on re-import.** Re-importing over an existing published dashboard can
orphan charts whose position or title changed (chart identity is keyed on title + row/col).
**Fix:** import to a **fresh dashboard title** rather than overwriting.
### Re-importing while you iterate on the same dashboard
This is the most common self-inflicted cause of the empty/duplicate panels above, and it
bites hardest **mid-session when you're iterating on one idea โ adding, removing, renaming,
or reordering charts on the same board and re-importing after each change.** Chart identity
is derived from `(dashboard title, row, column, chart title)`, and the dashboard from its
title alone, so:
- Re-import is **idempotent only when the spec is unchanged** (same titles, same layout) โ it
cleanly overwrites the same objects in place.
- The moment a chart is **added, removed, renamed, or moved**, its identity changes: Superset
creates the new version and **leaves the old one behind as an orphan** (on no dashboard).
Enough churn accumulates a pile of stale, sometimes-empty tiles that look like a data problem.
Pick one discipline and stick to it for the session:
- **Overwrite in place** โ keep the dashboard title, chart titles, and layout stable across
imports so every re-import updates the same board.
- **Fresh each pass** โ bump the dashboard title each iteration and delete the previous board,
so every import is a clean set with nothing orphaned.
Avoid the middle ground: repeatedly reshaping a same-named board and re-importing. If you've
already accumulated orphans, clean them in the UI โ delete charts that belong to no dashboard,
remove duplicate boards, then re-import your current spec once.
## CLI (without MCP)
```bash
python3 builder.py examples/network_intelligence.json out/network_intelligence_IMPORT.zip
```
## Run as MCP
```bash
pip install -r requirements.txt
python3 server.py
```
**Easiest:** just ask Claude to set it up โ point it at this repo and it'll wire the
MCP server into your client for you (that's what the [video](https://youtu.be/-gU7yu6liOw) shows).
**Manual:** register it yourself. For **Claude Desktop** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"r1-dash-master": {
"command": "python3",
"args": ["/path/to/r1_dash_master/server.py"]
}
}
}
```
## Examples vs. Gallery
- **`examples/*.json`** โ source specs, for driving the MCP/builder and learning the spec format.
- **`gallery/*.zip`** โ prebuilt, **ready-to-import** dashboards. Since bundles are tenant-less,
they auto-rescope to whatever EC you import them into. Grab one โ Data Studio โ Settings โ
Import Dashboard. Current set: executive_overview, capacity_rf, connection_health,
network_intelligence, switch_health, chart_gallery, delivered_throughput.
- Regenerate the gallery from specs anytime: **`./build_gallery.sh`** (keeps zips in sync).
## Status
Catalog: 18/19 datasets mapped (AP Alarms & Controller Inventory are SmartZone-only, N/A in R1).
Viz (15): bignum, bignum_trend, line, bar, area, scatter, pie, table, gauge, heatmap, funnel, pivot, mixed, tree, bubble. Query grammar: saved +
custom-SQL metrics, percent-of-total, dimension + time filters, d3 formats. Cross-filtering is
built in (click a chart value to filter the dashboard). Not yet: explicit dashboard-level native
filter bar; remaining viz (treemap, sunburst, box plot, radar, waterfall, graph, histogram,
calendar heatmap, sankey, smooth/stepped line); auto-import (needs an analytics-backend API โ import the zip via UI).
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessUnresponsive