search_p3_playlist_by_date
Find songs played on Sveriges Radio P3 for a specific date or date range within the last 90 days. Optionally filter results by artist.
Instructions
Search Sveriges Radio P3 playlist history for a specific date or date range. Returns an array of songs that played during the specified time period. Dates must be within the last 90 days and cannot be in the future.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | ISO 8601 date string (e.g., "2024-12-15") or date range (e.g., "2024-12-01 to 2024-12-31") | |
| artist_filter | No | Optional: Filter results by artist name (case-insensitive substring match) | |
| limit | No | Optional: Maximum number of songs to return (default: 25, max: 100) |
Implementation Reference
- src/tools/search-playlist.ts:164-244 (handler)Main handler function for the search_p3_playlist_by_date tool. Parses date input, fetches playlist from SR API, applies optional artist filter and limit, and returns a PlaylistResponse.
export async function searchPlaylistByDate( input: SearchPlaylistInput ): Promise<PlaylistResponse> { const apiClient = getApiClient(); const errors: string[] = []; let songs: Song[] = []; try { // Parse and validate date input const { startDateTime, endDateTime } = parseDateInput(input.date); // Fetch playlist from API const response = await apiClient.getPlaylistByDateRange(startDateTime, endDateTime); if (!response.song) { throw new Error('No playlist data returned from Sveriges Radio API'); } // Parse songs const apiSongs = normalizeToArray(response.song); songs = apiSongs .map((apiSong, index) => parseSong(apiSong, index)) .filter((song): song is Song => song !== null); // Apply artist filter if provided if (input.artist_filter) { const originalCount = songs.length; songs = filterByArtist(songs, input.artist_filter); if (songs.length === 0 && originalCount > 0) { errors.push( `No songs found matching artist filter: "${input.artist_filter}". ` + `Found ${originalCount} total songs in the date range.` ); } } // Apply limit if (songs.length > input.limit) { songs = songs.slice(0, input.limit); } return { songs, metadata: { channel: 'P3', channelId: P3_CHANNEL_ID, timestamp: new Date().toISOString(), query: { type: 'date-range', startDate: startDateTime, endDate: endDateTime, artistFilter: input.artist_filter, limit: input.limit, }, }, errors: errors.length > 0 ? errors : undefined, }; } catch (error) { // Convert error to user-friendly message const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred while searching the playlist'; return { songs: [], metadata: { channel: 'P3', channelId: P3_CHANNEL_ID, timestamp: new Date().toISOString(), query: { type: 'date-range', artistFilter: input.artist_filter, limit: input.limit, }, }, errors: [errorMessage], }; } } - src/tools/search-playlist.ts:13-33 (schema)Zod schema for input validation: requires 'date' (string), optional 'artist_filter' (string), optional 'limit' (number, default 25, max 100).
export const SearchPlaylistSchema = z.object({ date: z .string() .min(1, 'Date is required') .describe( 'ISO 8601 date string (e.g., "2024-12-15") or date range "2024-12-01 to 2024-12-31"' ), artist_filter: z .string() .optional() .describe('Filter results by artist name (case-insensitive substring match)'), limit: z .number() .int() .min(1) .max(100) .default(25) .describe('Maximum number of songs to return (default: 25, max: 100)'), }); export type SearchPlaylistInput = z.infer<typeof SearchPlaylistSchema>; - src/server.ts:42-72 (registration)Tool registration in TOOLS array: declares name, description, and inputSchema for the search_p3_playlist_by_date tool.
{ name: 'search_p3_playlist_by_date', description: 'Search Sveriges Radio P3 playlist history for a specific date or date range. ' + 'Returns an array of songs that played during the specified time period. ' + 'Dates must be within the last 90 days and cannot be in the future.', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'ISO 8601 date string (e.g., "2024-12-15") or date range (e.g., "2024-12-01 to 2024-12-31")', }, artist_filter: { type: 'string', description: 'Optional: Filter results by artist name (case-insensitive substring match)', }, limit: { type: 'number', description: 'Optional: Maximum number of songs to return (default: 25, max: 100)', default: 25, minimum: 1, maximum: 100, }, }, required: ['date'], }, }, - src/server.ts:124-137 (registration)Route handler in the CallToolRequestSchema switch: validates input with Zod schema, calls the handler, and returns JSON result.
case 'search_p3_playlist_by_date': { // Validate input with Zod const validatedInput = SearchPlaylistSchema.parse(args || {}); const result = await searchPlaylistByDate(validatedInput); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } - src/tools/search-playlist.ts:39-108 (helper)Helper function that parses a date string (single date or 'date1 to date2' range) and validates it's within the last 90 days and not in the future.
function parseDateInput(dateInput: string): { startDateTime: string; endDateTime: string } { const now = new Date(); const ninetyDaysAgo = new Date(now); ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90); // Check if it's a date range (contains "to") const isRange = dateInput.includes(' to '); let startDate: Date; let endDate: Date; try { if (isRange) { const [startStr, endStr] = dateInput.split(' to ').map((s) => s.trim()); startDate = new Date(startStr); endDate = new Date(endStr); if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { throw new Error('Invalid date format in range'); } // Set start to beginning of day, end to end of day startDate.setUTCHours(0, 0, 0, 0); endDate.setUTCHours(23, 59, 59, 999); } else { // Single date - search the entire day startDate = new Date(dateInput); if (isNaN(startDate.getTime())) { throw new Error('Invalid date format'); } // Set to beginning of day startDate.setUTCHours(0, 0, 0, 0); // End date is the same day at end of day endDate = new Date(startDate); endDate.setUTCHours(23, 59, 59, 999); } } catch { throw new Error( 'Invalid date format. Please use ISO 8601 format (e.g., "2024-12-15") or ' + 'date range format (e.g., "2024-12-01 to 2024-12-31")' ); } // Validate: no future dates if (startDate > now || endDate > now) { throw new Error( 'Future dates are not allowed. Please provide a date within the last 90 days.' ); } // Validate: within last 90 days if (startDate < ninetyDaysAgo) { throw new Error( 'Date is too far in the past. Please provide a date within the last 90 days.' ); } // Validate: start date before end date if (startDate > endDate) { throw new Error('Start date must be before or equal to end date.'); } return { startDateTime: startDate.toISOString(), endDateTime: endDate.toISOString(), }; }