Tailorlist MCP
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| TAILORLIST_HOME | No | Directory where Tailorlist MCP stores its data. | ~/.tailorlist-mcp |
| SPOTIFY_CLIENT_ID | Yes | Your Spotify app's Client ID. Put it in ~/.tailorlist-mcp/.env. No client secret is needed. | |
| TAILORLIST_OPEN_BROWSER | No | Set to 0 to prevent the dashboard from opening in your browser. | 1 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
Tailorlist reads, analyzes and reorganizes the user's own Spotify playlists.
Workflow: list_my_playlists -> ask the user which playlists to load -> load_playlists. load_playlists opens the overview dashboard by itself (in the user's browser, and inside the chat where the app supports it); show_overview opens it again later; show_playlist does the same for one playlist. If a tool says the user isn't connected, call connect_spotify and give the user the link it returns.
Never pick playlists to load on your own, and never load all of them unless the user says so: show the list and ask which ones. If the user asks to load playlists that are already in the cache ("current"), still call load_playlists: unchanged playlists are skipped instantly, followers are refreshed and the overview opens. To take playlists out of the analysis, use unload_playlists (it never touches Spotify).
After a dashboard opens, don't repeat its numbers as a long text: add a short comment and suggest what to look at next. Don't claim a dashboard is on screen: say it was opened in the user's browser (or saved as a page).
For anything about the user's playlists (what's in one, counts, artists...), use these tools: the cache has every track. Don't browse Spotify's website or look for the cache file.
collection_stats: counts and averages by decade, year, artist, album, genre, genre family, playlist, month added or popularity bin (optionally split by a second one). Returns a small table: draw the chart yourself (an artifact, where the app has them).
find_tracks: listings with filters (playlist, artist, album, genre, family, years, popularity, dates, duration, text) and sorting. Capped: narrow the filters instead of asking for more.
find_duplicates, compare_playlists, get_playlist_tracks: duplicates, playlist-vs-playlist numbers, one playlist's tracks in order.
fill_genres: Spotify leaves many artists without genres; this looks them up in MusicBrainz in the background (a few minutes per hundred artists). Offer it when many tracks have no genre; don't start it without the user's OK. Call it again to see progress. Building new playlists (Phase 2b): load -> pick filters (playlists, decades, genre families) -> group_tracks with preview_only=true to see the counts -> group_tracks (mode "category": by decade and/or genre family; mode "genres": one group per chosen genre tag) -> the draft opens as a dashboard (show_draft shows it again, with no argument the latest) -> review it with the user -> edit_draft (rename, move, assign unmatched tracks, drop or merge groups, keep a group visible, skip the unmatched) -> (a later phase) write the playlists to Spotify. A draft changes nothing on Spotify. RULE: show the user the draft and get their OK before anything is written; never decide the groups' final shape or write on your own. Tracks already organized ("done") leave the pool of the next grouping; organize_progress shows the % organized; reset_progress DELETES that progress: only after the user explicitly agrees (confirm=true). Grouping by audio similarity (KMeans) is not available yet (it needs audio features). Playlist arguments accept a name, a unique part of a name, an ID or a Spotify link.
Data comes from Spotify. When you mention playlists or tracks, include their Spotify links where the tool gave you one.
Definitions (use them when the user asks what a number means):
Unique tracks: distinct Spotify track IDs; a track saved in two playlists counts once. Entries: every saved occurrence (it counts twice). Artists are PRIMARY artists only; featured artists show as "A feat. B" but aren't counted.
Popularity: Spotify's 0-100 score from play counts and how recent they are. It reflects current global popularity, not the user's listening, and is 0 for album versions Spotify replaced with a newer release. "Avg popularity" averages unique tracks.
Release year: the album's release date, so compilations and reissues count with the album's year, not the song's original year.
Top decade: the decade with the most unique tracks. Median era: the median release year of unique tracks. Listening span: newest minus oldest release year.
Listening age: median era - 16 = estimated birth year (the age music taste forms); listening age = this year - that. Example: median 1994 -> born 1978 -> 48.
Playlist growth: tracks added per month; a playlist whose tracks all share one date was built in a single session.
Genres come from the primary artist (Spotify's tags, or MusicBrainz's when Spotify has none), not from the track. collection_stats group_by="genre" counts EXACT tags (a track is counted under each tag of its artist). The dashboards' genre list counts sub-genres too: "rock" includes "indie rock". Tracks without any tag are "untagged".
Genre families: Rock, Pop, Hip-Hop, Electronic, Jazz, Classical, Folk, Latin, Reggae, Other. A family matches when any of its keywords appears inside a genre tag (so "baroque pop" is Pop); a track can belong to several families; no match means Other. Keywords: Rock = rock, punk, metal, grunge, hardcore, emo, shoegaze; Pop = pop; Hip-Hop = hip hop, rap, trap, drill, r&b, soul, grime; Electronic = electronic, edm, house, techno, trance, ambient, synthwave, disco, new wave, dubstep, drum and bass, electro, downtempo, chillwave, darkwave, cold wave, eurodance, italo; Jazz = jazz, bebop, swing, blues, big band, boogie; Classical = classical, opera, orchestra, chamber; Folk = folk, acoustic, singer-songwriter, country, americana, bluegrass, newgrass, honky tonk; Latin = latin, reggaeton, salsa, cumbia, bossa nova, bachata, tango, bolero, merengue, mariachi, ranchera, mpb, trova, soca, calypso, folklore, folclor, candombe, murga, villancico; Reggae = reggae, dancehall, ska, ragga, riddim, dub. "Other" often holds adult standards, chanson, christmas, comedy, doo-wop, drone, easy listening, gospel, new age, schlager, soundtrack and very niche tags.
Exact duplicate: the same Spotify ID saved more than once (inside one playlist or across playlists). Different versions: the same song name and primary artist under different IDs (album vs. compilation, remaster, live...). Remaster suffixes aren't normalized, so "Song" and "Song - Remastered 2009" are not matched.
Genre diversity: distinct genre tags among a playlist's artists. Playlist age: when its first track was added.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| load_playlistsA | Copy playlists from Spotify into the local cache and show the overview dashboard. Only load the playlists the user chose. If they haven't said which ones, ask them first (list_my_playlists shows the options); don't load all of them unless they ask for that. Args: playlists: playlist IDs or Spotify playlist links of the playlists the user chose. force: re-read playlists even if they haven't changed since the last load. Playlists that haven't changed are skipped. The dashboard covers everything in the cache. |
| show_overviewA | Show the overview dashboard of the loaded playlists again: KPIs, oldest/newest track, playlists, top artists, decades, popularity, albums, genres, additions over time and duplicates. load_playlists already shows it after loading. |
| unload_playlistsA | Take one or more playlists out of the local cache, so they leave the analysis, and show the overview of what remains. Nothing changes on Spotify: the playlists stay in the user's account and load_playlists brings them back. Tracks and artists that no other loaded playlist has leave the cache too, and leave any draft that had them. Args: playlists: names (or a unique part of the name), IDs or Spotify links of loaded playlists. |
| show_playlistA | Show the dashboard of one loaded playlist: header numbers, track highlights, averages, top artists, duplicates inside it, era by decade and year, top genres, growth over time and all its tracks. Opens in the user's browser (and inside the chat where the app supports it). Args: playlist: the playlist's name (or a unique part of it), ID or Spotify link. |
| group_tracksA | Group the loaded tracks into a draft of new playlists, and open the draft dashboard. Workflow: narrow the pool with Args: mode: "category" (one group per decade and/or genre family) or "genres" (one group per chosen genre tag). "kmeans" (by audio similarity) comes with audio features, later. filters: playlists, decades and genre_families to narrow the pool before grouping. by: category mode only: "decade", "genre_family" or "both" (e.g. "Rock · 1990s"). A track in several families goes to the first one. Tracks without a genre or a release decade (when that dimension is used) are listed as unmatched, with the reason. genres: genres mode: exact genre tags to make groups for (a track with several chosen tags is in each group). If omitted, filters.genre_families picks every tag of those families; with neither, you get the list of genres in the pool (nothing is saved). also_by_decade: genres mode: also split each genre group by decade ("rock · 1980s"). include_done: include tracks already organized. preview_only: don't save a draft; return the pool counts and how many groups/unmatched. |
| show_draftA | Show a draft of new playlists as a dashboard (Cluster Results): one card per group with its size, hours, top artists, genres and decades and sample tracks, the unmatched tracks with the reason, and the overall progress bar. The full track lists are on the web page it opens. Args: draft: the draft ID ("d3"). Default: the latest one; the text lists the other drafts. |
| edit_draftA | Change a draft, then refresh its page. All the changes are applied in order, or none if one fails. Only the draft (and the organize progress it touches) changes; nothing is written to Spotify. Args:
draft: the draft ID ("d3").
changes: a list of operations, each with an "op":
rename {group, to}; move {tracks, to, from_group?, create_if_missing?} between groups;
assign {tracks, to} puts unmatched tracks in a group (saved as manual progress, like the
original's "Save assignments"); drop_group {group} (its tracks stay in the pool, listed as
left out); merge {groups, into?}; keep_visible {group, on?} (its tracks aren't marked done
when written); skip_unmatched {} (marks the unmatched tracks without data as done, so they
leave the pool); reopen {group} (a done group counts as not done).
|
| connect_spotifyA | Connect Tailorlist to the user's Spotify account (only needed once). Returns who is connected, or a login link the user has to open in their browser. After they approve access, call this again to confirm. |
| list_my_playlistsA | The user's Spotify playlists: id, name, track count, whether they own it, its link, and whether it is already in the local cache ("current", "changed" since loading, or null). Show the list to the user and ask which playlists they want to load before calling load_playlists. |
| get_playlist_tracksA | Every track of a loaded playlist, in playlist order: track, artist (with featured artists), album, year, duration, popularity and date added. Reads the local cache, so it always has the full playlist. Args: playlist: the playlist's name (or a unique part of it), ID or Spotify link. include_links: add each track's Spotify link (longer output). |
| collection_statsA | Counts and averages of the loaded playlists grouped by one dimension (optionally also split by a second one), as a small table you can chart. Examples: decade x playlist, top 20 artists, tracks per genre family, additions per month, one artist's albums (filters.artist). Args: group_by: decade, year, artist (primary), album, genre (exact tags), genre_family, playlist, month_added or popularity_bin (steps of 5). split_by: optional second dimension from the same list; one row per group x split value. metrics: any of unique_tracks, entries, hours, avg_popularity, share (% of the selection's unique tracks). Default: unique_tracks and share. filters: narrow the tracks first (same filters as find_tracks). limit: groups to return, biggest first (default 50, max 500). Time dimensions and popularity bins are returned in order. |
| find_tracksA | List unique tracks of the loaded playlists that pass the filters: track, artist, album, year,
duration, popularity, first date added and the playlists it is in. Capped at Args:
filters: playlists, artist, album, genre (exact tag), genre_family, decade, year_from/to,
popularity_min/max, added_from/to, duration_min/max (minutes), text (in the name).
sort: popularity, year, added, duration, name or artist. Default order is descending
(ascending for name and artist) unless |
| find_duplicatesA | Duplicates among the loaded playlists (or only the ones given): exact duplicates (the same Spotify ID saved more than once, inside or across playlists) and different versions (same song name and primary artist under different IDs), each with the playlists it is in and the IDs. Args: playlists: names, IDs or links; default all loaded playlists. kind: "all", "exact" or "versions". limit: rows per list (default 50, max 500). |
| compare_playlistsA | One row per playlist (or only the ones given): size, followers, hours, average, shortest and longest duration, average popularity, genre diversity, release era (median, oldest, newest year), first and last added, and the unique tracks that are in no other playlist; then how many tracks each pair of playlists shares. A data tool: draw charts from it yourself. Args: playlists: names, IDs or links; default all loaded playlists. |
| fill_genresA | Fill in the genres Spotify leaves empty, using MusicBrainz (free, about 1 request per second). Runs in the background and returns at once; call it again to see progress (it never starts a second run). Only primary artists with no Spotify genres are looked up, the most-used first; results are saved as they arrive, so every other tool uses them right away and a stopped run resumes where it left off. Takes a few minutes per hundred artists. Start it only if the user agrees. |
| organize_progressA | How much of the collection is organized: tracks already placed in a playlist built from a draft ("done", or skipped on purpose) or assigned to a group by hand ("manual") count toward the % of the library organized. Done tracks are left out of the next group_tracks. Reads the cache only. |
| reset_progressA | DESTRUCTIVE for the user's organize progress: forgets which tracks are organized (done and manual), so every track is back in the pool and the % goes to 0. Only the progress is cleared: drafts, the cache and Spotify are untouched. Ask the user first; with confirm=false it only says what would be cleared. Args: confirm: true only after the user agreed to clear their progress. |
| forget_my_dataA | Delete the local cache of the user's playlists. With also_log_out, delete the saved Spotify login too. Nothing on Spotify is changed. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| Tailorlist overview | |
| Tailorlist playlist | |
| Tailorlist draft |
TDQS
Scored across 18 tools
Each tool targets a distinct stage or resource: cache loading, overview, single-playlist view, draft creation/editing, statistics, track search, duplicates, comparison, genre filling, and progress management. The detailed descriptions make the boundaries clear even where tools display related information, such as show_overview versus collection_stats.
Almost all names follow a snake_case verb_noun pattern (load_playlists, show_draft, find_tracks, reset_progress). A couple are noun phrases instead (collection_stats, organize_progress), which is readable but slightly breaks the otherwise consistent action-oriented convention.
18 tools is somewhat above the typical 3–15 range, but the server covers a fairly broad workflow (Spotify connection, cache management, analysis, drafts, progress). The tools mostly earn their place without obvious redundant aliases, though the surface is on the heavy side.
The set covers loading/cache management, analysis, draft creation and editing, progress tracking, and cleanup. However, there is no MCP tool to export or publish a draft back to Spotify, and no tool to delete a whole draft, which leaves a notable gap in the end-to-end draft lifecycle.