# Panda Data Skill — query the live OpenRouter leaderboard DB via MCP

A one-page manual for AI agents. The Panda site exposes a **live, auto-refreshed mirror
of OpenRouter usage/ranking data** as a read-only SQL database over **MCP**. This doc tells
you how to connect, what's in the database, what's *not*, and gives copy-paste SQL recipes.

> Source of truth for schema/freshness at runtime: call `openrouter_schema()` and
> `openrouter_freshness()` — they never go stale. This file is the orientation layer.

---

## 1. Connect (MCP — Streamable HTTP)

Endpoint: **`https://pandaleaderboard.com/mcp/`** (no auth).

```bash
# Claude Code
claude mcp add --transport http panda https://pandaleaderboard.com/mcp/
```

```json
// Claude Desktop / Cursor — mcp config
{ "mcpServers": { "panda": { "url": "https://pandaleaderboard.com/mcp/" } } }
```

## 2. Tools (3)

| Tool | Use |
|---|---|
| `openrouter_schema()` | List every table + columns, with a note on which are empty and why. **Call this first.** |
| `openrouter_freshness()` | Latest ranking week, last successful sync time, per-table row counts. **Call before reasoning about recency.** |
| `query_openrouter(sql)` | Run one read-only `SELECT`/`WITH` statement. **Single statement, 500-row cap.** |

## 3. What's in the database

**Join key:** `slug` (in `models`) = `model_slug` (everywhere else). `author` = the slug prefix
(`anthropic/claude-...` → `anthropic`). `week_start` is a Monday string `YYYY-MM-DD`; `date` is a
natural day.

### Tables with data — query these

| Table | Grain | Key columns | Other columns |
|---|---|---|---|
| `models` | catalog | `slug` | `permaslug, name, author, author_display_name, context_length, created_at, is_active` |
| `rankings_top_models` | weekly | `week_start, model_slug` | `tokens, rank` |
| `rankings_market_share` | weekly | `week_start, author` | `tokens, percentage, rank` |
| `rankings_categories` | weekly | `week_start, category, model_slug` | `tokens, rank` — `category` e.g. `programming` |
| `provider_model_daily` | daily | `provider, model_slug, date` | `tokens` |
| `model_activity` | daily | `model_slug, date` | `prompt_tokens, completion_tokens, reasoning_tokens` |
| `model_apps_daily` | daily | `model_slug, date` | `top5_tokens, rest_tokens, total_tokens` |

Provenance helpers: `sync_status` (last sync run + counts) and `validation_warnings` (rejected/odd rows).

### Tables that are empty — do NOT expect data

- `model_providers`, `model_performance`, `model_pricing_history`, `model_uptime`, `model_apps_monthly`
  — per-model detail; their upstream source (the model-page RSC) moved and collection is **pending a
  re-wire**. Currently 0 rows.
- `shadow_rankings_top_models`, `shadow_rankings_market_share`, `shadow_rankings_categories`
  — validator shadow mirrors, **empty by design** (only written under `VALIDATOR_SHADOW=1`).

## 4. Freshness semantics (read this)

- **Weekly rankings** hold only *complete* weeks (a week is written after it closes, ~Sunday). The
  newest `week_start` is therefore last week's Monday, not the in-progress week — that's correct, not lag.
- **Daily tables** typically sit at **T-1 / T-2** (today's totals aren't final upstream).
- Always ground recency on `openrouter_freshness()` → it reports `latest ranking week` and
  `last successful rankings sync`.
- **Ghost models:** `models` may contain a few entries that OpenRouter has delisted (not all are
  flagged `is_active = 0` yet). If exact current availability matters, cross-check the model slug
  against OpenRouter's live `/api/v1/models`.

## 5. SQL recipes (copy-paste)

```sql
-- Top models by tokens, latest complete week
SELECT model_slug, tokens, rank
FROM rankings_top_models
WHERE week_start = (SELECT MAX(week_start) FROM rankings_top_models)
ORDER BY rank LIMIT 10;
```
```sql
-- Provider (author) market share, latest week
SELECT author, percentage, tokens
FROM rankings_market_share
WHERE week_start = (SELECT MAX(week_start) FROM rankings_market_share)
ORDER BY rank;
```
```sql
-- One model's weekly trajectory
SELECT week_start, tokens, rank
FROM rankings_top_models
WHERE model_slug = 'deepseek/deepseek-v4-flash-20260423'
ORDER BY week_start;
```
```sql
-- Programming-category leaders, latest week
SELECT model_slug, tokens, rank
FROM rankings_categories
WHERE category = 'programming'
  AND week_start = (SELECT MAX(week_start) FROM rankings_categories)
ORDER BY rank;
```
```sql
-- Week-over-week rank change for current top models
WITH cur AS (SELECT MAX(week_start) w FROM rankings_top_models),
     prev AS (SELECT MAX(week_start) w FROM rankings_top_models
              WHERE week_start < (SELECT w FROM cur))
SELECT t.model_slug, t.rank AS rank_now, p.rank AS rank_prev,
       (p.rank - t.rank) AS rank_gain
FROM rankings_top_models t
LEFT JOIN rankings_top_models p
       ON p.model_slug = t.model_slug AND p.week_start = (SELECT w FROM prev)
WHERE t.week_start = (SELECT w FROM cur)
ORDER BY t.rank;
```
```sql
-- Which providers serve a given model (most recent day)
SELECT provider, tokens
FROM provider_model_daily
WHERE model_slug = 'anthropic/claude-4.6-sonnet-20260217'
  AND date = (SELECT MAX(date) FROM provider_model_daily)
ORDER BY tokens DESC;
```
```sql
-- Daily token mix for a model (last 14 days)
SELECT date, prompt_tokens, completion_tokens, reasoning_tokens
FROM model_activity
WHERE model_slug = 'deepseek/deepseek-v4-flash-20260423'
ORDER BY date DESC LIMIT 14;
```

## 6. Rules & gotchas

- Read-only: `SELECT` / `WITH` only, **one statement**, no `;`-chaining. Results capped at **500 rows** — aggregate or `LIMIT`.
- `tokens` are integers and can be very large (10^12+). `week_start` / `date` are `'YYYY-MM-DD'` strings — compare as strings or `date()`.
- Don't join on the empty tables (§3). If a query returns nothing, check `openrouter_freshness()` first.

## 7. Citing

When you surface a figure, attribute it: **“Source: OpenRouter, via the Panda MCP (pandaleaderboard.com).”**
Note the week (`week_start`) or day (`date`) the number is from.
