Skip to main content
Glama
result-aggregator.ts6.38 kB
/** * Navidrome MCP Server - Search Result Aggregation * Copyright (C) 2025 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ import type { SongDTO, AlbumDTO, ArtistDTO } from '../../types/index.js'; import { transformSongsToDTO, transformAlbumsToDTO, transformArtistsToDTO } from '../../transformers/index.js'; import { logger } from '../../utils/logger.js'; /** * Raw API response data from parallel search requests */ export interface ParallelSearchResponses { songsResponse: unknown[]; albumsResponse: unknown[]; artistsResponse: unknown[]; } /** * Aggregated search results with metadata */ interface AggregatedSearchResult { artists: ArtistDTO[]; albums: AlbumDTO[]; songs: SongDTO[]; query: string; totalResults: number; appliedFilters?: Record<string, string>; } /** * Transform and aggregate parallel search responses into a unified result * Handles the transformation of raw API responses to DTOs and combines them with metadata * * @param responses - Raw responses from parallel API calls * @param query - Original search query string * @param appliedFilters - Filters that were successfully applied to the search * @returns Aggregated search result with transformed DTOs and metadata */ export function aggregateSearchResults( responses: ParallelSearchResponses, query: string, appliedFilters: Record<string, string> ): AggregatedSearchResult { // Data collection - extract responses const { songsResponse, albumsResponse, artistsResponse } = responses; // Processing - transform responses to DTOs const songs = transformSongsToDTO(songsResponse); const albums = transformAlbumsToDTO(albumsResponse); const artists = transformArtistsToDTO(artistsResponse); // Calculate total results across all content types const totalResults = songs.length + albums.length + artists.length; logger.debug(`Enhanced search completed: ${totalResults} total results (${songs.length} songs, ${albums.length} albums, ${artists.length} artists)`); // Output construction - build aggregated result const result: AggregatedSearchResult = { artists, albums, songs, query, totalResults, }; // Only include appliedFilters if any filters were applied if (Object.keys(appliedFilters).length > 0) { result.appliedFilters = appliedFilters; } return result; } /** * Parameters for building URL search parameters for different content types */ interface SearchParamsConfig { artistCount: number; albumCount: number; songCount: number; query: string; sort?: string | undefined; order?: 'ASC' | 'DESC' | undefined; randomSeed?: number | undefined; resolvedFilters: Record<string, string>; yearFrom?: number | undefined; yearTo?: number | undefined; starred?: boolean | undefined; } /** * Result of building search parameters for different content types */ interface ContentTypeParams { songParams: string; albumParams: string; artistParams: string; } /** * Build URL search parameters for different content types with appropriate sort fields * Each content type (songs, albums, artists) may have different optimal sort fields * * @param config - Configuration for building search parameters * @returns Object containing URL parameters for each content type */ export function buildContentTypeParams(config: SearchParamsConfig): ContentTypeParams { // Data collection - extract configuration values const { artistCount, albumCount, songCount, query, sort, order, randomSeed, resolvedFilters, yearFrom, yearTo, starred } = config; // Processing - create parameter building function const buildParams = ( limit: number, searchField: string, sortField: string ): string => { const searchParams = new URLSearchParams(); // Add pagination searchParams.set('_start', '0'); searchParams.set('_end', limit.toString()); // Add search term as direct parameter (only if not empty) if (query !== '' && query.trim() !== '') { searchParams.set(searchField, query); } // Add sorting searchParams.set('_sort', sortField); searchParams.set('_order', order ?? 'ASC'); // Add random seed if using random sort if (sortField === 'random' && randomSeed !== undefined) { searchParams.set('seed', randomSeed.toString()); } // Add resolved filters Object.entries(resolvedFilters).forEach(([key, value]) => { searchParams.set(key, value); }); // Add boolean filters if (starred !== undefined) { searchParams.set('starred', starred.toString()); } // Add year filtering (for albums and songs) if (yearFrom !== undefined) { searchParams.set('year_from', yearFrom.toString()); } if (yearTo !== undefined) { searchParams.set('year_to', yearTo.toString()); } return searchParams.toString(); }; // Determine appropriate sort field for each endpoint const getSortField = (defaultSort: string): string => { const requestedSort = sort ?? defaultSort; // Map common sort fields to endpoint-specific ones switch (requestedSort) { case 'name': return defaultSort === 'title' ? 'title' : 'name'; case 'recently_added': return 'recently_added'; case 'starred_at': return 'starred_at'; case 'random': return 'random'; default: return requestedSort; } }; // Output construction - build parameters for each endpoint type with appropriate sort fields const songParams = buildParams(songCount, 'title', getSortField('title')); const albumParams = buildParams(albumCount, 'name', getSortField('name')); const artistParams = buildParams(artistCount, 'name', getSortField('name')); return { songParams, albumParams, artistParams }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Blakeem/Navidrome-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server