Skip to main content
Glama

analyze_deck

Analyze any Hearthstone deck code to classify its archetype, explain its gameplan, and identify strengths, weaknesses, and matchup dynamics for strategic coaching.

Instructions

Analyze a Hearthstone deck code to classify its archetype, explain its gameplan, identify strengths and weaknesses, and predict matchup dynamics. Use this for strategic deck coaching — it combines card data with strategy knowledge.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
deck_codeYesHearthstone deck code (base64 deckstring)

Implementation Reference

  • Main handler function `analyzeDeck` that decodes a Hearthstone deck code, builds card data, classifies the archetype, and looks up strategy information (archetype info, matchups). Returns the full analysis result.
    export function analyzeDeck(
      db: Database.Database,
      input: AnalyzeDeckInputType,
    ): AnalyzeDeckResult {
      // 1. Decode the deck
      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)}`,
        };
      }
    
      // 2. Format
      const format = decoded.format === 1 ? 'Wild' : 'Standard';
    
      // 3. Hero class
      let heroClass = 'UNKNOWN';
      if (decoded.heroes.length > 0) {
        const heroDbfId = decoded.heroes[0];
        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];
        }
      }
    
      // 4. Build card list and profile data
      const deckCards: Array<{ name: string; mana_cost: number | null; count: number }> = [];
      const profileCards: DeckProfile['cards'] = [];
      const manaCurve: Record<string, number> = {};
      const typeDistribution: Record<string, number> = {};
      let totalCards = 0;
      let totalCost = 0;
      let cardsWithCost = 0;
    
      for (const [dbfId, count] of decoded.cards) {
        const row = db
          .prepare('SELECT * FROM cards WHERE id = ?')
          .get(String(dbfId)) as CardRow | undefined;
    
        const name = row ? row.name : `Unknown Card (${dbfId})`;
        const manaCost = row?.mana_cost ?? null;
        const type = row?.type ?? null;
        const text = row?.text ?? null;
        const keywords = row?.keywords ?? null;
    
        deckCards.push({ name, mana_cost: manaCost, count });
    
        // Add profile cards (expand by count for classification)
        for (let i = 0; i < count; i++) {
          profileCards.push({ mana_cost: manaCost, type, text, keywords });
        }
    
        totalCards += count;
    
        // Mana curve
        const bucket = manaCurveBucket(manaCost);
        manaCurve[bucket] = (manaCurve[bucket] ?? 0) + count;
    
        // Type distribution
        const cardType = type ?? 'UNKNOWN';
        typeDistribution[cardType] = (typeDistribution[cardType] ?? 0) + count;
    
        // Average cost tracking
        if (manaCost != null) {
          totalCost += manaCost * count;
          cardsWithCost += count;
        }
      }
    
      const avgCost = cardsWithCost > 0 ? totalCost / cardsWithCost : 0;
    
      // 5. Build DeckProfile and classify
      const profile: DeckProfile = {
        avg_cost: avgCost,
        cards: profileCards,
        total_cards: totalCards,
        mana_curve: manaCurve,
        type_distribution: typeDistribution,
      };
    
      const classification = classifyArchetype(profile);
    
      // 6. Look up archetype info from strategy tables
      let archetypeInfo: AnalyzeDeckSuccess['archetype_info'] | undefined;
      const archetypeRow = db
        .prepare('SELECT * FROM archetypes WHERE LOWER(name) = LOWER(?)')
        .get(classification.archetype) as ArchetypeRow | undefined;
    
      if (archetypeRow) {
        archetypeInfo = {
          description: archetypeRow.description,
          gameplan: archetypeRow.gameplan,
          strengths: parseJson(archetypeRow.strengths),
          weaknesses: parseJson(archetypeRow.weaknesses),
        };
      }
    
      // 7. Look up matchup expectations
      let matchups: AnalyzeDeckSuccess['matchups'] | undefined;
      const matchupRows = db
        .prepare(
          `SELECT archetype_a, archetype_b, favoured, key_tension FROM matchup_framework
           WHERE LOWER(archetype_a) = LOWER(?) OR LOWER(archetype_b) = LOWER(?)`,
        )
        .all(classification.archetype, classification.archetype) as MatchupRow[];
    
      if (matchupRows.length > 0) {
        matchups = matchupRows
          .filter((m) => m.archetype_a.toLowerCase() !== m.archetype_b.toLowerCase() ||
            m.archetype_a.toLowerCase() !== classification.archetype.toLowerCase())
          .map((m) => {
            const isA = m.archetype_a.toLowerCase() === classification.archetype.toLowerCase();
            return {
              vs_archetype: isA ? m.archetype_b : m.archetype_a,
              favoured: m.favoured,
              key_tension: m.key_tension,
            };
          });
      }
    
      return {
        success: true,
        deck: {
          format,
          hero_class: heroClass,
          cards: deckCards,
          total_cards: totalCards,
          mana_curve: manaCurve,
          type_distribution: typeDistribution,
        },
        classification,
        archetype_info: archetypeInfo,
        matchups: matchups && matchups.length > 0 ? matchups : undefined,
      };
    }
  • Input schema (`AnalyzeDeckInput`) and result types (`AnalyzeDeckSuccess`, `AnalyzeDeckError`, `AnalyzeDeckResult`). Input requires a `deck_code` string.
    export const AnalyzeDeckInput = z.object({
      deck_code: z.string().describe('Hearthstone deck code (base64 deckstring)'),
    });
    
    export type AnalyzeDeckInputType = z.infer<typeof AnalyzeDeckInput>;
    
    // --- Result Types ---
    
    export interface AnalyzeDeckSuccess {
      success: true;
      deck: {
        format: string;
        hero_class: string;
        cards: Array<{ name: string; mana_cost: number | null; count: number }>;
        total_cards: number;
        mana_curve: Record<string, number>;
        type_distribution: Record<string, number>;
      };
      classification: ClassificationResult;
      archetype_info?: {
        description: string;
        gameplan: string;
        strengths: string[];
        weaknesses: string[];
      };
      matchups?: Array<{
        vs_archetype: string;
        favoured: string;
        key_tension: string;
      }>;
    }
    
    export interface AnalyzeDeckError {
      success: false;
      message: string;
    }
    
    export type AnalyzeDeckResult = AnalyzeDeckSuccess | AnalyzeDeckError;
  • src/server.ts:164-183 (registration)
    Registration of the 'analyze_deck' tool on the MCP server using `server.tool()`, linking the input schema, handler, and formatter.
    // 5. analyze_deck
    server.tool(
      'analyze_deck',
      'Analyze a Hearthstone deck code to classify its archetype, explain its gameplan, identify strengths and weaknesses, and predict matchup dynamics. Use this for strategic deck coaching — it combines card data with strategy knowledge.',
      AnalyzeDeckInput.shape,
      async (params) => {
        try {
          const result = analyzeDeck(db, params);
          return {
            content: [
              { type: 'text' as const, text: formatAnalyzeDeck(result) },
            ],
          };
        } catch (err) {
          return {
            content: [
              {
                type: 'text' as const,
                text: `Error: ${err instanceof Error ? err.message : String(err)}`,
              },
  • Formatter function `formatAnalyzeDeck` that renders the analysis result as a human-readable markdown string (class, archetype, gameplan, strengths/weaknesses, mana curve, matchups).
    export function formatAnalyzeDeck(result: AnalyzeDeckResult): string {
      if (!result.success) {
        return result.message;
      }
    
      const lines: string[] = [];
    
      // Header
      lines.push('# Deck Analysis');
      lines.push('');
      lines.push(`**Class:** ${result.deck.hero_class} | **Format:** ${result.deck.format} | **Cards:** ${result.deck.total_cards}`);
      lines.push('');
    
      // Archetype classification
      const confidencePct = Math.round(result.classification.confidence * 100);
      lines.push(`## Archetype: ${result.classification.archetype.charAt(0).toUpperCase() + result.classification.archetype.slice(1)} (${confidencePct}% confidence)`);
      lines.push(result.classification.reasoning);
      lines.push('');
    
      // Gameplan (from archetype_info)
      if (result.archetype_info) {
        lines.push('## Gameplan');
        lines.push(result.archetype_info.gameplan);
        lines.push('');
    
        lines.push('## Strengths');
        for (const s of result.archetype_info.strengths) {
          lines.push(`- ${s}`);
        }
        lines.push('');
    
        lines.push('## Weaknesses');
        for (const w of result.archetype_info.weaknesses) {
          lines.push(`- ${w}`);
        }
        lines.push('');
      }
    
      // Mana Curve
      lines.push('## Mana Curve');
      const buckets = ['0', '1', '2', '3', '4', '5', '6', '7+'];
      for (const bucket of buckets) {
        const count = result.deck.mana_curve[bucket] ?? 0;
        if (count > 0) {
          const bar = '\u2588'.repeat(count);
          lines.push(`${bucket}: ${bar} ${count}`);
        }
      }
      lines.push('');
    
      // Matchup Expectations
      if (result.matchups && result.matchups.length > 0) {
        lines.push('## Matchup Expectations');
        for (const m of result.matchups) {
          lines.push(`- vs ${m.vs_archetype}: ${m.favoured} \u2014 ${m.key_tension}`);
        }
        lines.push('');
      }
    
      return lines.join('\n');
    }
  • Helper functions for analyzing decks: `manaCurveBucket` (buckets mana costs) and `parseJson` (parses JSON arrays from DB fields like strengths/weaknesses).
    function manaCurveBucket(cost: number | null): string {
      if (cost == null) return '0';
      if (cost >= 7) return '7+';
      return String(cost);
    }
    
    function parseJson(raw: string | null): string[] {
      if (!raw) return [];
      try {
        return JSON.parse(raw) as string[];
      } catch {
        return [];
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so description carries full burden. It implies read-only analysis but does not explicitly disclose safety, authentication needs, or any side effects. Acceptable but not comprehensive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences with front-loaded content. First sentence lists all major outputs; second provides usage context. No redundant words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, description does not explain return format or structure, which is a gap for a complex analysis tool. It covers purpose but lacks detail on how to interpret results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema covers 100% of parameters with a clear description of 'deck_code' as a base64 string. Description adds no additional meaning beyond stating it's a deck code, meeting baseline but not exceeding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description specifies exact verb 'Analyze' and resource 'Hearthstone deck code', and lists concrete outputs (archetype classification, gameplan, strengths/weaknesses, matchup dynamics). It clearly distinguishes from sibling tools like decode_deck or get_archetype, which provide narrower functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states 'Use this for strategic deck coaching', providing clear context. However, it lacks explicit exclusion of alternatives or when not to use it, leaving some ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/gregario/hearthstone-oracle'

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