UNPKG

@kaminaduck/scryfall-mcp-server

Version:

A Model Context Protocol (MCP) server that provides access to the Scryfall API for Magic: The Gathering card data

327 lines 17 kB
/** * Download-related MCP tools for the Scryfall server. */ import { downloadManager } from '../downloadManager.js'; import { createCardDatabase } from '../database.js'; import { getStorageDirectory } from '../config.js'; import { logger } from '../logger.js'; /** * Download a high-resolution image of a specific Magic: The Gathering card. * Returns response structure matching Python implementation. */ export async function mcpDownloadCard(cardName, setCode, collectorNumber, forceDownload = false) { logger.info(`[API] Downloading card: ${cardName} (Set: ${setCode}, Number: ${collectorNumber})`); try { // Validate input parameters if (!cardName || cardName.trim().length === 0) { return { status: 'error', message: 'Card name cannot be empty' }; } const trimmedCardName = cardName.trim(); // Prepare the parameters for download (matching Python filtering logic) const setCodes = setCode ? [setCode.toLowerCase()] : undefined; const collectorNumbers = collectorNumber ? [collectorNumber] : undefined; // Get the appropriate storage directory (always provide a valid path for MCP mode) const baseDir = await getStorageDirectory(); // Download the card image const downloadedFiles = await downloadManager.downloadCardImages([trimmedCardName], forceDownload, setCodes, collectorNumbers, baseDir); if (downloadedFiles.length > 0) { const isTransformCard = downloadedFiles.length > 1; const db = await createCardDatabase(); try { if (isTransformCard) { // Handle transform card with multiple faces const faces = []; let hasError = false; for (const file of downloadedFiles) { try { // Generate face-specific card version ID let faceCardVersionId; if (setCode && collectorNumber) { faceCardVersionId = `${trimmedCardName}_${setCode.toLowerCase()}_${collectorNumber}_face${file.face_index}`; } else { faceCardVersionId = `${trimmedCardName}_face${file.face_index}`; } const cardInfo = db.getCardInfo(faceCardVersionId); const fileId = cardInfo?.file_id; const faceResult = { status: 'success', message: `Face ${file.face_index} (${file.face_name}) downloaded successfully`, filepath: file.filePath, card_name: file.cardName, face_index: file.face_index, face_name: file.face_name, total_faces: file.total_faces, is_transform_card: file.is_transform_card }; // Add optional fields if (setCode !== undefined) { faceResult.set_code = setCode; } if (collectorNumber !== undefined) { faceResult.collector_number = collectorNumber; } // Add resource URI if we have a file_id if (fileId) { faceResult.resource_uri = `resource://download/card/${fileId}`; } faces.push(faceResult); } catch (faceError) { logger.error(`Error processing face ${file.face_index}:`, faceError); hasError = true; faces.push({ status: 'error', message: `Failed to process face ${file.face_index} (${file.face_name})`, face_index: file.face_index, face_name: file.face_name }); } } // Return main result with faces array const result = { status: hasError ? 'error' : 'success', message: hasError ? `Transform card '${trimmedCardName}' partially downloaded (some faces failed)` : `Transform card '${trimmedCardName}' downloaded successfully (${faces.length} faces)`, card_name: trimmedCardName, total_faces: downloadedFiles[0]?.total_faces, is_transform_card: true, faces: faces }; // Add optional fields if (setCode !== undefined) { result.set_code = setCode; } if (collectorNumber !== undefined) { result.collector_number = collectorNumber; } logger.info(`[API] Transform card download completed: ${trimmedCardName} -> ${faces.length} faces`); return result; } else { // Handle single-faced card (backward compatibility) const file = downloadedFiles[0]; const filePath = file.filePath; // Generate card version ID matching Python behavior let cardVersionId = trimmedCardName; if (setCode && collectorNumber) { cardVersionId = `${trimmedCardName}_${setCode.toLowerCase()}_${collectorNumber}`; } const cardInfo = db.getCardInfo(cardVersionId); let fileId = cardInfo?.file_id; // If no existing record found, register the file and get the file_id if (!fileId) { // File should already be registered by the download manager, but let's verify const newCardInfo = db.getCardInfo(cardVersionId); fileId = newCardInfo?.file_id; } // Build result matching Python implementation structure const result = { status: 'success', message: `Card '${trimmedCardName}' downloaded successfully`, filepath: filePath, card_name: trimmedCardName }; // Add optional fields only if defined (matching Python conditional assignment) if (setCode !== undefined) { result.set_code = setCode; } if (collectorNumber !== undefined) { result.collector_number = collectorNumber; } // Add resource URI if we have a file_id (matching Python resource_uri format) if (fileId) { result.resource_uri = `resource://download/card/${fileId}`; } logger.info(`[API] Card download successful: ${trimmedCardName} -> ${fileId || 'no file_id'}`); return result; } } finally { db.close(); } } else { return { status: 'error', message: `Failed to download card '${trimmedCardName}' - no files were downloaded` }; } } catch (error) { logger.error(`[Error] Failed to download card '${cardName}':`, error); return { status: 'error', message: error instanceof Error ? error.message : 'Unknown error occurred during card download' }; } } /** * Download an art crop image of a specific Magic: The Gathering card. * Returns response structure matching Python implementation. */ export async function mcpDownloadArtCrop(cardName, setCode, collectorNumber, forceDownload = false) { logger.info(`[API] Downloading art crop: ${cardName} (Set: ${setCode}, Number: ${collectorNumber})`); try { // Validate input parameters if (!cardName || cardName.trim().length === 0) { return { status: 'error', message: 'Card name cannot be empty' }; } const trimmedCardName = cardName.trim(); // Prepare the parameters for download (matching Python filtering logic) const setCodes = setCode ? [setCode.toLowerCase()] : undefined; const collectorNumbers = collectorNumber ? [collectorNumber] : undefined; // Get the appropriate storage directory (always provide a valid path for MCP mode) const baseDir = await getStorageDirectory(); // Download the art crop const downloadedFiles = await downloadManager.downloadArtCrops([trimmedCardName], forceDownload, setCodes, collectorNumbers, baseDir); // Create a unique identifier for the card version (matching Python behavior) let cardVersionId = `${trimmedCardName}_art_crop`; if (setCode && collectorNumber) { cardVersionId = `${trimmedCardName}_${setCode.toLowerCase()}_${collectorNumber}_art_crop`; } if (downloadedFiles.length > 0) { const isTransformCard = downloadedFiles.length > 1; const db = await createCardDatabase(); try { if (isTransformCard) { // Handle transform card with multiple faces const faces = []; let hasError = false; for (const file of downloadedFiles) { try { // Generate face-specific card version ID let faceCardVersionId; if (setCode && collectorNumber) { faceCardVersionId = `${trimmedCardName}_${setCode.toLowerCase()}_${collectorNumber}_face${file.face_index}_art_crop`; } else { faceCardVersionId = `${trimmedCardName}_face${file.face_index}_art_crop`; } const cardInfo = db.getCardInfo(faceCardVersionId); const fileId = cardInfo?.file_id; const faceResult = { status: 'success', message: `Art crop face ${file.face_index} (${file.face_name}) downloaded successfully`, filepath: file.filePath, card_name: file.cardName, face_index: file.face_index, face_name: file.face_name, total_faces: file.total_faces, is_transform_card: file.is_transform_card }; // Add optional fields if (setCode !== undefined) { faceResult.set_code = setCode; } if (collectorNumber !== undefined) { faceResult.collector_number = collectorNumber; } // Add resource URIs if we have a file_id if (fileId) { faceResult.resource_uri = `resource://download/art/${fileId}`; // Add metadata_uri if we have JSON metadata if (file.jsonPath) { faceResult.metadata_uri = `resource://download/metadata/${fileId}`; } } faces.push(faceResult); } catch (faceError) { logger.error(`Error processing art crop face ${file.face_index}:`, faceError); hasError = true; faces.push({ status: 'error', message: `Failed to process art crop face ${file.face_index} (${file.face_name})`, face_index: file.face_index, face_name: file.face_name }); } } // Return main result with faces array const result = { status: hasError ? 'error' : 'success', message: hasError ? `Transform card art crops for '${trimmedCardName}' partially downloaded (some faces failed)` : `Transform card art crops for '${trimmedCardName}' downloaded successfully (${faces.length} faces)`, card_name: trimmedCardName, total_faces: downloadedFiles[0]?.total_faces, is_transform_card: true, faces: faces }; // Add optional fields if (setCode !== undefined) { result.set_code = setCode; } if (collectorNumber !== undefined) { result.collector_number = collectorNumber; } logger.info(`[API] Transform card art crop download completed: ${trimmedCardName} -> ${faces.length} faces`); return result; } else { // Handle single-faced card (backward compatibility) const file = downloadedFiles[0]; const imagePath = file.filePath; const jsonPath = file.jsonPath; const cardInfo = db.getCardInfo(cardVersionId); let fileId = cardInfo?.file_id; // If no existing record found, register the file and get the file_id if (!fileId) { // File should already be registered by the download manager, but let's verify const newCardInfo = db.getCardInfo(cardVersionId); fileId = newCardInfo?.file_id; } // Build result matching Python implementation structure const result = { status: 'success', message: `Art crop for '${trimmedCardName}' downloaded successfully`, filepath: imagePath, card_name: trimmedCardName }; // Add optional fields only if defined (matching Python conditional assignment) if (setCode !== undefined) { result.set_code = setCode; } if (collectorNumber !== undefined) { result.collector_number = collectorNumber; } // Add resource URIs if we have file_id (matching Python resource_uri format) if (fileId) { result.resource_uri = `resource://download/art/${fileId}`; // Add metadata_uri if we have JSON metadata if (jsonPath) { result.metadata_uri = `resource://download/metadata/${fileId}`; } } logger.info(`[API] Art crop download successful: ${trimmedCardName} -> ${fileId || 'no file_id'}`); return result; } } finally { db.close(); } } else { return { status: 'error', message: `Failed to download art crop for '${trimmedCardName}' - no files were downloaded` }; } } catch (error) { logger.error(`[Error] Failed to download art crop for '${cardName}':`, error); return { status: 'error', message: error instanceof Error ? error.message : 'Unknown error occurred during art crop download' }; } } //# sourceMappingURL=downloadTools.js.map