decode_deck
Decode a Hearthstone deck code to see its full card list, mana curve, and card type breakdown. Get a clear view of the deck's composition.
Instructions
Decode a Hearthstone deck code into its full card list with mana curve and card type breakdown. Use this when a user shares a deck code and wants to see what's in it.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| deck_code | Yes | Hearthstone deck code (base64 deckstring) |
Implementation Reference
- src/tools/decode-deck.ts:83-169 (handler)The main handler function 'decodeDeck' that decodes a Hearthstone deck code. It uses the 'deckstrings' library to decode the base64 deck code, looks up hero class (via DB or hardcoded map), resolves each card's dbfId to a CardSummary from the DB (with a placeholder for unknown cards), computes mana curve and type distribution, and returns a DecodeDeckResult.
export function decodeDeck( db: Database.Database, input: DecodeDeckInputType, ): DecodeDeckResult { let decoded: { cards: Array<[number, number]>; heroes: number[]; format: number }; try { decoded = decode(input.deck_code); } catch (err) { return { success: false, message: `Invalid deck code: ${err instanceof Error ? err.message : String(err)}`, }; } // Format const format = decoded.format === 1 ? 'Wild' : 'Standard'; // Hero class — try to look up hero in DB first, then fall back to map let heroClass = 'UNKNOWN'; if (decoded.heroes.length > 0) { const heroDbfId = decoded.heroes[0]; // Try DB lookup const heroRow = db .prepare('SELECT player_class FROM cards WHERE id = ?') .get(String(heroDbfId)) as { player_class: string | null } | undefined; if (heroRow?.player_class) { heroClass = heroRow.player_class; } else if (HERO_CLASS_MAP[heroDbfId]) { heroClass = HERO_CLASS_MAP[heroDbfId]; } } // Cards const cards: Array<{ card: CardSummary; count: number }> = []; const manaCurve: Record<string, number> = {}; const typeDistribution: Record<string, number> = {}; let totalCards = 0; for (const [dbfId, count] of decoded.cards) { const row = db .prepare('SELECT * FROM cards WHERE id = ?') .get(String(dbfId)) as CardRow | undefined; let cardSummary: CardSummary; if (row) { cardSummary = toCardSummary(row); } else { // Unknown card — create a richer placeholder so the user understands // what happened and how to resolve it. The most common cause is a // recently-released card whose dbfId isn't in the local snapshot yet. cardSummary = { name: `Unknown card (dbfId: ${dbfId}) — possibly added in a recent expansion; refresh data to resolve`, mana_cost: null, type: null, player_class: null, rarity: null, text: null, attack: null, health: null, keywords: [], }; } cards.push({ card: cardSummary, count }); totalCards += count; // Mana curve const bucket = manaCurveBucket(cardSummary.mana_cost); manaCurve[bucket] = (manaCurve[bucket] ?? 0) + count; // Type distribution const cardType = cardSummary.type ?? 'UNKNOWN'; typeDistribution[cardType] = (typeDistribution[cardType] ?? 0) + count; } return { success: true, format, hero_class: heroClass, cards, total_cards: totalCards, mana_curve: manaCurve, type_distribution: typeDistribution, }; } - src/tools/decode-deck.ts:9-32 (schema)Input schema (DecodeDeckInput) requiring a 'deck_code' string, and output type definitions (DecodeDeckSuccess / DecodeDeckError) for the tool's result.
export const DecodeDeckInput = z.object({ deck_code: z.string().describe('Hearthstone deck code (base64 deckstring)'), }); export type DecodeDeckInputType = z.infer<typeof DecodeDeckInput>; // --- Result Types --- export interface DecodeDeckSuccess { success: true; format: string; hero_class: string; cards: Array<{ card: CardSummary; count: number }>; total_cards: number; mana_curve: Record<string, number>; type_distribution: Record<string, number>; } export interface DecodeDeckError { success: false; message: string; } export type DecodeDeckResult = DecodeDeckSuccess | DecodeDeckError; - src/server.ts:137-162 (registration)Registration of the 'decode_deck' tool on the MCP server via server.tool(), importing the handler and schema from src/tools/decode-deck.ts.
// 4. decode_deck server.tool( 'decode_deck', 'Decode a Hearthstone deck code into its full card list with mana curve and card type breakdown. Use this when a user shares a deck code and wants to see what\'s in it.', DecodeDeckInput.shape, async (params) => { try { const result = decodeDeck(db, params); return { content: [ { type: 'text' as const, text: formatDecodeDeck(result) }, ], }; } catch (err) { return { content: [ { type: 'text' as const, text: `Error: ${err instanceof Error ? err.message : String(err)}`, }, ], isError: true, }; } }, ); - src/format.ts:178-215 (helper)The formatDecodeDeck function that formats the DecodeDeckResult into a human-readable markdown string (hero class, format, cards list, mana curve, type distribution).
export function formatDecodeDeck(result: DecodeDeckResult): string { if (!result.success) { return result.message; } const lines: string[] = []; lines.push(`# Deck: ${result.hero_class} (${result.format})`); lines.push(`Total cards: ${result.total_cards}`); lines.push(''); // Cards list lines.push('## Cards'); for (const { card, count } of result.cards) { lines.push( `- ${count}x **${card.name}** (${card.mana_cost ?? '?'} mana) — ${card.type ?? 'Unknown'}`, ); } // Mana Curve lines.push(''); lines.push('## Mana Curve'); const buckets = ['0', '1', '2', '3', '4', '5', '6', '7+']; for (const bucket of buckets) { const count = result.mana_curve[bucket] ?? 0; if (count > 0) { lines.push(`${bucket}: ${'#'.repeat(count)} (${count})`); } } // Type Distribution lines.push(''); lines.push('## Type Distribution'); for (const [type, count] of Object.entries(result.type_distribution)) { lines.push(`- ${type}: ${count}`); } return lines.join('\n'); } - src/tools/decode-deck.ts:36-63 (helper)Helper functions used by the handler: parseJson (parses keyword JSON), toCardSummary (maps a DB row to CardSummary), manaCurveBucket (buckets mana cost), and HERO_CLASS_MAP (hardcoded hero dbfId to class mappings).
function parseJson(raw: string | null): string[] { if (!raw) return []; try { return JSON.parse(raw) as string[]; } catch { return []; } } function toCardSummary(row: CardRow): CardSummary { return { name: row.name, mana_cost: row.mana_cost, type: row.type, player_class: row.player_class, rarity: row.rarity, text: row.text ? row.text.split('\n')[0] : null, attack: row.attack, health: row.health, keywords: parseJson(row.keywords), }; } function manaCurveBucket(cost: number | null): string { if (cost == null) return '0'; if (cost >= 7) return '7+'; return String(cost); }