Atlas / MCP servers / blakeem / Navidrome Assistant

Navidrome AssistantCAUTION

mcp/blakeem/navidrome-assistant

Analyze listening patterns, build playlists, find missing albums, discover similar artists via Last.fm, fetch synced lyrics, and explore global radio. Play it all through your speakers via mpv, with a built-in web UI that makes any device with a brower act as a remote. Gives full control of your Nav

Verdict
CAUTION
Grade
C
Trust score
80 /100
Exposed tools
69 53r · 10w · 6d
Transport
stdio · streamable-http
License
AGPL-3.0
Stars
90
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

An MCP (Model Context Protocol) server for Navidrome. Claude Desktop, Claude Code, Cursor, and other MCP clients can browse your library, build playlists, discover new music, and play audio through your machine's speakers.

Table of Contents

  • Features
  • Available Tools
  • Installation & Setup
  • Troubleshooting
  • Development
  • License
  • Support

Features

🎵 Music Library

Browse and search songs, albums, artists, genres, and tags. Filters cover query, starred status, year range, sort order, and tag values, and they combine: "all my starred jazz albums from the 90s, sorted by year" or "every song tagged Soundtrack with a 5-star rating". Tag analysis tools show what is in your library, so you don't have to guess at filter values.

🔊 Local Audio Playback

Requires `mpv` on the host running the MCP server (see Installing mpv).

Audio plays through your machine's speakers with no browser or Navidrome web UI. Search and play in one step: "play 5 random starred albums", "queue everything I've starred from the 90s sorted by year", or "add 10 random rock songs to whatever's already playing, shuffled". Albums have three shuffle modes: keep order, randomize album order, or interleave tracks.

The queue is editable during playback: reorder or shuffle without interrupting the current song, and removing the current track advances to the next. Saved Navidrome radio stations (Icecast, SHOUTcast) stream through mpv with live ICY metadata, so you can see what the station is playing. Plays scrobble back to Navidrome, so play counts and recent activity stay in sync. mpv starts on first use, can survive MCP client restarts through a per-user socket (see MPV Remote setup for the lifetime rules), and works on Linux, macOS, and Windows 11.

This works with vo

Read from source at commit b77b10557d9cOBSERVED · 2026-10-07
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
claude mcp add navidrome-mcp --env NAVIDROME_PASSWORD=${NAVIDROME_PASSWORD} -- npx -y [email protected]
claude-desktop
{
  "mcpServers": {
    "navidrome-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "[email protected]"
      ],
      "env": {
        "NAVIDROME_PASSWORD": "${NAVIDROME_PASSWORD}"
      }
    }
  }
}
03

Exposed tools (69)

53 read · 10 write · 6 destructive. Blast radius: 6 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.

