search_sets
Find Magic: The Gathering sets by name, type, or release date using partial matches and optional filters for precise results.
Instructions
Search for Magic: The Gathering sets with optional filtering by name, type, and release dates
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Set name or code to search for (partial matches supported) | |
| released_after | No | ISO date string - only show sets released after this date (YYYY-MM-DD) | |
| released_before | No | ISO date string - only show sets released before this date (YYYY-MM-DD) | |
| type | No | Filter by set type. Common types: core (yearly core sets), expansion (rotational sets), masters (reprint sets), commander (preconstructed Commander decks), promo (promotional cards), token (tokens and emblems) |
Implementation Reference
- src/tools/search-sets.ts:17-185 (handler)SearchSetsTool class: defines the tool name, description, inputSchema, and the execute method that handles parameter validation, date validation, Scryfall API call via scryfallClient.getSets, result formatting with formatSetsAsText, and error handling.export class SearchSetsTool { readonly name = 'search_sets'; readonly description = 'Search for Magic: The Gathering sets with optional filtering by name, type, and release dates'; readonly inputSchema = { type: 'object' as const, properties: { query: { type: 'string', description: 'Set name or code to search for (partial matches supported)' }, type: { type: 'string', enum: SET_TYPES, description: 'Filter by set type. Common types: core (yearly core sets), expansion (rotational sets), masters (reprint sets), commander (preconstructed Commander decks), promo (promotional cards), token (tokens and emblems)' }, released_after: { type: 'string', description: 'ISO date string - only show sets released after this date (YYYY-MM-DD)', pattern: '^\\d{4}-\\d{2}-\\d{2}$' }, released_before: { type: 'string', description: 'ISO date string - only show sets released before this date (YYYY-MM-DD)', pattern: '^\\d{4}-\\d{2}-\\d{2}$' } }, required: [] }; constructor(private readonly scryfallClient: ScryfallClient) {} async execute(args: unknown) { try { // Validate parameters const params = validateSearchSetsParams(args); // Validate date strings if provided if (params.released_after) { validateDateString(params.released_after); } if (params.released_before) { validateDateString(params.released_before); } // Validate date range logic if (params.released_after && params.released_before) { const afterDate = new Date(params.released_after); const beforeDate = new Date(params.released_before); if (afterDate >= beforeDate) { throw new ValidationError('released_after date must be before released_before date'); } } // Execute sets search const sets = await this.scryfallClient.getSets({ query: params.query, type: params.type, released_after: params.released_after, released_before: params.released_before }); // Handle no results if (sets.length === 0) { let message = 'No sets found'; const filters = []; if (params.query) filters.push(`name/code: "${params.query}"`); if (params.type) filters.push(`type: ${params.type}`); if (params.released_after) filters.push(`released after: ${params.released_after}`); if (params.released_before) filters.push(`released before: ${params.released_before}`); if (filters.length > 0) { message += ` matching criteria (${filters.join(', ')})`; } message += '. Try broadening your search criteria.'; return { content: [ { type: 'text', text: message } ] }; } // Format sets for display const responseText = formatSetsAsText(sets); // Add search context let contextNote = ''; const appliedFilters = []; if (params.query) appliedFilters.push(`name/code: "${params.query}"`); if (params.type) appliedFilters.push(`type: ${params.type}`); if (params.released_after) appliedFilters.push(`released after: ${params.released_after}`); if (params.released_before) appliedFilters.push(`released before: ${params.released_before}`); if (appliedFilters.length > 0) { contextNote = `\n\n*Filtered by: ${appliedFilters.join(', ')}*`; } return { content: [ { type: 'text', text: responseText + contextNote } ] }; } catch (error) { // Handle different error types if (error instanceof ValidationError) { return { content: [ { type: 'text', text: `Validation error: ${error.message}` } ], isError: true }; } if (error instanceof RateLimitError) { const retry = error.retryAfter ? ` Retry after ${error.retryAfter}s.` : ''; return { content: [{ type: 'text', text: `Rate limit exceeded.${retry} Please wait and try again.` }], isError: true }; } if (error instanceof ScryfallAPIError) { let errorMessage = `Scryfall API error: ${error.message}`; if (error.status === 404) { errorMessage = 'No sets found. The Scryfall sets database may be temporarily unavailable.'; } else if (error.status === 429) { errorMessage = 'Rate limit exceeded. Please wait a moment and try again.'; } return { content: [ { type: 'text', text: errorMessage } ], isError: true }; } // Generic error handling return { content: [ { type: 'text', text: `Unexpected error: ${error instanceof Error ? error.message : 'Unknown error occurred'}` } ], isError: true }; } } }
- src/server.ts:70-70 (registration)Registration of the search_sets tool in the MCP server's tools Map during constructor initialization.this.tools.set("search_sets", new SearchSetsTool(this.scryfallClient));
- src/types/mcp-types.ts:91-98 (schema)Zod schema definition for SearchSetsParams used in input validation by the validator function.export const SearchSetsParamsSchema = z.object({ query: z.string().optional(), type: z .enum(["core", "expansion", "masters", "commander", "draft_innovation", "funny"]) .optional(), released_after: z.string().datetime().optional(), released_before: z.string().datetime().optional(), });
- src/utils/validators.ts:87-99 (helper)Helper function to validate tool input parameters using the SearchSetsParamsSchema, throwing ValidationError on failure.export function validateSearchSetsParams(params: unknown) { try { return SearchSetsParamsSchema.parse(params); } catch (error) { if (error instanceof z.ZodError) { const firstError = error.errors[0]; throw new ValidationError( `Invalid parameter '${firstError.path.join(".")}': ${firstError.message}`, firstError.path.join(".") ); } throw error; }