Skip to main content
Glama

analyze_deck_composition

Evaluate deck composition, mana curve, and card types for Magic: The Gathering decks. Receive balance recommendations tailored to specific formats and archetypes.

Instructions

Analyze deck composition, mana curve, card types, and provide balance recommendations

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commanderNoCommander card name (for Commander/Brawl formats)
deck_listYesList of card names in the deck, one per line or comma-separated
formatNoFormat to analyze for (affects recommendations)
strategyNoDeck strategy archetypeunknown

Implementation Reference

  • The execute method implements the core tool logic: parameter validation, deck parsing, card data fetching via Scryfall, composition analysis, recommendation generation, and formatted response.
    async execute(args: unknown) { try { const params = this.validateParams(args); // Parse deck list const cardNames = this.parseDeckList(params.deck_list); if (cardNames.length === 0) { return { content: [{ type: 'text', text: 'No valid card names found in deck list. Please provide a list of card names separated by commas or newlines.' }], isError: true }; } // Fetch card data const cardData = await this.fetchCardData(cardNames); if (cardData.length === 0) { return { content: [{ type: 'text', text: 'Unable to fetch data for any cards in the deck list. Please check card names and try again.' }], isError: true }; } // Analyze composition const analysis = this.analyzeComposition(cardData); // Generate recommendations const recommendations = this.generateRecommendations(analysis, params); // Format response const responseText = this.formatAnalysisResponse(analysis, recommendations, params); return { content: [{ type: 'text', text: responseText }] }; } catch (error) { if (error instanceof ValidationError) { return { content: [{ type: 'text', text: `Validation error: ${error.message}` }], isError: true }; } return { content: [{ type: 'text', text: `Unexpected error: ${error instanceof Error ? error.message : 'Unknown error occurred'}` }], isError: true }; } }
  • Input schema defining parameters for deck_list (required), format, strategy, and commander.
    readonly inputSchema = { type: 'object' as const, properties: { deck_list: { type: 'string', description: 'List of card names in the deck, one per line or comma-separated' }, format: { type: 'string', enum: ['standard', 'modern', 'legacy', 'vintage', 'commander', 'pioneer', 'brawl', 'standardbrawl'], description: 'Format to analyze for (affects recommendations)' }, strategy: { type: 'string', enum: ['aggro', 'midrange', 'control', 'combo', 'ramp', 'tribal', 'unknown'], default: 'unknown', description: 'Deck strategy archetype' }, commander: { type: 'string', description: 'Commander card name (for Commander/Brawl formats)' } }, required: ['deck_list'] };
  • src/server.ts:78-78 (registration)
    Registration of the AnalyzeDeckCompositionTool instance in the MCP server's tools Map during constructor initialization.
    this.tools.set("analyze_deck_composition", new AnalyzeDeckCompositionTool(this.scryfallClient));
  • Core analysis helper that computes mana curve, type/color/rarity breakdowns, average CMC, expensive cards, and key cards from fetched card data.
    private analyzeComposition(cardData: any[]) { const analysis: any = { totalCards: cardData.length, manaCurve: {}, typeBreakdown: {}, colorBreakdown: {}, rarityBreakdown: {}, averageCMC: 0, expensiveCards: [], keyCards: [], problems: [] }; let totalCMC = 0; // Analyze each card for (const card of cardData) { const cmc = card.cmc || 0; const types = card.type_line?.toLowerCase() || ''; const colors = card.color_identity || []; const rarity = card.rarity || 'common'; const price = parseFloat(card.prices?.usd || '0'); // Mana curve analysis.manaCurve[cmc] = (analysis.manaCurve[cmc] || 0) + 1; totalCMC += cmc; // Type breakdown if (types.includes('creature')) { analysis.typeBreakdown.creatures = (analysis.typeBreakdown.creatures || 0) + 1; } else if (types.includes('instant')) { analysis.typeBreakdown.instants = (analysis.typeBreakdown.instants || 0) + 1; } else if (types.includes('sorcery')) { analysis.typeBreakdown.sorceries = (analysis.typeBreakdown.sorceries || 0) + 1; } else if (types.includes('artifact')) { analysis.typeBreakdown.artifacts = (analysis.typeBreakdown.artifacts || 0) + 1; } else if (types.includes('enchantment')) { analysis.typeBreakdown.enchantments = (analysis.typeBreakdown.enchantments || 0) + 1; } else if (types.includes('planeswalker')) { analysis.typeBreakdown.planeswalkers = (analysis.typeBreakdown.planeswalkers || 0) + 1; } else if (types.includes('land')) { analysis.typeBreakdown.lands = (analysis.typeBreakdown.lands || 0) + 1; } // Color breakdown for (const color of colors) { analysis.colorBreakdown[color] = (analysis.colorBreakdown[color] || 0) + 1; } // Rarity breakdown analysis.rarityBreakdown[rarity] = (analysis.rarityBreakdown[rarity] || 0) + 1; // Expensive cards (>$5) if (price > 5) { analysis.expensiveCards.push({ name: card.name, price }); } // Key cards (mythic/rare with relevant abilities) if ((rarity === 'mythic' || rarity === 'rare') && card.oracle_text) { analysis.keyCards.push(card.name); } } analysis.averageCMC = totalCMC / cardData.length; // Sort expensive cards by price analysis.expensiveCards.sort((a: any, b: any) => b.price - a.price); return analysis; }
  • Parameter validation helper ensuring required deck_list and valid enums for format/strategy.
    private validateParams(args: unknown): { deck_list: string; format?: string; strategy: string; commander?: string; } { if (!args || typeof args !== 'object') { throw new ValidationError('Invalid parameters'); } const params = args as any; if (!params.deck_list || typeof params.deck_list !== 'string') { throw new ValidationError('Deck list is required and must be a string'); } if (params.format) { const validFormats = ['standard', 'modern', 'legacy', 'vintage', 'commander', 'pioneer', 'brawl', 'standardbrawl']; if (!validFormats.includes(params.format)) { throw new ValidationError(`Format must be one of: ${validFormats.join(', ')}`); } } const strategy = params.strategy || 'unknown'; const validStrategies = ['aggro', 'midrange', 'control', 'combo', 'ramp', 'tribal', 'unknown']; if (!validStrategies.includes(strategy)) { throw new ValidationError(`Strategy must be one of: ${validStrategies.join(', ')}`); } return { deck_list: params.deck_list.trim(), format: params.format, strategy, commander: params.commander }; } async execute(args: unknown) { try { const params = this.validateParams(args); // Parse deck list const cardNames = this.parseDeckList(params.deck_list); if (cardNames.length === 0) { return { content: [{ type: 'text', text: 'No valid card names found in deck list. Please provide a list of card names separated by commas or newlines.' }], isError: true }; } // Fetch card data const cardData = await this.fetchCardData(cardNames); if (cardData.length === 0) { return { content: [{ type: 'text', text: 'Unable to fetch data for any cards in the deck list. Please check card names and try again.' }], isError: true }; } // Analyze composition const analysis = this.analyzeComposition(cardData); // Generate recommendations const recommendations = this.generateRecommendations(analysis, params); // Format response const responseText = this.formatAnalysisResponse(analysis, recommendations, params); return { content: [{ type: 'text', text: responseText }] }; } catch (error) { if (error instanceof ValidationError) { return { content: [{ type: 'text', text: `Validation error: ${error.message}` }], isError: true }; } return { content: [{ type: 'text', text: `Unexpected error: ${error instanceof Error ? error.message : 'Unknown error occurred'}` }], isError: true }; } }

Latest Blog Posts

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/bmurdock/scryfall-mcp'

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