Async-first TIDAL Model Context Protocol server for music discovery and playlist management. Built with FastMCP 2.12+ for stability and performance.
- 🔍 Track search with async operations
- 🎵 Playlist creation and management
- ❤️ Favorites access
- 🔐 OAuth authentication with automatic token refresh
- ⚡ FastMCP 2.12+ with async/await architecture
- 🚀 Non-blocking I/O for all TIDAL API operations
- 🎯 Clean error handling with ToolError exceptions
- 🔄 Automatic expired session cleanup
# Install dependencies (including anyio for async support)
cd /path/to/tidal-mcp
uv sync
uv pip install -e .
# Test with MCP Inspector
npx @modelcontextprotocol/inspector uv run tidal-mcp
# Quick protocol test
echo '{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test", "version": "1.0.0"}}, "id": 1}' | uv run tidal-mcp| Tool | Description | Parameters |
|---|---|---|
login |
OAuth authentication | None |
search_tracks |
Search catalog | query, limit (max 50) |
get_favorites |
Get favorite tracks | limit (default 20) |
create_playlist |
Create playlist | name, description (optional) |
add_tracks_to_playlist |
Add tracks | playlist_id, track_ids |
get_user_playlists |
List playlists | limit (default 20) |
get_playlist_tracks |
Get playlist tracks | playlist_id, limit (default 100) |
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"tidal-mcp": {
"command": "/full/path/to/uv",
"args": ["--directory", "/path/to/tidal-mcp", "run", "tidal-mcp"]
}
}
}Find your uv path: which uv
This tutorial is designed for use with Claude Desktop after you've configured the MCP server.
Build playlists as emotional journeys, not random collections. Think Larry Levan's Saturday night ritual - constructing arcs from midnight to sunrise.
Open Claude Desktop and start with context before tools:
"Tell me about Paradise Garage era and Larry Levan's approach to DJing.
What labels and artists defined that sound?"
In a new Claude Desktop conversation, deploy with specific intent:
You have Tidal MCP access. Build a Paradise Garage playlist from midnight to sunrise.
Start with dubby hypnotics (Dinosaur L, Loose Joints).
Move through gospel-disco (First Choice, Loleatta Holloway).
Peak with proto-house (Peech Boys, Class Action).
Close with cosmic dubs (Grace Jones, Manuel Göttsching).
When tracks aren't available, find emotional equivalents.
The flow is sacred - every substitution must maintain the arc.
- Authenticate - Browser opens for TIDAL login (first time only)
- Search - Hunts for tracks, tries variations
- Substitute - Finds emotional equivalents when needed
- Build - Creates playlist with proper sequencing
Iterate: "Need more gospel energy in the middle section"
Genre Jump - Explore other legendary sessions:
- Ron Hardy Music Box - darker, more aggressive
- David Mancuso Loft - earlier, eclectic, audiophile
Modern Connections: "Show the DNA from Levan to Moodymann to Palms Trax"
Remember: You're creating what Levan called "a feeling." Every track earns its place.
Pure magic. Build something that would make the heads at King Street proud.
- Framework: FastMCP 2.12+ with async/await support
- Async Runtime: anyio for thread pool execution
- TIDAL Integration: tidalapi library (synchronous, wrapped with anyio.to_thread)
- Error Handling: FastMCP ToolError exceptions for clean protocol errors
- Session Management: OAuth tokens persisted in
.tidal-sessions/ - Data Models: Pydantic schemas for structured outputs
- All MCP tools are async functions for non-blocking I/O
- Blocking tidalapi operations run in thread pool via
anyio.to_thread.run_sync() - Global session instance with async authentication checks
- Clean separation between MCP protocol layer and TIDAL API layer
Authentication Issues
- IMPORTANT: Requires tidalapi >= 0.8.6 (updated OAuth client credentials)
- Expired sessions are automatically cleaned up when detected
- Manual session reset:
rm -rf .tidal-sessions/ # Run login tool again - browser will open for OAuth - OAuth tokens expire after ~6 months
- Session files are stored in
.tidal-sessions/session.json - If you see "401 invalid_client", update tidalapi:
uv add "tidalapi>=0.8.6"
Port Conflicts
lsof -i :6274 -i :6277
kill <PID>Search Not Finding Tracks
- Simplify queries (e.g., "Radiohead" instead of "Radiohead OK Computer")
- Try artist name alone or song title alone
- Check spelling and avoid special characters
tidal-mcp/
├── pyproject.toml # Dependencies: fastmcp, tidalapi, anyio
├── README.md # This file
├── CLAUDE.md # AI development guidance
└── src/
└── tidal_mcp/
├── __init__.py
├── models.py # Pydantic models for structured outputs
└── server.py
Testing
# Direct test
echo '{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test", "version": "1.0.0"}}, "id": 1}' | uv run tidal-mcp
# Interactive
npx @modelcontextprotocol/inspector uv run tidal-mcp- Remove tracks from playlist
- Reorder playlist tracks
- Update playlist details
- Delete playlist
- Python 3.10+
- uv package manager
- TIDAL account
MIT
