Skip to main content
Glama
boristopalov

Spotify MCP Server

by boristopalov

SpotifyPlayback

Control Spotify playback: get current track info, start or resume music, pause playback, 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 of resumes playback. - pause: Pauses current playback. - skip: Skips current track.

Input Schema

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

Implementation Reference

  • Handler logic for the 'SpotifyPlayback' tool inside handle_call_tool(), dispatching actions to Spotify API 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(track_id=arguments.get("track_id"))
                logger.info("Playback started successfully")
                return [types.TextContent(
                    type="text",
                    text="Playback starting with no errors."
                )]
            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 successfully."
                )]
            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."
                )]
  • Pydantic model defining the input schema for SpotifyPlayback tool, used to generate the tool schema via model_json_schema().
    class Playback(ToolModel):
        """Manages the current playback with the following actions:
        - get: Get information about user's current track.
        - start: Starts of resumes playback.
        - pause: Pauses current playback.
        - skip: Skips current track.
        """
        action: str = Field(description="Action to perform: 'get', 'start', 'pause' or 'skip'.")
        track_id: Optional[str] = Field(default=None, description="Specifies track 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 'SpotifyPlayback') by including Playback.as_tool() in the list returned by list_tools().
    @server.list_tools()
    async def handle_list_tools() -> list[types.Tool]:
        """List available tools."""
        logger.info("Listing available tools")
        tools = [
            Playback.as_tool(),
            Search.as_tool(),
            Queue.as_tool(),
            GetInfo.as_tool(),
        ]
        logger.info(f"Available tools: {[tool.name for tool in tools]}")
        return tools
  • Core helper method get_current_track() in SpotifyClient, called by '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", exc_info=True)
            raise
  • Core helper method start_playback() in SpotifyClient, called by 'start' action to start/resume playback with optional track_id. Includes validation decorator. (excerpt omitted for length)
    def start_playback(self, track_id=None, device=None):
        """
        Starts track playback. If track_id is omitted, resumes current playback.
        - track_id: ID of track to play, or None.
        """
        try:
            if not track_id:
                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.")
    
            uris = [f'spotify:track:{track_id}'] if track_id else None
            device_id = device.get('id') if device else None
    
            result = self.sp.start_playback(uris=uris, device_id=device_id)
            self.logger.info(f"Playback started successfully{' for track_id: ' + track_id if track_id else ''}")
            return result
        except Exception as e:
            self.logger.error(f"Error starting playback: {str(e)}", exc_info=True)
            raise
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. While it describes what each action does at a basic level, it lacks important behavioral details like authentication requirements, rate limits, error conditions, or what happens during concurrent playback management. The description doesn't contradict any annotations since none exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with a clear opening statement followed by bullet points for each action. Each bullet is concise and directly relevant. However, the repetition of the opening sentence in the schema description suggests some redundancy that could be eliminated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a playback management tool with 3 parameters and no output schema, the description provides adequate basic information but lacks completeness. Without annotations or output schema, it should ideally cover more behavioral aspects like response format, error handling, or prerequisites. The description meets minimum requirements but leaves gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all three parameters thoroughly. The description lists the four action types but doesn't add meaningful parameter semantics beyond what's in the schema. The baseline of 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose as managing current playback with four specific actions (get, start, pause, skip), providing a verb+resource combination. However, it doesn't explicitly differentiate from sibling tools like SpotifyQueue or SpotifySearch, which likely handle different aspects of Spotify functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage context through action descriptions (e.g., 'get' for track information, 'skip' for skipping tracks), but doesn't provide explicit guidance on when to use this tool versus alternatives like SpotifyQueue or SpotifySearch. No when-not scenarios or clear alternatives are mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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

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