Atlas / MCP servers / novalux12 / SpotifyMCP

SpotifyMCPCAUTION

mcp/novalux12/spotify-mcp-server

The definitive Spotify MCP server — full non-deprecated Web API coverage, paginated everything, podcast-first, tested end to end

Verdict
CAUTION
Grade
C
Trust score
73 /100
Exposed tools
200 487r · 91w · 24d
Transport
stdio · streamable-http
License
MIT
Stars
1
01

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

Read from source at commit 3d951d561ce3OBSERVED · 2026-10-12
02

Connect

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-code (npm)
claude mcp add spotify-mcp -- npx -y @novalux12/[email protected]
03

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.

ToolRiskDescription
FocusreadDeep work
Otherreadnothing
add_to_playlistwriteAdd tracks or episodes to a playlist. Max 100 URIs per call.
add_to_queuewriteAdd a track or episode to the end of the playback queue.
added_on_this_dayreadShow tracks you saved on this calendar day (month + day) in previous years — your library
album_anniversary_checkreadUpcoming album anniversaries for an artist within the next
album_duration_reportreadRuntime profile for an artist\
album_edition_lintreadLint 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_reportreadDeep focus report on one album: track-by-track listing with cumulative runtime, duration statistics, edition
album_openers_reportreadList track 1 (the side-A opener) of every studio album by an artist, chronologically — the
album_representative_planreadBuilds a deterministic
album_track_explorerreadFull 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_statsread[local-compute] Track-length statistics for one album: min/max/mean/median plus a longest-track callout,
albums_runtime_batchread[local-compute] Runtime per album — total and mean track length — for up to 20 albums in one pass
apply_scenewriteApply 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_changeswrite
archive_played_episodesread
artist_album_completenessreadChecks how complete your saved-album library is for one artist: canonical studio-album titles from the
artist_album_timelineread[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_statsreadNumbers-only catalog profile for one artist: release totals by group, summed track counts, average tracks per
artist_collab_networkread[local-compute] Featured/collab artists extracted from an artist\
artist_collaboration_networkreadBuilds a depth-2 collaboration web for one artist from discography walks alone: first-degree collaborators
artist_collection_gapsreadList an artist\
artist_complete_checkreadCollector completeness: the artist
artist_completeness_scorereadFor 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_finderreadDig up an artist\
artist_decade_spanreadHistogram of an artist\
artist_deep_cutsreadFinds an artist\
artist_deep_divereadBuilds a one-artist dossier from the artist object plus a full discography walk: genres, release counts by
artist_discography_explorerreadOverview stats for one artist\
artist_discography_gapsreadChronological discography table (oldest to newest by default) with per-release gap-in-days versus the previous
artist_discography_searchreadFull-text filter over an artist\
artist_discography_statsread[local-compute] Discography shape for one artist: release counts by album/single/compilation, first and
artist_discography_timelineread[local-compute] Chronological release table (year · type · name · tracks) built from a paginated
artist_era_mapreadCluster an artist\
artist_era_samplerreadSplits an artist discography into equal year-span eras and deterministically picks representative releases per
artist_first_releasereadLocates an artist\
artist_genres_compactreadUp to 50 artist IDs → name·genres two-column projection (compact roster view).
artist_latest_release_reportreadReports on an artist\
artist_latest_releasesreadFetch the most recent N releases from an artist\
artist_listening_clockreadProfile 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_finderreadList an artist\
artist_name_disambiguatorreadResolves an ambiguous artist name: runs a typed artist search and profiles each candidate (genres, active
artist_reissue_detectorreadDetect alternate versions (live/deluxe/remaster/expanded editions) in an artist\
artist_release_digestreadShow a digest of new releases since the last check for a watchlist.
artist_release_type_breakdownreadBreak an artist\
artist_representation_censusreadCensus 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_playlistsreadFind 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_timelinereadChronological singles timeline for an artist (date · title · track count). Quota: one paginated /artists/{id}/albums walk (singles group).
artist_top_vs_savedreadCompares your top artists (from /me/top/artists) against your own library: saved-album and saved-track counts
artist_velocity_reportreadClassify 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_additionsreadWatches your FOLLOWED artists for new material: walks /me/following, probes each artist\
ascii_onlyreadplain
audiobook_chapter_mapread[local-compute] Chapter-by-chapter duration map, total runtime and the mid-point chapter for one audiobook.
audiobook_library_progressreadAll saved audiobooks with % complete and estimated time remaining, sorted by progress —
audiobook_progressreadAudiobook 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_authorreadAuthor catalogue via the
auth_errorread401
b_sides_detectorreadDetect B-sides: tracks that appear on an artist\
b_sides_finderreadSurfaces an artist\
backup_firstwriteCreate a pre-flight library snapshot before a destructive operation. Returns snapshot file path and counts for later restore. Read-only against Spotify.
backup_libraryread
bad_request_errorread400
balance_playlist_pairswritePlan (and optionally commit) rebalancing 2–10 playlists to similar track counts or
bare_footerreadBare footer
batch_add_to_playlistwrite
batch_add_to_queuewriteAdd 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_reportreadFlag 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_deepdivereadCategory → playlists → optional items peek in one call. Removed Feb 2026, no replacement endpoint. Quota: 2–3 calls.
budget_proberead
call_toolreadCall 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_downreadCancel the in-process wind-down ramp, if one is running
canonicalize_spotify_urireadCanonicalise up to 500 equivalent Spotify IDs, URIs, links, and official share URLs to spotify:<kind>:<id>.
capture_playback_positionread
catalog_batch_lookupreadResolve a mixed list of Spotify URIs (tracks/albums/artists/shows/episodes/audiobooks/chapters) in partitioned batch calls. Quota: 1 per distinct type + chunking.
category_playlistsreadPaged collection
category_resolverreadFree-text genre/vibe → best-matching browse category ID via local fuzzy match over GET /browse/categories
chapter_bookmarksreadNamed chapter+position bookmarks per audiobook, stored in the local sidecar (jump later
check_artist_releasesreadCheck watched artists for new releases since last check (or within lookback_days).
check_episode_savedreadCheck 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_artistsreadCheck 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_libraryread
check_playlist_followingreadCheck 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_playbackreadOne-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_playlistsreadScan every playlist in your library for duplicate items (repeated URIs, and on opt-in
clean_backup_artifactsdestructiveDelete the NON-library files sharing SPOTIFY_MCP_BACKUP_DIR with your library backups: closed listening sessions
clone_playlist_coverwriteCopy cover image from source playlist to target. Quota: GET images + PUT images (plus image fetch).
collab_density_reportreadMeasure collaboration density: how many of your saved tracks credit 2+ artists, with the top collaborations ranked. Read-only.
collab_mix_from_followedreadBuild a collaborative-style mix playlist from your FOLLOWED artists\
compare_devicesreadList every available Spotify device ranked by active state, then device type (Computer > Smartphone > Tablet > TV > Speaker), then volume. Read-only.
compare_playlist_coversread
configured_capreadConfigured cap
conflict_errorreadconflict
continue_lastread
copy_playlistread
count_thingsreadcount things
create_playlistwrite
create_smart_playlistwriteCreate a playlist from rules over your own listening data: top tracks (by time range),
daily_pickreadDeterministic
dead_library_finderreadFind saved tracks that never appear in your recent history AND sit in none of your
decade_sampler_planreadRead-only sampling plan across decades: groups your saved albums by release decade and deterministically picks
dedupe_playlist_applydestructiveRemove duplicate uris from a playlist keeping the first (or last) occurrence: backs up
dedupe_playlist_planreadCensus every duplicate uri in a playlist — groups, all positions, and the exact keep-
dedupe_spotify_urisreadDeduplicate equivalent Spotify references by canonical URI, preserving first-seen order and retaining invalid inputs as distinct entries.
deep_cuts_finderreadDeep 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_reportreadDeep-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_backupdestructive
delete_playback_bookmarkdestructiveDelete 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_snapshotdestructive
delete_scenedestructiveDelete a saved playback scene from the local sidecar
describe_listening_sessionreadPlayback history timeline from recently-played, optionally grouped into sessions (30-min gap via detectSessions). Quota: 1-2 pages. Read-only.
device_healthreadDevice 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_statewriteReconcile sidecar device labels/volume presets (and scene device hints) against the
device_type_censusreadCensus of available Spotify devices grouped by type, with counts, names and volume range per group. Read-only.
diff_playlist_snapshotsread
diff_playlistsreadCompare 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_snapshotreadCompare current playlist state to a stored snapshot
discover_weekly_diffreadThis week\
discovery_digestreadOne combined discovery digest from your own data: latest releases from your top artists with freshness flags,
discovery_ratioreadMeasure 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_errorreadvalidation
duplicate_saved_versionsreadFind songs you saved more than once from different albums (same normalised track name + primary artist, distinct album IDs). Read-only.
echo_readingreadA read-shaped tool used to prove dispatch reaches a registered handler.
echo_writingwriteA write-shaped tool used to prove dispatch reaches a registered handler.
enable_toolsetwrite
episode_bookmarkreadBookmark 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_bundlereadEpisode + parent show + neighbouring episodes (prev/next by release date) in one card.
episode_guest_censusreadCensus recurring GUESTS across a show\
episode_resumewriteJump 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_reportreadRuntime statistics for one show\
era_distribution_reportreadHistogram of releases across eras: pass artist_id for that artist\
era_preference_reportreadCompare 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_searchreadExact match
exact_searchreadExact match
expand_mood_to_queriesreadTurn a free-text listening mood into search terms (genres, keywords, terms to avoid) for search.
explicit_content_ratioreadShare of your saved tracks flagged explicit, with the top explicit artists. Read-only.
export_all_playlistsreadExport 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_artistsreadExport your followed artists to a local directory as JSON or CSV. Fields: uri, name, genres. The file\
export_library_jsonreadExport 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_historyreadExport 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_playlistreadExport a playlist
export_playlist_jsonreadFull-fidelity JSON export of one playlist: items + added_at + added_by + URIs (the
export_playlist_markdownreadExport one playlist as a paste-ready markdown table (the doc-friendly variant of
export_profile_statereadExport 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_opmlreadExport your saved shows as OPML XML — the interchange format podcast apps speak.
export_snapshot_bundleread
extract_playlist_rangewriteExtract a positional range of a playlist (0-based start, EXCLUSIVE end; negative values
featuring_density_reportreadMeasure how many of your saved track titles carry a
fetch_all_readerreadReads every page when asked
filter_by_artistreadfilter by artist name
filter_by_genrereadList 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_artistreadFilter a playlist by artist (IDs/URIs or names, matched on id or exact name) and either
filter_playlist_by_durationreadFilter a playlist by track duration (min/max seconds) and either report the matches
filter_playlist_by_erareadFilter a playlist by album release era (decade and/or year window) and either report the
find_canonical_trackread[local-compute] Given a title + artist, search all versions, group by (artist, title) and rank them —
find_collaborationsreadFinds tracks where two artists appear together: walks artist A\
find_duplicate_playlistsreadScan your playlists for exact and near-duplicate track sets. Exact = identical URI sets (order-insensitive); near = Jaccard overlap >= threshold. Read-only.
find_duplicate_saved_tracksreadRead-only duplicate detection over your saved (liked) tracks. Exact groups require the
find_duplicate_tracks_across_playlistsreadFind tracks that appear in more than one of the given playlists (cross-playlist dupes). Quota: N GETs (one per playlist).
find_duplicates_in_playlistreadFind duplicate tracks in a playlist under one published matching rule.
find_lost_since_snapshotread
find_new_since_snapshotread
find_show_by_publisherreadSearch the catalog and match shows whose PUBLISHER (network) matches your query — the
find_toolread
finite_totalreadFinite total
follow_playlistwriteFollow 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_auditreadInventory of followed vs owned playlists: counts, collab, public, follower totals. Quota: GET /me/playlists paged.
following_analyticsreadFollowed-artist rollups from the tag sidecar; popularity/followers unavailable (Spotify no longer returns those fields). Quota: GET /me/following.
forbidden_errorread403
format_spotify_urireadValidate an entity kind and Spotify ID pair, then format its canonical spotify: URI.
front_to_back_planreadFull front-to-back listening plan for one album: ordered track listing with cumulative start times and
gated_probereadprobe
gated_removedestructive
genre_dive_searchreadSearch artists by genre keyword (e.g.
genre_trends_over_timereadHow your taste shifts — per-period top genres from user-declared tags, with deltas and emerging/declining tags. Read-only.
get_albumreadGet album details and track list by ID
get_album_tracksreadList the tracks of an album with pagination
get_artistreadGet artist info by ID
get_artist_albumsreadList an artist
get_artist_appearancesreadList albums an artist appears on (GET /artists/{id}/albums?include_groups=appears_on). Quota: 1 call.
get_artist_discographyreadGet filtered discography for an artist (GET /artists/{id}/albums with album-type filtering)
get_artist_genresreadGet genres for an artist (focused view of GET /artists/{id})
get_artist_singlesreadList an artist
get_artist_top_tracksreadGet an artist
get_audiobookreadGet full details for an audiobook by ID.${MARKET_NOTE}
get_audiobook_chaptersreadList the chapters of an audiobook with pagination.${MARKET_NOTE}
get_available_marketsreadList 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_categoryreadGet a single Spotify browse category by ID. Removed Feb 2026, no replacement endpoint. Quota: 1 call.
get_chapterreadGet full details for a single audiobook chapter by ID. Resume position requires the user-read-playback-position scope.${MARKET_NOTE}
get_context_inspectreadInspect 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_playingreadLightweight 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_reportreadReport volume_percent, supports_volume and active state for every Spotify device, highlighting the currently active device. Read-only.
get_devicesreadList available Spotify Connect devices. The same rows are readable as a resource with no tool call at spotify://player/devices (
get_episodereadGet full details for a podcast episode
get_episode_detailsreadFetch full metadata for one episode (duration, release date, resume point, show, description).
get_followed_artistsreadGet the artists the user follows. fetch_all=true walks every page; limit/after page manually.
get_mereadGet the current user
get_newly_released_episodesreadCollect episodes released since a date across ALL saved shows, merged newest-first and
get_now_playingreadFull 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_contextreadResolve 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.
04

Trust audit

CAUTIONgrade C · trust 73/100 Install with care. The audit found things worth knowing before you trust its output.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeWARN
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfaceWARN
L4Behavioural (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)

MEDIUMInformation disclosure · disclose.log_secret · CWE-209, CWE-532
scripts/edge-probe.mjs:167
console.log(`token refreshed (expires in ${t.expires_in ?? 3600}s)`);
MEDIUMInformation disclosure · disclose.log_secret · CWE-209, CWE-532
src/index.ts:125
console.log(`  token file        ${tokenFile}`);
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
.env.example:17
SPOTIFY_REDIRECT_URI=http://127.0.0.1:8888/callback
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
tests/auth.headless.test.ts:129
const secret = 'super-secret-authorization-code';
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
tests/http.security.test.ts:54
const TOKEN = 'e2e-bearer-token-0123456789abcdef';
MEDIUMFilesystem / path · mcp.destructive_tools · CWE-22, CWE-59
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
Why it matters. 24 tool(s) can delete or overwrite
Fix. prefer a read-only mode or scoped tokens; the page states the blast radius
LOWInventory / provenance · inv.hidden_file · CWE-1104
.release-please-config.json
.release-please-config.json
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInventory / provenance · inv.suspicious_name · CWE-1104
tests/payload-casts.test.ts
payload-casts.test.ts
Why it matters. member named after an attack tool
Fix. remove or justify
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/cli/session.ts:70
const { version } = createRequire(import.meta.url)('../../package.json') as { version: string };
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/account-keyed-stores.test.ts:228
'/tmp/tokens.../../../escape.json',
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/cli.session.test.ts:512
const out = await run(runExport, ['--kind', 'playlist', '--playlist', 'p1', '--out', '../../escape.m3u']);
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/fixtures/persist-exit-child.ts:20
import { SpotifyClient } from '../../src/client.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/fixtures/task-store-reconcile-child.ts:26
import { PersistentTaskStore } from '../../src/tasks.ts';
LOWNetwork egress · net.beacon_words · CWE-200, CWE-319
tests/paths.test.ts:374
() => writeOutputFile(link, 'exfiltrated'),
LOWNetwork egress · net.beacon_words · CWE-200, CWE-319
tests/tools.swarm3library.test.ts:200
track('tr-beacon', 'Beacon (feat. Iris)', [['ar-nova', 'Nova'], ['ar-iris', 'Iris']], NIGHTFALL, '2026-02-10T00:00:00Z', { duration_ms: 250_000, explicit: true }),
LOWNetwork egress · net.beacon_words · CWE-200, CWE-319
tests/tools.swarm3library.test.ts:246
{ id: 'tr-beacon', name: 'Beacon (feat. Iris)' },
LOWNetwork egress · net.beacon_words · CWE-200, CWE-319
tests/tools.swarm3library.test.ts:516
['spotify:track:tr-aurora-x', 'spotify:track:tr-beacon', 'spotify:track:tr-ember-song'],
LOWNetwork egress · net.beacon_words · CWE-200, CWE-319
tests/tools.swarm3library.test.ts:539
['spotify:track:tr-aurora-x', 'spotify:track:tr-beacon', 'spotify:track:tr-ember-song'],
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
AGENTS.md:78
- For local development, use `http://127.0.0.1` — not `http://localhost`.
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
AGENTS.md:79
The default is `http://127.0.0.1:8888/callback` and it must be registered
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
ARCHITECTURE.md:33
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
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
CONTRIBUTING.md:63
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
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/sdk, open, zod, @types/node, tsx, typescript
Why it matters. 6 dependency range(s) float
Fix. pin exact versions or ship a lockfile
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
ARCHITECTURE.md:33
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
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
ARCHITECTURE.md:192
| `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
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine

Gates applied: no_behavioural_pass.

Audited 2026-10-12 · audit v0.4.1 · source sha 3d951d561ce3full audit observations/trust-audit/mcp-server/novalux12__spotify-mcp-server.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-123d951d561ce3CAUTIONC73first audit
06

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.

Advertisement