yandex-music-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@yandex-music-mcpSearch for 'Bohemian Rhapsody' and play it."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
yandex-music-mcp
An MCP server that turns Yandex Music into tools an AI agent can call directly — search, queue, play, build playlists — with no browser involved.
Audio comes out of your Mac's speakers via a small local player daemon. Claude queues a whole DJ set in one call and the daemon advances through it on its own. On macOS it also ships a native player: a real Now Playing entry in Control Center, and a window with the full queue and per-track like/dislike.
Built with Claude Code. Design, code, debugging and this README were produced in conversation with Claude. Read it before you run it — it talks to your music account and writes a token to disk.
Unofficial. This uses Yandex Music's private API. It is not affiliated with, endorsed by, or supported by Yandex. Endpoints can change without warning.
Who this is for
Developers who want to clone a repo, build it, and mess about.
There is deliberately no installer, no Docker image, no one-click setup, and that is a design decision rather than a to-do:
Audio cannot be containerized on macOS. Docker Desktop runs containers in a Linux VM with no access to CoreAudio. A containerized player would run happily and produce silence. Routing audio to a network PulseAudio sink is possible but needs a server on the host and buys nothing on one machine.
The native UI is a compiled Swift
.appthat has to be built and registered on the machine that will display it. Nothing to ship in an image.The MCP server needs nothing kept running — it is stdio, launched on demand. The only long-lived piece is the player daemon, and
launchdis the native answer to that (ym install-agent).
So: clone it, build it, play with it. It is a couple of minutes.
Related MCP server: ytm-mcp
Why it exists
Driving music through a browser was miserable:
Browser | This | |
Login | separate session per browser profile, lost on tab close | one token, stored once |
Autoplay | Chrome needs a real click before audio starts | n/a |
Track changes | the agent wakes up between every track | daemon auto-advances |
Token cost | ~3 tool calls per track | 1 call per set |
Survives closing the tab | no | yes |
Requirements
macOS for playback and the native player. The API surface (search, playlists, resolving streams) is platform-agnostic; audio and UI are not.
Node 20+
mpv —
brew install mpv. Without it the daemon falls back toafplay, which downloads each track before playing it.Xcode command line tools for the Swift helper —
xcode-select --install.A Yandex Music account with Plus. Without Plus the API only returns 30-second previews.
Setup
git clone https://github.com/aramvr/yandex-music-mcp.git
cd yandex-music-mcp
pnpm install # npm install works too
pnpm build
node dist/cli.js authThen point your agent at it:
claude mcp add yandex-music -- node "$PWD/dist/index.js"Sanity check without an agent:
node dist/cli.js whoami
node dist/cli.js set "Rammstein - Sonne" "SOAD - Chop Suey" "Muse - Uprising"
node dist/cli.js player # opens the windowLogging in
ym auth opens Yandex's own authorize page, you approve, and you paste back
what lands in the address bar. That is the whole flow. The token is verified
against the music API before being written to ~/.yandex-music-mcp/.env with
0600 permissions, and a rejected token can never overwrite a working one.
Your password never goes near this code. Yandex issues the token to you in the browser; the only thing that crosses is the token itself.
Why there is no automatic login
Because it cannot work. Yandex publishes no Yandex Music scope for
third-party OAuth apps. Register your own app, wire up a perfectly correct
loopback flow, and the token you get back carries only
landing-play, feed-play, mix-play — the music API treats it as an anonymous
session with no uid and no login.
The only client id that can actually stream is Yandex's own music client, and
that one does not whitelist a localhost redirect. So the token has to travel
by clipboard. This was built the automatic way first; it does not work, and the
paste flow is what remains.
If a token ever does come back anonymous, getAccount() throws with the
permission list rather than silently pretending it worked.
Tokens are long-lived but not eternal. Run ym auth again when you see a 401;
ym whoami shows the expiry when it is known.
Audio quality
Defaults to the best the account is entitled to. Two places quality can leak, both closed.
1. What gets requested. YM_QUALITY=lossless (the default) tries the
get-file-info endpoint for FLAC first, then falls back to the
highest-bitrate MP3 rather than failing the track. YM_QUALITY=high skips
straight to MP3.
In practice you will get MP3 320 CBR — see below.
2. What the player does with it. mpv runs with every processing stage off:
Flag | Why |
| no loudness normalisation |
| no downmix gain |
| no resampling; the device follows the file |
| empty filter chain |
| soft volume above unity clips, so it is capped |
| network FLAC stutters without buffer |
Volume is therefore 0–100, not 0–130. For more output, use the system volume.
Verify rather than trust — this reports the codec actually served:
node dist/cli.js quality "Rammstein Sonne"ym_now_playing also shows a Quality: line, and every track logs its codec to
~/.yandex-music-mcp/daemon.log.
Why not lossless
The web player's "superb" setting calls a different endpoint:
GET api.music.yandex.com/get-file-info/batch
?trackIds=…&quality=lossless
&codecs=flac,aac,he-aac,mp3,flac-mp4,aac-mp4,he-aac-mp4
&transports=encraw&sign=…Two blockers, both intentional on Yandex's side:
signis an HMAC whose key lives in the obfuscated web bundle. Recovering it means lifting a secret out of their client.transports=encraw— theencis encrypted. Even with a valid signature the stream needs decrypting.
Together that is DRM circumvention, so this project stops at the unencrypted path. That is a deliberate boundary, not a missing feature. Don't "fix" it.
MP3 320 CBR is what the open path serves. If lossless matters to you, the official clients do it properly.
The macOS player
pnpm build:native compiles native/NowPlaying.swift into
bin/Yandex Music.app — a real .app, not a bare binary, and that detail
matters. Clicking the Now Playing widget asks LaunchServices to open the app
that owns the media session; a bare executable has no bundle to open, so macOS
walks up to the responsible process and opens Terminal instead.
The app is an LSUIElement agent — no Dock tile, no app-switcher entry — and it
does two jobs.
It owns the Now Playing session. mpv can publish one itself, but only from
the media file's own tags, and Yandex streams arrive with no ID3 at all, so
artist, album and artwork would simply be blank. The daemon runs mpv with
--input-media-keys=no so there is exactly one session, and pushes real
metadata into this helper instead.
Control Center's layout is fixed: artwork, title, artist, album, scrubber,
transport. It has no concept of a track list, and it does not draw like/dislike
even when a player registers MPFeedbackCommand. Which is the reason for the
second job.
It draws the player window — everything Control Center won't show. The full queue with the current row marked, per-row like and dislike, double-click any row to jump, a volume slider. Closing the window does not quit: the process still owns the media session and the daemon's pipe.
Wire protocol is one JSON object per line over stdio, both directions:
daemon -> app {"type":"track", title, artist, album, artworkUrl, durationSec,
elapsedSec, isPlaying, queueIndex, liked, disliked, volume}
{"type":"queue", items:[{title, artist, durationSec, feedback}], queueIndex}
clear | show
app -> daemon {"command":"play"|"pause"|"toggle"|"next"|"prev"|"stop"}
{"command":"seek","positionSec":N}
{"command":"jump","index":N}
{"command":"like"|"dislike","index":N?} // index omitted = current
{"command":"volume","percent":N}Track updates fire every few seconds; the queue is pushed only when it changes. Likes and dislikes go straight to the Yandex account and are remembered per track id in the daemon's state file, so the hearts survive a restart.
Everything degrades to a no-op off macOS or when the app hasn't been built — playback never depends on the UI being there.
Tools
Tool | What it does |
| verify token, report account + Plus state |
| explain how to log in (there is no automatic flow) |
| search the catalogue |
| the main one — queue a list of tracks and play through them |
| append without interrupting |
| jump the queue with one track |
| pause / resume / next / prev / stop |
| current track + position |
| open the macOS player window |
| list the queue |
| 0–100 |
| create a real playlist on the account |
| list your playlists |
| like the current or a named track |
| what has been played |
ym_play_set takes free-text "Artist - Title" strings and resolves each by
search, preferring plausible-length originals over snippets and karaoke covers.
It also accepts startAtIso to begin a set later.
CLI
Everything is reachable without an agent, which makes debugging easy:
ym search "rammstein sonne"
ym set "SOAD - Chop Suey" "Muse - Hysteria" "Rammstein - Sonne"
ym status
ym next
ym pause
ym playerArchitecture
Agent ──stdio──> MCP server (dist/index.js) ephemeral, spawned per session
│
│ unix socket ~/.yandex-music-mcp/daemon.sock
▼
Player daemon (dist/daemon.js) long-lived, detached
│
├── Yandex API (search, download-info → signed mp3 URL)
├── mpv (IPC) or afplay (download-then-play fallback)
└── stdio ──> Yandex Music.app (Now Playing + window)The split matters: the MCP server dies with the agent session, the daemon does
not. Queue state persists to ~/.yandex-music-mcp/state.json, so a fresh agent
session sees whatever is already playing.
Auto-advance is driven by the backend's end-of-track event, not by polling — so a 20-track set costs exactly one tool call.
One deliberate oddity: src/http.ts uses node:https rather than global
fetch. Node's fetch (undici) auto-injects browser-only headers — notably
Sec-Fetch-Mode: cors — which makes a server-side request look like a
malformed browser one, and Yandex's edge answers that with a 403 anti-bot page
instead of JSON. The identical request from curl or node:https succeeds.
Auto-start at login
node dist/cli.js install-agentInstalls a launchd LaunchAgent (dev.yandex-music-mcp.daemon) that starts the
player daemon at login and restarts it if it dies. Remove with
ym uninstall-agent.
Optional — the MCP server spawns the daemon on demand if it isn't running. Install the agent if you want music to survive restarts without a first call.
Development
pnpm dev # tsc --watch
pnpm test # compiles, then node:testTests cover the two pure pieces worth locking down: the free-text track resolver's ranking, and the token parser that decides whether a paste is usable. Everything else needs a live account.
Layout:
src/
index.ts MCP server — tool definitions
daemon.ts player daemon — queue, auto-advance, state
player.ts mpv / afplay backends
api.ts Yandex API client
http.ts node:https wrapper (see above)
auth.ts token paste flow
ipc.ts unix-socket protocol + daemon autospawn
config.ts paths, env, token storage
nowplaying.ts Node side of the macOS bridge
native/
NowPlaying.swift the .app: Now Playing session + player window
scripts/
build-native.sh assembles the .app bundle
make-icon.swift draws the iconCaveats
The private API can change without warning. If playback breaks, that is the first suspect — check
~/.yandex-music-mcp/daemon.log.The MCP server is stdio-only and unauthenticated by design; it assumes it is launched by a local agent. Don't expose it.
Your token grants full access to your Yandex account's music profile. It sits in
~/.yandex-music-mcp/.envat mode0600. Treat it like a password.Personal listening on an account you pay for. Not for redistributing audio.
License
MIT.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/aramvr/yandex-music-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server