Skip to main content
Glama
kylestratis

Spotify Playlist MCP Server

by kylestratis

spotify_add_tracks_to_playlist

Add tracks to Spotify playlists by specifying playlist ID and track URIs. Insert at specific positions or append to end, supporting 1-100 tracks per operation for user-owned or collaborative playlists.

Instructions

Add tracks to an existing Spotify playlist.

Adds 1-100 tracks to a playlist. Tracks can be inserted at a specific position or
appended to the end. Playlist must be owned by user or be collaborative.

Args:
    - playlist_id: Spotify playlist ID (not URI)
    - track_uris: List of track URIs, 1-100 (format: "spotify:track:ID", not just IDs)
    - position: Optional 0-indexed position to insert (default: append to end)

Returns:
    JSON: {"success": true, "snapshot_id": "...", "tracks_added": N, "message": "..."}

Examples:
    - "Add this track to my playlist" -> track_uris=["spotify:track:ID"], playlist_id="..."
    - "Add 10 songs to workout mix" -> track_uris=[list of URIs]
    - "Insert at the beginning" -> position=0

Errors: Returns error for invalid playlist (404), no permission (403), auth failure (401), rate limits (429).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The main asynchronous handler function for the 'spotify_add_tracks_to_playlist' tool. It validates input via Pydantic, calls the helper to interact with Spotify API, formats JSON response with success info, and handles errors.
    async def spotify_add_tracks_to_playlist(params: AddTracksToPlaylistInput) -> str:
        """Add tracks to an existing Spotify playlist.
    
        Adds 1-100 tracks to a playlist. Tracks can be inserted at a specific position or
        appended to the end. Playlist must be owned by user or be collaborative.
    
        Args:
            - playlist_id: Spotify playlist ID (not URI)
            - track_uris: List of track URIs, 1-100 (format: "spotify:track:ID", not just IDs)
            - position: Optional 0-indexed position to insert (default: append to end)
    
        Returns:
            JSON: {"success": true, "snapshot_id": "...", "tracks_added": N, "message": "..."}
    
        Examples:
            - "Add this track to my playlist" -> track_uris=["spotify:track:ID"], playlist_id="..."
            - "Add 10 songs to workout mix" -> track_uris=[list of URIs]
            - "Insert at the beginning" -> position=0
    
        Errors: Returns error for invalid playlist (404), no permission (403), auth failure (401), rate limits (429).
        """
        try:
            data = await add_tracks_to_playlist_helper(
                playlist_id=params.playlist_id,
                track_uris=params.track_uris,
                position=params.position,
            )
    
            return json.dumps(
                {
                    "success": True,
                    "snapshot_id": data.get("snapshot_id"),
                    "tracks_added": len(params.track_uris),
                    "message": f"Successfully added {len(params.track_uris)} track(s) to playlist",
                },
                indent=2,
            )
    
        except Exception as e:
            return handle_spotify_error(e)
  • Pydantic BaseModel defining the input schema for the tool, with fields for playlist_id, track_uris (list 1-100), and optional position, including validation constraints.
    class AddTracksToPlaylistInput(BaseModel):
        """Input model for adding tracks to a playlist."""
    
        model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True)
    
        playlist_id: str = Field(
            ..., description="Spotify playlist ID", min_length=1, max_length=100
        )
        track_uris: list[str] = Field(
            ...,
            description="List of Spotify track URIs to add (e.g., ['spotify:track:4iV5W9uYEdYUVa79Axb7Rh'])",
            min_length=1,
            max_length=100,
        )
        position: int | None = Field(
            default=None, description="Position to insert tracks (0-indexed)", ge=0
        )
  • server.py:224-233 (registration)
    The @mcp.tool decorator that registers the 'spotify_add_tracks_to_playlist' function as an MCP tool, specifying the name and operational hints.
    @mcp.tool(
        name="spotify_add_tracks_to_playlist",
        annotations={
            "title": "Add Tracks to Spotify Playlist",
            "readOnlyHint": False,
            "destructiveHint": False,
            "idempotentHint": False,
            "openWorldHint": True,
        },
    )
  • Helper utility that makes the actual HTTP POST request to Spotify's API endpoint for adding tracks to a playlist, constructing the payload and handling the response.
    async def add_tracks_to_playlist_helper(
        playlist_id: str,
        track_uris: list[str],
        position: int | None = None,
    ) -> dict[str, Any]:
        """Add tracks to an existing Spotify playlist.
    
        Args:
            playlist_id: Spotify playlist ID
            track_uris: List of Spotify track URIs to add
            position: Optional position to insert tracks (0-indexed)
    
        Returns:
            Response data from Spotify API (includes snapshot_id)
    
        Raises:
            httpx.HTTPStatusError: If the API request fails
        """
        payload: dict = {"uris": track_uris}
        if position is not None:
            payload["position"] = position
    
        data = await make_spotify_request(
            f"playlists/{playlist_id}/tracks", method="POST", json=payload
        )
    
        return data
Behavior4/5

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

The description adds valuable behavioral context beyond annotations: it specifies ownership/collaborative requirements, insertion vs. append behavior, and error conditions (404, 403, 401, 429). While annotations cover basic safety (readOnlyHint=false, destructiveHint=false), the description provides practical implementation details that help the agent understand real-world constraints. No contradiction with annotations exists.

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

Conciseness5/5

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

The description is well-structured and efficiently organized: purpose statement first, then key constraints, followed by parameter details, return format, examples, and error conditions. Every section adds value with zero redundant information. The examples are practical and illustrate common use cases without unnecessary elaboration.

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

Completeness5/5

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

For a mutation tool with annotations but 0% schema coverage and an output schema, this description is exceptionally complete. It covers purpose, constraints, all parameters, return values, examples, and error handling. The output schema exists, so the description appropriately focuses on explaining the JSON structure rather than just stating return types. All critical information for correct tool invocation is present.

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

Parameters5/5

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

With 0% schema description coverage (schema has no parameter descriptions), the description carries the full burden and excels: it clearly explains playlist_id format ('Spotify playlist ID (not URI)'), track_uris format and constraints ('List of track URIs, 1-100 (format: "spotify:track:ID", not just IDs)'), and position behavior ('Optional 0-indexed position to insert (default: append to end)'). Each parameter's purpose and format is thoroughly documented.

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

Purpose5/5

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

The description clearly states the specific action ('Add tracks to an existing Spotify playlist'), identifies the resource ('Spotify playlist'), and distinguishes from siblings like spotify_create_playlist (which creates new playlists) and spotify_get_playlist_tracks (which reads rather than modifies). The verb+resource combination is precise and unambiguous.

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

Usage Guidelines4/5

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

The description provides clear context about when to use this tool ('Playlist must be owned by user or be collaborative') and mentions track quantity limits (1-100). However, it doesn't explicitly contrast with alternatives like spotify_create_playlist for when you need a new playlist instead of adding to an existing one, or when to use this versus other modification tools if they existed.

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

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