Labsco
ericraio logo

MTG MCP Servers

โ˜… 2

from ericraio

Magic: The Gathering (MTG) servers for deck management and card search using the MCP protocol.

๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅโœ“ VerifiedFreeAdvanced setup

MTG MCP Servers

A Swift implementation of Magic: The Gathering (MTG) Model Context Protocol (MCP) servers, providing deck management and card search capabilities through the MCP protocol.

Overview

This project provides two MCP servers:

  1. MTG Deck Manager (mtg-mcp) - Game state management including deck loading, card drawing, mulligans, and sideboarding
  2. Scryfall API Server (scryfall-mcp) - Card information retrieval using the Scryfall API

Both servers implement the MCP protocol for seamless integration with MCP-compatible clients like Claude Desktop.

Features

MTG Deck Manager

  • Deck Loading: Parse and load deck lists in various formats
  • Game State Management: Track deck, hand, and sideboard contents
  • Card Drawing: Draw cards from deck to hand with proper shuffling
  • Mulligan Support: London mulligan implementation
  • Sideboarding: Swap cards between main deck/sideboard and hand
  • Game Reset: Reset to initial game state
  • Statistics: Real-time deck and hand statistics

Scryfall API Server

  • Card Search: Advanced card search using Scryfall query syntax
  • Random Cards: Generate random cards with optional filters
  • Name Lookup: Find cards by exact or fuzzy name matching
  • Rich Card Data: Complete card information including images, prices, and legalities

Example Claude Prompts

Here are example prompts you can use with Claude Desktop once the MCP servers are configured:

๐ŸŽฎ Game Management Prompts

"Help me test my burn deck"

Load this burn deck and simulate an opening hand:

4 Lightning Bolt
4 Lava Spike  
4 Monastery Swiftspear
4 Eidolon of the Great Revel
20 Mountain
4 Fireblast

Sideboard:
3 Smash to Smithereens
2 Pyroblast

Then draw 7 cards and tell me what kind of opening hand I got.

"Simulate a mulligan decision"

I want to practice mulligan decisions. Load a competitive deck, draw a 7-card opening hand, and tell me if you think I should keep it or mulligan based on the cards drawn.

๐Ÿ“š Rules Learning Prompts

"Teach me about the stack"

I'm confused about how the stack works in Magic. Can you:
1. Look up the official rules about the stack
2. Explain how priority works with it
3. Give me an example of how spells and abilities resolve

"Combat damage rules"

Look up the official MTG rules about combat damage and explain:
- When damage is dealt
- How first strike works
- What happens with deathtouch
- How trample calculates excess damage

๐Ÿ” Card Research Prompts

"Find cards for my combo deck"

I'm building an artifact combo deck. Search for:
1. Red instants that cost 3 mana or less
2. Artifacts that produce mana
3. Cards that let me draw cards when artifacts enter

Show me the most relevant options.

"Random deck inspiration"

Give me 5 random cards and help me brainstorm a deck theme that could use all of them together.

๐Ÿ† Learning & Strategy Prompts

"Rules quiz me"

Quiz me on MTG rules! Look up a random rule number and ask me to explain what it means, then show me the official rule text to check my answer.

"Deck analysis"

Load this deck list and analyze it:
- What's the mana curve?
- What's the game plan?
- What rules should I know for the key cards?
- What are potential weaknesses?

[paste deck list here]

Deck Format Support

The deck parser supports multiple formats:

Standard Format

Deck
4 Lightning Bolt
4 Counterspell
20 Island

Sideboard
2 Negate
1 Spell Pierce

Commander Format

Commander
1 Atraxa, Praetors' Voice

Deck
1 Sol Ring
1 Command Tower
98 Forest

With Set Information

Deck
4 Lightning Bolt (LEA) 
4 Counterspell [7ED]
20 Island

Headerless Format

4 Lightning Bolt
4 Counterspell  
20 Island

Available MCP Tools

MTG Deck Manager Tools

ToolDescriptionParameters
upload_deckLoad a deck listdeck_list: String containing deck list
draw_cardDraw cards from deckcount: Number of cards to draw (default: 1)
view_handGet current hand contentsNone
view_deck_statsGet deck statisticsNone
play_cardPlay a card from handcard_name: Name of card to play
mulliganMulligan to new hand sizenew_hand_size: Size of new hand (optional)
sideboard_swapSwap cards with sideboardremove_card: Card to remove
add_card: Card to add
reset_gameReset game stateNone
lookup_ruleLook up specific MTG rulerule_number: Rule number (e.g., "100", "601.2a")
search_rulesSearch MTG ruleskeywords: Keywords to search (optional)
concept: Game concept (optional)
explain_conceptGet comprehensive rule explanationsconcept: Game concept to explain

