SpotifyMCPCAUTION
The definitive Spotify MCP server — full non-deprecated Web API coverage, paginated everything, podcast-first, tested end to end
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
[](https://github.com/NovaLux12/spotify-mcp-server/actions/workflows/ci.yml) [](https://www.npmjs.com/package/@novalux12/spotify-mcp) [](LICENSE)
Spotify Web API MCP: playback, library, playlists, search, podcasts. Not affiliated with Spotify.
A broad Spotify Web API tool surface, plus extras most servers skip. Registration-gated wrappers are explained rather than hidden; see Registration-gated endpoints for the generated list.
What you get by default, and what to do about the rest. A server started with no environment registers a curated surface — search, playback, playlists, library, following — sized so the tool list does not dominate the context window before you have typed anything. SPOTIFY_MCP_TOOLSETS=all registers the entire registry instead, and the stats.fm tools are opt-in with SPOTIFY_MCP_STATSFM=1. Both are one line in docs/configuration.md.
A server started with no SPOTIFY_MCP_TOOLSETS registers 131 tools (150,111 bytes of schema) — the curated default surface (#889). SPOTIFY_MCP_TOOLSETS=all registers all 562 tools, along with 17 fixed resources, 28 resource templates, and 14 prompts. Toolsets and production gates can trim a configured host further; both figures describe a real production tools/list after finalizers.
🚧 3.0 is landing
3.0 is the release where the open issue list goes to zero — every epic, every issue under it, every contradiction we turned up auditing ourselves. Most of that backlog was never "bugs" in the crashing sense; it was
3d951d561ce3OBSERVED · 2026-10-12Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add spotify-mcp -- npx -y @novalux12/[email protected]
Exposed tools (200)
487 read · 91 write · 24 destructive. Blast radius: 24 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
Focus | read | Deep work |
Other | read | nothing |
add_to_playlist | write | Add tracks or episodes to a playlist. Max 100 URIs per call. |
add_to_queue | write | Add a track or episode to the end of the playback queue. |
added_on_this_day | read | Show tracks you saved on this calendar day (month + day) in previous years — your library |
album_anniversary_check | read | Upcoming album anniversaries for an artist within the next |
album_duration_report | read | Runtime profile for an artist\ |
album_edition_lint | read | 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. |
album_focus_report | read | Deep focus report on one album: track-by-track listing with cumulative runtime, duration statistics, edition |
album_openers_report | read | List track 1 (the side-A opener) of every studio album by an artist, chronologically — the |
album_representative_plan | read | Builds a deterministic |
album_track_explorer | read | 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_track_stats | read | [local-compute] Track-length statistics for one album: min/max/mean/median plus a longest-track callout, |
albums_runtime_batch | read | [local-compute] Runtime per album — total and mean track length — for up to 20 albums in one pass |
apply_scene | write | Apply a saved scene: resolve its device hint, transfer playback, then set volume/shuffle/repeat and start the saved context (in that order; missing targets are skipped) |
apply_snapshot_changes | write | |
archive_played_episodes | read | |
artist_album_completeness | read | Checks how complete your saved-album library is for one artist: canonical studio-album titles from the |
artist_album_timeline | read | [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_catalog_stats | read | Numbers-only catalog profile for one artist: release totals by group, summed track counts, average tracks per |
artist_collab_network | read | [local-compute] Featured/collab artists extracted from an artist\ |
artist_collaboration_network | read | Builds a depth-2 collaboration web for one artist from discography walks alone: first-degree collaborators |
artist_collection_gaps | read | List an artist\ |
artist_complete_check | read | Collector completeness: the artist |
artist_completeness_score | read | For your top N saved-track artists, fetch each artist\u2019s top tracks and score how many of them you have saved (0–100%). Stops at the first gated/removed /artists/{id}/top-tracks answer instead of retrying it once per artist. Read-only, capped at 25 artists. |
artist_debut_release_finder | read | Dig up an artist\ |
artist_decade_span | read | Histogram of an artist\ |
artist_deep_cuts | read | Finds an artist\ |
artist_deep_dive | read | Builds a one-artist dossier from the artist object plus a full discography walk: genres, release counts by |
artist_discography_explorer | read | Overview stats for one artist\ |
artist_discography_gaps | read | Chronological discography table (oldest to newest by default) with per-release gap-in-days versus the previous |
artist_discography_search | read | Full-text filter over an artist\ |
artist_discography_stats | read | [local-compute] Discography shape for one artist: release counts by album/single/compilation, first and |
artist_discography_timeline | read | [local-compute] Chronological release table (year · type · name · tracks) built from a paginated |
artist_era_map | read | Cluster an artist\ |
artist_era_sampler | read | Splits an artist discography into equal year-span eras and deterministically picks representative releases per |
artist_first_release | read | Locates an artist\ |
artist_genres_compact | read | Up to 50 artist IDs → name·genres two-column projection (compact roster view). |
artist_latest_release_report | read | Reports on an artist\ |
artist_latest_releases | read | Fetch the most recent N releases from an artist\ |
artist_listening_clock | read | Profile WHEN you play one specific artist (UTC hour-of-day histogram plus UTC daypart split; defaults to your most-played artist in the history window). Quota: GET /me/player/recently-played cursor walk. |
artist_live_albums_finder | read | List an artist\ |
artist_name_disambiguator | read | Resolves an ambiguous artist name: runs a typed artist search and profiles each candidate (genres, active |
artist_reissue_detector | read | Detect alternate versions (live/deluxe/remaster/expanded editions) in an artist\ |
artist_release_digest | read | Show a digest of new releases since the last check for a watchlist. |
artist_release_type_breakdown | read | Break an artist\ |
artist_representation_census | read | Census of your saved tracks by credited artist: rank the top N artists by how many saved tracks they appear on. Read-only. |
artist_scout_from_playlists | read | 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_singles_timeline | read | Chronological singles timeline for an artist (date · title · track count). Quota: one paginated /artists/{id}/albums walk (singles group). |
artist_top_vs_saved | read | Compares your top artists (from /me/top/artists) against your own library: saved-album and saved-track counts |
artist_velocity_report | read | 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. |
artistwatch_new_additions | read | Watches your FOLLOWED artists for new material: walks /me/following, probes each artist\ |
ascii_only | read | plain |
audiobook_chapter_map | read | [local-compute] Chapter-by-chapter duration map, total runtime and the mid-point chapter for one audiobook. |
audiobook_library_progress | read | All saved audiobooks with % complete and estimated time remaining, sorted by progress — |
audiobook_progress | read | Audiobook progress rollup: chapters total, played count, current chapter, percent complete. Walks every chapter page up to the shared fetch-all cap and reports scan coverage. Quota: 1 audiobook GET + chapter page GETs. |
audiobooks_by_author | read | Author catalogue via the |
auth_error | read | 401 |
b_sides_detector | read | Detect B-sides: tracks that appear on an artist\ |
b_sides_finder | read | Surfaces an artist\ |
backup_first | write | Create a pre-flight library snapshot before a destructive operation. Returns snapshot file path and counts for later restore. Read-only against Spotify. |
backup_library | read | |
bad_request_error | read | 400 |
balance_playlist_pairs | write | Plan (and optionally commit) rebalancing 2–10 playlists to similar track counts or |
bare_footer | read | Bare footer |
batch_add_to_playlist | write | |
batch_add_to_queue | write | 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). |
binge_detector_report | read | 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. |
browse_category_deepdive | read | Category → playlists → optional items peek in one call. Removed Feb 2026, no replacement endpoint. Quota: 2–3 calls. |
budget_probe | read | |
call_tool | read | Call any REGISTERED tool by name — the dispatcher that makes a trimmed surface usable. find_tool discovers a name, inspect_tool shows its schema. Reaches only tools this session registered; for anything else use enable_toolset. Refuses to call itself. |
cancel_wind_down | read | Cancel the in-process wind-down ramp, if one is running |
canonicalize_spotify_uri | read | Canonicalise up to 500 equivalent Spotify IDs, URIs, links, and official share URLs to spotify:<kind>:<id>. |
capture_playback_position | read | |
catalog_batch_lookup | read | Resolve a mixed list of Spotify URIs (tracks/albums/artists/shows/episodes/audiobooks/chapters) in partitioned batch calls. Quota: 1 per distinct type + chunking. |
category_playlists | read | Paged collection |
category_resolver | read | Free-text genre/vibe → best-matching browse category ID via local fuzzy match over GET /browse/categories |
chapter_bookmarks | read | Named chapter+position bookmarks per audiobook, stored in the local sidecar (jump later |
check_artist_releases | read | Check watched artists for new releases since last check (or within lookback_days). |
check_episode_saved | read | Check which episodes are already saved in your library via GET /me/library/contains (the Feb 2026 replacement for the removed GET /me/episodes/contains) — |
check_following_artists | read | Check if the user follows specific artists — this tests FOLLOW state, not library-saved state (for that use check_in_library). Accepts IDs or spotify:artist: URIs. Rows carry {id, uri, follows}; returns a boolean per ID. Max 50. |
check_in_library | read | |
check_playlist_following | read | Check if you follow 1–50 playlists. Follow state: GET /me/library/contains?uris=spotify:playlist:<id>,... (40/req, 1–2 GETs). Unreadable state reports unknown, never not-followed. |
checkpoint_playback | read | One-shot timestamped auto-named playback checkpoint (cp-2026-08-27T21:05 style) — saves you naming slots for save_playback_state. Writes the same shared position record capture_playback_position writes, so list_playback_bookmarks and continue_last both see it. Quota: 1 read + local sidecar write. |
clean_all_playlists | read | Scan every playlist in your library for duplicate items (repeated URIs, and on opt-in |
clean_backup_artifacts | destructive | Delete the NON-library files sharing SPOTIFY_MCP_BACKUP_DIR with your library backups: closed listening sessions |
clone_playlist_cover | write | Copy cover image from source playlist to target. Quota: GET images + PUT images (plus image fetch). |
collab_density_report | read | Measure collaboration density: how many of your saved tracks credit 2+ artists, with the top collaborations ranked. Read-only. |
collab_mix_from_followed | read | Build a collaborative-style mix playlist from your FOLLOWED artists\ |
compare_devices | read | List every available Spotify device ranked by active state, then device type (Computer > Smartphone > Tablet > TV > Speaker), then volume. Read-only. |
compare_playlist_covers | read | |
configured_cap | read | Configured cap |
conflict_error | read | conflict |
continue_last | read | |
copy_playlist | read | |
count_things | read | count things |
create_playlist | write | |
create_smart_playlist | write | Create a playlist from rules over your own listening data: top tracks (by time range), |
daily_pick | read | Deterministic |
dead_library_finder | read | Find saved tracks that never appear in your recent history AND sit in none of your |
decade_sampler_plan | read | Read-only sampling plan across decades: groups your saved albums by release decade and deterministically picks |
dedupe_playlist_apply | destructive | Remove duplicate uris from a playlist keeping the first (or last) occurrence: backs up |
dedupe_playlist_plan | read | Census every duplicate uri in a playlist — groups, all positions, and the exact keep- |
dedupe_spotify_uris | read | Deduplicate equivalent Spotify references by canonical URI, preserving first-seen order and retaining invalid inputs as distinct entries. |
deep_cuts_finder | read | Deep cuts per album: tracks past position 2 that are neither the title track nor among the SCANNED singles. Bounded by max_singles (default 500); payload: singles_capped. Quota: paginated walk + 1 GET /albums/{id} per album. |
deep_dive_report | read | 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. |
delete_backup | destructive | |
delete_playback_bookmark | destructive | Delete one saved playback position — a capture_playback_position bookmark, an exhaust2 checkpoint or a playback-ext state, whichever it came from. Commits by default — pass dry_run=true to preview. |
delete_playlist_snapshot | destructive | |
delete_scene | destructive | Delete a saved playback scene from the local sidecar |
describe_listening_session | read | Playback history timeline from recently-played, optionally grouped into sessions (30-min gap via detectSessions). Quota: 1-2 pages. Read-only. |
device_health | read | Device availability & capability report — merges GET /me/player/devices + GET /me/player active id + sidecar labels/presets. Quota: 1-2 reads plus a local merge. Read-only. |
device_sync_state | write | Reconcile sidecar device labels/volume presets (and scene device hints) against the |
device_type_census | read | Census of available Spotify devices grouped by type, with counts, names and volume range per group. Read-only. |
diff_playlist_snapshots | read | |
diff_playlists | read | Compare two playlists up to the configured source cap: tracks only in A, only in B (by track ID), and tracks present in both but at different positions. Rendered rows are capped by max_results; truncation metadata reports when source walks hit scan_cap. |
diff_since_snapshot | read | Compare current playlist state to a stored snapshot |
discover_weekly_diff | read | This week\ |
discovery_digest | read | One combined discovery digest from your own data: latest releases from your top artists with freshness flags, |
discovery_ratio | read | 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. |
domain_input_error | read | validation |
duplicate_saved_versions | read | Find songs you saved more than once from different albums (same normalised track name + primary artist, distinct album IDs). Read-only. |
echo_reading | read | A read-shaped tool used to prove dispatch reaches a registered handler. |
echo_writing | write | A write-shaped tool used to prove dispatch reaches a registered handler. |
enable_toolset | write | |
episode_bookmark | read | 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_context_bundle | read | Episode + parent show + neighbouring episodes (prev/next by release date) in one card. |
episode_guest_census | read | Census recurring GUESTS across a show\ |
episode_resume | write | 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. |
episode_runtime_report | read | Runtime statistics for one show\ |
era_distribution_report | read | Histogram of releases across eras: pass artist_id for that artist\ |
era_preference_report | read | 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. |
exact_isrc_search | read | Exact match |
exact_search | read | Exact match |
expand_mood_to_queries | read | Turn a free-text listening mood into search terms (genres, keywords, terms to avoid) for search. |
explicit_content_ratio | read | Share of your saved tracks flagged explicit, with the top explicit artists. Read-only. |
export_all_playlists | read | 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. |
export_followed_artists | read | Export your followed artists to a local directory as JSON or CSV. Fields: uri, name, genres. The file\ |
export_library_json | read | 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 ( |
export_listening_history | read | 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. |
export_playlist | read | Export a playlist |
export_playlist_json | read | Full-fidelity JSON export of one playlist: items + added_at + added_by + URIs (the |
export_playlist_markdown | read | Export one playlist as a paste-ready markdown table (the doc-friendly variant of |
export_profile_state | read | Export local sidecar stores (scenes, genre-tags, playback-ext, search-history, mutations, artist-watchlist) to a single schema-versioned JSON archive. artist-watchlist is ~/.spotify-mcp/artist-watchlist.json; SPOTIFY_MCP_DATA_DIR overrides that directory. |
export_shows_opml | read | Export your saved shows as OPML XML — the interchange format podcast apps speak. |
export_snapshot_bundle | read | |
extract_playlist_range | write | Extract a positional range of a playlist (0-based start, EXCLUSIVE end; negative values |
featuring_density_report | read | Measure how many of your saved track titles carry a |
fetch_all_reader | read | Reads every page when asked |
filter_by_artist | read | filter by artist name |
filter_by_genre | read | List URIs of your saved tracks or albums whose artists carry a given genre tag (case-insensitive tag-name match against your sidecar). Output is directly usable as create_playlist / add_to_playlist input. Read-only. |
filter_playlist_by_artist | read | Filter a playlist by artist (IDs/URIs or names, matched on id or exact name) and either |
filter_playlist_by_duration | read | Filter a playlist by track duration (min/max seconds) and either report the matches |
filter_playlist_by_era | read | Filter a playlist by album release era (decade and/or year window) and either report the |
find_canonical_track | read | [local-compute] Given a title + artist, search all versions, group by (artist, title) and rank them — |
find_collaborations | read | Finds tracks where two artists appear together: walks artist A\ |
find_duplicate_playlists | read | Scan your playlists for exact and near-duplicate track sets. Exact = identical URI sets (order-insensitive); near = Jaccard overlap >= threshold. Read-only. |
find_duplicate_saved_tracks | read | Read-only duplicate detection over your saved (liked) tracks. Exact groups require the |
find_duplicate_tracks_across_playlists | read | Find tracks that appear in more than one of the given playlists (cross-playlist dupes). Quota: N GETs (one per playlist). |
find_duplicates_in_playlist | read | Find duplicate tracks in a playlist under one published matching rule. |
find_lost_since_snapshot | read | |
find_new_since_snapshot | read | |
find_show_by_publisher | read | Search the catalog and match shows whose PUBLISHER (network) matches your query — the |
find_tool | read | |
finite_total | read | Finite total |
follow_playlist | write | Follow a playlist — save it to your Spotify library — via PUT /me/library. Spotify removed the playlist-followers endpoints in Feb 2026; this is the only way to follow a playlist. Supports dry_run (default true); writes require confirmation unless SPOTIFY_MCP_CONFIRM=never. |
followed_playlists_audit | read | Inventory of followed vs owned playlists: counts, collab, public, follower totals. Quota: GET /me/playlists paged. |
following_analytics | read | Followed-artist rollups from the tag sidecar; popularity/followers unavailable (Spotify no longer returns those fields). Quota: GET /me/following. |
forbidden_error | read | 403 |
format_spotify_uri | read | Validate an entity kind and Spotify ID pair, then format its canonical spotify: URI. |
front_to_back_plan | read | Full front-to-back listening plan for one album: ordered track listing with cumulative start times and |
gated_probe | read | probe |
gated_remove | destructive | |
genre_dive_search | read | Search artists by genre keyword (e.g. |
genre_trends_over_time | read | How your taste shifts — per-period top genres from user-declared tags, with deltas and emerging/declining tags. Read-only. |
get_album | read | Get album details and track list by ID |
get_album_tracks | read | List the tracks of an album with pagination |
get_artist | read | Get artist info by ID |
get_artist_albums | read | List an artist |
get_artist_appearances | read | List albums an artist appears on (GET /artists/{id}/albums?include_groups=appears_on). Quota: 1 call. |
get_artist_discography | read | Get filtered discography for an artist (GET /artists/{id}/albums with album-type filtering) |
get_artist_genres | read | Get genres for an artist (focused view of GET /artists/{id}) |
get_artist_singles | read | List an artist |
get_artist_top_tracks | read | Get an artist |
get_audiobook | read | Get full details for an audiobook by ID.${MARKET_NOTE} |
get_audiobook_chapters | read | List the chapters of an audiobook with pagination.${MARKET_NOTE} |
get_available_markets | read | List the country codes of every market where Spotify is available. Removed by Spotify\u2019s February 2026 Web API changes — unavailable for newer app registrations |
get_category | read | Get a single Spotify browse category by ID. Removed Feb 2026, no replacement endpoint. Quota: 1 call. |
get_chapter | read | Get full details for a single audiobook chapter by ID. Resume position requires the user-read-playback-position scope.${MARKET_NOTE} |
get_context_inspect | read | 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. |
get_currently_playing | read | 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. |
get_device_volume_report | read | Report volume_percent, supports_volume and active state for every Spotify device, highlighting the currently active device. Read-only. |
get_devices | read | List available Spotify Connect devices. The same rows are readable as a resource with no tool call at spotify://player/devices ( |
get_episode | read | Get full details for a podcast episode |
get_episode_details | read | Fetch full metadata for one episode (duration, release date, resume point, show, description). |
get_followed_artists | read | Get the artists the user follows. fetch_all=true walks every page; limit/after page manually. |
get_me | read | Get the current user |
get_newly_released_episodes | read | Collect episodes released since a date across ALL saved shows, merged newest-first and |
get_now_playing | read | 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. |
get_playback_context | read | Resolve the current playback context URI (from GET /me/player) to catalog metadata — playlist/album/artist/show name, owner, track count. Quota: 1-2 reads. Read-only. |
Trust audit
CAUTIONgrade C · trust 73/100 Install with care. The audit found things worth knowing before you trust its output.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | WARN |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (2 observation(s))
- Network
- declared (6 observation(s))
- Shell
- declared (5 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- found
Findings (25)
console.log(`token refreshed (expires in ${t.expires_in ?? 3600}s)`);console.log(` token file ${tokenFile}`);SPOTIFY_REDIRECT_URI=http://127.0.0.1:8888/callback
const secret = 'super-secret-authorization-code';
const TOKEN = 'e2e-bearer-token-0123456789abcdef';
clean_backup_artifacts, dedupe_playlist_apply, delete_backup, delete_playback_bookmark, delete_playlist_snapshot, delete_scene, gated_remove, playlist_dedupe_advanced, playlist_exclude_artists, playli
.release-please-config.json
payload-casts.test.ts
const { version } = createRequire(import.meta.url)('../../package.json') as { version: string };'/tmp/tokens.../../../escape.json',
const out = await run(runExport, ['--kind', 'playlist', '--playlist', 'p1', '--out', '../../escape.m3u']);
import { SpotifyClient } from '../../src/client.js';import { PersistentTaskStore } from '../../src/tasks.ts';() => writeOutputFile(link, 'exfiltrated'),
track('tr-beacon', 'Beacon (feat. Iris)', [['ar-nova', 'Nova'], ['ar-iris', 'Iris']], NIGHTFALL, '2026-02-10T00:00:00Z', { duration_ms: 250_000, explicit: true }),{ id: 'tr-beacon', name: 'Beacon (feat. Iris)' },['spotify:track:tr-aurora-x', 'spotify:track:tr-beacon', 'spotify:track:tr-ember-song'],
['spotify:track:tr-aurora-x', 'spotify:track:tr-beacon', 'spotify:track:tr-ember-song'],
- For local development, use `http://127.0.0.1` — not `http://localhost`.
The default is `http://127.0.0.1:8888/callback` and it must be registered
Both flows share the same PKCE primitives (`code_verifier` = base64url of 32 random bytes, `S256` challenge, random 16-byte `state`) and the same token exchange against `https://accounts.spotify.com/a
2. Fill in `SPOTIFY_CLIENT_ID` (a PKCE flow is used, so **only the Client ID is needed** — there is no client secret). The default redirect URI is `http://127.0.0.1:8888/callback`; add exactly that UR
@modelcontextprotocol/sdk, open, zod, @types/node, tsx, typescript
Both flows share the same PKCE primitives (`code_verifier` = base64url of 32 random bytes, `S256` challenge, random 16-byte `state`) and the same token exchange against `https://accounts.spotify.com/a
| `src/tools/doctortool.ts` | spotify_doctor (#111 idea 9 + #228): the diagnostic report, as an in-server TOOL so MCP agents can self-diagnose the most common failure class — missing/expired tokens, s
Gates applied: no_behavioural_pass.
3d951d561ce3full audit observations/trust-audit/mcp-server/novalux12__spotify-mcp-server.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-12 | 3d951d561ce3 | CAUTION | C | 73 | first audit |
Questions
What is the @novalux12/spotify-mcp MCP server?
The definitive Spotify MCP server — full non-deprecated Web API coverage, paginated everything, podcast-first, tested end to end
What tools does @novalux12/spotify-mcp expose?
200 in total: 487 read-only, 91 that write, and 24 that can delete or overwrite (clean_backup_artifacts, dedupe_playlist_apply, delete_backup, delete_playback_bookmark, delete_playlist_snapshot). Every one is listed on this page with its risk.
Is @novalux12/spotify-mcp safe to connect to an agent?
With care. The audit graded it C (73/100) and found 25 things worth knowing before you trust this server, listed below with the exact line each was found on. Separately from the audit: 24 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does @novalux12/spotify-mcp need?
It reads SPOTIFY_AUTH_TIMEOUT_MS, SPOTIFY_CLIENT_SECRET and SPOTIFY_MCP_TOKEN_FILE from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does @novalux12/spotify-mcp run?
It speaks stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as @novalux12/spotify-mcp at 3.1.0.
How current is this page?
The grade is for one exact copy of the source (3d951d561ce3), read on 2026-10-12. The repository is watched and re-audited when it changes.