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
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action to perform: 'get', 'get_tracks', 'add_tracks', 'remove_tracks', 'change_details', 'create'. | |
| playlist_id | No | ID of the playlist to manage. | |
| track_ids | No | List of track IDs to add/remove. | |
| name | No | Name for the playlist (required for create and change_details). | |
| description | No | Description for the playlist. | |
| public | No | Whether the playlist should be public (for create action). |
Implementation Reference
- src/spotify_mcp/server.py:238-355 (handler)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 _:
- src/spotify_mcp/server.py:88-104 (schema)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).")
- src/spotify_mcp/server.py:116-129 (registration)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