ToolRiskDescription
add_tracks_to_playlistwriteAdd multiple types of content to a playlist in a single efficient operation. Supports any combination of individual songs, complete albums, artist discographies, or specific disc tracks.
clear_play_queuedestructiveClear the live play queue and stop playback. Use to fully halt audio output.
clear_saved_queuedestructiveClear the saved playback queue stored on the Navidrome server (the queue shown in the web interface). Does not affect live playback.
click_stationreadRegister a play click for a radio station (helps with popularity metrics). Call this when starting playback.
create_playlistwriteCreate a new playlist with a name, optional description, and visibility setting
create_radio_stationwriteCreate one or more radio stations. Always provide stations as a JSON array - use a single-item array for one station. Each station requires name and streamUrl, with optional homePageUrl.
delete_playlistdestructiveDelete a playlist (owner or admin only)
delete_radio_stationdestructiveDelete an internet radio station by ID
discover_radio_stationsreadDiscover internet radio stations worldwide via Radio Browser API. Search by genre/tag, country, language, quality, and more. Returns validated streams with metadata, sorted by popularity by default.
get_albumreadReturns the full record for a single album by ID. Same fields as search_albums results — use this when you already have the album ID. Does NOT include the album\
get_album_inforeadDeep-dive on ONE album: full tracklist with durations, release year/type, genres, wiki summary,
get_artistreadReturns the full record for a single artist by ID. Same fields as search_artists results (id, name, albumCount, songCount, plus optional playCount/rating/starred). For biography, similar artists, and top tracks, use the Last.fm tools (get_artist_info, get_similar_artists, get_top_tracks_by_artist).
get_artist_albumsreadGet an artist
get_artist_inforeadGet detailed artist information from Last.fm
get_play_queuereadReturn the current live mpv play queue with track metadata and the index of the currently-playing track. Read-only; does not start mpv if it isn
get_playlistreadGet detailed information about a specific playlist by ID
get_playlist_tracksreadGet all tracks in a playlist (supports JSON or M3U export). Response shape is discriminated by
get_radio_filtersreadGet available filter options for radio station discovery (tags, countries, languages, codecs)
get_radio_stationreadGet detailed information about a specific radio station by ID
get_saved_queuereadRead the saved playback queue stored on the Navidrome server. This is the queue shown in the web interface and synced across Navidrome clients — it is not live playback state and reading it does not affect any audio.
get_similar_artistsreadGet similar artists using Last.fm API
get_similar_tracksreadGet similar tracks using Last.fm API
get_songreadReturns the full record for a single song by ID. Same fields as search_songs results — use this when you already have the song ID and want the canonical SongDTO without searching. To list a song\
get_song_playlistsreadGet all playlists that contain a specific song
get_station_by_uuidreadGet detailed information about a specific radio station by its UUID
get_tag_distributionreadAnalyze tag usage patterns and distribution across the music library. Shows statistics for metadata categories with their usage counts. Supports:
get_top_tracks_by_artistreadGet top tracks for an artist from Last.fm
get_trending_musicreadGet trending music charts from Last.fm
list_most_playedreadList most played songs, albums, or artists
list_playlistsreadList all playlists accessible to the user with clean, LLM-friendly data
list_radio_stationsreadList all internet radio stations from Navidrome
list_recently_playedreadList recently played tracks with time filtering
list_starred_itemsreadList starred/favorited songs, albums, or artists. If the goal is to PLAY the starred items (not just show them), use
list_top_ratedreadList top-rated songs, albums, or artists
move_in_play_queuewriteMove the play-queue entry at index
nextreadSkip to the next track in the local mpv playlist. Reports an empty queue when nothing is playing (does not start mpv).
now_playingreadReport the current local playback state — title, artist, album, position, duration, paused, and queue index/length. Reads from the engine
open_settingsreadOpen the Navidrome MCP settings page in a browser and return its local URL.
pausereadPause local audio playback (mpv). Reports nothing to pause when no playback is active (does not start mpv). Position is preserved so resume continues from the same spot.
pingreadreturns pong
play_albumsreadPlay one or many albums through the local speakers via mpv.
play_albums_searchreadONE-SHOT search + enqueue for albums — runs the album search AND pipes every matched album
play_playlistreadONE-SHOT load a Navidrome playlist into the local mpv queue — fetches every track in the playlist AND enqueues them in a single call. PREFER THIS over the two-step pattern (
play_queue_indexreadJump directly to the play-queue entry at the given index — equivalent to clicking a track row in the web UI. Does NOT reorder the queue, only moves the play head. Unpauses if paused. Reports an empty queue when nothing is playing (does not start mpv). Discovery flow: call
play_radio_stationreadPlay a radio station through the local mpv speakers (requires mpv on the host). Replaces the entire live play queue with this single radio stream — radio is mutually exclusive with songs/albums in the play queue, matching Navidrome\
play_songsreadPlay one or many songs through the local speakers via mpv.
play_songs_searchreadONE-SHOT search + enqueue for songs — runs the song search AND pipes the matched track IDs into mpv in a single call. PREFER THIS over the two-step pattern (
playback_statusreadProbe the playback engine. Returns whether mpv is currently spawned, its detected path/version, and current volume/idle state. Does NOT spawn mpv if the engine is not already running.
previousreadSkip to the previous track in the local mpv playlist. Reports an empty queue when nothing is playing (does not start mpv).
remove_from_play_queuedestructiveRemove the play-queue entry at the given index. mpv auto-advances if the removed track was currently playing.
remove_tracks_from_playlistdestructiveRemove tracks from a playlist by their
reorder_playlist_trackreadReorder a track within a playlist to a new position. Positions are 1-based and match the
resumereadResume local audio playback (mpv). Reports nothing to resume when no playback is active (does not start mpv) — use a play tool to begin.
save_queuewriteSave a playback queue to the Navidrome server so it appears in the web interface and syncs to other Navidrome clients. Does not start playback.
search_albumsreadSearch for albums by name with advanced filtering and sorting options. Leave query empty to list all albums.\n\nTIP: Use \
search_allreadSearch across all content types (artists, albums, songs) with advanced filtering and sorting options. Leave query empty to list all results.\n\nNote:
search_artistsreadSearch for artists by name with advanced filtering and sorting options. Leave query empty to list all artists.\n\nTIP: Use \
search_by_tagsreadSearch for tags by type (e.g., list all genres, find release types, etc.). Defaults to genre if no tagName specified. Use this to explore metadata categories like genres, release types, media formats, and more.
search_lyricsreadSearch LRCLIB for the lyrics of a track by title and artist. Returns candidate records, each with an lrclibId to pass to get_lyrics. Also returns the matching library song when there is one, so its own file lyrics can be used instead.
search_songsreadSearch for songs by title with advanced filtering and sorting options. Leave query empty to list all songs.\n\nNote: the query runs Navidrome\
seekwriteMove the playback position within the current track.
set_ratingwriteSet a rating (0-5 stars) for a song, album, or artist
set_volumewriteSet mpv
shuffle_play_queuewriteRandomize the order of items in the current live play queue. Does not change membership. The currently-playing track keeps playing (it is not restarted) and is moved to the top of the queue; the rest of the queue is shuffled around it.
star_itemreadStar/favorite a song, album, or artist
test_connectionreadReport Navidrome MCP configuration/connection status.
unstar_itemreadUnstar/unfavorite a song, album, or artist
update_playlistwriteUpdate a playlist\
vote_stationreadVote for a radio station to increase its popularity
04

Trust audit

CAUTIONgrade C · trust 80/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 (6 observation(s))
Network
declared (6 observation(s))
Shell
declared (4 observation(s))
Dependencies
not all pinned
Secrets in source
none-found

Findings (25)

MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
Dockerfile:56
CMD ["node", "-e", "require('node:http').get('http://127.0.0.1:3000/healthz',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))"]
MEDIUMObfuscation / stealth · obf.base64_blob · CWE-506, CWE-94
src/webui/public/app.js:1368
const WAKE_VIDEO_SRC = `${WAKE_VIDEO_SCHEME}video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAMBbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAE4gAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAA
MEDIUMFilesystem / path · mcp.destructive_tools · CWE-22, CWE-59
clear_play_queue, clear_saved_queue, delete_playlist, delete_radio_station, remove_from_play_queue, remove_tracks_from_playlist
Why it matters. 6 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
.prettierignore
.prettierignore
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInventory / provenance · inv.hidden_file · CWE-1104
.prettierrc.json
.prettierrc.json
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
src/utils/subsonic-auth.ts:40
const token = crypto.createHash('md5').update(password + salt).digest('hex');
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
tests/unit/utils/subsonic-auth.test.ts:39
const expectedToken = crypto.createHash('md5').update(password + salt).digest('hex');
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/services/playback/mpv-ipc.ts:27
} from '../../constants/timeouts.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/services/playback/mpv-ipc.ts:28
import { logger } from '../../utils/logger.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/services/playback/mpv-process.ts:21
import { logger } from '../../utils/logger.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/services/playback/playback-engine.ts:23
import type { Config } from '../../config.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/services/playback/playback-engine.ts:24
import { logger } from '../../utils/logger.js';
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
docs/mcp-2.0/spec-2026-07-28/basic/authorization/security-best-practices-2-ssrf-state-handles-local-servers-stdio-proxies.md:37
`http://169.254.169.254/` (AWS/GCP/Azure metadata service) can
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
docs/mcp-2.0/spec-2026-07-28/basic/authorization/security-best-practices-2-ssrf-state-handles-local-servers-stdio-proxies.md:54
MCP-->>Client: 401 + resource_metadata="http://169.254.169.254/..."
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
docs/mcp-2.0/spec-2026-07-28/basic/authorization/security-best-practices-2-ssrf-state-handles-local-servers-stdio-proxies.md:57
Client->>Internal: GET http://169.254.169.254/latest/meta-data/
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
tests/unit/utils/network-safety.test.ts:96
expect(isPrivateOrLocalIp('169.254.169.254')).toBe(true);
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
tests/unit/utils/network-safety.test.ts:153
expect(isPrivateOrLocalIp('::ffff:169.254.169.254')).toBe(true);
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
README.md:390
3. The player logs the LAN URLs it's reachable on at bind time (e.g. `http://192.168.1.42:8808`). Open one in your phone's browser and bookmark it.
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
README.md:498
node dist/web/main.js            # serves http://127.0.0.1:8808 and opens your browser
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
docs/mcp-2.0/sdk-v2/serving-express.md:74
curl -s -X POST http://127.0.0.1:3000/mcp \
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
docs/mcp-2.0/sdk-v2/serving-web-standard.md:69
Deploy the default export on your runtime — `wrangler dev server.ts` puts it on `http://127.0.0.1:8787`; `deno serve server.ts` and `bun run server.ts` serve the same `{ fetch }` shape. POST a `tools/
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/sdk, file-type, undici, zod, @eslint-community/eslint-plugin-eslint-comments, @eslint/js, @types/node, @typescript-eslint/eslint-plugin
Why it matters. 20 dependency range(s) float
Fix. pin exact versions or ship a lockfile
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
docs/api/README.md:266
TOKEN=$(curl -s -X POST http://localhost:4533/auth/login \
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
docs/lastfm/official/auth-spec.md:30
Web applications should send a user to [last.fm/api/auth](https://www.last.fm/api/auth), sending an API key as a parameter, in order to authenticate the user. This should be an HTTP GET request. Your
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
docs/lastfm/official/auth-spec.md:50
Send your api key along with an api signature and your authentication token as arguments to the [auth.getSession](https://www.last.fm/api/show/auth.getSession) API method call. The parameters for this
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-07 · audit v0.4.1 · source sha b77b10557d9cfull audit observations/trust-audit/mcp-server/blakeem__navidrome-assistant.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-07b77b10557d9cCAUTIONC80first audit
06

Questions

What is the Navidrome Assistant MCP server?

Analyze listening patterns, build playlists, find missing albums, discover similar artists via Last.fm, fetch synced lyrics, and explore global radio. Play it all through your speakers via mpv, with a built-in web UI that makes any device with a brower act as a remote. Gives full control of your Nav

What tools does Navidrome Assistant expose?

69 in total: 53 read-only, 10 that write, and 6 that can delete or overwrite (clear_play_queue, clear_saved_queue, delete_playlist, delete_radio_station, remove_from_play_queue). Every one is listed on this page with its risk.

Is Navidrome Assistant safe to connect to an agent?

With care. The audit graded it C (80/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: 6 of its tools can destroy data, so scope the token you give it to what you actually need.

What credentials does Navidrome Assistant need?

It reads NAVIDROME_PASSWORD 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 Navidrome Assistant run?

It speaks stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as navidrome-mcp at 2.3.0.

How current is this page?

The grade is for one exact copy of the source (b77b10557d9c), read on 2026-10-07. The repository is watched and re-audited when it changes.

Advertisement