Scryfall API Tools

ToolDescriptionParameters
search_cardsSearch for cardsquery: Scryfall search query
page: Page number (optional)
get_random_cardGet random cardquery: Filter query (optional)
lookup_card_exactFind card by exact nameexact: Exact card name
lookup_card_fuzzyFind card by fuzzy namefuzzy: Approximate card name

Rules Splitter Tool

ToolDescriptionParameters
rules-splitterDownload and split MTG rules into sectionsurl: Custom rules URL (optional)

Development

Project Structure

mtg-mcp/
โ”œโ”€โ”€ Sources/
โ”‚   โ”œโ”€โ”€ mtg-mcp/           # MTG Deck Manager server
โ”‚   โ”‚   โ””โ”€โ”€ mtg_mcp.swift  # Main server with rules integration
โ”‚   โ”œโ”€โ”€ scryfall-mcp/      # Scryfall API server
โ”‚   โ”‚   โ””โ”€โ”€ scryfall_mcp.swift
โ”‚   โ”œโ”€โ”€ rules-splitter/    # Rules processing tool
โ”‚   โ”‚   โ””โ”€โ”€ main.swift     # Rule file splitter CLI
โ”‚   โ”œโ”€โ”€ MTGModels/         # Shared models
โ”‚   โ”‚   โ”œโ”€โ”€ Card.swift
โ”‚   โ”‚   โ”œโ”€โ”€ GameState.swift
โ”‚   โ”‚   โ”œโ”€โ”€ ManaCost.swift
โ”‚   โ”‚   โ””โ”€โ”€ Rarity.swift
โ”‚   โ””โ”€โ”€ MTGServices/       # Shared services
โ”‚       โ”œโ”€โ”€ DeckParser.swift
โ”‚       โ””โ”€โ”€ RulesService.swift  # NEW: Rules lookup service
โ”œโ”€โ”€ Tests/
โ”‚   โ””โ”€โ”€ mtg-mcpTests/      # Comprehensive test suite
โ”‚       โ”œโ”€โ”€ CardModelTests.swift
โ”‚       โ”œโ”€โ”€ DeckParserTests.swift
โ”‚       โ”œโ”€โ”€ GameStateTests.swift
โ”‚       โ”œโ”€โ”€ IntegrationTests.swift
โ”‚       โ”œโ”€โ”€ TestDataLoader.swift
โ”‚       โ””โ”€โ”€ TestData/      # Sample deck files
โ”œโ”€โ”€ rules/                 # Generated MTG rules (144 files)
โ”‚   โ”œโ”€โ”€ 100_general.md
โ”‚   โ”œโ”€โ”€ 601_casting_spells.md
โ”‚   โ””โ”€โ”€ ... (142 more rule files)
โ””โ”€โ”€ Package.swift

Dependencies

  • MCP Swift SDK (0.9.0+) - MCP protocol implementation
  • SwiftGzip - Compression support for Scryfall API
  • ArgumentParser - Command-line argument parsing

Running Tests

# Run all tests
swift test

# Run specific test suite
swift test --filter CardModelTests

# Run tests with verbose output
swift test --verbose

Code Style

The project follows Swift best practices:

  • Swift actors for thread-safe game state management
  • Sendable protocol conformance for concurrent access
  • Comprehensive error handling with Result types
  • Async/await patterns throughout
  • Unit tests with >90% coverage

Architecture

Thread Safety

Game state is managed using Swift's actor system, ensuring thread-safe access to mutable state across concurrent MCP tool calls.

Error Handling

Comprehensive error handling using Swift's Result type and custom error types for different failure scenarios.

MCP Integration

Both servers implement the MCP protocol using the official Swift SDK, providing:

  • Tool registration and discovery
  • Request/response handling
  • Transport abstraction (stdio, HTTP)
  • Proper error propagation

API Reference

Card Model

public struct Card: Identifiable, Equatable, Hashable, Codable, Sendable {
    public let id: String
    public var name: String
    public var manaCostString: String
    public var kind: CardKind
    public var rarity: Rarity
    public var typeLine: String
    public var oracleText: String
    public var power: String?
    public var toughness: String?
    public var loyalty: String?
}

Game State Actor

public actor GameState {
    public func loadDeck(_ deckData: DeckData) async
    public func drawCards(count: Int) async -> [Card]
    public func playCard(named cardName: String) async -> Card?
    public func mulligan(newHandSize: Int) async -> [Card]
    public func sideboardSwap(removeCard: String, addCard: String) async -> (removed: Card?, added: Card?)
    public func resetGame() async
    public func getDeckStats() async -> DeckStats
    public func getHandContents() async -> [String: Int]
}