find_combos
Search for infinite combos and synergistic card combinations from Commander Spellbook by card name, names, or color identity. Returns combo steps, prerequisites, and results.
Instructions
Find known infinite combos and synergistic card combinations from Commander Spellbook. Use this when a user asks about combos involving specific cards, combos in specific colors, or wants to find win conditions. Returns combo steps, prerequisites, and results.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| card_name | No | Single card name to search combos for | |
| card_names | No | Multiple card names to search combos for | |
| color_identity | No | Filter combos within this color identity (e.g. ["W","U","B"]) | |
| limit | No | Max results (default 20, max 50) |
Implementation Reference
- src/tools/find-combos.ts:47-109 (handler)Main handler function that queries the database for combos based on card names and/or color identity filters.
export function handler(db: Database.Database, params: FindCombosParams): FindCombosResult { const limit = params.limit ?? 20; // Collect all card names to search for const searchNames: string[] = []; if (params.card_name) searchNames.push(params.card_name); if (params.card_names) searchNames.push(...params.card_names); if (searchNames.length === 0 && !params.color_identity) { return { combos: [], total: 0 }; } // Build query let sql = 'SELECT * FROM combos'; const conditions: string[] = []; const bindings: unknown[] = []; // Card name filter: search in the JSON cards array if (searchNames.length > 0) { const nameConditions = searchNames.map(() => 'LOWER(cards) LIKE LOWER(?)'); conditions.push(`(${nameConditions.join(' AND ')})`); for (const name of searchNames) { bindings.push(`%${name}%`); } } if (conditions.length > 0) { sql += ' WHERE ' + conditions.join(' AND '); } sql += ' ORDER BY popularity DESC LIMIT ?'; // Fetch more than limit if we need to post-filter by color identity const fetchLimit = params.color_identity ? limit * 5 : limit; bindings.push(fetchLimit); const rows = db.prepare(sql).all(...bindings) as ComboRow[]; // Post-filter by color identity constraint let filtered = rows; if (params.color_identity) { filtered = rows.filter(row => { const comboColors: string[] = row.color_identity ? JSON.parse(row.color_identity) as string[] : []; return fitsWithinIdentity(comboColors, params.color_identity!); }); } // Apply limit after filtering const limited = filtered.slice(0, limit); const combos: ComboResult[] = limited.map(row => ({ id: row.id, cards: JSON.parse(row.cards) as string[], color_identity: row.color_identity ? JSON.parse(row.color_identity) as string[] : [], prerequisites: row.prerequisites, steps: row.steps, results: row.results, popularity: row.popularity, })); return { combos, total: combos.length }; } - src/tools/find-combos.ts:7-12 (schema)Input schema (Zod) defining parameters: card_name, card_names, color_identity, and limit.
export const FindCombosInput = z.object({ card_name: z.string().optional().describe('Single card name to search combos for'), card_names: z.array(z.string()).optional().describe('Multiple card names to search combos for'), color_identity: z.array(z.string()).optional().describe('Filter combos within this color identity (e.g. ["W","U","B"])'), limit: z.number().min(1).max(50).optional().describe('Max results (default 20, max 50)'), }); - src/tools/find-combos.ts:28-31 (schema)Output type FindCombosResult containing an array of ComboResult objects and total count.
export interface FindCombosResult { combos: ComboResult[]; total: number; } - src/server.ts:209-220 (registration)Tool registration with the MCP server: binds the 'find_combos' tool name to FindCombosInput schema and the handler.
server.tool( 'find_combos', 'Find known infinite combos and synergistic card combinations from Commander Spellbook. Use this when a user asks about combos involving specific cards, combos in specific colors, or wants to find win conditions. Returns combo steps, prerequisites, and results.', FindCombosInput.shape, async (params) => { try { const result = findCombosHandler(db, params); return { content: [{ type: 'text' as const, text: formatFindCombos(result) }] }; } catch (err) { return { content: [{ type: 'text' as const, text: `Error finding combos: ${err instanceof Error ? err.message : String(err)}` }], isError: true }; } }, - src/format.ts:341-368 (helper)Formatting helper that renders FindCombosResult into a human-readable string for the response.
export function formatFindCombos(result: FindCombosResult): string { if (result.combos.length === 0) { return 'No combos found matching your criteria.'; } const lines: string[] = [`Found ${result.total} combo(s):\n`]; for (const combo of result.combos) { const colorPart = combo.color_identity.length > 0 ? ` [${combo.color_identity.join('')}]` : ''; lines.push(`## ${combo.cards.join(' + ')}${colorPart}`); if (combo.prerequisites) { lines.push(`**Prerequisites:** ${combo.prerequisites}`); } if (combo.steps) { lines.push(`**Steps:** ${combo.steps}`); } if (combo.results) { lines.push(`**Result:** ${combo.results}`); } if (combo.popularity) { lines.push(`Popularity: ${combo.popularity}`); } lines.push(''); } return lines.join('\n').trimEnd(); }