UNPKG

anilist-mcp

Version:

AniList MCP server for accessing AniList API data

120 lines (119 loc) 4.27 kB
/** * Utility functions to filter media responses from AniList API * to reduce token usage and prevent crashes from large JSON payloads */ /** * Filters a single media object to include only essential fields * Removes large arrays like character lists, staff lists, relations, recommendations */ function filterSingleMedia(media) { // Create a filtered object with only essential fields const filtered = { id: media.id, idMal: media.idMal, title: media.title, format: media.format, status: media.status, description: media.description, startDate: media.startDate, endDate: media.endDate, countryOfOrigin: media.countryOfOrigin, isLicensed: media.isLicensed, hashtag: media.hashtag, updatedAt: media.updatedAt, coverImage: media.coverImage, bannerImage: media.bannerImage, genres: media.genres, synonyms: media.synonyms, averageScore: media.averageScore, meanScore: media.meanScore, popularity: media.popularity, favourites: media.favourites, isAdult: media.isAdult, siteUrl: media.siteUrl, }; // Add anime-specific fields (type guard for AnimeEntry) if ("episodes" in media && media.episodes !== undefined) { filtered.episodes = media.episodes; filtered.season = media.season; filtered.seasonYear = media.seasonYear; filtered.duration = media.duration; filtered.source = media.source; } // Add manga-specific fields (type guard for MangaEntry) if ("chapters" in media && media.chapters !== undefined) { filtered.chapters = media.chapters; filtered.volumes = media.volumes; } // Include nextAiringEpisode for anime if (media.nextAiringEpisode && Array.isArray(media.nextAiringEpisode)) { filtered.nextAiringEpisode = media.nextAiringEpisode .slice(0, 1) // Only include the next airing episode .map((airing) => ({ airingAt: airing.airingAt, timeUntilAiring: airing.timeUntilAiring, episode: airing.episode, })); } // Include limited tags (top 5 by relevance) if (media.tags && Array.isArray(media.tags)) { filtered.tags = media.tags.slice(0, 5).map((tag) => ({ id: tag.id, name: tag.name, isMediaSpoiler: tag.isMediaSpoiler, })); } // Include limited studios (top 3 main studios) if (media.studios && Array.isArray(media.studios)) { filtered.studios = media.studios .slice(0, 3) .map((studio) => ({ id: studio.id, name: studio.name, isAnimationStudio: studio.isAnimationStudio, })); } // Include external links (limited to top 5) if (media.externalLinks && Array.isArray(media.externalLinks)) { filtered.externalLinks = media.externalLinks.slice(0, 5); } // Include limited streaming episodes (top 3) if (media.streamingEpisodes && Array.isArray(media.streamingEpisodes)) { filtered.streamingEpisodes = media.streamingEpisodes .slice(0, 3) .map((ep) => ({ title: ep.title, url: ep.url, })); } // Include top 3 rankings if (media.rankings && Array.isArray(media.rankings)) { filtered.rankings = media.rankings .slice(0, 3) .map((ranking) => ({ rank: ranking.rank, type: ranking.type, context: ranking.context, year: ranking.year, season: ranking.season, })); } // Remove undefined/null values to reduce payload size Object.keys(filtered).forEach((key) => { const typedKey = key; if (filtered[typedKey] === undefined || filtered[typedKey] === null) { delete filtered[typedKey]; } }); return filtered; } /** * Filters media response(s) to include only essential fields * Handles both single media objects and arrays of media */ export function filterMedia(media) { if (Array.isArray(media)) { return media.map((m) => filterSingleMedia(m)); } return filterSingleMedia(media); }