Skip to main content
Glama
piltover-archive

piltover-archive

by Liaxum

PiltoverArchives-mcp

An MCP server for the Piltover Archive API — a community card database, deck builder and proxy generator for the Riftbound TCG.

It exposes card search, card lookup, set listing and decklists as MCP tools, so an assistant can answer questions about Riftbound cards it otherwise knows nothing about.

Why this exists

Riftbound is recent enough that its card data is not in any language model's training data. Ask an assistant what Vi, Destructive costs and you get a guess. This server closes that gap by giving it the real database.

Related MCP server: Scryfall MCP Server

Install

git clone https://github.com/Liaxum/PiltoverArchives-mcp.git
cd PiltoverArchives-mcp
npm install
npm run build

Claude Code

claude mcp add piltover-archive -- node /absolute/path/to/PiltoverArchives-mcp/dist/index.js

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "piltover-archive": {
      "command": "node",
      "args": ["/absolute/path/to/PiltoverArchives-mcp/dist/index.js"]
    }
  }
}

No credentials are needed. See Authentication — supplying an API key currently makes things worse, not better.

Tools

Tool

What it does

piltover_search_cards

Search cards by text, colour, type, set, rarity, tag, and energy/power/might ranges

piltover_get_cards

Resolve known cards in one request by collector number, exact name, or UUID

piltover_list_sets

List every set with its prefix and release date

piltover_search_decks

Search public decklists by name, description or legend

piltover_get_deck

Fetch one decklist, resolving card UUIDs into real card names

All tools are read-only and take a response_format of markdown (default) or json.

piltover_get_deck is the one that earns its keep: the API stores deck contents as bare UUIDs with no names attached, so the tool batch-resolves every one of them and renders a readable list.

## Main deck (39)
- 3x Charm [OGN-043] — Spell, 1 energy, Calm
- 3x B.F. Sword [SFD-161] — Gear, 4 energy, Order

Configuration

Variable

Default

Purpose

PILTOVER_API_BASE_URL

https://piltoverarchive.com/api/external/v1

Override the API base

PILTOVER_API_KEY

(unset)

Optional bearer token. Leave unset — see below

Development

npm run build     # compile TypeScript
npm run inspect   # run against the MCP Inspector

API reference

The rest of this document records what was learned about the upstream API. It is kept because none of it is officially documented anywhere.

How this was determined

Piltover Archive publishes no API documentation. There is no docs. or developers. host, no OpenAPI/Swagger document, no developer section on the site, and nothing indexed by search engines.

