Skip to main content
Glama

Spotify MCP Server

by gentiku
spotify_client.py•7.8 kB
"""Spotify API client with authentication and core functionality.""" import json import os from typing import Dict, List, Optional, Any import spotipy from spotipy.oauth2 import SpotifyOAuth from spotipy.exceptions import SpotifyException import logging from config import config # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class SpotifyClient: """Spotify API client with authentication and core operations.""" def __init__(self): """Initialize the Spotify client.""" self.sp = None self.token_file = "spotify_token.json" self._authenticate() def _authenticate(self) -> None: """Authenticate with Spotify API.""" try: # Check if we have a cached token if os.path.exists(self.token_file): with open(self.token_file, 'r') as f: token_info = json.load(f) # Create Spotify client with cached token self.sp = spotipy.Spotify(auth=token_info['access_token']) # Test the token try: self.sp.current_user() logger.info("Successfully authenticated with cached token") return except SpotifyException: logger.info("Cached token expired, refreshing...") # Create OAuth flow auth_manager = SpotifyOAuth( client_id=config.SPOTIFY_CLIENT_ID, client_secret=config.SPOTIFY_CLIENT_SECRET, redirect_uri=config.SPOTIFY_REDIRECT_URI, scope=" ".join(config.SPOTIFY_SCOPES), cache_path=self.token_file ) # Get authorization URL auth_url = auth_manager.get_authorize_url() print(f"Please visit this URL to authorize the application: {auth_url}") # Get the authorization code from user auth_code = input("Enter the authorization code: ") # Get access token token_info = auth_manager.get_access_token(auth_code) # Create Spotify client self.sp = spotipy.Spotify(auth_manager=auth_manager) logger.info("Successfully authenticated with Spotify API") except Exception as e: logger.error(f"Authentication failed: {e}") raise def search(self, query: str, search_type: str = "track", limit: int = 20) -> Dict[str, Any]: """Search for tracks, albums, artists, or playlists.""" try: results = self.sp.search(q=query, type=search_type, limit=limit) return results except SpotifyException as e: logger.error(f"Search failed: {e}") raise def get_current_playback(self) -> Optional[Dict[str, Any]]: """Get current playback information.""" try: return self.sp.current_playback() except SpotifyException as e: logger.error(f"Failed to get current playback: {e}") return None def start_playback(self, device_id: Optional[str] = None, context_uri: Optional[str] = None, uris: Optional[List[str]] = None, offset: Optional[Dict[str, Any]] = None) -> bool: """Start or resume playback.""" try: self.sp.start_playback(device_id=device_id, context_uri=context_uri, uris=uris, offset=offset) return True except SpotifyException as e: logger.error(f"Failed to start playback: {e}") return False def pause_playback(self, device_id: Optional[str] = None) -> bool: """Pause playback.""" try: self.sp.pause_playback(device_id=device_id) return True except SpotifyException as e: logger.error(f"Failed to pause playback: {e}") return False def next_track(self, device_id: Optional[str] = None) -> bool: """Skip to next track.""" try: self.sp.next_track(device_id=device_id) return True except SpotifyException as e: logger.error(f"Failed to skip to next track: {e}") return False def previous_track(self, device_id: Optional[str] = None) -> bool: """Skip to previous track.""" try: self.sp.previous_track(device_id=device_id) return True except SpotifyException as e: logger.error(f"Failed to skip to previous track: {e}") return False def set_volume(self, volume_percent: int, device_id: Optional[str] = None) -> bool: """Set playback volume.""" try: self.sp.volume(volume_percent, device_id=device_id) return True except SpotifyException as e: logger.error(f"Failed to set volume: {e}") return False def get_devices(self) -> List[Dict[str, Any]]: """Get available devices.""" try: devices = self.sp.devices() return devices.get('devices', []) except SpotifyException as e: logger.error(f"Failed to get devices: {e}") return [] def get_user_playlists(self, limit: int = 50) -> List[Dict[str, Any]]: """Get user's playlists.""" try: playlists = self.sp.current_user_playlists(limit=limit) return playlists.get('items', []) except SpotifyException as e: logger.error(f"Failed to get user playlists: {e}") return [] def create_playlist(self, name: str, description: str = "", public: bool = False) -> Optional[Dict[str, Any]]: """Create a new playlist.""" try: user_id = self.sp.current_user()['id'] playlist = self.sp.user_playlist_create( user=user_id, name=name, description=description, public=public ) return playlist except SpotifyException as e: logger.error(f"Failed to create playlist: {e}") return None def add_tracks_to_playlist(self, playlist_id: str, track_uris: List[str]) -> bool: """Add tracks to a playlist.""" try: self.sp.playlist_add_items(playlist_id, track_uris) return True except SpotifyException as e: logger.error(f"Failed to add tracks to playlist: {e}") return False def get_user_top_tracks(self, time_range: str = "medium_term", limit: int = 20) -> List[Dict[str, Any]]: """Get user's top tracks.""" try: tracks = self.sp.current_user_top_tracks(time_range=time_range, limit=limit) return tracks.get('items', []) except SpotifyException as e: logger.error(f"Failed to get user top tracks: {e}") return [] def get_recently_played(self, limit: int = 20) -> List[Dict[str, Any]]: """Get recently played tracks.""" try: tracks = self.sp.current_user_recently_played(limit=limit) return tracks.get('items', []) except SpotifyException as e: logger.error(f"Failed to get recently played tracks: {e}") return [] def get_user_profile(self) -> Optional[Dict[str, Any]]: """Get current user's profile.""" try: return self.sp.current_user() except SpotifyException as e: logger.error(f"Failed to get user profile: {e}") return None

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/gentiku/mcp-spotify-vercel'

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