SpotifyMCP
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| SPOTIFY_CLIENT_ID | Yes | Spotify Client ID from your Spotify app. Required. | |
| SPOTIFY_MCP_HISTORY | No | Set to '1' to log mutations to JSONL for undo. | |
| SPOTIFY_MCP_READONLY | No | Set to '1' to hide every write tool (read-only mode). | |
| SPOTIFY_MCP_TOOLSETS | No | Comma-separated list of tool groups to trim by group for hosts that cap tool counts. Example: playback,catalog |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": true
} |
| resources | {
"listChanged": true
} |
| completions | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_now_playingA | Full device/session state for what is playing right now — item, progress, plus shuffle/repeat mode, active device, and volume. For a lightweight item+progress poll use get_currently_playing instead. Also covers: get_currently_playing lightweight poll — See also: get_currently_playing. |
| get_currently_playingA | Lightweight poll of what is playing right now: the item and progress only. For full session state (shuffle/repeat mode, active device, volume) use get_now_playing instead. Also covers: get_now_playing full-state poll — See also: get_now_playing. |
| play_from_searchA | Search Spotify by name and immediately play the best match. Works for songs and podcast episodes — no URI needed. |
| playC | Start or resume playback. Optionally target specific content. |
| pauseC | Pause playback on the active device |
| skip_nextB | Skip to the next track in the queue or context Also covers: batch skip via skip_n — See also: skip_n, skip_previous. |
| skip_previousA | Skip to the previous track. If more than 3 seconds in, restarts the current track first. Also covers: batch skip via skip_n — See also: skip_n, skip_next. |
| seekB | Seek to a position in the current track |
| set_volumeC | Set playback volume (0–100) |
| set_shuffleC | Enable or disable shuffle mode |
| set_repeatA | Set repeat mode: off, context (repeat playlist/album), or track (repeat single track) |
| get_queueB | Get the current playback queue Also covers: enriched queue via describe_queue, snapshot via get_queue_snapshot — See also: describe_queue, get_queue_snapshot. |
| add_to_queueA | Add a track or episode to the end of the playback queue Also covers: bulk via batch_add_to_queue, playlist queue via queue_playlist — See also: batch_add_to_queue, queue_playlist. |
| get_devicesC | List available Spotify Connect devices |
| transfer_playbackC | Move playback to a different Spotify Connect device |
| handoffA | Move playback to another device preserving the current track and play position (and optionally set the target volume) — a lossless "move to the kitchen speaker" |
| searchA | Search Spotify's catalog for tracks, artists, albums, playlists, shows, episodes, or audiobooks. Pass |
| get_trackB | Get full details for a track by ID |
| get_artistC | Get artist info by ID |
| get_artist_albumsC | List an artist's albums and singles |
| get_albumB | Get album details and track list by ID |
| get_album_tracksC | List the tracks of an album with pagination |
| get_showB | Get full details for a podcast show |
| get_show_episodesA | [Deprecated] use list_show_episodes — List a podcast show's episodes with pagination. Alias kept for backward compat; forwards to same GET /shows/{id}/episodes as list_show_episodes. Resume positions require the user-read-playback-position scope. Also covers: show episode listing, paged podcast episodes. |
| get_episodeC | Get full details for a podcast episode |
| get_meA | Get the current user's Spotify profile: display name, user ID, email, country, and subscription level. Email requires the user-read-email scope; country and product require user-read-private. |
| get_artist_top_tracksC | Get an artist's ten most-played tracks for a market. Removed by Spotify's February 2026 Web API changes — unavailable for newer app registrations |
| get_available_marketsA | List the country codes of every market where Spotify is available. Removed by Spotify’s February 2026 Web API changes — unavailable for newer app registrations |
| get_several_tracksA | Get full details for several tracks by ID in a single call (up to 50 per request) |
| get_several_albumsA | Get full details for several albums by ID in a single call (up to 20 per request) |
| get_several_artistsB | Get full details for several artists by ID in a single call (up to 50 per request) |
| get_several_episodesB | Get full details for several podcast episodes by ID in a single call (up to 50 per request) |
| get_several_showsA | Get full details for several podcast shows by ID in a single call (up to 50 per request) |
| get_several_audiobooksA | Get full details for several audiobooks by ID in a single call (up to 50 per request). Audiobooks are only available in the US, UK, Canada, Ireland, New Zealand and Australia markets. |
| get_several_chaptersB | Get full details for several audiobook chapters by ID in a single call (up to 50 per request) |
| get_categoryA | Get a single Spotify browse category by ID (GET /browse/categories/{id}). Quota: 🟢 single. |
| search_tracksA | Search tracks only (GET /search?type=track). Quota: 🟢 single. |
| search_artistsA | Search artists only (GET /search?type=artist). Quota: 🟢 single. |
| search_albumsA | Search albums only (GET /search?type=album). Quota: 🟢 single. |
| search_playlistsA | Search playlists only (GET /search?type=playlist). Quota: 🟢 single. |
| search_showsA | Search podcast shows only (GET /search?type=show). Quota: 🟢 single. |
| search_episodesA | Search podcast episodes only (GET /search?type=episode). Quota: 🟢 single. |
| search_audiobooksA | Search audiobooks only (GET /search?type=audiobook). Audiobooks are only available in the US, UK, CA, IE, NZ and AU markets. Quota: 🟢 single. |
| catalog_batch_lookupA | Resolve a mixed list of Spotify URIs (tracks/albums/artists/shows/episodes/audiobooks/chapters) in partitioned batch calls. Quota: 🟡 1 per distinct type + chunking. |
| get_artist_singlesA | List an artist's singles only (GET /artists/{id}/albums?include_groups=single). Quota: 🟢 single. |
| get_artist_appearancesA | List albums an artist appears on (GET /artists/{id}/albums?include_groups=appears_on). Quota: 🟢 single. |
| market_validateA | Validate ISO 3166-1 market codes against GET /markets (cached) and optionally return the account market from /me. Quota: 🟢 1–2 calls. |
| browse_category_deepdiveA | Category → playlists → optional items peek in one call (GET /browse/categories/{id} + /playlists (+ /playlists/{id}/tracks peek)). Quota: 🟡 2–3 calls. |
| show_episode_searchA | Full-text search within one show's episodes (GET /shows/{id}/episodes paged + client-side q). Quota: 🟡 1–N pages (fetch_all walks). |
| get_top_tracksC | Get the user's most-played tracks |
| get_top_artistsC | Get the user's most-played artists |
| get_recently_playedC | Get recently played tracks with timestamps |
| listening_streaksB | Compute consecutive-day listening streaks from recently-played history (up to 150 items). Quota: GET /me/player/recently-played cursor walk. |
| top_artists_by_rangeA | Top artists for each time window with rank deltas (short vs long). Quota: up to 3× GET /me/top/artists. |
| taste_shift_reportB | Compare short_term vs long_term top artists+tracks: rising/falling + Jaccard similarity. Quota: 4× GET /me/top/*. |
| listening_reportB | Aggregate listening report: compares your top tracks between two time windows (rising / constant / fading), plus era histogram, discovery ratio, repeat overlap with recently played, and hour-of-day buckets |
| statsfm_taste_profileA | Taste snapshot from stats.fm: core artists, top genres, loyalty-vs-novelty balance, and day-parting (when you listen). Read-only, no auth. |
| taste_profileA | Taste snapshot from stats.fm: core artists, top genres, loyalty-vs-novelty balance, and day-parting (when you listen). Read-only, no auth. (Legacy alias of statsfm_taste_profile — prefer the canonical name.) |
| statsfm_artist_affinityA | How deep does an artist run? Lifetime intensity (share of top-artist streams) plus a recency half-life fitted to recent stream ages. Read-only, no auth. |
| artist_affinityA | How deep does an artist run? Lifetime intensity (share of top-artist streams) plus a recency half-life fitted to recent stream ages. Read-only, no auth. (Legacy alias of statsfm_artist_affinity — prefer the canonical name.) |
| statsfm_exposure_checkA | Where does a subject sit on the exposure ladder — unheard / sampled / explored / established / favorite? Evidence cites lifetime + recent counts. Read-only, no auth. |
| exposure_checkA | Where does a subject sit on the exposure ladder — unheard / sampled / explored / established / favorite? Evidence cites lifetime + recent counts. Read-only, no auth. (Legacy alias of statsfm_exposure_check — prefer the canonical name.) |
| statsfm_listening_erasA | Change points in monthly listening: groups months into eras split on top-artist turnover or >60% volume shifts. Read-only, no auth. |
| listening_erasA | Change points in monthly listening: groups months into eras split on top-artist turnover or >60% volume shifts. Read-only, no auth. (Legacy alias of statsfm_listening_eras — prefer the canonical name.) |
| statsfm_listening_sessionsA | Group recent streams into sessions: a gap longer than gap_minutes starts a new session (default 30). Read-only, no auth. |
| listening_sessionsA | Group recent streams into sessions: a gap longer than gap_minutes starts a new session (default 30). Read-only, no auth. (Legacy alias of statsfm_listening_sessions — prefer the canonical name.) |
| statsfm_forgotten_favoritesB | High-lifetime tracks with zero recent plays — favorites that fell off. Ranked by lifetime streams. Read-only, no auth. |
| forgotten_favoritesA | High-lifetime tracks with zero recent plays — favorites that fell off. Ranked by lifetime streams. Read-only, no auth. (Legacy alias of statsfm_forgotten_favorites — prefer the canonical name.) |
| statsfm_taste_recommendationsA | Bridge-mode recommendations: adjacent genres/artists between the listener’s core and the unexplored, each with evidence and a risk note. Heuristic over stats.fm tops — read-only, no auth. |
| taste_recommendationsA | Bridge-mode recommendations: adjacent genres/artists between the listener’s core and the unexplored, each with evidence and a risk note. Heuristic over stats.fm tops — read-only, no auth. (Legacy alias of statsfm_taste_recommendations — prefer the canonical name.) |
| statsfm_record_feedbackA | Record a local-only taste verdict (love/like/mixed/boring/dislike) or list stored verdicts. Never touches the network — memory for future recommendations. |
| record_feedbackA | Record a local-only taste verdict (love/like/mixed/boring/dislike) or list stored verdicts. Never touches the network — memory for future recommendations. (Legacy alias of statsfm_record_feedback — prefer the canonical name.) |
| taste_to_playlistA | Taste profile → playlist track list (DRY RUN first): blend lifetime tops with recent streams into a copy-pasteable track list. Read-only — never writes to Spotify. |
| taste_daily_briefA | Yesterday (or a given date) in brief: top-3 tracks, 2 revival picks, novelty share vs the lifetime core. Read-only, no auth. |
| taste_era_playlistA | Era window → representative track list: pick a listening era and get its playlist spec. Read-only, no auth. |
| taste_forgotten_bangersA | Forgotten-bangers playlist spec: lifetime tops missing from the recent sample, ranked with a revival pick. Read-only, no auth. |
| taste_obsession_ladderB | Obsession ladder: artists ranked by stream share, each with an exposure tier. Read-only, no auth. |
| taste_diamond_rotationA | Diamond-mining rotation: mid-tier lifetime tracks (rank ~20–60) absent from recent streams — deep cuts to re-polish. Read-only, no auth. |
| taste_weekly_recapA | Week-in-review brief: stream count, top artists/tracks of the window, busiest day, novelty share. Read-only, no auth. |
| taste_genre_bridgeA | Genre-bridge playlist spec: picks spanning two genres with evidence + risk per pick. Read-only, no auth. |
| taste_novelty_loyaltyA | Loyalty-vs-novelty report: top-5 share, recent-outside-core share, and a verdict (comfort / balanced / explorer). Read-only, no auth. |
| taste_listening_clockA | Listening-clock summary: day-part split (UTC), peak window, and a sequencing note for playlist order. Read-only, no auth. |
| taste_revival_queueA | Revival queue builder: ordered re-listen queue from forgotten favorites + dormant-affinity artists, each with a search_tracks fallback line. Read-only, no auth. |
| library_hygieneA | Read-only album completion & consolidation analysis over your liked tracks: flags near-complete albums worth saving in full and lone singles with nothing else liked from their artist (low confidence). Suggests only — never mutates your library. |
| restore_library_snapshotA | STRICTLY ADDITIVE restore of a library snapshot written by backup_library_snapshot. Adds only what is missing: saves absent tracks/albums/shows/episodes/audiobooks, follows unfollowed artists, and creates NEW playlists named 'Restored · ()' — existing playlists are never touched and nothing is ever deleted, renamed, or overwritten. dry_run defaults to TRUE (read-only preview); setting dry_run=false requires explicit interactive confirmation before any write, and restores are refused entirely in environments without confirmation support. |
| backup_libraryA | Snapshot your ENTIRE library to a local JSON file (read-only against Spotify): liked tracks, saved albums/shows/episodes/audiobooks, followed artists, and every playlist with its items. Walks capped at SPOTIFY_MCP_FETCH_ALL_CAP (default 500 per category). Files land in SPOTIFY_MCP_BACKUP_DIR (default ~/.spotify-mcp/backups), mode 0600. |
| list_backupsA | List previous library backups (newest first) with path, creation date, size, and the _meta.counts summary from each snapshot |
| get_artist_genresA | Get genres for an artist (focused view of GET /artists/{id}) |
| get_categoriesB | List Spotify browse categories (GET /browse/categories) |
| get_category_playlistsB | Get playlists for a browse category (GET /browse/categories/{id}/playlists) |
| get_artist_discographyC | Get filtered discography for an artist (GET /artists/{id}/albums with album-type filtering) |
| resolve_artistC | Resolve an artist name or URI to a Spotify artist ID via search |
| save_artist_new_releasesA | Find new releases for an artist and save unsaved albums to Your Library (diffs against /me/library/contains) |
| watch_artistsA | Add artists to a local watchlist sidecar for new-release polling |
| check_artist_releasesA | Check watched artists for new releases since last check (or within lookback_days). WARNING: N artists in watchlist = N API requests. Use max_artists to budget and dry_run to preview cost. |
| artist_release_digestA | Show a digest of new releases since the last check for a watchlist. WARNING: N artists = N requests. Use max_artists to budget and dry_run to preview. |
| search_historyB | Recall past searches (local sidecar, 90-day expiry). Optionally filter by query substring. |
| search_rerunA | Re-execute a stored search by history id via GET /search. |
| search_within_playlistA | Text search inside a single playlist (client-side filter over full item walk). Quota: 🟢 GET /playlists/{id}/items paged. |
| search_history_statsA | Analytics over the local search-history sidecar: top queries, type breakdown, recency. Quota: 🟢 local only (no API). |
| audiobook_progressA | Audiobook progress rollup: chapters total, played count, current chapter, percent complete. Quota: 🟡 2 GETs (audiobook + chapters). |
| unsave_orphan_tracksA | Find saved tracks that appear in no playlist (orphans) and optionally unsave them. Quota: 🟡 walks library + all playlists (capped). Destructive when dry_run=false. |
| playlist_to_libraryB | Save all tracks of a playlist to your Liked Songs (library). Quota: 🟢 GET playlist items + PUT /me/tracks (chunked 50). |
| followed_playlists_auditB | Inventory of followed vs owned playlists: counts, collab, public, follower totals. Quota: 🟢 GET /me/playlists paged. |
| get_playlist_added_datesA | List when each track was added to a playlist (added_at + added_by). Quota: 🟢 GET /playlists/{id}/items paged. |
| split_playlistB | Split a playlist into N chunks (new playlists). Quota: 🟡 GET all + N POST /me/playlists + N POST items. |
| find_duplicate_tracks_across_playlistsA | Find tracks that appear in more than one of the given playlists (cross-playlist dupes). Quota: 🟡 N GETs (one per playlist). Also covers: find_duplicates_in_playlist (single-playlist), find_duplicate_playlists — See also: find_duplicates_in_playlist, find_duplicate_playlists. |
| remove_from_library_by_playlistA | Remove from Liked Songs any tracks that also appear in a given playlist. Quota: 🟡 2 GETs + DELETE (chunked). |
| search_advancedA | Structured advanced-search composer: builds a valid Spotify filter query from typed fields and runs it. Supported filter syntax: track:"name" artist:"name" album:"name" year:1984 year:1980-1989 genre:"pop" tag:new (last ~2 weeks) tag:hipster (lowest-popularity) isrc:CCXXXNNNNNNN upc:NNNNNNNNNNNNN. Pass whichever fields you have; they are quoted and composed for you. Quota: 🟢 one GET /search call. |
| track_album_bundleA | Context card for one track: the track plus its full album metadata and the album's remaining track listing (the "what am I listening to" card). Quota: 🟡 2 API calls (GET /tracks/{id} + GET /albums/{id}/tracks). |
| artist_discography_timelineA | [local-compute] Chronological release table (year · type · name · tracks) built from a paginated /artists/{id}/albums walk. Sorted newest first; |
| search_freshA | Query-scoped newness: runs your query with Spotify's |
| track_enrichment_batchA | Up to 50 track IDs → enriched rows: album release date, label and artist genres joined back onto each track via chunked several-tracks + several-albums + several-artists fan-in. Quota: 🟡 ~3 chunked API calls (one per several-* endpoint, more when chunking splits). |
| albums_runtime_batchA | [local-compute] Runtime per album — total and mean track length — for up to 20 albums in one pass (album objects embed the first 50 tracks, so totals are exact for albums up to 50 tracks and flagged as partial above that). Quota: 🟢 one GET /albums?ids= call. |
| album_track_statsA | [local-compute] Track-length statistics for one album: min/max/mean/median plus a longest-track callout, built from a paged /albums/{id}/tracks walk. Quota: 🟡 one paginated walk (usually a single call). |
| artist_discography_statsA | [local-compute] Discography shape for one artist: release counts by album/single/compilation, first and latest release, releases-per-year rate and the longest silence gap between releases. Quota: 🟡 one paginated /artists/{id}/albums walk (typically several API calls). |
| show_runtime_statsA | [local-compute] Runtime profile for one show: total and average episode runtime plus release cadence (days between episodes), built from a paged /shows/{id}/episodes walk. Quota: 🟡 one paginated walk (typically several API calls). |
| show_episode_timelineA | [local-compute] One show's episodes chronologically with hiatus-gap detection — any gap between consecutive release dates larger than the threshold is called out ("no episode in N days"). Quota: 🟡 one paginated /shows/{id}/episodes walk (typically several API calls). |
| category_resolverA | Free-text genre/vibe → best-matching browse category ID via local fuzzy match over GET /browse/categories results. REGISTRATION GATE: /browse/categories is on the #329 app-registration gated surface — on fresh registrations this short-circuits with a clear disclosure instead of a raw 403. Quota: 🟡 1–4 API calls (paged category walk). |
| search_by_isrcA | Exact track resolution from an ISRC via the |
| find_canonical_trackA | [local-compute] Given a title + artist, search all versions, group by (artist, title) and rank them — studio > album > live/remaster — to return the canonical URI plus a variant table. Quota: 🟡 1–2 GET /search calls (fallback broad search when the precise filter comes back empty). |
| audiobook_chapter_mapB | [local-compute] Chapter-by-chapter duration map, total runtime and the mid-point chapter for one audiobook. MARKET GATE: audiobooks are US/UK/CA/IE/NZ/AU only. Quota: 🟡 2+ API calls (GET /audiobooks/{id} + paged GET /audiobooks/{id}/chapters). |
| artist_collab_networkA | [local-compute] Featured/collab artists extracted from an artist's top tracks and recent albums with co-appearance counts — computed from real payloads, not the dead related-artists endpoint. NOTE: /artists/{id}/top-tracks is on the #329 registration-gated surface; if it 403s the network is computed from recent albums only, with an explicit disclosure. Quota: 🟡 1 + paginated API calls. |
| search_market_diffA | Same query run in two markets with the result sets diffed — availability/regional insight in one card. Quota: 🟡 2 GET /search calls (one per market). |
| episode_context_bundleA | Episode + parent show + neighbouring episodes (prev/next by release date) in one card. Quota: 🟡 2 API calls (GET /episodes/{id} + GET /shows/{id}/episodes). |
| audiobooks_by_authorA | Author catalogue via the |
| artist_genres_compactA | Up to 50 artist IDs → name·genres two-column projection (compact roster view over several-artists). Quota: 🟢 one GET /artists?ids= call. |
| sleep_timerA | Keep music playing and auto-pause after N minutes: registers an in-process timer (cancel-safe; calling again replaces it) that fires PUT /me/player/pause on expiry. No fade — distinct from stepped schedule_wind_down. Quota: 🟢 1 read now; 1 pause on expiry. Timer lives in this MCP server process only. |
| muteA | Set volume to 0 while remembering the previous level in the sidecar — one word beats volume_step ×N. unmute restores it. Quota: 🟢 1 read + 1 write. |
| unmuteA | Restore the volume level remembered by mute (falls back to 50% if nothing remembered). Quota: 🟢 1 write (sidecar read is local). |
| switch_deviceA | Transfer playback to a device by fuzzy name or sidecar label — pure handoff, no content args (complements play_on which plays content, and handoff which is pos-preserving id-only). Quota: 🟢 1 read + 1 write. |
| surprise_meA | "Surprise me": picks a random saved track, saved album, or owned playlist and plays it. Randomness seedable via seed for reproducible picks. Quota: 🟡 1-3 reads + 1 write (PUT /me/player/play). |
| skip_nA | Advance N tracks at once via N sequential POST /me/player/next calls. Quota: 🔴 N writes (1-20, one call per skip — Spotify has no batch-skip endpoint). Also covers: single skip via skip_next / skip_previous — See also: skip_next, skip_previous. |
| pause_everywhereA | Pause every live Connect device (attempts PUT /me/player/pause per non-restricted device) — kills the "which speaker is still playing" hunt. Quota: 🟡 1 read + N writes (one pause per live device). |
| volume_rampA | Generic volume ramp to a target percent over N minutes (up OR down, step-controlled, optional end-state pause/play) — a superset of schedule_wind_down (which is down-only with floor+pause). Quota: 🟢 stepped PUT /me/player/volume writes (ceil(minutes/step_minutes) calls), in-process, cancel-safe (restart replaces). |
| episode_bookmarkA | Bookmark the current podcast-episode position to the sidecar (title, show, progress, optional note) — where_was_i is audiobooks-only today. Quota: 🟢 1 read (GET /me/player). |
| episode_resumeA | Jump straight back to the newest (or a named) episode bookmark: transfer + play with position + seek (2-3 writes). Pairs with episode_bookmark. Quota: 🟡 2-3 writes. |
| queue_next_episodeA | Find the next unplayed episode of a show (not in recently-played and not fully played) within an episodes_back lookahead and queue it — podcast binge glue. Quota: 🟡 2-3 reads + 1 write (POST /me/player/queue). |
| queue_replace_via_playlistA | Honest no-clear-endpoint workaround to replace the live queue: snapshot it, optionally filter (drop dupes / keep only given artists), build a playlist, and start it as the playback context — the live queue is replaced via context switch (Spotify has no queue-clear endpoint). Quota: 🟡 1 read + 2-3 writes. |
| session_statsA | Session-size distribution from recently-played via detectSessions (30-min gap): session count, median/mean tracks per session, longest session, avg session length. Quota: 🟢 1-2 reads, local compute. |
| most_replayedA | Most-replayed tracks in the recent window: play counts per track from recently-played, deduped — "on repeat" computed locally (complements window-based listening_report). Quota: 🟢 1-2 reads, local compute. |
| last_heardA | For 1-10 artists: when did you last actually play them (recently-played cursor walk) + the gap — answers "when did I last listen to X?" without guessing. Quota: 🟡 2-10 pages walked (max_pages, disclosed; stops early once all found). |
| weekday_heatmapA | Plays bucketed by weekday × daypart (morning/afternoon/evening/night) — listening_heatmap is hour-of-day; this adds the weekly dimension. Quota: 🟢 1-2 reads, local compute. |
| queue_profileA | Composition profile of the current queue: unique artists, albums, track-vs-episode mix, longest consecutive block by one artist. Quota: 🟢 1 read (GET /me/player/queue), local compute. |
| checkpoint_playbackA | One-shot timestamped auto-named playback checkpoint (cp-2026-08-27T21:05 style) — saves you naming slots for save_playback_state. Quota: 🟢 1 read + local sidecar write. |
| continue_lastA | Resume the most recent checkpoint without knowing its name (sidecar lookup by saved_at). Pairs with checkpoint_playback. Quota: 🟡 2-3 writes (play + shuffle/repeat best-effort). |
| room_levelA | Level the room: read the active device volume and apply the same percent to every other live device. Quota: 🟡 1 read + N writes (one volume PUT per target device). |
| volume_reportA | Read-only volume snapshot across all devices, including sidecar presets vs live deltas — "what's the volume everywhere right now?". Quota: 🟢 1 read (GET /me/player/devices). |
| daily_pickA | Deterministic "banger of the day": date-seeded pick from recently-played highlights (most-played pool + seeded tiebreak) — same date, same pick. Quota: 🟢 1-2 reads, local compute. |
| playlist_intersectA | Keep only the tracks present in ALL of 2–10 playlists, written as one atomic replace — the missing set op (union/subtract/XOR exist). Without a target it reports the intersection read-only. Quota: 🟢 N GETs + 1 PUT when committing. Also covers: playlist_intersection (same op, unified) — See also: playlist_intersection. |
| playlist_add_by_searchA | "Add Radiohead Paranoid Android to Chill Mix" in one shot: search the catalog, pick the top result(s), and add the URI(s) to a playlist. Highest-traffic curation gesture. Quota: 🟡 1 search + 1 add call. |
| playlist_trim_to_durationA | Fit a playlist to a target runtime (e.g. "exactly 30 min for the commute"): greedy keep-first/last/random selection within ±tolerance seconds, written as one atomic replace. Complements item-count playlist_trim. Quota: 🟢 GET + 1 PUT. |
| saved_tracks_rouletteA | Deal N random cards from your saved tracks into a FRESH playlist — an instant rediscovery sampler. Quota: 🟡 getAllPages + 1 create + chunked adds. |
| playlist_sliceA | Copy a slice of one playlist — positions start..end, first/last N, or an added-at date range — into a NEW playlist. Era snapshots, side A/B, decadal splits. Quota: 🟢 GET + create + chunked adds. |
| playlist_names_bulk_normalizeA | One-shot name hygiene across your library: strip "(Official Copy)"-style noise, trailing "2" duplicates, apply a prefix/suffix, or renumber. Preview → commit. Quota: 🟡 GET + N PUTs (N = renamed only). |
| playlist_keep_onlyA | Inverse removal: keep only matching items (by uri / artist / type / query) and drop everything else — one atomic replace, no N+1 deletes. Quota: 🟢 GET + 1 PUT. |
| playlist_strip_episodesA | Purify a playlist after collab drift: strip every podcast EPISODE (or every TRACK) with one client-side filter + atomic replace. Quota: 🟢 GET + 1 PUT. |
| playlist_move_to_topA | Bring matching items (uris / artist / query) to the FRONT of a playlist in one atomic replace. Deliberately avoids Spotify reorder N+1 for large moves. Quota: 🟢 GET + 1 PUT. |
| playlist_exclude_artistsB | Remove every track by one or more artist IDs from a playlist — the "purge the artist" one-shot. Quota: 🟢 GET + chunked deletes. |
| playlist_staleness_scoreA | Local staleness check: days since the most-recent / median added_at in a playlist, a fresh/aging/stale/fossil grade, and refresh suggestions. Read-only. Quota: 🟢 1–2 GETs. |
| playlist_artist_heatA | Local artist-concentration check: top-artist share, an HHI concentration index, and the repeat-offender list with track counts. "Is this mix just one band?" Quota: 🟢 1 GET. |
| playlist_era_profileA | Local release-era profile: decade histogram, median track age, and a time-capsule verdict. Pairs with playlist_era slices. Quota: 🟢 1 GET (market refetch disclosed). |
| playlist_overlap_matrixB | Pairwise Jaccard overlap for 2–10 playlists — which of your mixes have drifted into the same set. Quota: 🟢 N GETs. |
| saved_tracks_by_artistA | List your SAVED tracks for one artist ("everything I've saved by X"): resolves the artist ID when given a name, then filters the liked shelf. Quota: 🟡 getAllPages + 1 search (name input). |
| saved_library_deltaA | Diff your current saved tracks/albums against a local backup snapshot (from backup_now): added since / removed since. Quota: 🟡 getAllPages walks + local file read. |
| library_to_playlistA | Export your saved tracks (or saved albums' first tracks) into a NEW playlist — sort by save order, cap at N, chunked adds. Quota: 🟡 getAllPages + create + chunked adds. |
| collab_mix_from_followedA | Build a collaborative-style mix playlist from your FOLLOWED artists' recent releases: walks /me/following (cursor-paged, capped by artists_cap), fans out one albums GET per artist plus one album GET per picked album (concurrency 5 — disclosed fan-out), takes ≤per_artist newest tracks per artist, then round-robin-merges them into a new playlist. dry_run=true (default) previews the mix. Quota: 🔴 ~artists_cap×(1+per_artist) GETs + create/adds. |
| quick_save_nowA | One-call "like this song": saves the currently-playing track (or the last |
| morning_briefingA | Daily digest in one call: new releases from followed artists + new episodes from saved shows + per-show backlog + today's listening so far. Superset of whats_new + show_new_episodes. Quota: roughly 2 + artists + shows reads (budgeted). |
| monthly_listening_reportA | Calendar-month listening report: top tracks/artists, estimated minutes, active days and session count, rendered as markdown; optionally archives a sidecar snapshot for month-over-month diffs. Local compute over recently-played (90-day window — older months cannot be fully reconstructed) + /me/top short-term data. ~6 reads. |
| year_in_reviewA | Spotify-Wrapped substitute: top tracks/artists across all top-list time ranges, decade mix, library growth and discovery ratio, rendered as a markdown review. Local compute. Quota: ~9 reads (3 top ranges × 2 lists + library + history). |
| taste_checkpointA | Save a snapshot of your current top artists/tracks/genres to a dated sidecar slot for longitudinal taste tracking (pair with taste_checkpoint_diff). 2 reads, sidecar write only. |
| taste_checkpoint_diffA | Diff two saved taste checkpoints: new entrants, drop-offs, genre drift and Jaccard similarity. Pure local sidecar — zero API calls. List slots with from="?" or omit. |
| discover_weekly_diffA | This week's Discover Weekly vs the last copy in your archive playlist: what is new, what overlapped, and which tracks you already liked. 2-3 reads (+1 write only with save_after). Resolves both playlists by exact name. |
| dead_library_finderA | Find saved tracks that never appear in your recent history AND sit in none of your playlists — unsave candidates. Local compute over /me/tracks + playlists + history. dry_run defaults to true; disabling it actually removes the candidates. |
| week_in_review_playlistA | Create a "Week of " playlist from the last 7 days of plays: deduped, ordered by recency. Rerunning replaces the same playlist's content — your weekly ritual in one call. Quota: 1-3 reads + 1-3 writes. dry_run previews the tracklist. |
| scope_auditA | Decode the current token's granted OAuth scopes and classify every registered tool module: callable, scope-gated, or read-only. Optional probe fires one lightweight read for actionable evidence. Supports the #329 gating audit. |
| quota_probeA | Fire 2-3 lightweight authenticated reads and report Retry-After / quota state plus a per-endpoint 403 gating map — actionable evidence for the #330 gauntlet. Quota: 2-3 reads. |
| playlist_staleness_reportA | Per-playlist staleness report: newest/oldest added_at, median item age and count added in the last 90 days — find playlists rotting in place. Quota: 1 + N reads (N = playlists scanned), page-capped. |
| show_backlog_reportA | Per saved podcast show: unplayed episodes (resume_point), hours of backlog and newest-episode age — decide what to unsubscribe from. Quota: 1 + N reads (budgeted). |
| audiobook_library_progressA | All saved audiobooks with % complete and estimated time remaining, sorted by progress — what am I actually reading. Quota: 1 + N reads. NOTE: audiobook endpoints are market-gated (US/UK/CA/IE/NZ/AU) — outside these a clear error is returned. |
| chapter_bookmarksA | Named chapter+position bookmarks per audiobook, stored in the local sidecar (jump later via jump_to_chapter). save/list/delete. Zero API calls — pure sidecar. |
| artist_complete_checkB | Collector completeness: the artist's full album list vs your saved albums — what's missing, with album/single/compilation breakdown. Quota: 2 reads (artist albums + your saved albums, page-capped). |
| playlist_from_tagsA | Create or refresh a playlist from saved library items whose artists carry the given genre tags (the write side of tag_management — uses the same sidecar rule pattern). Quota: 2+ reads + 1-3 writes. dry_run previews the match list. |
| listening_journal_appendA | Attach a timestamped note to today's journal, optionally tagged with a session id or tag — makes tag_listening_session actually useful for recall. Sidecar only, zero API calls. |
| export_playlist_markdownA | Export one playlist as a paste-ready markdown table (the doc-friendly variant of export_playlist). 1 read. |
| export_shows_opmlA | Export your saved shows as OPML XML — the interchange format podcast apps speak. DISCLOSURE: the Spotify API does not expose publishers' underlying RSS feed URLs, so each OPML entry links to the Spotify show page (some apps import it, some ignore it). |
| sidecar_export_bundleA | One-call export of ALL local sidecar state (scenes, device presets, tags, smart rules, playback states, bookmarks, checkpoints, journal) as a single JSON for machine migration, plus a restore checklist. Zero API calls. |
| listening_week_in_timeB | Charts for any specific past week within the ~90-day recently-played window, via a before/after cursor walk — retro "what was I playing then". Quota: 2-6 reads. |
| mutation_log_exportA | Render the JSONL mutation history as a CSV or markdown report, date/uri-filtered — an audit trail you can actually read. Local file only, zero API calls. |
| undo_previewA | Dry-run for undo_mutation: shows exactly what a receipt-driven revert WOULD do (diff of before/after, target inversion calls) without executing. 0-2 reads. |
| receipt_lookupA | Find mutation receipts by id, date range or affected URI — closes the receipts loop (issue → lookup). Local, zero API calls. |
| export_playlist_jsonA | Full-fidelity JSON export of one playlist: items + added_at + added_by + URIs (the fields CSV/M3U lose). 1 read. |
| device_sync_stateC | Reconcile sidecar device labels/volume presets (and scene device hints) against the live device list; flags dead labels and can prune them. 1 read + sidecar. dry_run plans. |
| playlist_fill_from_searchA | Grow a playlist to N items from search queries you supply: round-robin one pick per query per pass, first unseen track match wins, chunked adds. Complements listening-data grow_playlist. Quota: 🟡 len(queries) searches + chunked adds. |
| playlist_expression_algebraA | Mini set-algebra over playlists: |
| playlist_cover_from_trackA | Set the playlist cover from a track album art: pick by position in the playlist, pass any track URI, or default to the first track with art. Fetches the image (largest JPEG candidate ≤ 256 KB) and PUTs /playlists/{id}/images. Quota: 🟢 GET + PUT (+1 image fetch, disclosed). |
| library_coverage_reportA | Report coverage between your saved tracks and playlists: orphan saved tracks (liked but not in any playlist) and unsaved playlist items. Read-only. |
| listening_heatmapA | When do you listen? Buckets recently-played tracks into 168 hourly slots (24h x 7d) and reports peak/quiet windows. Read-only. |
| library_growth_reportA | How fast your library is growing — buckets saved tracks/albums/shows/episodes by added_at period (weekly/monthly/yearly). Read-only. |
| genre_trends_over_timeB | How your taste shifts — per-period top genres via artist genres/tags, with deltas and emerging/declining tags. Read-only. |
| save_discover_weeklyA | Archive your Discover Weekly into a regular playlist (creates or overwrites the archive). Resolves Discover Weekly via /me/playlists exact match first, falling back to search (unverified); dry_run previews; idempotent if archive already matches. Result echoes source identity (owner, url, verified). |
| save_release_radarA | Archive your Release Radar into a regular playlist (creates or overwrites the archive). Resolves Release Radar via /me/playlists exact match first, falling back to search (unverified); dry_run previews; idempotent if archive already matches. Result echoes source identity (owner, url, verified). |
| export_library_jsonA | Export your full library (saved tracks, albums, shows, episodes, audiobooks) to a local directory as JSON or CSV sidecar files. Respects SPOTIFY_MCP_FETCH_ALL_CAP per type; when capped, reports cap_reached + truncated and a prose footer ("first N of … — raise SPOTIFY_MCP_FETCH_ALL_CAP"). |
| export_followed_artistsA | Export your followed artists to a local directory as JSON or CSV. Fields: uri, name, genres. The file's exported_at is the export time, not a per-artist follow date (Spotify does not expose followed_at). |
| export_profile_stateA | Export local sidecar stores (scenes, genre-tags, playback-ext, search-history, mutations, artist-watchlist) to a single schema-versioned JSON archive. Note: artist-watchlist defaults to ./data/artist-watchlist.json (cwd-relative, not ~/.spotify-mcp/) — a quirk flagged for future alignment. |
| import_profile_stateA | Restore local sidecar stores from a profile-state archive. Merge adds to existing stores; overwrite replaces them. Refuses archives newer than this server's schema version. |
| export_listening_historyA | Export your listening history (recently played) to a JSON or CSV sidecar by walking /me/player/recently-played with before-cursor pagination. Respects SPOTIFY_MCP_FETCH_ALL_CAP; writes file 0600 and reports path + counts. Analogous to export_library_json. |
| export_all_playlistsA | Export every owned (or all) playlist with metadata + items to a sidecar file. Quota: GET /me/playlists + N×GET /playlists/{id}/items; capped by fetchAllCap. |
| library_snapshot_diffA | Diff two portability/library sidecar JSON files (library.json or playlists.json): added/removed counts + samples. Quota: 🟢 local only (no API). |
| history_searchA | Search local portability/backups for files matching a query (filename substring). Quota: 🟢 local only (no API). |
| import_from_sidecarA | Additive restore from a portability sidecar (library.json / playlists.json): re-adds missing saved items and creates missing playlists. Skips existing. dry_run=true by default. Quota: 🟢 local read + 🟡 contains-check + writes when dry_run=false (chunked). |
| archive_played_episodesA | Remove fully-played episodes from your episode library in bulk (checks resume_point.fully_played). Batch DELETE /me/episodes; elicitation >50; dry_run supported. |
| playlist_health_checkA | Audit a playlist for unavailable, local, duplicate, and empty issues (read-only) |
| get_playlist_followersA | Get follower count for a playlist, optionally including public owner profile |
| playlist_collaboration_reportA | Report who added what to a playlist (counts + first/last timestamps, most-active) |
| snapshot_playlistA | Snapshot a playlist's current URIs+positions+timestamp to a sidecar JSON file (legacy, simple path playlistId→file). For transactional local snapshots with plsnapi naming, diff, and bundle tooling, use take_playlist_snapshot instead. Also covers: playlist snapshot (legacy). |
| diff_since_snapshotC | Compare current playlist state to a stored snapshot |
| remove_unavailable_playlist_itemsA | Remove unavailable (null track) items from a playlist — actionable companion to playlist_health_check. Targets only unavailable occurrences by position so healthy copies are preserved. Read-only dry_run preview available. |
| find_duplicate_playlistsA | Scan your playlists for exact and near-duplicate track sets. Exact = identical URI sets (order-insensitive); near = Jaccard overlap >= threshold. Read-only. Also covers: find_duplicates_in_playlist, find_duplicate_tracks_across_playlists — See also: find_duplicates_in_playlist, find_duplicate_tracks_across_playlists. |
| list_playlist_snapshotsB | List stored snapshots (optionally filtered by playlist_id) |
| batch_add_to_playlistA | Add tracks from multiple source URIs (tracks, albums, artists, playlists) to a target playlist in one call. Dedupes within the batch and optionally against the existing playlist. Batches writes in groups of 100. Dry-run previews without writing. Elicitation for 100+ tracks. |
| copy_playlistB | Duplicate an existing playlist into a new playlist, preserving track order. Creates the new playlist then adds tracks in batches of 100. Dry-run reports what would be created. |
| move_items_between_playlistsA | Bulk rehome items between playlists. Mode copy keeps the source intact; mode move removes from source after copying. Supports dedupe against target and optional name/artist filter. |
| pin_playlistC | Follow (pin) a playlist to your library. PUT /playlists/{id}/followers. |
| unpin_playlistB | Unfollow (unpin) a playlist. DELETE /playlists/{id}/followers. Supports dry_run and elicitation for bulk use. |
| playlist_template_applyA | Create an instant mood/vibe playlist from a template (focus, wind-down, gym, commute) composed from your existing listening data. Creates a new playlist and fills it. |
| queue_playlistA | Queue all tracks from a playlist/album/artist URI in order (cap 200). mode=append adds to end; mode=replace is not supported — Spotify has no queue-clear endpoint. Also covers: single add via add_to_queue, bulk via batch_add_to_queue — See also: add_to_queue, batch_add_to_queue. |
| save_queue_as_playlistA | Capture the current playback queue as a durable playlist. Reads GET /me/player/queue, creates (or appends to) a playlist, adds URIs in batches of 100 preserving order. Handles mixed track/episode URIs. |
| batch_add_to_queueA | Add multiple URIs to the playback queue in one shot. POSTs each URI to /me/player/queue and returns a summary of queued/failed counts. Quota: 🟡 N writes (one POST per URI). Also covers: single add via add_to_queue, playlist queue via queue_playlist — See also: add_to_queue, queue_playlist. |
| save_playback_stateB | Snapshot current playback state to a named local slot (sidecar JSON). |
| restore_playback_stateA | Restore a saved playback state snapshot (seeks, shuffle/repeat, queue context) to a device. |
| list_playback_statesA | List saved playback state snapshots from the local sidecar. |
| rename_deviceA | Rename a device locally (sidecar label — Spotify has no rename endpoint). |
| set_device_volume_presetB | Store a per-device volume preset (0–100) in the local sidecar. |
| apply_device_presetsB | Apply all stored per-device volume presets via PUT /me/player/volume. |
| list_device_presetsA | List stored device name labels and volume presets. |
| tag_listening_sessionB | Tag a listening session (auto-detected 30-min gaps from recently-played) with labels. |
| replay_sessionC | Replay a tagged listening session: queue its tracks or create a playlist. |
| list_sessionsB | List tagged listening sessions (optionally filter by tag). Auto-detect mode can also scan recently-played gaps. |
| save_smart_playlist_ruleA | Persist a smart-playlist rule to the local sidecar for later refresh (mirrors backup sidecar pattern). |
| refresh_smart_playlistA | Refresh a persisted smart playlist: rebuild candidates from the stored rule and recreate the playlist content. |
| save_show_digestA | Create or update a digest playlist from the latest show_new_episodes radar (auto-save helper). |
| play_onA | Play a context/uris/search query on a named device (resolves device name → id via GET /me/player/devices, then PUT /me/player/play). 🟡 (1 read + 1 write; +1 if volume/shuffle). Supports device name substring or exact id. |
| queue_nextB | Queue a track/episode to play next (tail insert with honest disclosure — Spotify has no insert-next API; tail placement is the API reality). Optionally notes temp-playlist workaround. 🟢 (1 write) |
| describe_queueA | Enriched queue view: currently playing + up-next with durations, total remaining, and source context label. 🟢 (1 read) or 🟡 (2 if include_context resolves playlist/album name). Also covers: raw queue via get_queue, snapshot via get_queue_snapshot — See also: get_queue, get_queue_snapshot. |
| describe_listening_sessionA | Playback history timeline from recently-played, optionally grouped into sessions (30-min gap via detectSessions). 🟢 (1 page) / 🟡 (2 pages). Read-only. |
| play_atB | Start playback at a specific position — accepts H:MM:SS / MM:SS / seconds string or position_ms. Wraps PUT /me/player/play with offset/position_ms. 🟢 (1 write). |
| device_healthA | Device availability & capability report — merges GET /me/player/devices + GET /me/player active id + sidecar labels/presets. 🟢 (1-2 reads, local merge). Read-only. |
| seek_relativeA | Relative seek — forward/back by delta_ms from current progress (GET /me/player then PUT /me/player/seek, clamped to [0, duration]). 🟢 (1 read + 1 write). |
| playback_timelineA | Progress forecast — elapsed/remaining for current track and optional queue runway, plus ETA wall-clock. 🟢/🟡 (1 read; +1 if include_queue). Read-only. |
| repeat_queue_toggleB | One-call queue-repeat helper — sets repeat=context/off and optionally shuffle in 1-2 writes. 🟢. |
| now_playing_historyA | Merged listening stream — recently-played plus currently-playing item on top (deduped). 🟡 (2 reads). Read-only. |
| playback_compare_statesA | Diff two saved playback snapshots (sidecar only, no API). Shows item/shuffle/repeat/progress/device/context changes. 🟢 (0 API calls). |
| peek_nextA | Queue lookahead — next N tracks with durations and total runway. Right-sized via max_results. 🟢 (1 read). Read-only. |
| get_playback_contextB | Resolve the current playback context URI (from GET /me/player) to catalog metadata — playlist/album/artist/show name, owner, track count. 🟢/🟡 (1-2 reads). Read-only. |
| volume_stepA | Nudge volume up/down by a step (reads current volume via GET /me/player, then PUT /me/player/volume clamped 0-100). 🟡 (1 read + 1 write). |
| market_availabilityA | Per-entity multi-market preview — checks if a track/episode/album is playable in each of 1-10 given markets (N× GET /{type}/{id}?market=X). Reports per-market available/404 plus full available_markets. 🟡 (N reads, 1-10). Read-only. |
| spotify_doctorA | Run local diagnostics: token presence/expiry, auth-time scopes vs write tools enabled by active toolsets, Premium gating notes, rate-limit cooldown, and a config snapshot. Read-only, best-effort live account probe for product/country when network available. |
| get_playback_snapshotA | Return the full current playback state compactly: device, track/episode, position, shuffle/repeat and context. Read-only. |
| capture_playback_positionA | Bookmark the current track, playback position and device to a local JSON file under the backup dir so it can be resumed later with resume_playback_position. Writes only a local sidecar file, never touches Spotify. |
| resume_playback_positionA | Resume a captured playback bookmark: transfer playback to the bookmarked device and seek to the bookmarked position. Preview by default — pass dry_run=false to execute. |
| list_playback_bookmarksA | List all captured playback bookmarks stored locally under the backup dir, newest last. Read-only. |
| delete_playback_bookmarkA | Delete one captured playback bookmark file from the local backup dir. Preview by default — pass dry_run=false to delete. |
| compare_devicesA | List every available Spotify device ranked by active state, then device type (Computer > Smartphone > Tablet > TV > Speaker), then volume. Read-only. |
| get_device_volume_reportA | Report volume_percent, supports_volume and active state for every Spotify device, highlighting the currently active device. Read-only. |
| plan_volume_level_across_devicesA | Plan setting every (volume-capable) Spotify device to one target volume level — returns the exact per-device PUT calls without executing anything. Read-only planner. |
| apply_volume_planA | Set one target volume level across all (or selected) Spotify devices via per-device PUT /me/player/volume. Preview by default — pass dry_run=false to apply. |
| get_queue_snapshotA | Return the full upcoming queue with per-track runtime and the total queue runtime. Read-only. Also covers: raw queue via get_queue, enriched view via describe_queue — See also: get_queue, describe_queue. |
| queue_runtime_reportA | Compute runtime statistics for the upcoming queue: total, average, longest and shortest items plus time remaining on the current track. Read-only. |
| split_queue_planA | Plan splitting the upcoming queue into runtime-bounded playlist chunks (default 30 minutes each). Preview by default — pass dry_run=false to actually create the playlists and fill them. |
| queue_duplicate_checkA | Check the upcoming queue for duplicate tracks/episodes and report each duplicate group with its positions and wasted runtime. Read-only. |
| queue_prune_planA | Plan pruning the upcoming queue: identify redundant duplicate entries (and optionally podcast episodes) to drop, and emit a clean re-queue list. Spotify has no queue-removal endpoint, so this is a plan you act on with queue playback. Read-only planner. |
| get_context_inspectA | Inspect the currently-playing context (playlist/album/artist) in detail, including the position of the current track within that context when it is enumerable. Read-only. |
| predict_next_tracksB | Predict the next N tracks that will play from the queue, each with its own runtime and the cumulative time until it plays. Read-only. |
| shuffle_state_reportA | Report the current shuffle and repeat state, the device they apply to and the active context. Read-only. |
| playback_health_checkA | Probe the playback stack end to end: token validity, available devices, current playback state and queue readability — reporting pass/fail per probe. Read-only. |
| listening_session_startA | Start a local listening-session log: captures the current playback snapshot into a session file under the backup dir; close it later with listening_session_close. Writes only a local sidecar file. |
| listening_session_closeA | Close a listening session: captures an end-of-session playback snapshot, stamps the duration and marks the session log closed. Writes only a local sidecar file. |
| listening_session_reportB | Report a listening session (or the most recent one when no id is given): duration, start/end snapshots and closed status. Read-only. |
| transfer_playback_with_stateA | Transfer playback to another device while restoring the full state: same track, position, shuffle and repeat. Preview by default — pass dry_run=false to execute the transfer. |
| sleep_timer_planA | Plan a sleep timer: pick the leading queue items whose cumulative runtime best approximates a target duration, then a final pause call. Read-only planner. |
| device_type_censusA | Census of available Spotify devices grouped by type, with counts, names and volume range per group. Read-only. |
| sort_playlist_planA | Plan a sort of a playlist by name, artist, album, duration, added-at date or release era and preview the exact resulting order — read-only, commits nothing (use sort_playlist_apply to commit). Quota: 🟢 1–2 GETs. |
| sort_playlist_applyA | Sort a playlist in place by name, artist, album, duration, added-at date or release era: backs up the current items to a local file first, then rewrites via one atomic replace. dry_run=true (default) previews only. Quota: 🟢 GET + 1 local write + 1 PUT. |
| reverse_playlist_planA | Plan (and optionally commit) reversing a playlist's entire order via one atomic replace — dry_run defaults to TRUE so it returns the reversed PLAN read-only. Quota: 🟢 GET + 1 PUT when committing. Also covers: playlist_reverse variant — See also: playlist_reverse. |
| rotate_playlist_planA | Plan (and optionally commit) rotating a playlist by N positions — positive moves the first N items to the end, negative moves the last |N| to the front; dry_run defaults to TRUE so it returns the rotated PLAN read-only. Quota: 🟢 GET + 1 PUT when committing. |
| interleave_playlists_planA | Plan (and optionally commit) interleaving 2–10 playlists — round-robin one track each or in N-track chunks. Without a target it returns the interleaved PLAN read-only; with target_playlist_id and dry_run=false it atomically overwrites the target. Quota: 🟢 N GETs + 1 PUT when committing. |
| merge_playlists_planA | Plan (and optionally commit) merging 2–10 playlists in order into a NEW playlist (optional first/last dedupe) — dry_run defaults to TRUE so it returns the merged PLAN read-only. Quota: 🟡 N GETs + create + chunked adds when committing. |
| playlist_difference_planA | Plan (and optionally commit) the set difference "base minus subtrahends": keep the base playlist's tracks that appear in NONE of up to 5 others. Without a target it returns the PLAN read-only; with target_playlist_id and dry_run=false it atomically overwrites the target. Quota: 🟢 ≤7 GETs + 1 PUT when committing. |
| playlist_intersectionA | Report the tracks present in ALL of 2–10 playlists, including which of the source playlists each common track appears in — the read-only intersection analysis (commit variants live in the set-op plan tools). Quota: 🟢 N GETs. |
| playlist_union_previewA | Preview the union of 2–10 playlists as a first-seen-ordered sequence, with per-playlist counts and how many tracks are unique to each — read-only, no writes. Quota: 🟢 N GETs. |
| extract_playlist_rangeA | Extract a positional range of a playlist (0-based start, EXCLUSIVE end; negative values count from the end) into a NEW playlist, reporting the original→new position map — dry_run defaults to TRUE. Quota: 🟢 GET + create + chunked adds when committing. |
| remove_playlist_rangeA | Delete a positional range of a playlist (0-based start, EXCLUSIVE end; negative values count from the end): backs up the current items to a local file first, then removes by positions + uris. dry_run defaults to TRUE. Quota: 🟢 GET + chunked deletes when committing. |
| dedupe_playlist_planA | Census every duplicate uri in a playlist — groups, all positions, and the exact keep-first/keep-last removal plan — read-only (commit with dedupe_playlist_apply). Quota: 🟢 1–2 GETs. |
| dedupe_playlist_applyA | Remove duplicate uris from a playlist keeping the first (or last) occurrence: backs up the current items to a local file first, then rewrites via one atomic replace. dry_run defaults to TRUE. Quota: 🟢 GET + 1 local write + 1 PUT when committing. |
| split_playlist_by_countA | Split a playlist into N roughly-equal parts, each written to a NEW playlist named " 1..N" — dry_run defaults to TRUE so it returns the part plan read-only. Quota: 🟡 GET + N creates + chunked adds when committing. |
| split_playlist_by_durationA | Split a playlist greedily into consecutive parts of a target runtime (e.g. 60-minute commute blocks), each written to a NEW playlist — dry_run defaults to TRUE. Quota: 🟡 GET + N creates + chunked adds when committing. |
| filter_playlist_by_eraA | Filter a playlist by album release era (decade and/or year window) and either report the matches read-only or — with a name and dry_run=false — write them to a NEW playlist. Quota: 🟢 GET (+ create/adds when committing). |
| filter_playlist_by_artistA | Filter a playlist by artist (IDs/URIs or names, matched on id or exact name) and either report the matches read-only or — with a name and dry_run=false — write them to a NEW playlist. Quota: 🟢 GET (+ create/adds when committing). |
| filter_playlist_by_durationA | Filter a playlist by track duration (min/max seconds) and either report the matches read-only or — with a name and dry_run=false — write them to a NEW playlist. Quota: 🟢 GET (+ create/adds when committing). |
| sample_playlist_tracksA | Draw a uniform random sample of N tracks from a playlist — deterministic when a seed is given (same seed + same playlist = same sample), read-only. Quota: 🟢 1–2 GETs. |
| playlist_table_of_contentsA | Build a table of contents for a playlist: totals, runtime, and "chapters" — contiguous position ranges grouped by the month each block was added — plus contributor counts. Read-only. Quota: 🟢 1–2 GETs. |
| playlist_edit_journalA | Journal a playlist's live edit history from added_at/added_by metadata: add-batches by date with who added them, collab contributors, and drift verdict — read-only, no local snapshot needed. Quota: 🟢 1–2 GETs. |
| move_tracks_between_playlistsA | Move matching tracks (by uris, name substring, or artist) from one playlist to another: backs up the source to a local file first, deletes the matched positions from the source, then appends them to the destination. dry_run defaults to TRUE. Quota: 🟡 GETs + chunked deletes + chunked adds when committing. |
| balance_playlist_pairsA | Plan (and optionally commit) rebalancing 2–10 playlists to similar track counts or runtimes: computes surplus moves from the larger to the smaller. dry_run defaults to TRUE so it returns the move PLAN read-only. Quota: 🟡 N GETs + moves when committing. |
| playlist_clone_liveA | Clone a playlist AS IT IS RIGHT NOW into a NEW playlist (name, optional description and publicity) — works on the live playlist, not a local snapshot; dry_run defaults to TRUE. Quota: 🟡 GET + create + chunked adds when committing. |
| artist_deep_diveB | Builds a one-artist dossier from the artist object plus a full discography walk: genres, release counts by group, active span, most prolific year, first/latest releases and most frequent collaborators. Quota: 1 + one paginated /artists/{id}/albums walk. |
| artist_discography_gapsA | Chronological discography table (oldest to newest by default) with per-release gap-in-days versus the previous release, built from a paginated /artists/{id}/albums walk. Quota: one paginated walk (several queued calls on long discographies). |
| artist_era_samplerA | Splits an artist discography into equal year-span eras and deterministically picks representative releases per era (albums first, earliest and fullest first) as a sampling plan. Quota: one paginated /artists/{id}/albums walk. |
| artist_deep_cutsA | Finds an artist's deep cuts: tracks whose (normalized) title appears on exactly one release across the artist's recent albums and singles, ranked longest-first. Quota: 2 walks + one chunked /albums?ids= fan-in (1 call per 20 releases). |
| artist_first_releaseB | Locates an artist's earliest release across all album groups and returns a full card for it: label, copyright, track listing and runtime via the full album payload. Quota: one paginated walk + 1 GET /albums/{id}. |
| artist_latest_release_reportB | Reports on an artist's most recent release: full metadata (label, copyright, track listing, runtime), days since release and the gap to the release before it. Quota: one paginated walk + 1-2 GET /albums/{id}. |
| artist_album_completenessA | Checks how complete your saved-album library is for one artist: canonical studio-album titles from the discography walk matched (edition-insensitively) against your /me/albums walk, with a missing list. Quota: 2 paginated walks (artist albums + saved albums). |
| artist_top_vs_savedA | Compares your top artists (from /me/top/artists) against your own library: saved-album and saved-track counts per top artist, ranked by library presence. Read-only; no popularity fields used. Quota: 1 /me/top/artists + 2 capped library walks. |
| find_collaborationsA | Finds tracks where two artists appear together: walks artist A's releases, matches artist B (name or ID) on release-level credits, then pinpoints the exact shared tracks via full-album payloads. Quota: 1-2 walks/searches + chunked /albums?ids= fan-in. |
| artist_collaboration_networkB | Builds a depth-2 collaboration web for one artist from discography walks alone: first-degree collaborators with co-appearance counts, then each top collaborator's recent releases to surface second-degree links and mutual connections. Quota: 1 + up to first_degree_cap additional small walks. |
| label_explorerB | Census of record labels across your saved albums (label comes from chunked full-album fan-in); pass a label name to list just that label's albums in your library. Quota: 1 walk + 1 /albums?ids= call per 20 albums. |
| year_explorerA | Time-machine view of one release year built from your own library plus a small catalog supplement: saved albums and saved tracks originally released in that year, plus top catalog matches via a year: search. Quota: 2 capped library walks + 1 /search call. |
| decade_sampler_planA | Read-only sampling plan across decades: groups your saved albums by release decade and deterministically picks evenly spaced representatives per decade (spread across the decade, longest-held tiebreak). Quota: 1 capped /me/albums walk. |
| artist_name_disambiguatorA | Resolves an ambiguous artist name: runs a typed artist search and profiles each candidate (genres, active year span and a sample release from a small discography probe) so you can pick the right ID. Quota: 1 /search + 1 small albums call per candidate. |
| artistwatch_new_additionsA | Watches your FOLLOWED artists for new material: walks /me/following, probes each artist's latest release and flags those released within the last N days. Quota: 1 cursor walk + 1 small albums call per followed artist. |
| album_representative_planA | Builds a deterministic "sample this album" plan: opener, mid-point, closer and the longest track, ordered by original position with cumulative offsets, from the full album payload. Quota: 1 GET /albums/{id} (+1 paged tracks walk above 50 tracks). |
| front_to_back_planA | Full front-to-back listening plan for one album: ordered track listing with cumulative start times and vinyl-style side breaks at a configurable minutes-per-side budget. Read-only. Quota: 1 GET /albums/{id} (+1 paged tracks walk above 50 tracks). |
| b_sides_finderB | Surfaces an artist's b-sides: tracks on singles/compilation releases whose normalized titles never appear on the artist's core album-group releases, found via discography walks + chunked full-album fan-in. Quota: 2 walks + 1 /albums?ids= call per 20 releases per group. Also covers: b_sides_detector — See also: b_sides_detector. |
| album_focus_reportA | Deep focus report on one album: track-by-track listing with cumulative runtime, duration statistics, edition detection (same base title elsewhere in the artist discography) and label/copyright capture. Quota: 1 GET /albums/{id} + 1 paginated discography walk. |
| artist_catalog_statsB | Numbers-only catalog profile for one artist: release totals by group, summed track counts, average tracks per release, active span, busiest year and median release gap, from one paginated discography walk. Quota: one paginated /artists/{id}/albums walk. |
| lyric_snippet_searchA | Finds candidate tracks for a remembered lyric or title fragment: runs quoted-phrase /search over tracks and ranks exact title matches first. DISCLOSURE: Spotify's public API has no lyrics endpoint, so matching is title/album-based; verify the snippet against the returned candidates. Quota: 1-2 /search calls. |
| new_music_from_top_artistsA | Fresh-music digest from YOUR listening: probes the latest release of each of your top artists (from /me/top/artists) and flags those released within the last N days. Quota: 1 /me/top/artists + 1 small albums call per top artist. |
| discovery_digestB | One combined discovery digest from your own data: latest releases from your top artists with freshness flags, a tag:new catalog search seeded with your most common top-artist genre, and a followed-artist count. Quota: ~2 + N small API calls (top artists, per-artist probes, 1 search, 1 followed walk). |
| era_distribution_reportA | Histogram of releases across eras: pass artist_id for that artist's discography in 5-year buckets, or omit it for your own saved-album library in decade buckets, with percentages and peak-era callouts. Quota: 1 walk (discography or saved albums). |
| artist_discography_explorerA | Overview stats for one artist's discography: release counts by type, first and latest releases, active span and releases per active year. Accepts an artist ID, URI, URL or name (name → closest search match). Quota: 🟡 one paginated /artists/{id}/albums walk. |
| artist_album_timelineB | [local-compute] Chronological release table (oldest → newest) with inter-release gap days, median gap and the longest drought highlighted. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| artist_era_mapA | Cluster an artist's releases into eras separated by quiet periods longer than |
| artist_release_type_breakdownA | Break an artist's discography down by album type (album · single · compilation · appears_on): counts, first and latest per type, and sample titles. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| artist_latest_releasesA | Fetch the most recent N releases from an artist's catalog with age in days — a quick "what's new here" read. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| artist_reissue_detectorA | Detect alternate versions (live/deluxe/remaster/expanded editions) in an artist's release titles and group them by base title — spot the canonical release vs its variants. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| artist_debut_release_finderA | Dig up an artist's earliest release with full detail plus everything else they released in that same year — the origin story view. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| label_discography_explorerA | Group an artist's albums and singles by record label (via batched /albums?ids= payloads) and rank labels by release count with year ranges. Quota: 🔴 paginated walk + batched /albums lookups. |
| artist_discography_searchA | Full-text filter over an artist's release titles — find that live album, deluxe edition or collaboration without scrolling the whole catalog. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| artist_decade_spanA | Histogram of an artist's releases per decade with the dominant decade called out — instantly see which era carries the catalog. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| album_track_explorerA | Full track listing for one album with per-track cross-album duplicate counts — spot which songs are unique to this release vs recycled across the discography. Quota: 🔴 one album fetch + one paginated discography walk. |
| album_openers_reportA | List track 1 (the side-A opener) of every studio album by an artist, chronologically — the "how each record begins" view. Quota: 🔴 paginated walk + batched /albums lookups. |
| deep_cuts_finderA | Surface deep cuts: album tracks past position 2 that are neither the title track nor released as singles — the forgotten album material, per album. Quota: 🔴 paginated walk + batched /albums lookups. |
| b_sides_detectorB | Detect B-sides: tracks that appear on an artist's singles but never on any album — the non-LP catalogue. Also covers: b_sides_finder (same discography scan) — See also: b_sides_finder. Quota: 🔴 paginated walks + batched /albums lookups. |
| track_release_originA | Find where a track first appeared: walks an artist's releases chronologically and reports the earliest album/single/compilation containing the track, plus later re-appearances. Quota: 🔴 paginated walk + batched /albums lookups. |
| album_anniversary_checkA | Upcoming album anniversaries for an artist within the next |
| album_duration_reportB | Runtime profile for an artist's studio albums: total length, track count and longest track per album, ranked by runtime — find the epics and the EPs. Quota: 🔴 paginated walk + batched /albums lookups. |
| new_music_from_saved_artistsA | Newest releases across the artists you follow (and optionally those in your saved albums), sorted by release date — your personal new-release feed. Quota: 🔴 followed-artist walk + per-artist album peeks. |
| artist_scout_from_playlistsA | Find new-to-you artists: pull the artist roster from one of your playlists and rank the ones you have NOT saved any tracks of — discovery from your own rotation. Quota: 🔴 playlist walk + saved-tracks membership check. |
| artist_live_albums_finderA | List an artist's live releases (titles matching live/unplugged/live-at patterns) chronologically — the concert-record shelf. Quota: 🟡 one paginated /artists/{id}/albums walk. |
| artist_collection_gapsA | List an artist's studio albums missing from your saved collection — the exact records to add next. Quota: 🔴 paginated walk + saved-albums membership check. |
| artist_singles_timelineA | Chronological singles timeline for an artist (date · title · track count) — the 45-rpm history in one table. Quota: 🟡 one paginated /artists/{id}/albums walk (singles group). |
| genre_dive_searchA | Search artists by genre keyword (e.g. "britpop", "afrobeat") and list the matching profiles with their genre tags — the entry point for a genre dive. Quota: 🟢 one GET /search call. |
| scene_sampler_searchA | Build a scene sampler: search artists by a scene/genre keyword, then fetch each act's newest release as a one-track-per-artist listening plan. Quota: 🟡 one /search call + one album peek per artist. |
| saved_albums_by_decadeB | Group your saved albums by release decade (from each album release_date) and show a histogram. Read-only. |
| saved_albums_by_labelB | Group your saved albums by record label (from each album payload) and rank labels by count. Read-only. |
| saved_albums_by_typeA | Group your saved albums by album_type (album / single / compilation / EP). Read-only. |
| artist_representation_censusA | Census of your saved tracks by credited artist: rank the top N artists by how many saved tracks they appear on. Read-only. |
| orphaned_artist_checkA | Find asymmetries in your library: artists you saved tracks from but never saved an album of, and vice versa. Read-only. |
| saved_track_age_reportB | Histogram of when you saved each liked track (added_at): age buckets plus a per-year add histogram. Read-only. |
| added_on_this_dayA | Show tracks you saved on this calendar day (month + day) in previous years — your library "on this day". Read-only. |
| library_growth_timelineB | Show how fast your saved-track library grew: adds per month, busiest month, and your longest/current monthly add streaks. Read-only. |
| duplicate_saved_versionsA | Find songs you saved more than once from different albums (same normalised track name + primary artist, distinct album IDs). Read-only. |
| album_edition_lintA | Lint your saved albums for the same record kept across multiple editions (same name after edition-noise stripping + primary artist, distinct album IDs). Read-only. |
| never_played_savedA | List saved tracks absent from your recently-played window. Honest bounds: recently-played only covers roughly your last 50 plays, so this is "not played lately", not "never played". Read-only. |
| saved_vs_playlist_coverageA | Report which of your saved tracks appear in none of your owned/followed playlists, with a coverage ratio. Read-only. |
| artist_completeness_scoreA | For your top N saved-track artists, fetch each artist’s top tracks and score how many of them you have saved (0–100%). Read-only, capped at 25 artists. |
| saved_runtime_by_eraB | Total listening runtime of your saved tracks grouped by the release decade of each track’s album. Read-only. |
| longest_saved_tracksA | Rank your saved tracks by duration, longest first. Read-only. |
| shortest_saved_tracksA | Rank your saved tracks by duration, shortest first. Read-only. |
| saved_albums_by_yearB | Histogram of your saved albums by release year. Read-only. |
| collab_density_reportC | Measure collaboration density: how many of your saved tracks credit 2+ artists, with the top collaborations ranked. Read-only. |
| featuring_density_reportA | Measure how many of your saved track titles carry a "feat." / "featuring" marker, with examples ranked. Read-only. |
| title_length_outliersB | Statistical outliers in saved-track title length (characters): mean/median plus the longest and shortest titles. Read-only. |
| explicit_content_ratioB | Share of your saved tracks flagged explicit, with the top explicit artists. Read-only. |
| is_local_censusA | Count your saved tracks flagged as local files (is_local true) and list them — these often fail to play on other devices. Read-only. |
| unplayable_saved_checkA | Audit your saved library for tracks the API marks unplayable (is_playable false or a restrictions block) and show why. Read-only. |
| library_value_summaryA | One mega-report over your saved library: totals, runtime, explicit/local/unplayable counts, collab + featuring density, growth streaks, era histogram, duplicate-version and edition-lint counts, and top artists. Read-only. |
| list_saved_showsB | List the shows saved in your library with publisher, episode count, and saved date — truncated to max_results with a fetch-all hint when longer. Default 20 rows, 'concise' prose. |
| get_show_detailsB | Fetch full metadata for one show (description, publisher, episode count, languages, media type). Defaults to 'concise' prose. |
| list_show_episodesA | Page one show's episode list newest-first with duration and release date — the browse view for a single podcast. Default limit 20, 'concise' prose. Canonical for GET /shows/{id}/episodes; get_show_episodes is a deprecated alias. Also covers: paged podcast episodes. |
| get_show_latest_episodeA | Return the single newest episode of a show with duration, description, and resume point — 'what dropped last?' in one GET. Defaults to 'concise' prose. |
| saved_shows_publisher_censusA | Count your saved shows by publisher and rank the biggest presses in your subscriptions — read-only census over the whole shelf. Defaults to top 10 publishers, 'concise' prose. |
| shows_without_new_episodesA | Find saved shows with no episode released inside the lookback window ("which pods went quiet?") — pages /me/shows then the latest episode per show. Defaults to 30 days, 50 show lookups. |
| stale_saved_shows_planA | Build a PLAN for pruning saved shows that have published nothing within the staleness threshold — read-only by design, it never unfollows anything; pair with remove_saved_shows to commit. Defaults to 90 days, 50 show lookups. |
| subscribe_to_showA | Save one or more shows to your library (PUT /me/shows) — previews a deterministic PLAN by default; pass dry_run=false to commit. |
| unsubscribe_from_showA | Remove ONE saved show from your library (DELETE /me/shows) — removal verb: also see remove_saved_shows (bulk), delete_playlist_snapshot (local snapshots). After confirming what it is — previews a PLAN naming the show by default; pass dry_run=false to commit. |
| remove_saved_showsA | Bulk-remove shows from your library (DELETE /me/shows) — removal verb family: also see unsubscribe_from_show (single), remove_saved_episode (episodes). After cross-checking which of the given IDs are actually saved — previews a PLAN by default; pass dry_run=false to commit. |
| get_episode_detailsA | Fetch full metadata for one episode (duration, release date, resume point, show, description). Defaults to 'concise' prose. |
| check_episode_savedA | Check which episodes are already saved in your library (GET /me/episodes/contains) — batch yes/no, no guessing. Read-only. |
| save_episodeA | Save episodes to your library (PUT /me/episodes) — previews a PLAN with episode names by default; pass dry_run=false to commit. |
| remove_saved_episodeA | Remove episodes from your library (DELETE /me/episodes) — removal verb: also see remove_saved_shows (shows), delete_playlist_snapshot (local). After cross-checking which are actually saved — previews a PLAN by default; pass dry_run=false to commit. |
| mark_episode_played_planA | Plan (never execute — Spotify removed the mark-played API, issue #230) which episodes to mark fully played: fetches current resume points and outputs the target positions the player would seek to. Read-only by design. |
| get_newly_released_episodesA | Collect episodes released since a date across ALL saved shows, merged newest-first and tagged with their show — the unified new-episode inbox. Defaults to the last 7 days. |
| shows_release_calendarA | Estimate each saved show's next expected release from its recent publish cadence (median interval between the latest publishes) — a forward calendar for your subscriptions. Defaults to 30 show lookups. |
| episode_runtime_reportA | Runtime statistics for one show's episodes — count, total/min/median/max duration, and the longest and shortest episodes — to plan listening time. Defaults to the 100 newest episodes. |
| publisher_portfolioA | Per-publisher portfolio across your saved shows: show count, listed episode totals, and sampled runtime of their recent episodes — who owns your listening time. Defaults to 10 recent episodes per show. |
| show_activity_feedA | Chronological feed of the most recent episodes across ALL your saved shows, merged and sorted newest-first — one scroll instead of N show visits. Defaults to 3 episodes per show, newest first. |
| show_backlog_planA | Plan (dry-run only, never mutates) your podcast backlog: unlistened episodes across saved shows ordered SHORTEST-FIRST, so the queue clears with quick wins. Defaults to 20 recent episodes per show, 20 shows. |
| find_show_by_publisherA | Search the catalog and match shows whose PUBLISHER (network) matches your query — the missing publisher facet on show search. Defaults to 10 search results. |
| episode_guest_censusA | Census recurring GUESTS across a show's recent episodes by mining description credits ("with X", "featuring X", "guest X") — who keeps coming back. Defaults to the 20 newest episodes. |
| show_recommendation_briefA | Cross-reference newly released episodes against your saved-episode and recently-played history: which new drops are NOT yet saved or played — a listen-next brief. Defaults to the last 14 days. |
| top_artist_ranking_deltaA | Show how each top artist’s rank moved between two time windows (climbers first; default compares short_term against medium_term). Quota: 2× GET /me/top/artists. |
| top_track_ranking_deltaA | Show how each top track’s rank moved between two time windows (climbers first; default compares short_term against medium_term). Quota: 2× GET /me/top/tracks. |
| top_artist_leaderboardA | Score top artists across all three time windows with recency-weighted points (short×3, medium×2, long×1) and rank the combined leaderboard. Quota: 3× GET /me/top/artists. |
| top_track_leaderboardA | Score top tracks across all three time windows with recency-weighted points (short×3, medium×2, long×1) and rank the combined leaderboard. Quota: 3× GET /me/top/tracks. |
| artist_velocity_reportB | Classify each top artist’s momentum by comparing short_term and long_term ranks (surging / climbing / steady / slipping / falling / new_entry). Quota: 2× GET /me/top/artists. |
| track_rotation_reportA | Report how many times each track appears in recently-played history and bucket them into heavy/regular/light rotation (default 150 history items). Quota: GET /me/player/recently-played cursor walk. |
| discovery_ratioA | Measure what share of your recently-played tracks are NOT in your top tracks (discovery vs staple listening; default compares against medium_term). Quota: GET /me/player/recently-played + 1× GET /me/top/tracks. |
| listening_clockB | Profile when you listen by local hour (24-bucket histogram plus daypart totals and peak hour) from recently-played history (default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| weekday_listening_reportA | Break recently-played history down by weekday (plays, unique tracks, unique artists, busiest day; Mon→Sun ordering, default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| binge_detector_reportA | Flag artists whose recently-played counts exceed a play threshold (default ≥5 plays) with span and track coverage, sorted by intensity. Quota: GET /me/player/recently-played cursor walk. |
| repeat_listener_reportA | Quantify repeat listening in recently-played history: share of plays going to tracks heard more than once plus consecutive same-track replays (default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| listening_streak_reportA | Compute consecutive-day listening streaks from recently-played history, including the longest streak and whether it is still alive (default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| top_genre_censusA | Build a weighted genre census from your top artists (rank-weighted across the three windows; genres resolved via the batch /artists endpoint, default 40 artists per window). Quota: 3× GET /me/top/artists + 1× GET /artists?ids=. |
| mood_bucket_reportA | Segment recently-played plays into daypart × familiarity buckets (fresh tracks vs staples from your top tracks, default medium_term) as a lightweight listening-mood proxy. Quota: GET /me/player/recently-played + 1× GET /me/top/tracks. |
| deep_dive_reportB | Deep-dive one artist: recently-played counts, which of their tracks rotate, and their rank in each top-artists window (accepts ID, URI, or URL). Quota: GET /me/player/recently-played + 3× GET /me/top/artists. |
| listening_clock_heatmapB | Render a weekday × hour listening heatmap from recently-played history with the peak cell highlighted (default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| artist_listening_clockA | Profile WHEN you play one specific artist (hour-of-day histogram plus daypart split; defaults to your most-played artist in the history window). Quota: GET /me/player/recently-played cursor walk. |
| session_length_reportA | Split recently-played history into listening sessions by inactivity gap (default 30 minutes) and report size/duration stats plus a distribution (default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| listening_gaps_reportA | Find the quiet stretches in your recently-played history: every inactivity gap above a threshold (default ≥120 minutes) with the longest gaps listed (default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| weekly_rotation_reportA | Track day-by-day rotation from recently-played history: plays, unique tracks/artists, and first-heard-this-window tracks per day (oldest→newest, default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| listening_consistency_scoreA | Score how consistent your listening is (0-100) from recently-played history: active-day coverage, hour spread, and weekday balance with each component shown (default 150 items). Quota: GET /me/player/recently-played cursor walk. |
| era_preference_reportA | Compare the decade mix (album release eras) of your recently-played tracks against a top-tracks window, highlighting where recent listening skews older or newer (default medium_term). Quota: GET /me/player/recently-played + 1× GET /me/top/tracks. |
| listening_recap_briefA | Produce a one-call recap of your recent listening: headline plays, top artist/track, leaderboard leaders, peak hour, busiest weekday, discovery ratio, and streak status (default 200 history items). Quota: GET /me/player/recently-played + 4× GET /me/top/*. |
| listening_history_exportB | Export your recently-played history as a paginated chronological table (oldest→newest; supports offset continuation; default walks up to 500 items). Quota: GET /me/player/recently-played cursor walk. |
| parse_spotify_uriA | Parse a single spotify: URI into its entity kind and ID parts without any network call; reports validity and a parse error when malformed. |
| format_spotify_uriA | Format an entity kind + ID pair into a canonical spotify: URI locally; validates the ID shape and returns null when the pair is malformed. |
| spotify_uri_to_open_urlA | Convert a spotify: URI (or bare ID + kind) into its https://open.spotify.com share URL locally, stripping nothing else. |
| open_url_to_spotify_uriB | Convert an open.spotify.com URL (including /embed/ forms with ?si= tracking params) into a canonical spotify: URI locally. |
| extract_spotify_idA | Extract the bare 22-char entity ID from any Spotify reference — URI, open.spotify.com URL (embed/intl forms ok), or bare ID — with zero network calls. |
| validate_spotify_uriA | Validate a Spotify reference against the URI grammar and 22-char base62 ID rule locally, returning a field-by-field validity report. |
| normalize_spotify_uriA | Normalise any accepted Spotify reference form (URI with extra segments, URL with tracking params, bare ID) into its canonical lowercase spotify: URI, offline. |
| spotify_uri_kindA | Report the entity kind of a single Spotify reference (uri/url/bare ID) locally, or null when the kind segment is missing or unknown. |
| batch_parse_spotify_urisA | Parse a batch of Spotify references into kind/ID parts in one local pass — no network calls; one result row per input. |
| is_valid_spotify_uriA | Boolean validity check for a single Spotify reference — true only for well-formed spotify: URIs with a 22-char base62 ID; evaluated locally. |
| uri_namespace_censusA | Census of a batch of Spotify references grouped by reference form (uri/url/id/invalid) and by entity kind — a local frequency table, no network calls. |
| base62_to_uriA | Reinterpret a base62 ID string as a Spotify entity ID and build the full spotify: URI for a given kind, offline. |
| uri_to_base62A | Strip a spotify: URI (or URL) down to its raw base62 ID string, locally; null when the reference has no extractable ID. |
| make_spotify_uriA | Construct a spotify: URI from kind + ID with strict validation — rejects wrong-length IDs and unknown kinds before anything is built. |
| split_uri_listA | Split a delimited string of Spotify references into a clean array — accepts comma, semicolon, whitespace, or newline separators; local only. |
| join_uri_listA | Join an array of Spotify references into a single delimited string — comma, semicolon, newline, or a custom glue; local only. |
| dedupe_spotify_urisA | Remove duplicate Spotify references from a list, preserving first-seen order, comparing canonical URI forms where possible — local, no network. |
| find_duplicate_spotify_urisA | List duplicate Spotify references in a batch with counts and positions, comparing canonical URI forms — computed locally with no network calls. |
| sort_uris_by_kindA | Sort Spotify references into deterministic groups by entity kind (track, album, artist, playlist, show, episode, audiobook, user) and return them labelled per group — offline. |
| count_uris_by_typeA | Count how many references in a batch belong to each entity kind (track, album, artist, …) — a local tally, no network calls. |
| canonicalize_spotify_uriA | Canonicalise a batch of Spotify references in one local pass: *** scheme, known kind, 22-char base62 ID, tracking params stripped — no network. |
| uri_kind_statsB | Aggregate statistics over a batch of Spotify references: counts per form, per kind, share percentages, and the modal kind — all computed locally. |
| classify_spotify_urisA | Classify each reference in a batch: reference form, entity kind, canonical-form verdict, and per-row issues — one local pass, no network calls. |
| uri_shorthand_expandA | Expand shorthand Spotify refs like "t:", "pl/", or "track:" into canonical spotify: URIs. Codes: t/tr=track, al=album, ar=artist, pl/p=playlist, sh=show, ep=episode, ab=audiobook, u=user — local only. |
| take_playlist_snapshotA | Capture a live playlist’s items (uri, name, added_at) into a timestamped local JSON snapshot file; dry_run=true previews the walk and target filename without writing. Newest, transactional snapshot (swarm) — preferred over legacy snapshot_playlist. See also list_saved_snapshots, read_playlist_snapshot, diff_playlist_snapshots. Also covers: playlist snapshot (swarm). Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| list_saved_snapshotsA | List local playlist snapshots (newest first) with path, creation time, size, and the _meta summary; optionally filtered to one playlist Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| read_playlist_snapshotA | Read one local playlist snapshot and return its meta plus the captured track rows (truncated by max_results) Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| delete_playlist_snapshotA | Delete one local playlist snapshot file; dry_run=true (default) only reports what would be removed Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| diff_playlist_snapshotsB | Diff two local snapshots of a playlist and report every added and removed track between them Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_new_tracksA | List only the tracks added between an older and a newer snapshot of a playlist Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_removed_tracksA | List only the tracks removed between an older and a newer snapshot of a playlist Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_added_at_reportA | Report when the tracks in a snapshot were added to the playlist, bucketed by month (YYYY-MM) Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_changelogB | Build a chronological changelog of a playlist from every local snapshot, describing the changes between consecutive snapshots Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_stats_reportA | Compute stats for one snapshot: track count, unique URIs, duplicates, and the oldest/newest added_at dates Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_integrity_checkB | Validate one snapshot (or all of them): JSON parses, required _meta keys exist, and the declared track_count matches the tracks array Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| prune_old_snapshotsB | Delete old local snapshots, keeping the newest N per playlist and optionally dropping those older than a cutoff; dry_run=true (default) only reports what would go Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| restore_playlist_from_snapshotA | Make a live playlist match a snapshot (add missing, remove extra tracks); dry_run=true (default) returns the deterministic PLAN without touching Spotify Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| restore_playlist_planA | Read-only plan of exactly which tracks would be added/removed to make a live playlist match a snapshot — never mutates Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_diff_summaryA | One-paragraph summary of the differences between two snapshots: counts plus the first few changed tracks Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| find_new_since_snapshotA | Compare a live playlist against a snapshot and list tracks added to the playlist since it was taken Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| find_lost_since_snapshotA | Compare a live playlist against a snapshot and list snapshot tracks that have since disappeared from the playlist Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| merge_snapshot_changes_planA | Read-only plan to replay the changes between two snapshots onto the live playlist (add new and/or remove lost tracks) Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| apply_snapshot_changesB | Execute a merge plan on the live playlist (replay the changes between two snapshots); dry_run=true (default) only returns the plan Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_retention_planA | Report which snapshots a keep-last-N (+ optional age cutoff) retention policy would keep or delete — never deletes anything Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_disk_usageB | Report disk usage of the snapshot directory: total size, file count, per-playlist breakdown, and the largest files Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_integrity_reportA | Aggregate health report over all local snapshots: valid vs corrupt files, per-playlist coverage, and the age of each playlist’s newest snapshot Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| export_snapshot_bundleA | Bundle selected snapshots (all of one playlist, or explicit ids) into a single portable JSON export file locally; dry_run=true (default) previews the bundle Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| snapshot_registry_reportA | Registry view of every local snapshot: per playlist, all snapshot ids with taken_at and track counts, plus naming anomalies in the snapshot directory Snapshot guide: take_playlist_snapshot (create), list_saved_snapshots (list), read_playlist_snapshot (read), diff_playlist_snapshots / snapshot_new_tracks / snapshot_removed_tracks (diff), restore_playlist_from_snapshot / restore_playlist_plan (restore). |
| find_toolA | Search the live tool registry by name or description substring — the fastest way to discover which of the 500+ tools handles a job. Discovery set: find_tool/inspect_tool/toolset_report are always available (also via catalog). Use this first when unsure which verb to use (e.g., playlist vs snapshot vs search). |
| inspect_toolA | Show one tool's full description and input schema before calling it |
| toolset_reportA | Report the active toolsets and registration modules, plus the live registered tool count — answers "how much surface is exposed right now". Discovery set; always available. Also see find_tool / inspect_tool. |
| playlist_resequenceA | Sort a playlist in place — by track name, artist, album, duration, or date added — written back as one atomic replace. Episodes sort last (no artist/album key). Quota: 🟢 2 GETs + 1 PUT. Also covers: reorder_playlist_items (range-based), playlist_move_block — See also: reorder_playlist_items, playlist_move_block. |
| playlist_rotateB | Rotate a playlist by N positions: positive N moves the first N items to the end, negative N moves the last |N| to the front (wraps around). Written as one atomic replace. Quota: 🟢 2 GETs + 1 PUT. |
| playlist_seed_shuffleA | Shuffle a playlist in place (Fisher–Yates) with an optional deterministic seed — same seed, same order, so you can preview and commit the exact same shuffle. Unavailable items are kept, pinned at the end. Quota: 🟢 2 GETs + 1 PUT. |
| playlist_flip_orderB | Reverse a playlist: last item becomes first, written as one atomic replace. The standard fix for imports that arrived backwards. Quota: 🟢 2 GETs + 1 PUT. |
| playlist_move_blockA | Move a contiguous block of items (1-based start + count) so its first item lands at a target position expressed in the ORIGINAL numbering. The rest of the playlist closes up around it. Written as one atomic replace. Quota: 🟢 2 GETs + 1 PUT. Also covers: reorder_playlist_items, playlist_resequence — See also: reorder_playlist_items, playlist_resequence. |
| playlist_swap_positionsA | Swap the items at two 1-based positions — e.g. flip tracks 3 and 7. Positions may be any two distinct slots in the playlist. Written as one atomic replace. Quota: 🟢 2 GETs + 1 PUT. |
| playlist_dedupe_advancedA | Remove duplicate items from a playlist, matching by URI (exact copies) OR by track name (catches re-adds of the same song from different albums/singles when combined with dedupe by uri). Choose keep-first or keep-last. Written as one atomic replace. Quota: 🟢 2 GETs + 1 PUT. |
| playlist_remove_artistA | Remove every track by one artist from a playlist (match by artist name, case-insensitive, or by artist ID / spotify:artist: URI). Shows exactly what would go. Quota: 🟢 2 GETs + 1 PUT when committing. |
| playlist_keep_artistA | Inverse filter: keep ONLY tracks by one artist in a playlist and drop everything else. Optionally keep podcast episodes too (they have no artist). One atomic replace. Quota: 🟢 2 GETs + 1 PUT. |
| playlist_filter_runtimeA | Keep only items whose duration falls inside a window (e.g. min_sec 120 → drop intros/interludes; max_sec 360 → drop 6-minute epics). At least one bound required. One atomic replace. Quota: 🟢 2 GETs + 1 PUT. |
| playlist_chunk_previewA | Read-only pagination preview: how a playlist splits into write-sized chunks (the 100-URI replace limit) or any custom size — per-chunk position ranges, first/last items, and item counts. Plan batched edits before running them. Quota: 🟢 2 GETs. |
| playlist_diffA | Compare two playlists: what is only in A, only in B, and in both — plus whether the shared tracks appear in the same relative order. Read-only. Quota: 🟢 4 GETs. Also covers: diff_playlists (live playlist diff), playlist_difference_plan (subtract plan) — See also: diff_playlists, playlist_difference_plan. |
| playlist_historyA | List your local backup snapshots (from backup_now): file name, created timestamp, and the counts each snapshot carries. The entry point for snapshot_detail / changelog / clone_snapshot. Read-only, no API calls. |
| playlist_snapshot_detailA | Inspect one playlist inside a local backup snapshot: its item_count as recorded plus the full item list (truncated by max_results). Read-only, no API calls. |
| playlist_clone_snapshotA | Restore a playlist from a local backup snapshot as a NEW playlist (never overwrites the live one — clone, don't clobber). Items restore by URI; catalog-removed items are skipped by Spotify automatically. Quota: 🟡 0 GETs + create + chunked adds. |
| playlist_changelogA | Diff ONE playlist across two backup snapshots (older → newer): tracks added, removed, and kept — plus whether the kept tracks were reordered. The "what changed since last week" view. Read-only, no API calls. |
| playlist_pair_checkA | Pairwise relationship report for two playlists: sizes, overlap, Jaccard similarity, and sampled candidates from each side that the other lacks (for merging or splitting decisions). Read-only. Quota: 🟢 4 GETs. |
| playlist_balanceA | Split a playlist into N balanced new playlists: sequential chunks (part 1 = first third, …) or interleave (round-robin deal, so every part samples the whole span). Creates N new playlists; the source is left untouched. Quota: 🟡 2 GETs + N creates + chunked adds. |
| statsfm_resolve_userA | Resolve a stats.fm user id or customId to their profile (falls back to user search) |
| statsfm_top_tracksC | A stats.fm user's most-streamed tracks |
| statsfm_top_artistsC | A stats.fm user's most-streamed artists |
| statsfm_top_albumsC | A stats.fm user's most-streamed albums |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| dj | Act as a DJ. Based on my top artists and current mood, queue up a set of songs. |
| playlist_from_mood | Create a playlist for a given mood. Searches for tracks and adds them to a new playlist. |
| music_taste_summary | Summarize the user's music taste based on their top tracks and artists. |
| discover_weekly_alternative | Based on my top tracks and recently played songs, find lesser-known songs I probably haven't heard. |
| playlist_audit | Audit a playlist for duplicate tracks and unplayable ('dead') entries, with cleanup suggestions. |
| listening_recap | Write a recap of recent listening: top tracks/artists plus recently-played context. |
| migrate_library | Collect tracks from your saved albums into a single playlist. |
| podcast_catchup | List new podcast episodes published since a date across your saved shows, and queue them if asked. |
| artist_deep_dive | Tour an artist's discography: profile, albums, standout tracks. |
| music_briefing | Daily or weekly music briefing: new podcast episodes, new releases from followed artists, catalog freshness, and recently played — composed from radar tools. |
| morning_briefing | Morning briefing: new releases + listening streak + top track. |
| weekly_digest | Weekly digest: taste shift + streaks + recommendations. |
| crate_digging | Crate dig deep cuts for your top artists. |
| triage_liked_songs | Triage your Liked Songs backlog into era- or genre-bucket playlists. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| me | Current user profile ('?format=json' returns the raw API object) |
| player-state | Current Spotify playback state (live; '?format=json' returns the raw API object) |
| player-queue | Current playback queue ('?format=json' returns the raw API object) |
| top-tracks | User's top tracks (medium term; '?format=json' returns the raw API object) |
| top-artists | User's top artists (medium term; '?format=json' returns the raw API object) |
| recently-played | Last 20 recently played tracks ('?format=json' returns the raw API object) |
| playlists | All user playlists, names and IDs ('?format=json' returns the raw items) |
| saved-albums | Albums saved in your library |
| saved-shows | Podcast shows saved in your library |
| saved-episodes | Podcast episodes saved in your library |
| saved-tracks | Tracks saved in your library, paginated via ?offset&limit ('?format=json' returns raw paged object) |
| followed-artists | Artists you follow ('?format=json' returns the raw items) |
| saved-audiobooks | Audiobooks saved in your library ('?format=json' returns the raw items) |
| listening-history | Recent listening history (last 20, live; '?format=json' returns raw API object) |
| genre-heatmap | Genre heatmap from followed_artists sidecar ('?format=json' returns raw counts) |
| rate-limit | Last rate-limit event: Retry-After/wait or 'never throttled' |
TDQS
Scored across 608 tools
With 608 tools, the surface is saturated with near-duplicates: get_now_playing vs get_currently_playing vs get_playback_snapshot, listening_streaks vs listening_streak_report, playlist_intersect vs playlist_intersection, b_sides_finder vs b_sides_detector, plus roughly 20 URI-parsing utilities and 30+ snapshot tools. Explicit legacy aliases help at the margins, but an agent cannot reliably disambiguate such overlapping clusters.
Names mix verb_noun (get_track, create_playlist), noun_verb (playlist_sort, playlist_intersect), bare verbs (play, pause, mute, seek), and there is no consistent preview/commit pattern (sort_playlist_plan vs playlist_sort vs sort_playlist_apply; reverse_playlist_plan vs playlist_reverse). Arbitrary aliasing like get_show_episodes/list_show_episodes and statsfm_taste_profile/taste_profile adds further chaos.
608 tools is an extreme count for any MCP server, far beyond the 50+ threshold for a failing score. The scope could be served by a small fraction of these tools; dozens of report tools, snapshot variants, and URI helpers are redundant.
The server covers the full Spotify API surface plus stats.fm and local sidecar features: playback, library, playlists, search, podcasts, audiobooks, analytics, snapshots, and mutation tracking. Missing features are mostly removed API endpoints (artist top tracks, user profile) with explicit disclosures, and genuine gaps (queue clear, insert-next) have documented workarounds.