get_playlist_tracks
Retrieve tracks from a Spotify playlist with pagination support for handling large collections. Specify playlist ID and optional limit/offset parameters to get specific track ranges or all tracks.
Instructions
Get tracks from a playlist with full pagination support.
Args:
playlist_id: Playlist ID
limit: Max tracks to return (None for all tracks, up to 10,000 safety limit)
offset: Number of tracks to skip for pagination (default 0)
Returns:
Dict with 'items' (list of tracks), 'total', 'limit', 'offset'
Note: Large playlists require pagination. Use limit/offset to get specific ranges:
- Get first 100: limit=100, offset=0
- Get next 100: limit=100, offset=100
- Get all tracks: limit=None (use with caution on very large playlists)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| playlist_id | Yes | ||
| limit | No | ||
| offset | No |
Implementation Reference
- Main handler function for the 'get_playlist_tracks' MCP tool. Registers the tool with @mcp.tool(), logs execution, fetches tracks using paginated helper, retrieves total count, and returns structured response with pagination metadata.@mcp.tool() @log_tool_execution def get_playlist_tracks( playlist_id: str, limit: int | None = None, offset: int = 0 ) -> dict[str, Any]: """Get tracks from a playlist with full pagination support. Args: playlist_id: Playlist ID limit: Max tracks to return (None for all tracks, up to 10,000 safety limit) offset: Number of tracks to skip for pagination (default 0) Returns: Dict with 'items' (list of tracks), 'total', 'limit', 'offset' Note: Large playlists require pagination. Use limit/offset to get specific ranges: - Get first 100: limit=100, offset=0 - Get next 100: limit=100, offset=100 - Get all tracks: limit=None (use with caution on very large playlists) """ try: logger.info( f"📋 Getting playlist tracks: {playlist_id} (limit={limit}, offset={offset})" ) tracks = get_playlist_tracks_paginated(playlist_id, limit, offset) # Get total track count from playlist info playlist_info = spotify_client.playlist(playlist_id, fields="tracks.total") total_tracks = playlist_info.get("tracks", {}).get("total", len(tracks)) # Log pagination info log_pagination_info("get_playlist_tracks", total_tracks, limit, offset) logger.info(f"📋 Retrieved {len(tracks)} tracks from playlist {playlist_id}") return { "items": tracks, "total": total_tracks, "limit": limit, "offset": offset, "returned": len(tracks), } except SpotifyException as e: raise convert_spotify_error(e) from e
- Core pagination helper that fetches playlist tracks in batches (max 100 per API call), handles offsets/limits, parses tracks, and includes safety limits to prevent infinite loops.def get_playlist_tracks_paginated( playlist_id: str, limit: int | None = None, offset: int = 0 ) -> list[Track]: """Get playlist tracks with proper pagination support. Args: playlist_id: Spotify playlist ID limit: Maximum number of tracks to return (None for all) offset: Number of tracks to skip Returns: List of Track objects """ tracks = [] current_offset = offset batch_size = min(limit, 100) if limit else 100 # Spotify API max is 100 per request remaining = limit logger.info( f"📄 Starting paginated fetch for playlist {playlist_id} (limit={limit}, offset={offset})" ) while True: # Determine how many to fetch in this batch batch_limit = min(batch_size, remaining) if remaining else batch_size logger.info(f"📄 Fetching batch: offset={current_offset}, limit={batch_limit}") # Get playlist tracks with pagination tracks_result = spotify_client.playlist_tracks( playlist_id, limit=batch_limit, offset=current_offset ) if not tracks_result or not tracks_result.get("items"): break # Parse and add tracks batch_tracks = [] for item in tracks_result["items"]: if item and item.get("track"): batch_tracks.append(parse_track(item["track"])) tracks.extend(batch_tracks) logger.info( f"📄 Batch complete: retrieved {len(batch_tracks)} tracks (total so far: {len(tracks)})" ) # Update remaining count if we have a limit if remaining: remaining -= len(batch_tracks) if remaining <= 0: break # Check if we've reached the end if len(tracks_result["items"]) < batch_limit or not tracks_result.get("next"): break current_offset += len(tracks_result["items"]) # Safety check to prevent infinite loops if current_offset > 10000: logger.warning( f"⚠️ Safety limit reached: stopping at offset {current_offset}" ) break logger.info(f"📄 Pagination complete: total {len(tracks)} tracks retrieved") return tracks
- Pydantic model defining the structured output format for individual tracks returned by the tool.class Track(BaseModel): """A Spotify track with metadata.""" name: str id: str artist: str artists: list[str] | None = None album: str | None = None album_id: str | None = None release_date: str | None = None duration_ms: int | None = None popularity: int | None = None external_urls: dict[str, str] | None = None
- Utility function that transforms raw Spotify API track dictionaries into standardized Track model instances.def parse_track(item: dict[str, Any]) -> Track: """Parse Spotify track data into Track model.""" album_data = item.get("album", {}) return Track( name=item["name"], id=item["id"], artist=item["artists"][0]["name"] if item.get("artists") else "Unknown", artists=[a["name"] for a in item.get("artists", [])], album=album_data.get("name"), album_id=album_data.get("id"), release_date=album_data.get("release_date"), duration_ms=item.get("duration_ms"), popularity=item.get("popularity"), external_urls=item.get("external_urls"), )
- src/spotify_mcp/fastmcp_server.py:625-625 (registration)FastMCP decorator that registers the get_playlist_tracks function as an MCP tool, automatically generating input/output schemas from type hints.@mcp.tool()