5.9 KiB
BPM Enrichment Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add a bpm field (tempo from Spotify audio-features) to every track object returned by the MCP server.
Architecture: A private _enrich_with_bpm(sp, tracks) helper in spotify_client.py batch-fetches audio features for a list of parsed track dicts (100 IDs per API call) and adds a bpm field in place. Each of the three track-returning functions stores the track id during parsing, then calls this helper before returning.
Tech Stack: Python 3.11+, spotipy (sp.audio_features()), no new dependencies.
Task 1: Add _enrich_with_bpm helper
Files:
-
Modify:
spotify_client.py— add helper after_format_duration -
Step 1: Add the helper function
In spotify_client.py, insert the following function immediately after _format_duration (around line 58):
def _enrich_with_bpm(sp: spotipy.Spotify, tracks: list[dict]) -> None:
ids = [t["id"] for t in tracks]
for i in range(0, len(ids), 100):
chunk_ids = ids[i : i + 100]
features = sp.audio_features(chunk_ids)
for track, feat in zip(tracks[i : i + 100], features):
track["bpm"] = round(feat["tempo"], 1) if feat else None
- Step 2: Verify syntax
cd /Users/kriss/Dev/scm/scm.vilanet.fr/kriss/mcp-spotify
.venv/bin/python -c "import spotify_client; print('OK')"
Expected: OK
Task 2: Add id field and BPM enrichment to get_playlist_tracks
Files:
-
Modify:
spotify_client.py:get_playlist_tracks -
Step 1: Add
"id"to the track dict and call the helper
In get_playlist_tracks, add "id": track["id"], to the results.append({...}) block (alongside "uri"), and add _enrich_with_bpm(sp, results) just before the return statement:
results.append({
"name": track["name"],
"artists": [a["name"] for a in track["artists"]],
"album": track["album"]["name"],
"duration_ms": track["duration_ms"],
"duration": _format_duration(track["duration_ms"]),
"popularity": track.get("popularity"),
"id": track["id"],
"uri": track["uri"],
"added_at": item["added_at"],
})
page += 1
response = sp.next(response) if response["next"] else None
_enrich_with_bpm(sp, results)
_dbg(f"get_playlist_tracks returning {len(results)} tracks")
return results
- Step 2: Smoke test
SPOTIPY_CLIENT_ID=d66baed203d1461a860acbc5db27e3f5 \
SPOTIPY_CLIENT_SECRET=6d9ccf95957749ffac433919b585f4ff \
SPOTIPY_REDIRECT_URI=http://127.0.0.1:8888/callback \
.venv/bin/python -c "
import spotify_client, json
tracks = spotify_client.get_playlist_tracks('7LhqpCM88rPqDqxTLadLmf')
for t in tracks[:3]:
print(t['name'], '|', t.get('bpm'), 'bpm')
" 2>/dev/null
Expected: 3 lines like oh baby | 128.0 bpm
Task 3: Add id field and BPM enrichment to list_saved_tracks
Files:
-
Modify:
spotify_client.py:list_saved_tracks -
Step 1: Add
"id"to the track dict and call the helper
In list_saved_tracks, add "id": track["id"], to the results.append({...}) block, and add _enrich_with_bpm(sp, results) just before the return statement:
results.append({
"name": track["name"],
"artists": [a["name"] for a in track["artists"]],
"album": track["album"]["name"],
"duration_ms": track["duration_ms"],
"duration": _format_duration(track["duration_ms"]),
"popularity": track["popularity"],
"id": track["id"],
"uri": track["uri"],
"added_at": item["added_at"],
})
_enrich_with_bpm(sp, results)
return results
- Step 2: Smoke test
SPOTIPY_CLIENT_ID=d66baed203d1461a860acbc5db27e3f5 \
SPOTIPY_CLIENT_SECRET=6d9ccf95957749ffac433919b585f4ff \
SPOTIPY_REDIRECT_URI=http://127.0.0.1:8888/callback \
.venv/bin/python -c "
import spotify_client
tracks = spotify_client.list_saved_tracks(limit=3)
for t in tracks:
print(t['name'], '|', t.get('bpm'), 'bpm')
" 2>/dev/null
Expected: 3 lines with bpm values.
Task 4: Add id field and BPM enrichment to search_tracks
Files:
-
Modify:
spotify_client.py:search_tracks -
Step 1: Add
"id"to the track dict and call the helper
In search_tracks, add "id": track["id"], to the results.append({...}) block, and add _enrich_with_bpm(sp, results) just before the return statement:
for track in response["tracks"]["items"]:
results.append({
"name": track["name"],
"artists": [a["name"] for a in track["artists"]],
"album": track["album"]["name"],
"duration_ms": track["duration_ms"],
"duration": _format_duration(track["duration_ms"]),
"popularity": track["popularity"],
"id": track["id"],
"uri": track["uri"],
})
_enrich_with_bpm(sp, results)
return results
- Step 2: Smoke test
SPOTIPY_CLIENT_ID=d66baed203d1461a860acbc5db27e3f5 \
SPOTIPY_CLIENT_SECRET=6d9ccf95957749ffac433919b585f4ff \
SPOTIPY_REDIRECT_URI=http://127.0.0.1:8888/callback \
.venv/bin/python -c "
import spotify_client
tracks = spotify_client.search_tracks('LCD Soundsystem', limit=3)
for t in tracks:
print(t['name'], '|', t.get('bpm'), 'bpm')
" 2>/dev/null
Expected: 3 lines with bpm values.
Task 5: Commit
- Commit all changes
git add spotify_client.py
git commit -m "feat: add bpm field to all track-returning tools via audio-features endpoint"