get_card
Retrieve complete details for any Hearthstone card by name, including stats, text, keywords, and type. Supports partial name matching.
Instructions
Get complete details for a specific Hearthstone card including stats, text, keywords, and type. Use this when you know the card name (or close to it) and need full information. Supports fuzzy matching.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Card name to look up (exact or partial match) |
Implementation Reference
- src/tools/get-card.ts:55-90 (handler)The main handler function that executes the 'get_card' tool logic. It first tries an exact case-insensitive match on the card name, then a fuzzy (LIKE) match, and finally returns suggestions based on the first word if no match is found.
export function getCard( db: Database.Database, input: GetCardInputType, ): GetCardResult { // 1. Exact match (case-insensitive) const exact = db .prepare('SELECT * FROM cards WHERE LOWER(name) = LOWER(?)') .get(input.name) as CardRow | undefined; if (exact) { return { found: true, card: toCardDetail(exact) }; } // 2. Fuzzy match via LIKE const fuzzy = db .prepare('SELECT * FROM cards WHERE LOWER(name) LIKE LOWER(?)') .get(`%${input.name}%`) as CardRow | undefined; if (fuzzy) { return { found: true, card: toCardDetail(fuzzy) }; } // 3. Not found — provide suggestions based on first word const firstWord = input.name.split(/\s+/)[0]; const suggestions = db .prepare('SELECT name FROM cards WHERE LOWER(name) LIKE LOWER(?) LIMIT 5') .all(`%${firstWord}%`) as Array<{ name: string }>; const suggestionNames = suggestions.map((s) => s.name); return { found: false, message: `No card found matching "${input.name}".`, suggestions: suggestionNames.length > 0 ? suggestionNames : undefined, }; } - src/tools/get-card.ts:8-12 (schema)Zod input schema for the 'get_card' tool. Requires a single 'name' field (string) for the card name to look up.
export const GetCardInput = z.object({ name: z.string().describe('Card name to look up (exact or partial match)'), }); export type GetCardInputType = z.infer<typeof GetCardInput>; - src/format.ts:52-54 (schema)Type definition for the 'get_card' tool result. A discriminated union: either {found: true, card: CardDetail} or {found: false, message: string, suggestions?: string[]}.
export type GetCardResult = | { found: true; card: CardDetail } | { found: false; message: string; suggestions?: string[] }; - src/server.ts:83-108 (registration)Registration of the 'get_card' tool on the MCP server. Uses server.tool() with the name 'get_card', description, GetCardInput.shape schema, and an async handler that calls getCard() and formats the result.
// 2. get_card server.tool( 'get_card', 'Get complete details for a specific Hearthstone card including stats, text, keywords, and type. Use this when you know the card name (or close to it) and need full information. Supports fuzzy matching.', GetCardInput.shape, async (params) => { try { const result = getCard(db, params); return { content: [ { type: 'text' as const, text: formatGetCard(result) }, ], }; } catch (err) { return { content: [ { type: 'text' as const, text: `Error: ${err instanceof Error ? err.message : String(err)}`, }, ], isError: true, }; } }, ); - src/format.ts:77-141 (helper)Formats the 'get_card' result into a human-readable string. Handles both found (displaying full card details including stats, keywords, flavor text) and not-found cases (with suggestions).
export function formatGetCard(result: GetCardResult): string { if (!result.found) { let msg = result.message; if (result.suggestions && result.suggestions.length > 0) { msg += '\n\nDid you mean:\n'; msg += result.suggestions.map(s => `- ${s}`).join('\n'); } return msg; } const card = result.card; const lines: string[] = []; // Header lines.push(`# ${card.name} {${card.mana_cost ?? '?'} mana}`); // Type line const typeParts: string[] = []; if (card.rarity) typeParts.push(card.rarity); if (card.type) typeParts.push(card.type); if (card.race) typeParts.push(`(${card.race})`); if (card.player_class) typeParts.push(`[${card.player_class}]`); lines.push(typeParts.join(' ')); // Set if (card.card_set || card.set_name) { lines.push(`Set: ${card.set_name ?? card.card_set}`); } // Text if (card.text) { lines.push(''); lines.push(card.text); } // Stats if (card.type === 'MINION' && card.attack != null && card.health != null) { lines.push(''); lines.push(`${card.attack}/${card.health}`); } else if (card.type === 'WEAPON' && card.attack != null && card.durability != null) { lines.push(''); lines.push(`${card.attack}/${card.durability}`); } else if (card.type === 'HERO' && card.armor != null) { lines.push(''); lines.push(`Armor: ${card.armor}`); } // Keywords if (card.keywords.length > 0) { lines.push(`Keywords: ${card.keywords.join(', ')}`); } // Spell school if (card.spell_school) { lines.push(`Spell School: ${card.spell_school}`); } // Flavor if (card.flavor) { lines.push(''); lines.push(`*${card.flavor}*`); } return lines.join('\n'); }