youtube-music-mcp
by ElisonSz
README.md
# youtube-music-mcp
`youtube-music-mcp` is a small local [Model Context Protocol](https://modelcontextprotocol.io/)
server for searching YouTube Music and working with playlists. It exposes seven focused tools over
STDIO and uses [`ytmusicapi`](https://ytmusicapi.readthedocs.io/) for every interaction with
YouTube Music. Playlist creation, additions, and specific track removals require explicit
confirmation. It does not expose playlist deletion, bulk library removal, likes, history, uploads,
subscriptions, or account-management operations.
> [!WARNING]
> `ytmusicapi` uses unofficial YouTube Music APIs. YouTube can change these APIs without notice,
> which may temporarily break this server. Browser authentication is also marked deprecated by
> the current `ytmusicapi` CLI.
## Architecture
- `server.py` configures MCP, registers tools, disables SDK telemetry, and starts STDIO.
- `config.py` resolves and validates the authentication-file path without reading its contents.
- `client.py` owns the single, lazy `YTMusic` instance used by every tool.
- `schemas.py` defines the allowlisted structured outputs.
- `tools/search.py` contains identity and song-search behavior.
- `tools/playlists.py` contains playlist reads and confirmation-gated writes.
The server does not expose an HTTP transport. It does not send telemetry or analytics, and the
application never calls an HTTP library directly.
## Requirements
- Python 3.12 or newer
- [`uv`](https://docs.astral.sh/uv/)
- A YouTube Music browser-auth file for live operations
## Installation
Clone the repository and install the locked environment:
```console
git clone https://github.com/ElisonSz/youtube-music-mcp.git
cd youtube-music-mcp
uv sync --locked --dev
```
Run the MCP server directly with:
```console
uv run youtube-music-mcp
```
The process waits for MCP messages on STDIN and writes protocol messages to STDOUT.
## Authentication
The default authentication path is:
```text
~/.config/youtube-music-mcp/browser.json
```
Follow the
[`ytmusicapi` browser-authentication instructions](https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html)
to locate a successful authenticated `POST /browse` request on `music.youtube.com`. Firefox's
**Copy Request Headers** option provides the most reliable input format. In Chrome or Edge, copy
the contents of the **Request Headers** section; **Copy as fetch** can omit the cookie. Then run the
project's interactive setup CLI:
```console
uv run ytmusic-mcp setup-auth
```
The command creates the configuration directory, asks for the request headers with terminal echo
disabled, writes the file with mode `0600`, and validates it with a minimal account-information
request. Finish multiline input by typing `END` on a separate line and pressing Enter. The command
also accepts EOF. It displays only the validated account name and channel handle. `setup-auth` is a
local administration command and is not exposed as an MCP tool.
The pasted data must include `Authorization`, `Cookie`, and `X-Goog-AuthUser`. The CLI reports
missing or invalid header names, but never their values. If YouTube rejects the session, capture
fresh headers after confirming that the selected `/browse` request completed successfully while
you were signed in.
If the browser copies header names and values in a format that cannot be parsed, use guided mode:
```console
uv run ytmusic-mcp setup-auth --manual
```
Copy only the value of each requested header from DevTools. All three inputs are hidden. The CLI
does not repeat or log their values.
To use another location, set `YTMUSIC_AUTH_FILE` in the environment that starts the MCP server:
```console
export YTMUSIC_AUTH_FILE=/absolute/path/to/browser.json
uv run ytmusic-mcp setup-auth
```
On POSIX systems the file must be owned by the current user and must not grant permissions to its
group or other users. The server accepts `0600`, `0400`, or stricter modes. It never prints the
file contents, cookies, or authorization headers.
The server initializes `YTMusic` only when a tool needs to contact YouTube Music. Creation and
addition previews are local; removal previews authenticate and read the current playlist so they
can resolve exact items, but do not modify it. The normal MCP server never creates or prompts for
credentials. If authentication is missing, it refuses to start and reports:
```text
Authentication not configured. Run: ytmusic-mcp setup-auth
```
If credentials that previously worked start failing later, the YouTube Music browser session may
have expired or been revoked. Sign in to YouTube Music again, capture fresh request headers, and
rerun `ytmusic-mcp setup-auth` to replace `browser.json` after reviewing the overwrite prompt.
Run the local, read-only diagnostic at any time with:
```console
uv run ytmusic-mcp doctor
```
It validates the authentication-file metadata and makes one minimal account-information request.
It reports only whether configuration and authentication are valid, followed by the allowlisted
account name and channel handle. It never prints the authentication path, headers, cookies, or raw
responses, and it does not modify the account.
## Codex integration
If the package is installed as a uv tool, register its STDIO server with:
```console
uv tool install .
codex mcp add ytmusic -- ytmusic-mcp serve
```
During development, run it directly from the project without installing a global tool:
```console
codex mcp add ytmusic -- uv run --project /path/to/youtube-music-mcp ytmusic-mcp serve
```
The equivalent project-scoped `.codex/config.toml` entry is:
```toml
[mcp_servers.ytmusic]
command = "uv"
args = ["run", "--project", "/path/to/youtube-music-mcp", "ytmusic-mcp", "serve"]
env_vars = ["YTMUSIC_AUTH_FILE"]
default_tools_approval_mode = "writes"
startup_timeout_sec = 20
```
You may omit `env_vars` when using the default authentication path. Restart Codex after changing
the configuration, then use `/mcp` to inspect the connected server. See the
[official Codex MCP documentation](https://developers.openai.com/codex/mcp) for the current CLI,
IDE, and `config.toml` options.
## MCP tools
| Tool | Type | Behavior |
| --- | --- | --- |
| `whoami` | Read | Returns the authenticated account name and channel handle. |
| `search_songs` | Read | Searches with `filter="songs"`; accepts a limit from 1 to 50. |
| `list_playlists` | Read | Lists up to 100 library playlists. |
| `get_playlist` | Read | Returns playlist metadata and up to 100 allowlisted tracks. |
| `create_playlist` | Write | Previews or creates a playlist. Defaults to `PRIVATE`. |
| `add_tracks` | Write | Previews or adds between 1 and 100 video IDs. |
| `remove_tracks` | Destructive write | Previews or removes up to 100 exact playlist items. |
Search results contain only `videoId`, `title`, `artists`, `album`, `duration`, and `isExplicit`.
Playlist and track results are similarly reduced to the documented fields; each playlist track
includes `videoId`, `setVideoId`, `title`, `artists`, `duration`, and its one-based `position`. Raw
`ytmusicapi` responses are never returned.
## Confirming writes
`create_playlist`, `add_tracks`, and `remove_tracks` default to `confirm=false`. In that mode they
return a preview without modifying YouTube Music. Removal previews read the current playlist so
they can resolve exact playlist-item IDs.
Set `confirm=true` only after reviewing that preview. Confirmed calls are not idempotent: repeating
one may create another playlist or add tracks again. `add_tracks` passes `allow_duplicates` to
`ytmusicapi` and defaults it to `false`.
Conceptual write workflow:
```python
create_playlist(title="Road trip", privacy="PRIVATE", confirm=False)
add_tracks(playlist_id="...", video_ids=["..."], confirm=False)
remove_tracks(playlist_id="...", items=[{"set_video_id": "..."}], confirm=False)
```
Review the returned preview before repeating the intended call with `confirm=True`.
### Removing playlist tracks
`remove_tracks` is destructive. A `videoId` identifies a song, while `setVideoId` identifies one
specific occurrence of that song inside a playlist. The tool always reads the playlist and obtains
the `videoId`/`setVideoId` pair from YouTube Music instead of trusting a caller-supplied pairing.
If a `video_id` occurs once, the preview resolves its `setVideoId`. If it occurs more than once, the
tool makes no change and returns every occurrence with its `setVideoId`, title, artists, and
position. Select the intended occurrence explicitly before confirming:
```python
remove_tracks(
playlist_id="...",
items=[{"video_id": "..."}],
confirm=False,
)
```
After reviewing the preview:
```python
remove_tracks(
playlist_id="...",
items=[{"set_video_id": "..."}],
confirm=True,
)
```
A confirmed call reads the playlist again, aborts if a selected `setVideoId` no longer exists,
deduplicates repeated selections, calls `ytmusicapi.remove_playlist_items`, and reads the playlist
once more to verify that every selected occurrence disappeared.
## Development
Run the same checks used by CI:
```console
uv run ruff check .
uv run ruff format --check .
uv run pytest
uv build --no-sources
```
Tests inject a mocked `YTMusic` backend and never use a real account or authentication file. CI
also installs the built wheel in an isolated environment and runs `ytmusic-mcp --help` as a package
smoke test.
The project follows [Semantic Versioning](https://semver.org/) and remains in `0.x.x` while its
public API is unstable. Commit messages follow Conventional Commits using `feat:`, `fix:`,
`refactor:`, `test:`, `docs:`, `chore:`, and `security:`.
## Limitations
- Authenticated operations depend on browser-session credentials and unofficial APIs.
- Browser authentication can expire or be invalidated by signing out.
- `get_playlist` returns at most 100 tracks per call in version 0.1.0, while `trackCount` describes
the full playlist.
- Permission-bit enforcement is available on POSIX systems; Windows ACLs are outside the scope of
version 0.1.0.
- The only destructive operation is removal of explicitly selected playlist items; playlist
deletion, bulk library removal, history, feedback, upload, subscription, and account-management
operations are not exposed.
## Security and license
Read [SECURITY.md](SECURITY.md) before creating or sharing an authentication file. This project is
available under the [MIT License](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues