Skip to main content
Glama

Tailorlist MCP

Work in progress. Phases 1, 2a and 2b work: load playlists, dashboards, stats, searches, duplicates, comparison, grouping into drafts.

An MCP server that lets Claude read, analyze and reorganize your own Spotify library: find duplicates across playlists, group thousands of tracks by genre family or by sound, compare playlists, and write the result back to Spotify as playlists with the exact tracks.

Why

Claude's official Spotify connector has five tools: search (up to 20 results), generate_playlist (from a text prompt only), save_to_library, remove_from_library and get_currently_playing. It can't read the full contents of a playlist, and it can't create a playlist from an exact list of tracks. Reorganizing a collection you already have needs both.

Related MCP server: Spotify MCP Server

How it works

  • Runs locally (stdio) and works with Claude Desktop and Claude Code.

  • Uses your own Spotify app (free to register at developer.spotify.com, needs Spotify Premium). Your app, your token, your machine: nothing goes through a third-party server.

  • Caches your collection in a local SQLite file. Claude only sees summaries and IDs, so a 5,000-track library doesn't flood the conversation.

  • Genres come from Spotify, filled in from MusicBrainz where Spotify has none. Audio features come from ReccoBeats.

  • It previews every change before writing, and never deletes a playlist.

Setup (for now, from source)

  1. Create an app at https://developer.spotify.com/dashboard (needs Spotify Premium). Add http://127.0.0.1:8888/callback as a Redirect URI and tick "Web API".

  2. Install: uv venv && uv pip install -e .

  3. Put your app's Client ID in ~/.tailorlist-mcp/.env (see .env.example). No client secret needed.

  4. Log in once: .venv/bin/tailorlist-mcp login (or ask Claude to call connect_spotify).

  5. Add it to Claude Code: claude mcp add -s user tailorlist -- /path/to/.venv/bin/tailorlist-mcp. For Claude Desktop, add it under mcpServers in claude_desktop_config.json.

Tools so far: connect_spotify, list_my_playlists, load_playlists (shows the overview dashboard after loading), show_overview, show_playlist, get_playlist_tracks, collection_stats (counts and averages by decade, artist, genre, playlist, month...), find_tracks, find_duplicates, compare_playlists, fill_genres (MusicBrainz genres for artists Spotify leaves empty, in the background, about 1 request per second), group_tracks (draft playlists by decade / genre family / chosen genres), show_draft, edit_draft, organize_progress, reset_progress (clears your progress), unload_playlists, forget_my_data. The dashboard opens in your browser (~/.tailorlist-mcp/overview.html; set TAILORLIST_OPEN_BROWSER=0 to turn that off) and is also an MCP App, which Claude Desktop can show inside the chat. Everything is stored in ~/.tailorlist-mcp (TAILORLIST_HOME to change it).

Spotify's terms

Spotify's Developer Terms forbid using Spotify content "to train a machine learning or AI model or otherwise ingest Spotify Content into a machine learning or AI model". This tool trains nothing, but it does show your track and artist names to Claude, and Spotify hasn't said whether that counts. You use it with your own Spotify app, under your own developer account, for personal use. Read the Developer Terms and decide for yourself.

Origin

The analysis code (src/tailorlist_mcp/analysis/) is ported from the Tailorlist Streamlit app.

License

MIT

Available Tools

18 tools
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.

Inputs: group_by (required), limit, filters, metrics, +1 more. Returns: result

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.

Inputs: playlists. Returns: result

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.

Inputs: no named parameters. Returns: result

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). tracks picks tracks by artist, text (in the name), genre, genre_family, decade, year_from/to, popularity, track_ids, reason (unmatched only) or all=true. Group names accept a unique part.

Inputs: draft (required), changes (required).

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.

Inputs: no named parameters. Returns: result

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).

Inputs: kind, limit, playlists. Returns: result

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 limit rows (the header says how many match), so narrow the filters instead of asking for everything.

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 ascending says otherwise. limit: rows to return (default 50, max 500). include_links: add each track's Spotify link (longer output).

Inputs: sort, limit, filters, ascending, +1 more. Returns: result

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.

Inputs: also_log_out. Returns: result

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).

Inputs: playlist (required), include_links. Returns: result

group_tracksA

Group the loaded tracks into a draft of new playlists, and open the draft dashboard.

Workflow: narrow the pool with filters, call with preview_only=true to see the counts, then run it. The draft is saved (ID like "d3") and nothing is written to Spotify. Tracks already organized ("done", see organize_progress) are left out unless include_done=true.

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.

Inputs: mode (required), by, genres, filters, +3 more.

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.

Inputs: no named parameters.

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.

Inputs: playlists (required), force.

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.

Inputs: no named parameters. Returns: result

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.

Inputs: confirm. Returns: result

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.

Inputs: draft.

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.

Inputs: no named parameters.

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.

Inputs: playlist (required).

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.

Inputs: playlists (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 18 tools

    Disambiguation5/5

    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.

    Naming Consistency4/5

    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.

    Tool Count4/5

    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.

    Completeness3/5

    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.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      Not graded
      maintenance
      Connects Claude to Spotify for music discovery, playlist creation, and collection analysis through natural language. Enables searching songs, analyzing music diversity, creating playlists, and getting recommendations using Spotify's API.
      -
    • A
      license
      A
      quality
      D
      maintenance
      Integrates Spotify with Claude to enable voice-controlled music playback, playlist management, and personalized recommendations through conversational AI. It provides comprehensive tools for searching tracks, managing liked songs, and analyzing listening habits.
      15
      1
      MIT