Skip to main content
Glama
varunneal

Spotify MCP Server

by varunneal

SpotifyPlaylist

Create, manage, and modify Spotify playlists by adding or removing tracks, updating details, and viewing playlist contents.

Instructions

Manage Spotify playlists. - get: Get a list of user's playlists. - get_tracks: Get tracks in a specific playlist. - add_tracks: Add tracks to a specific playlist. - remove_tracks: Remove tracks from a specific playlist. - change_details: Change details of a specific playlist. - create: Create a new playlist.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform: 'get', 'get_tracks', 'add_tracks', 'remove_tracks', 'change_details', 'create'.
playlist_idNoID of the playlist to manage.
track_idsNoList of track IDs to add/remove.
nameNoName for the playlist (required for create and change_details).
descriptionNoDescription for the playlist.
publicNoWhether the playlist should be public (for create action).

Implementation Reference

  • Handler logic for executing the SpotifyPlaylist tool. Dispatches based on 'action' parameter to various playlist operations by calling corresponding methods on spotify_client.
    case "Playlist": logger.info(f"Playlist operation with arguments: {arguments}") action = arguments.get("action") match action: case "get": logger.info(f"Getting current user's playlists with arguments: {arguments}") playlists = spotify_client.get_current_user_playlists() return [types.TextContent( type="text", text=json.dumps(playlists, indent=2) )] case "get_tracks": logger.info(f"Getting tracks in playlist with arguments: {arguments}") if not arguments.get("playlist_id"): logger.error("playlist_id is required for get_tracks action.") return [types.TextContent( type="text", text="playlist_id is required for get_tracks action." )] tracks = spotify_client.get_playlist_tracks(arguments.get("playlist_id")) return [types.TextContent( type="text", text=json.dumps(tracks, indent=2) )] case "add_tracks": logger.info(f"Adding tracks to playlist with arguments: {arguments}") track_ids = arguments.get("track_ids") if isinstance(track_ids, str): try: track_ids = json.loads(track_ids) # Convert JSON string to Python list except json.JSONDecodeError: logger.error("track_ids must be a list or a valid JSON array.") return [types.TextContent( type="text", text="Error: track_ids must be a list or a valid JSON array." )] spotify_client.add_tracks_to_playlist( playlist_id=arguments.get("playlist_id"), track_ids=track_ids ) return [types.TextContent( type="text", text="Tracks added to playlist." )] case "remove_tracks": logger.info(f"Removing tracks from playlist with arguments: {arguments}") track_ids = arguments.get("track_ids") if isinstance(track_ids, str): try: track_ids = json.loads(track_ids) # Convert JSON string to Python list except json.JSONDecodeError: logger.error("track_ids must be a list or a valid JSON array.") return [types.TextContent( type="text", text="Error: track_ids must be a list or a valid JSON array." )] spotify_client.remove_tracks_from_playlist( playlist_id=arguments.get("playlist_id"), track_ids=track_ids ) return [types.TextContent( type="text", text="Tracks removed from playlist." )] case "change_details": logger.info(f"Changing playlist details with arguments: {arguments}") if not arguments.get("playlist_id"): logger.error("playlist_id is required for change_details action.") return [types.TextContent( type="text", text="playlist_id is required for change_details action." )] if not arguments.get("name") and not arguments.get("description"): logger.error("At least one of name, description or public is required.") return [types.TextContent( type="text", text="At least one of name, description, public, or collaborative is required." )] spotify_client.change_playlist_details( playlist_id=arguments.get("playlist_id"), name=arguments.get("name"), description=arguments.get("description") ) return [types.TextContent( type="text", text="Playlist details changed." )] case "create": logger.info(f"Creating playlist with arguments: {arguments}") if not arguments.get("name"): logger.error("name is required for create action.") return [types.TextContent( type="text", text="name is required for create action." )] playlist = spotify_client.create_playlist( name=arguments.get("name"), description=arguments.get("description"), public=arguments.get("public", True) ) return [types.TextContent( type="text", text=json.dumps(playlist, indent=2) )] case _: return [types.TextContent( type="text", text=f"Unknown playlist action: {action}." "Supported actions are: get, get_tracks, add_tracks, remove_tracks, change_details, create." )] case _:
  • Pydantic model defining the input schema for the SpotifyPlaylist tool, used to generate the tool schema.
    class Playlist(ToolModel): """Manage Spotify playlists. - get: Get a list of user's playlists. - get_tracks: Get tracks in a specific playlist. - add_tracks: Add tracks to a specific playlist. - remove_tracks: Remove tracks from a specific playlist. - change_details: Change details of a specific playlist. - create: Create a new playlist. """ action: str = Field( description="Action to perform: 'get', 'get_tracks', 'add_tracks', 'remove_tracks', 'change_details', 'create'.") playlist_id: Optional[str] = Field(default=None, description="ID of the playlist to manage.") track_ids: Optional[List[str]] = Field(default=None, description="List of track IDs to add/remove.") name: Optional[str] = Field(default=None, description="Name for the playlist (required for create and change_details).") description: Optional[str] = Field(default=None, description="Description for the playlist.") public: Optional[bool] = Field(default=True, description="Whether the playlist should be public (for create action).")
  • Registers the SpotifyPlaylist tool (as Playlist.as_tool()) in the list of available tools returned to MCP clients.
    @server.list_tools() async def handle_list_tools() -> list[types.Tool]: """List available tools.""" logger.info("Listing available tools") # await server.request_context.session.send_notification("are you recieving this notification?") tools = [ Playback.as_tool(), Search.as_tool(), Queue.as_tool(), GetInfo.as_tool(), Playlist.as_tool(), ] logger.info(f"Available tools: {[tool.name for tool in tools]}") return tools
  • Helper method to retrieve the current user's playlists, called by 'get' action.
    def get_current_user_playlists(self, limit=50) -> List[Dict]: """ Get current user's playlists. - limit: Max number of playlists to return. """ playlists = self.sp.current_user_playlists() if not playlists: raise ValueError("No playlists found.") return [utils.parse_playlist(playlist, self.username) for playlist in playlists['items']]
  • Helper method to create a new playlist, called by 'create' action in the handler.
    def create_playlist(self, name: str, description: Optional[str] = None, public: bool = True): """ Create a new playlist. - name: Name for the playlist. - description: Description for the playlist. - public: Whether the playlist should be public. """ if not name: raise ValueError("Playlist name is required.") try: user = self.sp.current_user() user_id = user['id'] playlist = self.sp.user_playlist_create( user=user_id, name=name, public=public, description=description ) self.logger.info(f"Created playlist: {name} (ID: {playlist['id']})") return utils.parse_playlist(playlist, self.username, detailed=True) except Exception as e: self.logger.error(f"Error creating playlist: {str(e)}") raise

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

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