Scryfall-backed MCP server for Magic: The Gathering search, rules lookup, pricing, set discovery, and deckbuilding workflows.
The project currently supports:
stdioas the primary transport for local MCP clients- local-first Streamable HTTP via
src/http.ts - 14 MCP tools, 2 resources, and 2 prompts
search_cards: Run Scryfall card searches with paging, sorting, and optional price filtering.get_card: Fetch one card by name, set/collector number, or Scryfall ID.get_card_prices: Return price data with optional format context and alternatives.random_card: Get a random card with optional filters.search_sets: Search and filter Magic sets.query_rules: Search the local comprehensive rules file with context.build_scryfall_query: Convert natural language into an explainable Scryfall query.search_format_staples: Find staples and role players for a format.search_alternatives: Find cheaper, upgraded, or similar cards.find_synergistic_cards: Find synergy pieces for a card, theme, or archetype.batch_card_analysis: Analyze multiple cards for legality, prices, synergy, or composition.validate_brawl_commander: Check Brawl and Standard Brawl commander legality.analyze_deck_composition: Evaluate deck lists for curve, colors, and structural issues.suggest_mana_base: Recommend land counts and fixing packages from color requirements.
card-database://bulk: Cached Oracle bulk snapshot.set-database://all: Cached set list snapshot.
analyze_cardbuild_deck
Recommended for Claude Desktop, Codex, MCP Inspector, and most local MCP clients.
npm run devnpm startAvailable as a separate entrypoint for local or explicitly controlled environments.
npm run dev:httpnpm run start:httpFor local MCP testing:
npm run dev:http:local
npm run smoke:httpThe smoke test checks /health, performs MCP initialize, sends notifications/initialized, and verifies tools/list.
It also makes representative validate_brawl_commander and search_cards tool calls so the local HTTP endpoint is checked beyond discovery.
Current HTTP behavior:
- binds to
127.0.0.1by default - serves
POST|GET|DELETEon/mcp - serves
GET /health - rejects non-loopback
Originheaders by default unlessHTTP_ALLOWED_ORIGINSis set
The HTTP entrypoint is useful today, but it is still documented conservatively. It is not presented here as a public-hosting story.
- Node.js 18+
- npm
git clone https://github.com/bmurdock/scryfall-mcp.git
cd scryfall-mcp
npm install
cp .env.example .envnpm run lint
npm run type-check
npm testnpm run buildnpm run dev
npm run dev:http
npm start
npm run start:http
npm test
npm run test:watch
npm run test:ui
npm run lint
npm run type-check
npm run inspectorSee .env.example for the canonical values. The main variables in active use are:
SCRYFALL_USER_AGENTRATE_LIMIT_MSRATE_LIMIT_QUEUE_MAXSCRYFALL_TIMEOUT_MSCACHE_MAX_SIZECACHE_MAX_MEMORY_MBLOG_LEVELNODE_ENVHEALTHCHECK_DEEPHTTP_HOSTHTTP_PORTHTTP_MCP_PATHHTTP_HEALTH_PATHHTTP_ALLOWED_ORIGINS
Operational notes:
- Scryfall API calls are globally serialized by the shared rate limiter. Batch tools may schedule multiple local lookups, but upstream Scryfall request completion remains one-at-a-time by design.
- HTTP 429 responses are not retried automatically. The server records Scryfall's throttle window and delays the next request start so callers can decide whether to retry.
CACHE_MAX_MEMORY_MBcontrols whether large in-memory snapshots, includingcard-database://bulk, can be retained. Oversized bulk snapshots can still be returned for the current read, but metadata will report that they were not cached.- Deck-list analysis resolves card names exactly first, then falls back to fuzzy lookup for exact misses and reports any fuzzy resolutions in the response.
- Deck-scale tools may return partial analysis or an explicit retry-after message when Scryfall throttles the underlying card lookups.
Example local HTTP startup:
HTTP_HOST=127.0.0.1 HTTP_PORT=3000 npm run start:http{
"natural_query": "blue counterspells under $20 for modern",
"optimize_for": "precision"
}{
"query": "c:r t:instant mv=1",
"limit": 10,
"order": "name"
}{
"type": "expansion",
"released_after": "2020-01-01"
}{
"focus_card": "Obeka, Splitter of Seconds",
"synergy_type": "theme",
"format": "commander",
"color_identity": "UBR",
"limit": 12
}For commander-like workflows, pass color_identity when the focus is a theme rather than a resolvable card. When the focus resolves to a card, the tool infers that card's color identity and filters final results by requested legality, Arena availability, and color identity.
{
"deck_list": "4 Lightning Bolt\n4 Monastery Swiftspear\n20 Mountain",
"format": "modern",
"strategy": "aggro"
}Add the built stdio entrypoint to your Claude Desktop configuration.
macOS path: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows path: %APPDATA%/Claude/claude_desktop_config.json
{
"mcpServers": {
"scryfall": {
"command": "node",
"args": ["/absolute/path/to/scryfall-mcp/dist/index.js"]
}
}
}- Rate limiting is enforced in-process with a 100 ms default minimum interval between Scryfall requests.
- Search responses, card details, prices, sets, and bulk snapshots are cached with bounded in-memory limits.
- The bulk card resource stores a pre-serialized snapshot to keep repeated reads cheap.
- Set filtering is derived from one canonical cached
/setsdataset to avoid incorrect filtered cache reuse. - Health checks are available through
ScryfallMCPServer.healthCheck()and the HTTP/healthendpoint. - If an MCP connector reports a JSON-RPC/SSE deserialization error, compare it against the raw HTTP smoke path:
npm run dev:http:local
npm run smoke:httpIf the smoke command succeeds, capture the connector error text and the smoke output together; that separates local endpoint framing from connector-specific parsing.
Current source-of-truth docs:
MIT