Harmonic Mixing Over MCP: The DJ Set-Builder Spotify Never Shipped
When Spotify deprecated audio_features, recommendations and related-artists for new apps in November 2024, a wave of “drop-in replacement” APIs appeared. Almost all of them stop at parity: send a track, get BPM, key and energy back. Useful — but that’s the same lookup Spotify already gave you.
FreqBlog went a layer further. It rebuilt the dead endpoints, then shipped the thing Spotify never had: a set-builder — pairwise transition scoring, next-track ranking, and full setlist ordering around the Camelot wheel. And the whole surface is exposed over a remote MCP server, so an LLM or agent can plan a beat-matched DJ set by calling tools directly — no glue code between the model and the music theory.
Prefer plain Python? We already wrote the pure-REST version — a working harmonic set planner in ~50 lines using /next-track, /transition and /setlist. This post is that same power handed to an agent over the Model Context Protocol, so the model does the orchestrating itself.
Parity first: the endpoints Spotify killed
Before the interesting part, the drop-ins that get you back to where Spotify left off:
GET /recommendations(MCP toolget_recommendations) — the replacement for the removed/v1/recommendations, seeded on tracks you already like and re-ranked by genre affinity so a feature-close cross-genre track can’t outrank same-genre picks. Full walkthrough in the recommendations-replacement guide.GET /related-artists(get_related_artists) — a derived artist graph to replace the killed related-artists endpoint.GET /v1/audio-features/{id}returns a bare SpotifyAudioFeaturesObject;GET /v1/audio-features?ids=returns the{"audio_features":[…]}array envelope. Both mirror Spotify’s own shapes, so porting existing code is a small diff.
The native lookup is flatter and richer. GET /lookup resolves a track by name, ISRC, MusicBrainz ID or Spotify ID and returns one flat object — 40-odd fields, no nesting, with bpm and key always populated:
curl -s "https://api.freqblog.com/lookup?track=Strobe&artist=deadmau5&wait=10" \
-H "X-Api-Key: fb_live_your_key_here"
{ "track_name": "Strobe", "artist_name": "deadmau5",
"bpm": 128.0, "key": "B", "camelot": "1A", "mode": "minor",
"energy": 0.61, "danceability": 0.72, "genre": "progressive house", ... }
The ?wait=10 opts into a bounded synchronous mode (up to 25 seconds) that returns a freshly-analysed track inline as a 200 instead of the default 202 + Retry-After when it isn’t cached yet.
The differentiator: harmonic mixing you can call
Camelot in 30 seconds
Every musical key maps to a clock position on the Camelot wheel: a number 1–12 plus a letter (A = minor, B = major). Two tracks mix without a key clash when they’re neighbours on the wheel — the same key, the relative major/minor (same number, flipped letter), or adjacent ±1. Jump +7 for the classic energy-boost mix. find_compatible_keys is pure theory — no catalog hit, zero quota:
// find_compatible_keys(camelot="8A", extended=true)
{
"camelot": "8A",
"compatible": [
{ "camelot": "8A", "relation": "same" },
{ "camelot": "8B", "relation": "relative" }, // minor <-> major
{ "camelot": "7A", "relation": "adjacent_down" }, // -1
{ "camelot": "9A", "relation": "adjacent_up" }, // +1
{ "camelot": "3A", "relation": "energy_boost" }, // +7 (extended=true)
{ "camelot": "1A", "relation": "energy_drop" } // -7 (extended=true)
]
}
Scoring an actual transition
Knowing which keys could mix is table stakes. score_transition rates how well one real track mixes into another, 0–100, blending Camelot compatibility, octave-aware BPM proximity (a 70→140 half-time blend still counts) and energy smoothness — and hands back a human-readable reason:
// score_transition(from_track_id="apple_ad1829eeccb70f9a",
// to_track_id="1443810719")
{
"score": 99,
"components": { "harmonic": 100, "tempo": 98, "energy": 100 },
"detail": { "key_relation": "same", "from_camelot": "11B", "to_camelot": "11B",
"bpm_delta": -0.29, "energy_delta": 0.12 },
"reason": "11B->11B same key, 118->117 BPM (-0.29), energy +0.12"
}
No raw key/BPM endpoint gives you that — the pairwise judgement is the product.
From one pick to a whole set
suggest_next_track takes the track that’s playing and returns the best catalog tracks to follow it, each with the same score, components and reason. It’s genre-aware by default, so a track that only coincidentally shares your key and tempo sinks down the list. build_setlist goes all the way: hand it 2–100 tracks and it orders the whole crate into an energy arc — peak_time, warmup, cooldown or flat — keeping every consecutive transition harmonically and tempo-smooth, and returns an overall flow_score. The 50-line planner post shows the full REST version end to end.
Letting an agent do it over MCP
This is where it stops being an API and starts being a capability you hand to a model. Point any MCP client — Claude Desktop, an agent framework, or your own — at:
https://mcp.freqblog.com/mcp
That exposes twelve tools: search_catalog, get_audio_features, get_audio_features_batch, find_tracks_by_bpm, find_tracks_by_key, find_compatible_keys, get_recommendations, get_related_artists, score_transition, suggest_next_track, build_setlist and tag_track. The model chains them itself — a single prompt like “build me a 90-minute peak-time set from these ten tracks” becomes:
search_catalogon each fuzzy name → concreteitunes_track_idsbuild_setlist(track_ids=[…], arc="peak_time")→ ordered set +flow_score- hand the ordered IDs to
GET /export/rekordbox(alsotraktor,m3u,cuesheet,csv) and drop the crate into your DJ software
No orchestration code on your side — the tool descriptions carry enough for the model to sequence them. The set-builder tools cost a little more quota than a plain lookup, because each is doing real combinatorial work; you’re only ever charged on a served result:
| Tool / endpoint | Quota | Why |
|---|---|---|
score_transition · /transition | 1 | A single A→B comparison. |
get_recommendations · /recommendations | 2 | Seed-blend candidate ranking. |
suggest_next_track · /next-track | 3 | Scores a wide neighbour set to rank the top picks. |
build_setlist · /setlist | 5 | An N² ordering problem — the premium call. |
Auth, REST, and pricing
Auth is an X-Api-Key header (a ?key= query fallback exists for browser and email links). Everything above is also plain REST — GET /transition, GET /next-track, POST /setlist, GET /similar?track_id=… — if you’d rather not run an MCP client, and it’s on RapidAPI too. Pricing starts at £0.17 per 1,000 requests, and the free tier is 1,000 requests a month — plenty to prototype a set planner.
Honest gaps
- It’s catalog-bound. The set-builder tools work on catalog
itunes_track_ids, so a track has to resolve first (search_catalog//lookup). Coverage is deep but not universal; niche or regional catalogs have holes, and a miss queues an on-demand analysis. - Spotify-ID lookups only hit a mapped subset. If you’re keyed on Spotify IDs, expect misses — name or ISRC resolves far more reliably.
- Features are computed, not gospel. BPM, key and energy come from audio analysis (Essentia); occasionally a lookup matches the wrong recording of a title.
- No audio hosting. You get features and metadata back, plus upload-based
/analyzeand/identify— not the audio itself. - The set-builder is playable, not provably optimal.
/setlistuses a greedy energy-arc ordering built for clean running orders; theflow_scoretells you how smooth the result is.
Give your agent an ear — free tier, no card
Grab an API key in seconds, point your MCP client at mcp.freqblog.com/mcp, and let a model plan the set. The first 1,000 requests each month are free.
Further reading
- Build a Harmonic DJ Set Planner in 50 Lines — the pure-REST/Python version of everything above
- Camelot Wheel for Developers — the harmonic rules behind the transition score
- The Spotify /recommendations Replacement — seed-track recommendations and a derived artist graph
- Spotify Audio Features Is Dead. Here’s What to Use Instead in 2026 — the landscape and the host swap
- Half-Time vs Double-Time BPM Detection — why the tempo score is octave-aware