Everything below was reverse-engineered from the production Next.js client bundles (https://piltoverarchive.com/_next/static/chunks/*.js, notably the API client in module 491560) and then verified with live requests. Where something was inferred but not confirmed against the running API, it is marked as such.

Because none of this is a published contract, it may change without notice. Treat it as observed behaviour, not a stable interface.


Base URL

The API is not on a separate host. It is a route inside the main Next.js app:

https://piltoverarchive.com/api/external/v1

The client bundle reads a server-side EXTERNAL_API_URL environment variable and falls back to the relative path /api/external, which suggests the backend may also be reachable directly on another origin. That origin is not public; api.piltoverarchive.com does not resolve.

Infrastructure observed from response headers: Cloudflare in front of Railway (x-railway-edge: iad1), with Clerk for user authentication (x-clerk-auth-status).

Authentication

Bearer tokens only:

Authorization: Bearer <token>

An x-api-key header is not read — sending the key that way is treated as no credential at all.

Two kinds of bearer token exist. The web app sends a Clerk session token for signed-in users. Programmatic clients are expected to send an API key with an ak_ prefix.

Important: the read endpoints are public

/v1/cards, /v1/decks and /v1/sets return 200 with no credentials at all. A card-data MCP server needs no authentication to be useful.

The ak_ API key and partner status

An ak_-prefixed API key was tested against this API. The results are worth recording, because the failure mode is misleading:

Request

Response

Deliberately invalid ak_… token

401 INVALID_API_KEY — "Invalid API key"

The real ak_… key

401 PARTNER_REQUIRED — "Partner identification required"

The real key on /v1/partners

403 ADMIN_REQUIRED — "Admin access required"

The key is valid and recognised — it produces a different error than a forged one, and it passes the authentication gate on /v1/partners only to fail the authorization gate. What it lacks is an association with a partner record on the server side.

The practical consequence is that sending the key is currently worse than sending nothing:

Endpoint

No auth

With the ak_ key

GET /v1/cards

200

401 PARTNER_REQUIRED

GET /v1/decks

200

401 PARTNER_REQUIRED

GET /v1/sets

200

401 PARTNER_REQUIRED

GET /v1/notifications

401

401 PARTNER_REQUIRED

GET /v1/collection/stats

401

401 PARTNER_REQUIRED

Authenticating downgrades otherwise-public endpoints to 401 and unlocks nothing.

This is not a missing request header. X-Partner-Id, X-Partner and X-Partner-Key were each tried alongside the bearer token and all returned the identical PARTNER_REQUIRED. Partner association has to be granted server-side. The app has an internal /admin/partners route, a checkIsPartner server action and a usePartnerStatus hook, and exposes "Partner" as an account tier alongside admin and supporter — so this is an account flag the Piltover Archive maintainers set, reachable through their help channels (email or Discord).

Recommendation for this server: build against the public endpoints and do not send a key by default. Put the Authorization header behind an opt-in setting so it can be switched on if and when partner access is granted.

Response envelope

List endpoints return:

{
  "data": [ ... ],
  "pagination": {
    "total": 1238, "page": 1, "limit": 1,
    "totalPages": 1238, "hasNext": true, "hasPrevious": false
  },
  "meta": { "filters": { "colors": [ { "id": "…", "name": "Body", "count": 223 } ] } }
}

meta.filters carries facet counts for the current result set, which is handy for exposing filter options without a second request.

Errors are JSON: {"error": "CODE", "message": "…"}. Some responses also carry a requestId. Validation failures use a different shape: {"type":"validation","on":"params","found":{…}} with status 422.

Observed error codes: INVALID_API_KEY, AUTH_REQUIRED, PARTNER_REQUIRED, ADMIN_REQUIRED, INVALID_REQUEST, PREMIUM_REQUIRED, NOT_FOUND.

Endpoints

Cards

GET  /v1/cards               # search — public
POST /v1/cards/batch         # bulk lookup — public

/v1/cards/batch accepts at least one of variantNumbers, names or ids:

curl -X POST https://piltoverarchive.com/api/external/v1/cards/batch \
  -H 'Content-Type: application/json' \
  -d '{"variantNumbers":["ARC-001"]}'

Search parameters, taken from the client's own parameter normaliser (urlToCardSearchParams) and confirmed working:

Kind

Parameters

Notes

Free text

q, name, description, artist, flavor

trimmed, truncated to 200 chars

Identity

id

truncated to 64 chars

Multi-value

colors, sets, types, supertypes, variants, excludeVariants, rarities, tags, artworks, releaseDateAfter

comma-separated, truncated to 500 chars

Ranges

energyMin/energyMax, powerMin/powerMax, mightMin/mightMax, qtyMin/qtyMax

non-negative integers

Sorting

sortBy, or sort + dir (asc|desc)

sortBy takes precedence

Paging

page (≥1), limit (≥1, capped at 100)

Convenience

new=true

client-side sugar that sets releaseDateAfter to a "new card" cutoff

Example — ?q=Vi&limit=2&sort=name&dir=asc returns 56 matches; ?energyMin=5 returns 313.

A card variant looks like:

{
  "id": "a60d2063-…", "variantNumber": "ARC-001",
  "rarity": "Showcase", "variantType": "Promo", "foilMode": "foil_only",
  "imageUrl": "https://piltoverarchive.b-cdn.net/…webp",
  "artist": "Fortiche Production", "releaseDate": "2025-10-14",
  "variantLabel": "Arcane Box Promo",
  "showInLibrary": true, "isCollectible": true,
  "cardmarketId": 858987, "tcgplayerId": 678053,
  "cardmarketPrice": 94.35, "tcgplayerPrice": 97.16,
  "set":  { "id": "…", "name": "Arcane Box Set", "prefix": "ARC", "releaseDate": "2025-12-31" },
  "card": {
    "id": "…", "name": "Vi, Destructive",
    "types": ["Unit"], "type": "Unit", "super": "Champion",
    "description": "[GANKING] …", "energy": 2, "might": 3, "power": 1,
    "tags": ["Vi", "Piltover"], "maxCopies": null, "banEffectiveDate": null,
    "colors": [ { "id": "…", "name": "Fury", "hexCode": "#CB222D", "imageUrl": "…" } ]
  }
}

Note the card/variant split: a card is the game object, and the top level is a printing of it. Market prices come from Cardmarket and TCGplayer.

Sets

GET /v1/sets                 # public

Returns { id, name, prefix, releaseDate, imageUrl }, e.g. Origins | Nexus Night (OGN-NN).

Decks

GET    /v1/decks             # list — public
GET    /v1/decks/{uuid}      # detail
PATCH  /v1/decks/{uuid}
DELETE /v1/decks/{uuid}
GET    /v1/decks/{uuid}/likes
POST   /v1/decks/{uuid}/like
DELETE /v1/decks/{uuid}/like

{uuid} must be a UUID — /v1/decks/me returns 422.

Deck list parameters differ from card search in two ways worth knowing:

  • The request parameter is limit, but the response reports it back as pageSize. Sending pageSize is ignored and you silently get the default 20.

  • q searches deck name, description and legend name, so a champion name finds decks led by that champion even when the title never mentions it.

  • sort + dir order the results; sortBy is ignored here (unlike card search).

  • total appears to cap at 10000.

Deck contents are stored as bare identifiers — {cardId, variantId, quantity} — with no card names. Sections are champions, battlefields, runes, maindeck, sideboard and bench. To render a decklist you must collect the ids and resolve them through POST /v1/cards/batch with ids.

Deck export

All POST, all taking a deckCode plus an options object. image and proxies return a binary blob rather than JSON; text returns { "text": … }.

POST /v1/decks/export/image           # deck image (PNG)
POST /v1/decks/export/proxies         # printable proxy PDF
POST /v1/decks/export/registration    # tournament registration sheet
POST /v1/decks/export/tts             # Tabletop Simulator
POST /v1/decks/export/text            # plain text list

Export options seen in the client include expandCards, showQR, showWatermark, sortBy (as {field, order} pairs) and per-section toggles (legend, battlefields, runes). Some option combinations return PREMIUM_REQUIRED. Only the image export passes a bearer token.

Authenticated / restricted

Reachable but gated; not usable without partner or admin status:

GET    /v1/notifications                       # AUTH_REQUIRED
GET    /v1/collection/stats                    # AUTH_REQUIRED
GET    /v1/collection/export
GET    /v1/collection/sets/{id}/details
GET    /v1/collection/users/{id}/ownership
PATCH  /v1/collection/{id}
PATCH  /v1/collection/binders/{id}
DELETE /v1/collection/{id}
DELETE /v1/collection/binders/{id}
GET    /v1/partners/{id}
GET    /v1/partners                            # ADMIN_REQUIRED
PATCH  /v1/admin/decks/{id}/featured           # admin only

/v1/users and /v1/news return a bare 403 {"error":"Forbidden"}. /v1/tournaments returns NOT_FOUND, despite tournaments existing in the product — that data is presumably served another way.

Implementation status

The tools listed at the top of this document are implemented and verified against the live API. All of them use unauthenticated endpoints.

Collection and notification tools are not implemented: they require partner access that the current key does not have. They can be added once Piltover Archive grants it, without changing anything already built.

Caveats

  • None of this is a published API. It can change at any time, without notice.

  • Be a good citizen: the endpoints are public but not advertised as such. Cache responses, respect the limit cap of 100, and do not hammer the service.

  • Card data and images are Riot Games intellectual property, served by a community project. This repository is unaffiliated with both.

Available Tools

5 tools
piltover_get_cardsLook up Riftbound cards by identifierA
Read-onlyIdempotent

Resolve known Riftbound cards in one request, by collector number, exact name, or UUID.

Provide at least one of variantNumbers, names or ids; they can be combined. This is the right tool for turning the cardId/variantId values inside a deck into real card names, and for looking up a list of cards without one search per card.

Args:

  • variantNumbers: collector numbers, e.g. ["OGN-007"]

  • names: exact card names, e.g. ["Vi, Destructive"]

  • ids: card or variant UUIDs

  • response_format: 'markdown' (default) or 'json'

Examples:

  • "What is OGN-007?" -> { variantNumbers: ["OGN-007"] }

  • Resolving a decklist -> { ids: ["0283d70f-…", "f40b0e74-…"] }

  • Don't use when: you are searching by criteria rather than identity — use piltover_search_cards.

Inputs: ids, names, variantNumbers, response_format.

piltover_get_deckGet a Riftbound decklistA
Read-onlyIdempotent

Fetch one deck by ID and return its full contents.

The API stores deck contents as bare card UUIDs, so by default this tool resolves every one of them into card names, types, costs and colours — that resolution is the point of the tool, and it costs a few extra requests. Set resolve_cards=false for the raw id form.

Sections returned: champions, battlefields, runes, main deck, sideboard, bench.

Args:

  • deck_id: the deck UUID, as returned by piltover_search_decks

  • resolve_cards: resolve card UUIDs to names (default true)

  • response_format: 'markdown' (default) or 'json'

Examples:

  • "What's in deck ae198cdf-…?" -> { deck_id: "ae198cdf-…" }

  • Don't use when: you only need deck metadata for many decks — piltover_search_decks already returns that.

Inputs: deck_id (required), resolve_cards, response_format.

piltover_list_setsList Riftbound setsA
Read-onlyIdempotent

List every Riftbound card set, with its prefix and release date.

Set prefixes (e.g. OGN, ARC) are what the sets filter of piltover_search_cards expects, so call this first when a question names a set in words ("the Arcane box") rather than by prefix.

Args:

  • response_format: 'markdown' (default) or 'json'

Examples:

  • "What Riftbound sets exist?" -> {}

  • "What's the prefix for Origins?" -> {} then read the table

Inputs: response_format.

piltover_search_cardsSearch Riftbound cardsA
Read-onlyIdempotent

Search the Piltover Archive database of Riftbound trading card game cards.

Riftbound is a Riot Games TCG whose card data is not part of any model's training data, so use this tool rather than answering from memory whenever a specific card, cost, rules text or price is involved.

Filters combine with AND. Every filter is optional; with none, it lists all cards. Results are card printings (variants), each carrying the game card under card.

Args:

  • q: free-text across name, rules text, flavour and artist

  • name / description / artist / flavor: search one field only

  • colors / sets / types / supertypes / rarities / tags: multi-value filters

  • energyMin|Max, powerMin|Max, mightMin|Max: numeric ranges

  • releaseDateAfter: ISO date lower bound

  • sort + dir: ordering; page + limit: pagination (limit max 100)

  • response_format: 'markdown' (default) or 'json'

Examples:

  • "What does Vi, Destructive do?" -> { name: "Vi, Destructive" }

  • "Cheap Fury units" -> { colors: ["Fury"], types: ["Unit"], energyMax: 2 }

  • "Most expensive Showcase cards" -> { rarities: ["Showcase"], sort: "cardmarketPrice", dir: "desc" }

  • Don't use when: you already have exact collector numbers or IDs — use piltover_get_cards, which is one request instead of many.

Inputs: q, dir, name, page, +21 more.

piltover_search_decksSearch Riftbound decksA
Read-onlyIdempotent

Search public decklists published on Piltover Archive.

Useful for seeing how a champion or archetype is actually built, and what the community is playing right now.

Args:

  • q: free-text across deck name, description and legend name, so a champion name finds decks led by that champion even when the title never says it

  • sort: field to order by, e.g. likes, views, createdAt (pair with dir)

  • dir: 'asc' or 'desc'

  • page, limit: pagination (limit max 100)

  • response_format: 'markdown' (default) or 'json'

Examples:

  • "Popular Akali decks" -> { q: "Akali", sort: "likes", dir: "desc" }

  • "Newest decks" -> { sort: "createdAt", dir: "desc" }

Returns deck summaries including a deck ID; pass that to piltover_get_deck for the full card list.

Inputs: q, dir, page, sort, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.6/5.0

    Scored across 5 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose: search vs. identity lookup for cards, list sets, search vs. fetch for decks. The descriptions even include explicit 'Don't use when' guidance steering between the card-search and card-get pair, which is the only plausible overlap.

    Naming Consistency5/5

    All five tools follow a strict piltover_<verb>_<noun> snake_case pattern (search_cards, get_cards, list_sets, search_decks, get_deck). Search/get/list verbs are used consistently and distinguish the operations predictably.

    Tool Count5/5

    Five tools is well-scoped for a read-only card and deck archive: two for cards, one for sets, two for decks. No bloat, and no single tool is doing unrelated work.

    Completeness4/5

    The surface covers the core lifecycle for a read-only archive: card search, identity resolution, set enumeration, deck search, and full deck retrieval, with resolution of deck UUIDs built in. Minor gaps remain (e.g. no explicit rulings/price-history or set-contents browsing beyond the search filter), but these are workable via piltover_search_cards.

    Maintenance

    ActivityStale
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Enables Claude to search and retrieve Magic: The Gathering card details, prices, set information, and random cards from Scryfall's database through natural language.
      4
      2
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Provides AI assistants with access to Magic: The Gathering card data via Scryfall API, enabling card search, image downloads, and database management.
      60 npm
      2
      Apache 2.0
    • F
      license
      A
      quality
      D
      maintenance
      Provides AI assistants with real-time access to Magic: The Gathering card data, prices, rulings, search, and synergy discovery via the Scryfall API.
      5
      -