Skip to main content
Glama
varunneal

Spotify MCP Server

by varunneal

SpotifyPlayback

Control Spotify playback: get current track info, start/resume playback, pause music, or skip tracks using the Spotify MCP Server.

Instructions

Manages the current playback with the following actions: - get: Get information about user's current track. - start: Starts playing new item or resumes current playback if called with no uri. - pause: Pauses current playback. - skip: Skips current track.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform: 'get', 'start', 'pause' or 'skip'.
spotify_uriNoSpotify uri of item to play for 'start' action. If omitted, resumes current playback.
num_skipsNoNumber of tracks to skip for `skip` action.

Implementation Reference

  • Pydantic model defining the input schema for the SpotifyPlayback tool, including action (get/start/pause/skip), spotify_uri, and num_skips parameters.
    class Playback(ToolModel):
        """Manages the current playback with the following actions:
        - get: Get information about user's current track.
        - start: Starts playing new item or resumes current playback if called with no uri.
        - pause: Pauses current playback.
        - skip: Skips current track.
        """
        action: str = Field(description="Action to perform: 'get', 'start', 'pause' or 'skip'.")
        spotify_uri: Optional[str] = Field(default=None, description="Spotify uri of item to play for 'start' action. " +
                                                                     "If omitted, resumes current playback.")
        num_skips: Optional[int] = Field(default=1, description="Number of tracks to skip for `skip` action.")
  • Registers the SpotifyPlayback tool (as Playback.as_tool()) in the MCP server's list_tools handler, constructing name as 'SpotifyPlayback'.
    @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
  • Main handler logic for SpotifyPlayback tool within the @server.call_tool() function. Dispatches based on 'action' to spotify_client methods: get_current_track, start_playback, pause_playback, skip_track.
    case "Playback":
        action = arguments.get("action")
        match action:
            case "get":
                logger.info("Attempting to get current track")
                curr_track = spotify_client.get_current_track()
                if curr_track:
                    logger.info(f"Current track retrieved: {curr_track.get('name', 'Unknown')}")
                    return [types.TextContent(
                        type="text",
                        text=json.dumps(curr_track, indent=2)
                    )]
                logger.info("No track currently playing")
                return [types.TextContent(
                    type="text",
                    text="No track playing."
                )]
            case "start":
                logger.info(f"Starting playback with arguments: {arguments}")
                spotify_client.start_playback(spotify_uri=arguments.get("spotify_uri"))
                logger.info("Playback started successfully")
                return [types.TextContent(
                    type="text",
                    text="Playback starting."
                )]
            case "pause":
                logger.info("Attempting to pause playback")
                spotify_client.pause_playback()
                logger.info("Playback paused successfully")
                return [types.TextContent(
                    type="text",
                    text="Playback paused."
                )]
            case "skip":
                num_skips = int(arguments.get("num_skips", 1))
                logger.info(f"Skipping {num_skips} tracks.")
                spotify_client.skip_track(n=num_skips)
                return [types.TextContent(
                    type="text",
                    text="Skipped to next track."
                )]
  • Helper method spotify_client.get_current_track() called by handler for 'get' action.
    def get_current_track(self) -> Optional[Dict]:
        """Get information about the currently playing track"""
        try:
            # current_playback vs current_user_playing_track?
            current = self.sp.current_user_playing_track()
            if not current:
                self.logger.info("No playback session found")
                return None
            if current.get('currently_playing_type') != 'track':
                self.logger.info("Current playback is not a track")
                return None
    
            track_info = utils.parse_track(current['item'])
            if 'is_playing' in current:
                track_info['is_playing'] = current['is_playing']
    
            self.logger.info(
                f"Current track: {track_info.get('name', 'Unknown')} by {track_info.get('artist', 'Unknown')}")
            return track_info
        except Exception as e:
            self.logger.error("Error getting current track info.")
            raise
  • Helper method spotify_client.start_playback() called by handler for 'start' action.
    def start_playback(self, spotify_uri=None, device=None):
        """
        Starts spotify playback of uri. If spotify_uri is omitted, resumes current playback.
        - spotify_uri: ID of resource to play, or None. Typically looks like 'spotify:track:xxxxxx' or 'spotify:album:xxxxxx'.
        """
        try:
            self.logger.info(f"Starting playback for spotify_uri: {spotify_uri} on {device}")
            if not spotify_uri:
                if self.is_track_playing():
                    self.logger.info("No track_id provided and playback already active.")
                    return
                if not self.get_current_track():
                    raise ValueError("No track_id provided and no current playback to resume.")
    
            if spotify_uri is not None:
                if spotify_uri.startswith('spotify:track:'):
                    uris = [spotify_uri]
                    context_uri = None
                else:
                    uris = None
                    context_uri = spotify_uri
            else:
                uris = None
                context_uri = None
    
            device_id = device.get('id') if device else None
    
            self.logger.info(f"Starting playback of on {device}: context_uri={context_uri}, uris={uris}")
            result = self.sp.start_playback(uris=uris, context_uri=context_uri, device_id=device_id)
            self.logger.info(f"Playback result: {result}")
            return result
        except Exception as e:
            self.logger.error(f"Error starting playback: {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