{
  "markdown": "# startgg-mcp-server\n\nA [Model Context Protocol](https://modelcontextprotocol.io) server for the\n[start.gg](https://www.start.gg) GraphQL API. It lets MCP clients (Claude Code,\nClaude Desktop, and others) discover tournaments, inspect events, entrants,\nsets, standings, and streams for **any game on start.gg** using natural\nlanguage.\n\n## What is this?\n\nstart.gg exposes a powerful but complex GraphQL API: entrants vs participants\nvs players, integer set states, complexity-limited pagination, epoch\ntimestamps. This server wraps that API in a small set of MCP tools with:\n\n- **Normalized output** — sets come back as `{ round, state: \"COMPLETED\", entrant1: { gamerTag, seed }, score, winnerEntrantId, ... }` instead of raw GraphQL nesting\n- **URL resolution** — paste a start.gg URL, get tournament/event ids back\n- **Built-in rate limiting, retries, and caching** tuned to start.gg's documented limits\n\nThe server is game-agnostic. Game-specific logic (e.g. Smash upset detection)\nbelongs in applications built on top — see\n[`examples/smash-ultimate-watcher`](examples/smash-ultimate-watcher/).\n\n## Features\n\n- 15 read-only tools covering discovery, tournaments, events, players, streams, and URL resolution\n- Input validation (Zod) on every tool — bad ids, oversized page sizes, and malformed URLs never reach the API\n- Sliding-window rate limiter (default 75 req/60s vs start.gg's 80), retries with exponential backoff, and `Retry-After` support\n- Short-TTL in-memory cache for metadata queries\n- Typed error codes: `AUTH_ERROR`, `RATE_LIMITED`, `NOT_FOUND`, `INVALID_INPUT`, `STARTGG_GRAPHQL_ERROR`, `NETWORK_ERROR`, `INTERNAL_ERROR`\n- GraphQL documents kept in [`graphql/`](graphql/) files, separate from code\n- The API token never appears in output, logs, or error messages\n\n## Requirements\n\n- Node.js >= 22\n- A start.gg API token\n\n## Getting a start.gg API token\n\n1. Log in to start.gg\n2. Open **[developer settings](https://start.gg/admin/profile/developer)** (Profile → Developer Settings)\n3. Create a personal access token and copy it\n\nTreat the token like a password. This server reads it only from the\n`STARTGG_TOKEN` environment variable.\n\n## Installation\n\n```bash\ngit clone https://github.com/tomo789/startgg-mcp-server.git\ncd startgg-mcp-server\nnpm install\nnpm run build\n```\n\n## MCP client setup\n\n### Claude Code (CLI)\n\n```bash\nclaude mcp add startgg --env STARTGG_TOKEN=YOUR_TOKEN -- node /path/to/startgg-mcp-server/dist/cli.js\n```\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"startgg\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/startgg-mcp-server/dist/cli.js\"],\n      \"env\": {\n        \"STARTGG_TOKEN\": \"YOUR_TOKEN\"\n      }\n    }\n  }\n}\n```\n\nAny MCP client that supports stdio servers works the same way: run\n`node dist/cli.js` (or the `startgg-mcp-server` bin once installed via npm)\nwith `STARTGG_TOKEN` set.\n\n## Available tools\n\n### Discovery\n\n| Tool                           | Purpose                                                                                                 |\n| ------------------------------ | ------------------------------------------------------------------------------------------------------- |\n| `search_videogames`            | Find videogame ids by name (e.g. \"Super Smash Bros. Ultimate\" → 1386)                                   |\n| `search_tournaments`           | General tournament search: name, videogame, country/state, date range, upcoming/past, open registration |\n| `get_upcoming_tournaments`     | Tournaments that haven't ended yet (includes in-progress), soonest first, with a days window            |\n| `get_tournaments_by_videogame` | Tournaments for one videogame id (upcoming / past / all)                                                |\n\n### Tournament\n\n| Tool                      | Purpose                                                                                    |\n| ------------------------- | ------------------------------------------------------------------------------------------ |\n| `get_tournament`          | Details, schedule, venue, events list, configured streams                                  |\n| `get_tournament_events`   | Events (brackets) of a tournament, optionally filtered by videogame                        |\n| `get_tournament_entrants` | Tournament-level participants (attendees); per-event seeding lives in `get_event_entrants` |\n| `get_stream_queue`        | Stream queue: streams (with derived Twitch URLs) and the sets assigned to each             |\n\n### Event\n\n| Tool                  | Purpose                                                                |\n| --------------------- | ---------------------------------------------------------------------- |\n| `get_event`           | Event details including phases (Pools, Top 8, ...) with phase ids      |\n| `get_event_entrants`  | Entrants with seed, players, DQ flag; pagination or `fetchAll`         |\n| `get_event_standings` | Placements (use `perPage: 8` for Top 8)                                |\n| `get_event_sets`      | Normalized sets; filter by state, phase, round, entrants, VOD presence |\n\n### Player\n\n| Tool              | Purpose                                      |\n| ----------------- | -------------------------------------------- |\n| `get_player`      | Player by id: gamer tag, prefix, linked user |\n| `get_player_sets` | A player's recent sets across tournaments    |\n\n### Utility\n\n| Tool                  | Purpose                                                             |\n| --------------------- | ------------------------------------------------------------------- |\n| `resolve_startgg_url` | start.gg URL/slug → `{ type, tournamentId, eventId, slugs, names }` |\n\nTournament/event tools accept **either** a numeric id, a slug, or a full\nstart.gg URL — you rarely need `resolve_startgg_url` explicitly, but it is\nthere when you want the ids.\n\n### Normalized set shape\n\n```json\n{\n  \"id\": 106877974,\n  \"round\": \"Grand Final\",\n  \"roundNumber\": 3,\n  \"state\": \"COMPLETED\",\n  \"stateRaw\": 3,\n  \"completedAt\": \"2026-08-24T07:19:34.000Z\",\n  \"entrant1\": {\n    \"entrantId\": 24480092,\n    \"name\": \"LittleMacMain\",\n    \"seed\": 5,\n    \"players\": [{ \"playerId\": 3655189, \"gamerTag\": \"LittleMacMain\", \"prefix\": \"\" }],\n    \"score\": 2\n  },\n  \"entrant2\": { \"...\": \"same shape\" },\n  \"score\": { \"entrant1\": 2, \"entrant2\": 3, \"displayScore\": \"LittleMacMain 2 - RenSuø 3\" },\n  \"winnerEntrantId\": 24481002,\n  \"phase\": { \"id\": 1994001, \"name\": \"Bracket\" },\n  \"vodUrl\": null\n}\n```\n\nNotes grounded in the live API:\n\n- `roundNumber < 0` means losers bracket; `round` is the human-readable name\n- a score of `-1` is start.gg's disqualification marker\n- unstarted \"preview\" sets have **string** ids like `\"preview_3430499_2_0\"`\n- `state` names are decoded from the integer `stateRaw`; both are always returned\n- `entrant1`/`entrant2` use a `players` array, so doubles/teams work unchanged\n\n## Examples\n\nThings to ask an MCP client once connected:\n\n```text\nFind upcoming Super Smash Bros. Ultimate tournaments this week.\n\nGet the entrants and seeds for this start.gg tournament URL:\nhttps://www.start.gg/tournament/.../event/...\n\nShow me completed sets from Top 8 of that event.\n\nWhich streams are assigned to sets at this tournament?\n\nWhat were the biggest seed upsets in this event?\n```\n\nA standalone example application (videogame lookup → upcoming tournaments →\nsets → upset candidates by seed difference) lives in\n[`examples/smash-ultimate-watcher`](examples/smash-ultimate-watcher/).\n\n## Environment variables\n\n| Variable                | Required | Default | Purpose                                                         |\n| ----------------------- | -------- | ------- | --------------------------------------------------------------- |\n| `STARTGG_TOKEN`         | yes      | —       | start.gg API token                                              |\n| `STARTGG_ENABLE_WRITES` | no       | `false` | Reserved. No write tools exist yet; the flag only logs a notice |\n| `STARTGG_RATE_LIMIT`    | no       | `75`    | Requests per 60s window (hard-capped at 80)                     |\n| `STARTGG_TIMEOUT_MS`    | no       | `30000` | Per-request HTTP timeout                                        |\n| `STARTGG_CACHE`         | no       | `on`    | Set `off` to disable the in-memory cache                        |\n\nThe API endpoint is deliberately not configurable through the environment: the\ntoken is only ever sent to `api.start.gg`. When using the client as a library\n(tests, tooling), inject `apiUrl`/`fetchFn` via the `StartggClient` constructor.\n\nWithout `STARTGG_TOKEN` the server still starts and lists tools, but every\ncall returns a clear `AUTH_ERROR` explaining how to fix it.\n\n## Security\n\n- The token is read from the environment only, sent only to `api.start.gg`, and never included in tool output, logs, or error messages\n- All tools are read-only; no mutations are implemented\n- `.env` files are git-ignored; use `.env.example` as a template\n- User-supplied input is schema-validated before any request is built\n\n## Rate limits\n\nstart.gg allows **80 requests per 60 seconds** and at most **1000 objects per\nrequest**. This server:\n\n- keeps a sliding-window budget below the request limit (default 75/60s)\n- retries `429` (honoring `Retry-After`) and transient 5xx errors with exponential backoff, at most 3 retries — GraphQL errors are never retried\n- caps `perPage` per tool so responses stay under the 1000-object complexity limit (sets are expensive: ~26+ objects each, hence `perPage <= 30`)\n- caps `fetchAll` at a fixed page budget and reports `truncated: true` when it stops early\n\n## Development\n\n```bash\nnpm run dev        # run from source (tsx)\nnpm run build      # compile to dist/\nnpm run typecheck  # tsc --noEmit\nnpm run lint       # eslint\nnpm run format     # prettier\n```\n\nGraphQL documents live in `graphql/*.graphql` (one file per domain, multiple\nnamed operations per file; requests select an operation via `operationName`).\nSchema facts verified against the live API are recorded in\n[`docs/startgg-api-notes.md`](docs/startgg-api-notes.md) — read it before\nadding fields.\n\n## Testing\n\n```bash\nnpm test                    # unit tests (fixtures/mocks only, no network)\nSTARTGG_INTEGRATION=1 STARTGG_TOKEN=... npm test   # + 2 live API smoke tests\nSTARTGG_TOKEN=... node scripts/smoke.mjs           # full stdio end-to-end smoke (~10 live requests)\n```\n\nUnit tests cover the URL resolver, normalizers, input validation, pagination,\nGraphQL/HTTP error handling, the rate limiter, and the cache.\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 10591,
  "sha": "bc1d1e5dff7d411f5cf24849523931208778164a4b380b5c1b915619ca3d7d0a",
  "repo_slug": "tomo789/startgg-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_tomo789_startgg_mcp_server_6f585fe9/readme"
}