Skip to main content
Glama

MTG Deck Manager MCP Servers

by artillect
mtg_server.py7.64 kB
from mcp.server.fastmcp import FastMCP import random from typing import List, Dict, Any, Optional # Initialize FastMCP server mcp = FastMCP("mtg-manager") # State management state = { "deck": [], "sideboard": [], "hand": [] } # Helper functions def parse_deck_list(deck_text: str) -> Dict[str, List[Dict[str, Any]]]: """Parse a deck list into main deck and sideboard card objects.""" main_deck = [] sideboard = [] lines = deck_text.strip().split('\n') current_section = None for line in lines: line = line.strip() if not line: continue # Check for section headers if line.lower() == "deck": current_section = "main" continue elif line.lower() == "sideboard": current_section = "side" continue # Skip if no section defined yet if current_section is None: continue # Try to parse card entry (number + name) parts = line.split(' ', 1) if len(parts) != 2: continue try: count = int(parts[0]) card_name = parts[1] # Create card objects for i in range(count): card = { "name": card_name, "id": f"{card_name.lower().replace(' ', '_')}_{len(main_deck) + len(sideboard) + i}" } if current_section == "main": main_deck.append(card) else: # sideboard sideboard.append(card) except ValueError: # Skip lines that don't start with a number continue return { "main_deck": main_deck, "sideboard": sideboard } @mcp.tool() async def upload_deck(deck_list: str) -> str: """Upload a Magic: The Gathering deck list. Args: deck_list: Text format deck list with "Deck" and "Sideboard" sections """ parsed_cards = parse_deck_list(deck_list) main_deck = parsed_cards["main_deck"] sideboard = parsed_cards["sideboard"] state["deck"] = main_deck state["sideboard"] = sideboard state["hand"] = [] random.shuffle(state["deck"]) return f"Deck uploaded with {len(state['deck'])} main deck cards and {len(state['sideboard'])} sideboard cards." @mcp.tool() async def draw_card(count: int = 1) -> str: """Draw cards from your deck to your hand. Args: count: Number of cards to draw (default: 1) """ if len(state["deck"]) < count: return f"Not enough cards in deck. Only {len(state['deck'])} remaining." drawn_cards = [] for _ in range(count): card = state["deck"].pop(0) state["hand"].append(card) drawn_cards.append(card["name"]) return f"Drew {count} card(s): {', '.join(drawn_cards)}" @mcp.tool() async def play_card(card_name: str) -> str: """Play a card from your hand to the battlefield/stack. Args: card_name: Name of the card to play """ for i, card in enumerate(state["hand"]): if card["name"].lower() == card_name.lower(): played_card = state["hand"].pop(i) return f"Played {played_card['name']}." return f"Card '{card_name}' not found in hand." @mcp.tool() async def view_hand() -> str: """View the cards in your hand.""" if not state["hand"]: return "Your hand is empty." card_counts = {} for card in state["hand"]: name = card["name"] card_counts[name] = card_counts.get(name, 0) + 1 hand_str = "\n".join([f"{count}x {name}" for name, count in card_counts.items()]) return f"Your hand ({len(state['hand'])} cards):\n{hand_str}" @mcp.tool() async def view_deck_stats() -> str: """View statistics about your current deck.""" if not state["deck"]: return "Your deck is empty." card_counts = {} for card in state["deck"]: name = card["name"] card_counts[name] = card_counts.get(name, 0) + 1 result = [ f"Cards in deck: {len(state['deck'])}", f"Cards in hand: {len(state['hand'])}", f"Sideboard cards: {len(state['sideboard'])}", "", "Top card types in deck:" ] # Sort by quantity sorted_cards = sorted(card_counts.items(), key=lambda x: x[1], reverse=True) for name, count in sorted_cards[:5]: # Show top 5 cards result.append(f" {count}x {name}") return "\n".join(result) @mcp.tool() async def mulligan(new_hand_size: Optional[int] = None) -> str: """Perform a mulligan, shuffling your hand into your deck and drawing a new hand. Args: new_hand_size: Number of cards to draw for new hand (default: same as current hand) """ if not state["hand"]: return "Cannot mulligan with an empty hand." current_hand_size = len(state["hand"]) draw_size = new_hand_size if new_hand_size is not None else current_hand_size # Return hand to deck state["deck"].extend(state["hand"]) state["hand"] = [] # Shuffle random.shuffle(state["deck"]) # Draw new hand drawn_cards = [] for _ in range(min(draw_size, len(state["deck"]))): card = state["deck"].pop(0) state["hand"].append(card) drawn_cards.append(card["name"]) return f"Mulliganed and drew {len(drawn_cards)} new cards." @mcp.tool() async def sideboard_swap(remove_card: str, add_card: str) -> str: """Swap a card from your deck with a card from your sideboard. Args: remove_card: Name of card to remove from deck add_card: Name of card to add from sideboard """ # Find card in sideboard sideboard_card_idx = None for i, card in enumerate(state["sideboard"]): if card["name"].lower() == add_card.lower(): sideboard_card_idx = i break if sideboard_card_idx is None: return f"Card '{add_card}' not found in sideboard." # Find all instances of the card to remove (combine deck and hand) all_deck = state["deck"] + state["hand"] main_deck_card_indices = [ i for i, card in enumerate(all_deck) if card["name"].lower() == remove_card.lower() ] if not main_deck_card_indices: return f"Card '{remove_card}' not found in deck or hand." # Get the first instance and swap main_card_idx = main_deck_card_indices[0] # Fixed variable name here main_card = all_deck.pop(main_card_idx) sideboard_card = state["sideboard"].pop(sideboard_card_idx) # Add removed card to sideboard state["sideboard"].append(main_card) # Rebuild deck and hand if main_card_idx < len(state["deck"]): # Was in deck state["deck"].insert(main_card_idx, sideboard_card) else: # Was in hand hand_idx = main_card_idx - len(state["deck"]) state["hand"].insert(hand_idx, sideboard_card) return f"Swapped out {main_card['name']} for {sideboard_card['name']} from sideboard." @mcp.tool() async def reset_game() -> str: """Reset the game state completely.""" # Get all cards back including those in hand deck_backup = state["deck"].copy() + state["hand"].copy() state["deck"] = deck_backup state["hand"] = [] random.shuffle(state["deck"]) return "Game reset. Deck shuffled." if __name__ == "__main__": # Initialize and run the server mcp.run(transport='stdio')

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/artillect/mtg-mcp-servers'

If you have feedback or need assistance with the MCP directory API, please join our Discord server