search_cards
Find One Piece TCG cards by name, color, type, cost, power, rarity, or set. Filter results to get card details and effects for deck building.
Instructions
Search One Piece TCG cards by name, color, type, cost, power, rarity, attribute, character type, or set. Returns card details with effects.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Search card name, number, effect text, or character type | |
| color | No | Card color: Red, Blue, Green, Purple, Yellow, Black | |
| card_type | No | Card type: LEADER, CHARACTER, EVENT, STAGE | |
| cost | No | Exact cost value | |
| cost_min | No | Minimum cost | |
| cost_max | No | Maximum cost | |
| power_min | No | Minimum power | |
| power_max | No | Maximum power | |
| rarity | No | Rarity: C, UC, R, SR, SEC, L, SP CARD, P, TR | |
| attribute | No | Attribute: Slash, Strike, Ranged, Special, Wisdom | |
| type | No | Character type (e.g., "Straw Hat Crew", "Navy", "Supernovas") | |
| set | No | Set name or code (e.g., "OP-01", "ROMANCE DAWN") | |
| has_counter | No | Filter cards with/without counter value | |
| limit | No | Max results (default 10) | |
| offset | No | Offset for pagination |
Implementation Reference
- src/tools/search-cards.ts:30-53 (handler)The handler for the `search_cards` tool. It processes the request by calling `searchCards` and formatting the output.
async (args) => { const result = searchCards(args); if (result.total === 0) { return { isError: true, content: [ { type: 'text' as const, text: 'No cards found matching your search. Try broader filters or check spelling.', }, ], }; } const start = (args.offset ?? 0) + 1; const end = start + result.cards.length - 1; const header = `Found ${result.total} card${result.total !== 1 ? 's' : ''} (showing ${start}-${end})\n`; const cards = result.cards.map((c) => formatCard(c)).join('\n\n---\n\n'); return { content: [{ type: 'text' as const, text: header + '\n' + cards }], }; }, - src/data/cards.ts:49-141 (handler)The core logic implementation for searching cards based on filters.
export function searchCards(filters: SearchFilters): SearchResult { let cards = getCards(); if (filters.query) { const q = filters.query; cards = cards.filter((c) => matchesQuery(c, q)); } if (filters.color) { const color = filters.color.toLowerCase(); cards = cards.filter((c) => c.colors.some((cl) => cl.toLowerCase() === color), ); } if (filters.card_type) { const ct = filters.card_type.toUpperCase(); cards = cards.filter((c) => c.card_type === ct); } if (filters.cost !== undefined) { cards = cards.filter((c) => parseNumeric(c.cost) === filters.cost); } if (filters.cost_min !== undefined) { cards = cards.filter((c) => { const n = parseNumeric(c.cost); return n !== null && n >= filters.cost_min!; }); } if (filters.cost_max !== undefined) { cards = cards.filter((c) => { const n = parseNumeric(c.cost); return n !== null && n <= filters.cost_max!; }); } if (filters.power_min !== undefined) { cards = cards.filter((c) => { const n = parseNumeric(c.power); return n !== null && n >= filters.power_min!; }); } if (filters.power_max !== undefined) { cards = cards.filter((c) => { const n = parseNumeric(c.power); return n !== null && n <= filters.power_max!; }); } if (filters.rarity) { const r = filters.rarity.toUpperCase(); cards = cards.filter((c) => c.rarity === r); } if (filters.attribute) { const attr = filters.attribute.toLowerCase(); cards = cards.filter((c) => c.attributes.some((a) => a.toLowerCase() === attr), ); } if (filters.type) { const t = filters.type.toLowerCase(); cards = cards.filter((c) => c.types.some((tp) => tp.toLowerCase().includes(t)), ); } if (filters.set) { const s = filters.set.toLowerCase(); cards = cards.filter((c) => c.card_sets.toLowerCase().includes(s)); } if (filters.has_counter !== undefined) { if (filters.has_counter) { cards = cards.filter((c) => c.counter !== '-' && c.counter !== ''); } else { cards = cards.filter((c) => c.counter === '-' || c.counter === ''); } } const total = cards.length; const limit = filters.limit ?? 20; const offset = filters.offset ?? 0; return { cards: cards.slice(offset, offset + limit), total, }; } - src/tools/search-cards.ts:6-54 (registration)Registration of the `search_cards` tool on the McpServer.
export function registerSearchCards(server: McpServer): void { server.registerTool( 'search_cards', { description: 'Search One Piece TCG cards by name, color, type, cost, power, rarity, attribute, character type, or set. Returns card details with effects.', inputSchema: { query: z.string().optional().describe('Search card name, number, effect text, or character type'), color: z.string().optional().describe('Card color: Red, Blue, Green, Purple, Yellow, Black'), card_type: z.string().optional().describe('Card type: LEADER, CHARACTER, EVENT, STAGE'), cost: z.number().optional().describe('Exact cost value'), cost_min: z.number().optional().describe('Minimum cost'), cost_max: z.number().optional().describe('Maximum cost'), power_min: z.number().optional().describe('Minimum power'), power_max: z.number().optional().describe('Maximum power'), rarity: z.string().optional().describe('Rarity: C, UC, R, SR, SEC, L, SP CARD, P, TR'), attribute: z.string().optional().describe('Attribute: Slash, Strike, Ranged, Special, Wisdom'), type: z.string().optional().describe('Character type (e.g., "Straw Hat Crew", "Navy", "Supernovas")'), set: z.string().optional().describe('Set name or code (e.g., "OP-01", "ROMANCE DAWN")'), has_counter: z.boolean().optional().describe('Filter cards with/without counter value'), limit: z.number().min(1).max(50).optional().default(10).describe('Max results (default 10)'), offset: z.number().min(0).optional().default(0).describe('Offset for pagination'), }, }, async (args) => { const result = searchCards(args); if (result.total === 0) { return { isError: true, content: [ { type: 'text' as const, text: 'No cards found matching your search. Try broader filters or check spelling.', }, ], }; } const start = (args.offset ?? 0) + 1; const end = start + result.cards.length - 1; const header = `Found ${result.total} card${result.total !== 1 ? 's' : ''} (showing ${start}-${end})\n`; const cards = result.cards.map((c) => formatCard(c)).join('\n\n---\n\n'); return { content: [{ type: 'text' as const, text: header + '\n' + cards }], }; }, );