drag-race-mcp
by tkalejandro
README.md
# Drag Race MCP π
[](https://glama.ai/mcp/servers/tkalejandro/drag-race-mcp)
[](https://glama.ai/mcp/servers/tkalejandro/drag-race-mcp)
> An unofficial, community-driven MCP (Model Context Protocol) server for Drag Race knowledge.
`drag-race-mcp` gives AI assistants structured access to queens, seasons, episodes, and fan lore through MCP tools.
> **Disclaimer**
>
> This is an **unofficial fan project** and is **not affiliated with, endorsed by, or sponsored by** RuPaul, World of Wonder, or the Drag Race franchise.
---
## Use as an MCP client
This package is a **stdio** MCP server. Your client spawns it as a child process and talks JSON-RPC over stdin/stdout β do not start the server yourself.
**Launch command** (pick one):
| How | Command / args |
|-----|----------------|
| Published package | `npx` β `@tkalejandro/drag-race-mcp` |
| Local clone (built) | `node` β `dist/index.js` (run `pnpm build` first) |
| Local clone (dev) | `pnpm` β `exec` `tsx` `src/index.ts` (from the repo root) |
Hosts like Cursor can wire the same command in MCP config (see [Test locally with Cursor](#test-locally-with-cursor)). Below are minimal **programmatic** clients.
### TypeScript
```bash
npm install @modelcontextprotocol/client
```
```ts
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
const client = new Client({ name: "drag-race-client", version: "1.0.0" });
const transport = new StdioClientTransport({
command: "npx",
args: ["-y", "@tkalejandro/drag-race-mcp"],
// Local clone instead:
// command: "node",
// args: ["dist/index.js"],
// cwd: "/path/to/drag-race-mcp",
});
await client.connect(transport);
const { tools } = await client.listTools();
console.log(tools.map((t) => t.name));
const result = await client.callTool({
name: "search_queens",
arguments: { query: "jinkx", limit: 5 },
});
for (const block of result.content) {
if (block.type === "text") console.log(block.text);
}
await client.close();
```
### Python
```bash
pip install mcp
# or: uv add mcp
```
```python
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main() -> None:
server = StdioServerParameters(
command="npx",
args=["-y", "@tkalejandro/drag-race-mcp"],
# Local clone instead:
# command="node",
# args=["dist/index.js"],
# cwd="/path/to/drag-race-mcp",
)
async with stdio_client(server) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print([t.name for t in tools.tools])
result = await session.call_tool(
"search_queens",
arguments={"query": "jinkx", "limit": 5},
)
for block in result.content:
if block.type == "text":
print(block.text)
asyncio.run(main())
```
Tool names and arguments match the table in [MCP tools](#mcp-tools) (e.g. `get_queen`, `get_season`, `get_queen_earnings`).
---
## Features
**Ready**
- Typed knowledge model (seasons, queens, episodes, lore) including queen `origin`
- JSON knowledge base + Zod validation + integrity checks
- MCP read tools: discovery, rankings, stats, alumni hosts/judges, track records
- Cursor-friendly local MCP config + workflow docs
**Coming next**
- Broader franchise coverage; optional RAG later
- `recommend_season` is skipped until ratings data exists (would be made-up)
---
## Example questions
- Who are the Porkchop queens?
- Which queens competed on both Season 5 and All Stars?
- Compare Jinkx Monsoon and Bianca Del Rio.
- Which queens have the most challenge wins?
- Recommend a season with lots of comedy.
- Explain the rivalry between Alyssa Edwards and Coco Montrese.
---
## Data model (for agents & contributors)
IDs and catalogs live in `src/kb/catalogs.ts`; entity shapes are Zod schemas in `src/kb/schemas/` (types via `z.infer`). Designed so models can navigate facts without burning tokens on repeated prose.
### Hard facts
| Type | Where | What it stores |
|------|-------|----------------|
| `Season` | `kb/schemas/season.ts` | Franchise season metadata, cast IDs, winner, prize, hosts/judges |
| `Queen` | `kb/schemas/queen.ts` | Drag name, aliases, `origin.countries` (ISO-3166-1-alpha-2, required), optional `hometown`, per-season appearances & wins |
| `Episode` | `kb/schemas/episode.ts` | Week-by-week challenges, runway, lip sync, eliminations |
| `Money` | `kb/schemas/money.ts` | Prize record: `{ amount, currency, context, isSponsor?, isCharity? }` β season `cashPrice`, episode mini/maxi/lip-sync `earnings`, mirrored on queen wins. Use `amount: 0` + `context` for non-cash sponsor prizes; `isCharity` for charity purses |
| `PersonRef` | `kb/schemas/person.ts` | Host/judge `{ name, queenId? }` |
| `SeasonId` / `Currency` / `LoreTag` | `kb/catalogs.ts` | Closed catalogs + string ID aliases |
| `Country` / `OriginRegion` | `kb/origin.ts` | ISO countries used in origin + derived regions (`latin_america` excludes Spain; Spain is `iberia`) |
Queen origin is nationality/heritage from sources β **not** an ethnicity field. `originRegion=latin_america` excludes Spain (`ES`). Show geography (`franchise=FR`) is not queen origin. Regions are derived in the service from `origin.countries`; they are not stored on each JSON file.
These are the **source of truth** for placements, wins, cast lists, and episode outcomes.
### IDs keep tokens small
Everywhere possible we link by **stable IDs**, not full nested objects:
| ID | Example | Notes |
|----|---------|--------|
| `SeasonId` | `US-S17`, `AS-S10`, `CVTW-S02` | Closed enum of known seasons |
| `EpisodeId` | `US-S17-E05` | `{SeasonId}-E{NN}` |
| `QueenId` | `jinkx-monsoon` | Kebab-case slug (open set; validated from data later) |
| `LoreId` | `alyssa-coco-rivalry` | Kebab-case slug |
**Why IDs?**
- Smaller payloads in tool responses (pass `castIds`, expand only when needed)
- Safer LLM contributions (reference `US-S06` instead of rewriting season text)
- Easy joins: season β queen IDs β look up queen; queen win β `episodeId` β look up episode
A season stores `castIds` and `episodeIds`. A queen stores appearance stats that point at `episodeId`s. Tools (and later `list_queen_ids`) help the model discover valid IDs instead of inventing them.
### Lore expands the modelβs power
Hard facts answer *what happened*. **Lore** answers *why it matters* and *how things connect*:
```ts
Lore {
id, title, summary, tags,
queenIds?, seasonIds?, episodeIds?
}
```
Add lore entries to teach rivalries, iconic moments, comedy seasons, drag families, controversies, and more β tagged (`rivalry`, `drama`, `comedy`, `iconic`, β¦) and linked back to the fact catalogs via IDs.
**Contribution loop for LLMs / humans:**
1. Prefer existing IDs when linking
2. Add or fix hard-fact records when something is missing or wrong
3. Add lore when you want narrative, correlation, or fan context
Thatβs how the KB grows without duplicating full queen/season blobs everywhere.
---
## Data coverage
Track what data exists for each `SeasonId`. Flip `β` β `β
` when that slice is in the knowledge base.
| Column | Meaning |
|--------|---------|
| **Season** | Season record (meta, cast IDs, winner, prize, hosts/judges) |
| **Queens** | Queen records for the cast (appearances, wins) |
| **Episodes** | Episode records for the season |
| **Lore** | At least one lore entry linked to this season |
Everything starts empty β **this is the contribution map**. Pick any `β` and fill it.
### Challenge prizes (Money coverage)
Episodes β
means episode records exist β **not** that every mini/maxi prize is filled. Weekly prizes live on optional `earnings` (`Money`) on challenges and lip syncs; queen `challengeWins` / `miniChallengeWins` / `lipSyncWins` mirror those amounts. Season grand prizes use `cashPrice` (required on every season).
- **Source-first:** omit `earnings` when public sources donβt list a prize (many UK RuPeter weeks have no cash bullet).
- **Career totals:** use MCP tool `get_queen_earnings` (personal cash vs charity vs non-cash `context` prizes).
Roughly **70 / 82** season packs have at least some weekly `earnings` today.
**No weekly `earnings` yet (12)** β good contribution targets if Fandom or episode sources list prizes Wikipedia omitted:
`AS-S01`, `CVTW-S01`, `CVTW-S02`, `ES-S05`, `UK-S02`, `UK-S03`, `UK-S04`, `UK-S05`, `UK-S07`, `UKVTW-S01`, `UKVTW-S02`, `UKVTW-S03`
**Thin / incomplete (patterns):**
- Early US minis often advantage-only (`US-S01`β`S08`: maxis mostly filled; minis sparse)
- UK / UKVTW / CVTW generally sparse (badges or undocumented weekly cash)
- Some All Stars tip/maxi holes remain (e.g. `AS-S04` tips; `AS-S07` / `AS-S09` maxis)
- Several intl packs strong on maxi **or** mini but not both (e.g. FR minis empty; BR / MX / TH partial)
Do not add a Prize column to the franchise tables below β track prize depth here instead.
### US β RuPaul's Drag Race
| SeasonId | Season | Queens | Episodes | Lore |
|----------|:------:|:------:|:--------:|:----:|
| `US-S01` | β
| β
| β
| β
|
| `US-S02` | β
| β
| β
| β
|
| `US-S03` | β
| β
| β
| β
|
| `US-S04` | β
| β
| β
| β
|
| `US-S05` | β
| β
| β
| β
|
| `US-S06` | β
| β
| β
| β
|
| `US-S07` | β
| β
| β
| β
|
| `US-S08` | β
| β
| β
| β
|
| `US-S09` | β
| β
| β
| β
|
| `US-S10` | β
| β
| β
| β
|
| `US-S11` | β
| β
| β
| β
|
| `US-S12` | β
| β
| β
| β
|
| `US-S13` | β
| β
| β
| β
|
| `US-S14` | β
| β
| β
| β
|
| `US-S15` | β
| β
| β
| β
|
| `US-S16` | β
| β
| β
| β
|
| `US-S17` | β
| β
| β
| β
|
| `US-S18` | β
| β
| β
| β
|
### AS β All Stars
| SeasonId | Season | Queens | Episodes | Lore |
|----------|:------:|:------:|:--------:|:----:|
| `AS-S01` | β
| β
| β
| β
|
| `AS-S02` | β
| β
| β
| β
|
| `AS-S03` | β
| β
| β
| β
|
| `AS-S04` | β
| β
| β
| β
|
| `AS-S05` | β
| β
| β
| β
|
| `AS-S06` | β
| β
| β
| β
|
| `AS-S07` | β
| β
| β
| β
|
| `AS-S08` | β
| β
| β
| β
|
| `AS-S09` | β
| β
| β
| β
|
| `AS-S10` | β
| β
| β
| β
|
| `AS-S11` | β
| β
| β
| β
|
### UK
| SeasonId | Season | Queens | Episodes | Lore |
|----------|:------:|:------:|:--------:|:----:|
| `UK-S01` | β
| β
| β
| β
|
| `UK-S02` | β
| β
| β
| β
|
| `UK-S03` | β
| β
| β
| β
|
| `UK-S04` | β
| β
| β
| β
|
| `UK-S05` | β
| β
| β
| β
|
| `UK-S06` | β
| β
| β
| β
|
| `UK-S07` | β
| β
| β
| β
|
| `UKVTW-S01` | β
| β
| β
| β
|
| `UKVTW-S02` | β
| β
| β
| β
|
| `UKVTW-S03` | β
| β
| β
| β
|
### Canada
| SeasonId | Season | Queens | Episodes | Lore |
|----------|:------:|:------:|:--------:|:----:|
| `CA-S01` | β
| β
| β
| β
|
| `CA-S02` | β
| β
| β
| β
|
| `CA-S03` | β
| β
| β
| β
|
| `CA-S04` | β
| β
| β
| β
|
| `CA-S05` | β
| β
| β
| β
|
| `CA-S06` | β
| β
| β
| β
|
| `CVTW-S01` | β
| β
| β
| β
|
| `CVTW-S02` | β
| β
| β
| β
|
| `CAS-S01` | β
| β
| β
| β
|
### Europe
| SeasonId | Season | Queens | Episodes | Lore |
|----------|:------:|:------:|:--------:|:----:|
| `ES-S01` | β
| β
| β
| β
|
| `ES-S02` | β
| β
| β
| β
|
| `ES-S03` | β
| β
| β
| β
|
| `ES-S04` | β
| β
| β
| β
|
| `ES-S05` | β
| β
| β
| β
|
| `ESAS-S01` | β
| β
| β
| β
|
| `ESAS-S02` | β | β | β | β |
| `FR-S01` | β
| β
| β
| β
|
| `FR-S02` | β
| β
| β
| β
|
| `FR-S03` | β
| β
| β
| β
|
| `FR-S04` | β | β | β | β |
| `IT-S01` | β
| β
| β
| β
|
| `IT-S02` | β
| β
| β
| β
|
| `IT-S03` | β
| β
| β
| β
|
| `DE-S01` | β
| β
| β
| β
|
| `NL-S01` | β
| β
| β
| β
|
| `NL-S02` | β
| β
| β
| β
|
| `BE-S01` | β
| β
| β
| β
|
| `BE-S02` | β
| β
| β
| β
|
| `SE-S01` | β
| β
| β
| β
|
### Latin America
| SeasonId | Season | Queens | Episodes | Lore |
|----------|:------:|:------:|:--------:|:----:|
| `MX-S01` | β
| β
| β
| β
|
| `MX-S02` | β
| β
| β
| β
|
| `MX-S03` | β | β | β | β |
| `MXLR-S01` | β | β | β | β |
| `BR-S01` | β
| β
| β
| β
|
| `BR-S02` | β
| β
| β
| β
|
### AsiaβPacific & global
| SeasonId | Season | Queens | Episodes | Lore |
|----------|:------:|:------:|:--------:|:----:|
| `TH-S01` | β
| β
| β
| β
|
| `TH-S02` | β
| β
| β
| β
|
| `TH-S03` | β
| β
| β
| β
|
| `PH-S01` | β
| β
| β
| β
|
| `PH-S02` | β
| β
| β
| β
|
| `PH-S03` | β
| β
| β
| β
|
| `PH-S04` | β | β | β | β |
| `PHSR-S01` | β
| β
| β
| β
|
| `GAS-S01` | β
| β
| β
| β
|
| `DU-S01` | β
| β
| β
| β
|
| `DU-S02` | β
| β
| β
| β
|
| `DU-S03` | β
| β
| β
| β
|
| `DU-S04` | β
| β
| β
| β
|
| `DUVTW-S01` | β | β | β | β |
---
## Project structure
```text
βββ src/
β βββ index.ts # MCP entry (stdio)
β βββ server.ts # Server + tool registration
β βββ data/ # JSON only (no TypeScript)
β β βββ queens/ # One JSON file per QueenId
β β βββ seasons/
β β βββ US-S01/ β¦ US-S07/, AS-S01/, AS-S02/
β β βββ season.json
β β βββ episodes.json
β β βββ lore.json
β βββ kb/ # Data layer: catalogs, Zod, load, integrity
β β βββ catalogs.ts
β β βββ origin.ts # Country + OriginRegion catalogs
β β βββ schemas/
β β βββ load.ts
β β βββ integrity.ts
β β βββ index.ts
β βββ services/ # Utilities tools call (not MCP)
β β βββ accessors/ # accessors.ts β get* / list*Ids
β β βββ seasons/ # list_season_ids, list_catalogs, list_winners
β β βββ queens/ # search, earnings, ranks, stats, roles, track record
β β βββ episodes/ # search_episodes.ts
β β βββ lore/ # search_lore.ts
β β βββ shared/ # limits.ts
β βββ tools/ # MCP registerTool wrappers only
β βββ general/ # welcome_user.ts
β βββ seasons/ # list_catalogs, list_season_ids, get_season, list_winners
β βββ queens/ # search, get, ranks, stats, lists, track record
β βββ episodes/ # get_episode, search_episodes
β βββ lore/ # get_lore.ts, search_lore.ts
β
βββ .cursor/
β βββ mcp.json # Local Cursor MCP config ADD WHEN YOU NEED IT.
β βββ skills/
β βββ drag-race-data/ # How to contribute season/queen JSON
β βββ mcp-tools/ # Tool layout, TDQS descriptions, annotations
β βββ typescript-style/ # Arrow-const functions + TS conventions
β
βββ package.json
βββ glama.json
βββ tsconfig.json
βββ README.md
```
JSON facts live under `src/data/` (queens global; seasons as `src/data/seasons/<SeasonId>/`). The `src/kb/` layer validates and indexes them via Zod + Maps. See `.cursor/skills/drag-race-data/` when contributing with an agent.
### Validating data
After adding or editing anything under `src/data/`, run:
```bash
pnpm test
```
That loads every JSON file through Zod, rejects bad fields and duplicate IDs, and fails with file + field paths. `git commit` runs the same check via a Husky pre-commit hook β still run the test yourself while editing so you catch issues before commit. After a fresh clone, run `pnpm install` once so Husky installs the hook.
---
## Test locally with Cursor
This is how an Agent chat (like this one) can see and call tools from `drag-race-mcp`.
### 1. Install & run prerequisites
```bash
pnpm install
```
You do **not** need to keep a terminal process running yourself. Cursor starts the MCP server using `.cursor/mcp.json`.
### 2. Project MCP config
This repo already includes `.cursor/mcp.json`:
```json
{
"mcpServers": {
"drag-race-mcp": {
"command": "pnpm",
"args": [
"--dir",
"/ABS/PATH/TO/drag-race-mcp",
"exec",
"tsx",
"src/index.ts"
]
}
}
}
```
Update the `--dir` path to **your** machineβs clone path if it differs.
Cursor loads project MCP config from `.cursor/mcp.json` when the workspace is open. You can also add the same server under **Cursor Settings β MCP** (user-level) if you prefer.
### 3. Enable / refresh the server
1. Open **Cursor Settings β MCP**
2. Find `drag-race-mcp`
3. Confirm it connects (no error state). Use refresh/restart if you changed code or config
4. Open a **new Agent chat** in this workspace so it picks up the tool list
### 4. Smoke-test a tool
Ask the agent something like:
> Call the `welcome_user` tool with my name.
If MCP is wired correctly, the agent will invoke `welcome_user` and return a welcome string from this server.
Thatβs the same path used in development: **Cursor discovers the serverβs tools over MCP, then the model can call them** (list tools β choose one β run with args β read the result).
### 5. After you add tools
1. Register the tool on the server (see `src/tools/general/welcome_user.ts`)
2. Restart/refresh the MCP server in Cursor
3. Start a new Agent chat
4. Ask the agent to use the new tool by name or describe the task and let it pick the tool
**Tips**
- Tool `description` and Zod `.describe(...)` text are what the model reads β keep them clear
- If tools donβt show up: check MCP error logs, path in `mcp.json`, and that `pnpm exec tsx src/index.ts` runs cleanly in a terminal
- Prefer a new chat after MCP restarts so the tool catalog is fresh
---
## MCP tools
Call `list_catalogs` when mapping names like `"france"` or `"latinas"` to codes. France the **show** is `franchise=FR`. Latin American **origin** is `originRegion=latin_america` (not Spain). Rankings run **inside the server** β do not loop `get_queen_earnings`.
| Tool | Description |
|------|-------------|
| `welcome_user` | Connectivity smoke test only β not for facts |
| `list_catalogs` | Franchise / region / origin-region / currency codes + aliases |
| `list_season_ids` | Loaded seasons; optional `franchise` (`FR`, `ES`, β¦) and/or `region` |
| `get_season` | Season record + linked IDs |
| `list_winners` | Crowned winner ids + `cashPrice` |
| `list_queen_ids` | Cast queen ids for one `seasonId` (required) |
| `search_queens` | Name/alias and/or `seasonId` / `franchise` / `region` / `originCountry` / `originRegion` |
| `get_queen` | Full queen record (includes `origin`) |
| `get_queen_earnings` | One queen's prize breakdown |
| `rank_queens_by_earnings` | In-process cash ranking per currency (no FX) |
| `rank_queens_by_stats` | Rank by challenge/mini/lip-sync wins or appearances |
| `get_queen_stats` | One queen: appearances, W-L, placements, cash by currency |
| `compare_queens` | Side-by-side stats for 2β4 queen ids |
| `list_returning_queens` | Queens with more than one contestant appearance |
| `list_porkchops` | First-outs from `season.porkchopIds` |
| `list_queens_as_judges` | Alumni panel + guest judges (`queenId` only) |
| `list_queens_as_hosts` | Alumni hosts (`queenId` only; not contestant appearances) |
| `get_queen_track_record` | Weekly outcomes for one queen on one season |
| `get_episode` | Episode detail |
| `search_episodes` | Substring on title, runway, challenge names |
| `get_lore` | Lore entry by id |
| `search_lore` | Search lore by query, tags, queen, and/or season |
**Skipped:** `recommend_season` β no ratings data.
---
## Roadmap
### v1
- [x] Stable TypeScript interfaces (IDs + hard facts + lore)
- [x] JSON data + Zod schemas
- [x] Core read tools (queen / season / episode / lore)
- [x] Local Cursor workflow documented
### v2
- [x] Origin on every queen + origin-region filters (`latin_america` β Spain)
- [x] Rankings / compare / stats aggregations
- [x] Alumni host/judge lists; porkchops; track records
- Broader franchise coverage in data
- Recommendations only if ratings data exists
### v3
- Semantic search (RAG)
- Community ratings / richer metadata
---
## Contributing
Contributions welcome β especially filling gaps in **[Data coverage](#data-coverage)**:
- Fix incorrect hard facts
- Add missing queens / seasons / episodes (use ID conventions)
- Add lore that links existing IDs
- Fill missing challenge / lip-sync `earnings` (and mirror queen win rows) for seasons listed under [Challenge prizes (Money coverage)](#challenge-prizes-money-coverage); follow `.cursor/skills/drag-race-data/SKILL.md` Money rules
- Mark the matching cell `β
` in this README when a slice is done
- New MCP tools & docs
### Pull requests & commits
Do **not** push straight to `main` (branch protection). Work on a branch and open a PR.
**Branch names**
- `feat/...` β new capability (types, tools, data for a season)
- `fix/...` β bug fix
- `docs/...` β README / comments only
- `chore/...` β tooling, deps, cleanup
**Commit / PR titles** (Conventional Commits style)
| Prefix | Use for |
|--------|---------|
| `feat:` | New feature or data/types that unlock new agent behavior |
| `fix:` | Bug fix |
| `docs:` | Documentation only |
| `chore:` | Maintenance (deps, config, formatting) |
| `refactor:` | Code change with no behavior change |
Examples:
- `feat: add Season and SeasonId types`
- `feat: add US-S06 season + cast data`
- `fix: correct porkchopIds for US-S01`
- `docs: update data coverage checklist`
**PR body** β short summary of *why*, plus a tiny test plan (e.g. `tsc`, which coverage cells flipped, MCP tool smoke test).
Open an issue or PR.
---
## License
MIT
---
## Acknowledgements
Built by the community, for the community. Drag Race fans, MCP builders, and AI enthusiasts β youβre welcome here.
> **Disclaimer:** Drag Race MCP is an unofficial fan project. It is not affiliated with, endorsed by, or sponsored by RuPaul, World of Wonder, or the Drag Race franchise.
TDQS
C2.2/5.0
Scored across 1 tool
Disambiguation5/5
With only one tool, there is no possibility of ambiguity or confusion between tools. The tool's purpose is distinct by default.
Naming Consistency5/5
The single tool name 'welcome_user' follows a clear verb_noun pattern. With only one tool, consistency is trivially maintained.
Tool Count1/5
The server is named 'drag-race-mcp' but contains only a generic 'welcome_user' tool, which is a trivial and mismatched utility. This represents an extreme mismatch between the server's implied purpose and its actual tool surface.
Completeness1/5
The server provides no functionality related to drag racing or any meaningful operations. A lone welcome tool is a dead end, leaving the domain completely uncovered.
Maintenance
ActivityMaintained
ResponsivenessNo issues