analyze_commander
Analyze a legendary creature as a potential Commander, returning color identity, strategies, archetypes, and recommended card categories for deck building.
Instructions
Analyze a legendary creature as a potential Commander. Returns color identity, suggested strategies, archetypes, and recommended card categories for building a deck around this commander. Use this when a user wants help building or evaluating a Commander deck.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Commander card name to analyze |
Implementation Reference
- src/tools/analyze-commander.ts:125-197 (handler)Main handler function that looks up a card in the database, verifies it's a legendary creature, extracts color identity/keywords, checks for Partner, matches strategies by color identity, matches archetypes by oracle text/keywords, recommends card categories, and returns a structured CommanderAnalysis result.
export function handler(db: Database.Database, params: AnalyzeCommanderParams): AnalyzeCommanderResult { // 1. Look up the card let card = db.prepare( 'SELECT * FROM cards WHERE LOWER(name) = LOWER(?)' ).get(params.name) as CardRow | undefined; if (!card) { card = db.prepare( 'SELECT * FROM cards WHERE LOWER(name) LIKE LOWER(?)' ).get(`%${params.name}%`) as CardRow | undefined; } if (!card) { return { found: false, message: `No card found matching "${params.name}"`, }; } // 2. Verify it's a legendary creature const typeLine = card.type_line ?? ''; if (!typeLine.toLowerCase().includes('legendary') || !typeLine.toLowerCase().includes('creature')) { return { found: false, message: `"${card.name}" is not a legendary creature (type: ${typeLine}). Only legendary creatures can be commanders.`, }; } // 3. Extract data const colorIdentity: string[] = card.color_identity ? JSON.parse(card.color_identity) as string[] : []; const keywords: string[] = card.keywords ? JSON.parse(card.keywords) as string[] : []; // 4. Check for Partner const hasPartner = keywords.some(k => k.toLowerCase() === 'partner' || k.toLowerCase().startsWith('partner with') ) || (card.oracle_text ?? '').toLowerCase().includes('partner'); // 5. Match strategies & archetypes const strategies = matchStrategies(colorIdentity); const archetypes = matchArchetypes(card.oracle_text, keywords, colorIdentity); const categories = recommendCategories(card.oracle_text, keywords, card.type_line); const analysis: CommanderAnalysis = { name: card.name, color_identity: colorIdentity, type_line: typeLine, oracle_text: card.oracle_text, edhrec_rank: card.edhrec_rank, has_partner: hasPartner, suggested_strategies: strategies.map(s => ({ id: s.id, name: s.name, description: s.description, power_brackets: [...s.powerBrackets], staple_cards: [...s.stapleCards], key_synergies: [...s.keySynergies], })), suggested_archetypes: archetypes.map(a => ({ id: a.id, name: a.name, description: a.description, key_mechanics: [...a.keyMechanics], })), recommended_categories: categories, }; return { found: true, analysis }; } - src/tools/analyze-commander.ts:13-47 (schema)Input schema (Zod) accepting a commander name string, output types including CommanderAnalysis interface with strategies/archetypes/categories, and the discriminated union result type.
export const AnalyzeCommanderInput = z.object({ name: z.string().describe('Commander card name to analyze'), }); export type AnalyzeCommanderParams = z.infer<typeof AnalyzeCommanderInput>; // --- Output types --- export interface CommanderAnalysis { name: string; color_identity: string[]; type_line: string; oracle_text: string | null; edhrec_rank: number | null; has_partner: boolean; suggested_strategies: Array<{ id: string; name: string; description: string; power_brackets: string[]; staple_cards: string[]; key_synergies: string[]; }>; suggested_archetypes: Array<{ id: string; name: string; description: string; key_mechanics: string[]; }>; recommended_categories: string[]; } export type AnalyzeCommanderResult = | { found: true; analysis: CommanderAnalysis } | { found: false; message: string }; - src/server.ts:195-207 (registration)Registration of the 'analyze_commander' tool in the MCP server via server.tool(), linking the Zod input schema, the handler, and the formatter.
server.tool( 'analyze_commander', 'Analyze a legendary creature as a potential Commander. Returns color identity, suggested strategies, archetypes, and recommended card categories for building a deck around this commander. Use this when a user wants help building or evaluating a Commander deck.', AnalyzeCommanderInput.shape, async (params) => { try { const result = analyzeCommanderHandler(db, params); return { content: [{ type: 'text' as const, text: formatAnalyzeCommander(result) }] }; } catch (err) { return { content: [{ type: 'text' as const, text: `Error analyzing commander: ${err instanceof Error ? err.message : String(err)}` }], isError: true }; } }, ); - src/tools/analyze-commander.ts:54-59 (helper)Helper to build a WUBRG color identity key string (e.g., 'WU', 'BRG') used for matching against commander strategies.
function buildColorIdentityKey(colors: string[]): ColorIdentity { if (colors.length === 0) return 'colorless'; const order = ['W', 'U', 'B', 'R', 'G']; const sorted = colors.slice().sort((a, b) => order.indexOf(a) - order.indexOf(b)); return sorted.join('') as ColorIdentity; } - Helper that recommends card categories (e.g., Token Generators, Graveyard Recursion, Sacrifice Outlets) based on the commander's oracle text, keywords, and type line.
function recommendCategories( oracleText: string | null, keywords: string[], typeLine: string | null, ): string[] { const categories: string[] = ['Ramp', 'Card Draw', 'Removal', 'Board Wipes']; const text = (oracleText ?? '').toLowerCase(); const type = (typeLine ?? '').toLowerCase(); if (text.includes('token') || text.includes('create')) categories.push('Token Generators'); if (text.includes('counter') && !text.includes('counterspell')) categories.push('+1/+1 Counter Support'); if (text.includes('graveyard') || text.includes('return from')) categories.push('Graveyard Recursion'); if (text.includes('sacrifice') || text.includes('dies')) categories.push('Sacrifice Outlets'); if (keywords.includes('Flying') || keywords.includes('Trample')) categories.push('Evasion'); if (text.includes('equipment') || text.includes('equip')) categories.push('Equipment'); if (text.includes('enchantment') || type.includes('enchantment')) categories.push('Enchantments'); if (text.includes('artifact') || type.includes('artifact')) categories.push('Artifacts'); if (text.includes('land') || text.includes('landfall')) categories.push('Lands Matter'); return categories; }