lenpanda commited on
Commit
58f77d6
·
verified ·
1 Parent(s): 86e3e77

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +129 -7
  2. requirements.txt +3 -0
  3. server.py +377 -0
README.md CHANGED
@@ -1,13 +1,135 @@
1
  ---
2
- title: Spotify Mcp
3
- emoji: 👁
4
- colorFrom: indigo
5
  colorTo: purple
6
  sdk: gradio
7
- sdk_version: 5.33.1
8
- app_file: app.py
9
  pinned: false
10
- short_description: A MCP Server for Spotify using Gradio
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Spotify MCP Server
3
+ emoji: 🎵
4
+ colorFrom: blue
5
  colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 4.0.0
8
+ app_file: server.py
9
  pinned: false
 
10
  ---
11
 
12
+ # Spotify MCP Server
13
+
14
+ A Gradio-based MCP server that provides a user-friendly interface to interact with Spotify's API. This server offers various tools for managing your Spotify experience, from authentication to playlist management and music playback control.
15
+
16
+ ## Demo Video
17
+ [Watch the demo video](https://asset.cloudinary.com/dedgdfu0l/0f0cb2dda40de41f419c50b57e9e74c5)
18
+
19
+ ## Features
20
+
21
+ - **Authentication**: Secure Spotify authentication using OAuth2
22
+ - **Music Search**: Search for tracks and get artist information
23
+ - **Queue Management**: Add songs to your Spotify queue (requires Spotify Premium)
24
+ - **Recently Played**: View your recently played tracks
25
+ - **Playlist Management**:
26
+ - Create new playlists
27
+ - View existing playlists
28
+ - Add songs to playlists
29
+ - **User Insights**:
30
+ - View top artists
31
+ - View top tracks
32
+ - Get genre information
33
+
34
+ ## Requirements
35
+
36
+ - Python 3.x
37
+ - Spotify Premium account (required for some queue management features)
38
+ - Spotify Developer account for API credentials
39
+
40
+ ## Setup
41
+
42
+ 1. Clone this repository
43
+ 2. Install the required dependencies:
44
+ ```bash
45
+ pip install gradio spotipy
46
+ ```
47
+
48
+ ## Configuration
49
+
50
+ ### Getting Spotify API Keys
51
+
52
+ 1. Create an account on [developer.spotify.com](https://developer.spotify.com)
53
+ 2. Navigate to the dashboard
54
+ 3. Create a new app with the following settings:
55
+ - Redirect URI: `http://127.0.0.1:5000/callback` (you can choose any port, but must use http and an explicit loopback address)
56
+ - Note: You may need to restart your MCP environment (e.g., Claude Desktop) once or twice before it works
57
+
58
+ ### Environment Variables
59
+
60
+ The server uses the following environment variables:
61
+ Please create a .env file with the credentials
62
+
63
+ - `CLIENT_ID`: Your Spotify API client ID
64
+ - `CLIENT_SECRET`: Your Spotify API client secret
65
+ - `REDIRECT_URI`: Your Spotify API redirect URI (default: http://127.0.0.1:5000/callback)
66
+
67
+ ### Running Locally
68
+
69
+ This project is designed to run locally and is not yet set up for ephemeral environments (e.g., uvx usage). To run:
70
+
71
+ 1. Clone this repository
72
+ 2. Set up your environment variables
73
+ 3. Run the server
74
+
75
+ ## Example Queries for Claude Desktop
76
+
77
+ Try these example queries in Claude Desktop to explore different features:
78
+
79
+ ### Music Discovery & Analytics
80
+ "Show me my top 10 artists and their genres from the last 6 months"
81
+
82
+ ### Smart Playlist Creation
83
+ "Create a playlist of my top 20 tracks from this month"
84
+
85
+ ### Queue Management
86
+ "Search for 'I Like Me Better' and add it to my queue"
87
+
88
+ ### Playlist Organization
89
+ "Show me all my playlists"
90
+
91
+ ### Music Curation
92
+ "Compare my top 10 tracks from this month with my all-time favorites"
93
+
94
+ ### Quick Actions
95
+ "Show me my 5 most recently played songs"
96
+
97
+ ## API Endpoints
98
+
99
+ The server exposes several endpoints through Gradio interfaces:
100
+
101
+ - `add_to_queue_song`: Add a song to the queue (Premium required)
102
+ - `get_artist_and_track`: Search for tracks
103
+ - `auth_with_spotify`: Authenticate with Spotify
104
+ - `get_recently_played_songs`: Get recently played tracks
105
+ - `create_playlist`: Create a new playlist
106
+ - `get_playlist_name_and_id`: Get playlist information
107
+ - `add_songs_to_playlist`: Add songs to a playlist
108
+ - `get_users_top_artists`: Get user's top artists
109
+ - `get_user_top_tracks`: Get user's top tracks
110
+
111
+ ## Premium Features
112
+
113
+ The following features require a Spotify Premium account:
114
+ - Adding songs to queue
115
+
116
+ ## Claude Desktop Configuration
117
+
118
+ To use this MCP server with Claude Desktop, add the following configuration to your settings (port number can vary, please check):
119
+
120
+ ```json
121
+ {
122
+ "mcpServers": {
123
+ "gradio": {
124
+ "command": "npx",
125
+ "args": ["mcp-remote", "http://127.0.0.1:7860/gradio_api/mcp/sse"]
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ This configuration allows Claude Desktop to connect to the Gradio MCP server running on port 7860.
132
+
133
+ ## Tags
134
+
135
+ mcp-server-track
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio>=4.0.0
2
+ spotipy>=2.23.0
3
+ python-dotenv>=1.0.0
server.py ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import spotipy
3
+ from spotipy.oauth2 import SpotifyOAuth
4
+ from collections import defaultdict
5
+ import os
6
+
7
+
8
+ CLIENT_ID = os.getenv("CLIENT_ID")
9
+ CLIENT_SECRET = os.getenv("CLIENT_SECRET")
10
+ REDIRECT_URI = os.getenv("REDIRECT_URI")
11
+
12
+
13
+ SCOPE = "playlist-read-private playlist-read-collaborative user-top-read user-read-currently-playing user-read-recently-played user-modify-playback-state playlist-modify-public playlist-modify-private"
14
+
15
+ sp = None
16
+
17
+ id = None
18
+
19
+ # Tool
20
+ def auth_with_spotify():
21
+
22
+ """
23
+ It auths with the spotify and returns current user info to confirm the success of auth
24
+ """
25
+ global sp
26
+ global id
27
+
28
+ try:
29
+ sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=SCOPE,client_id=CLIENT_ID,client_secret=CLIENT_SECRET,redirect_uri=REDIRECT_URI))
30
+
31
+ current_user = sp.current_user()
32
+
33
+ if current_user.get("id") != None:
34
+ id = current_user.get("id")
35
+
36
+ return current_user
37
+
38
+ except Exception as e:
39
+ return f"error auth with spotify {e}"
40
+
41
+
42
+
43
+
44
+ # Tool
45
+ def get_artist_and_track(track_name):
46
+
47
+ """
48
+ Searches for a track and returns artist info and URIs
49
+ """
50
+
51
+ if not sp:
52
+ return "Please authenticate with Spotify first!"
53
+
54
+ if not track_name.strip():
55
+ return "Please enter a track name!"
56
+
57
+ artist_song = defaultdict(list)
58
+
59
+ response = sp.search(q="track:"+track_name,type="track")
60
+
61
+ if response:
62
+ items = response.get("tracks", {}).get("items", [])
63
+ if items:
64
+ for item in items:
65
+ if item and item.get("artists")[0]:
66
+ artist_song["uri"].append(item["uri"])
67
+ artist_song["name"].append(item["artists"][0].get("name"))
68
+ else:
69
+ artist_song["uri"].append(None)
70
+ artist_song["name"].append(None)
71
+
72
+ return artist_song
73
+
74
+
75
+ # Tool
76
+ def add_to_queue_song(uri_of_song):
77
+
78
+ """
79
+ Adds a song to the Spotify queue using its URI
80
+ """
81
+
82
+ if not sp:
83
+ return "Please authenticate with Spotify first!"
84
+
85
+ if not uri_of_song.strip():
86
+ return "Please enter a track name!"
87
+
88
+ try:
89
+ sp.add_to_queue(uri_of_song)
90
+ return "success adding song to the queue"
91
+
92
+ except Exception as e:
93
+ return f"error adding song to the queue, check if you have a active session error- {e}"
94
+
95
+
96
+ # Tool
97
+ def get_recently_played_songs(limit_song=5):
98
+
99
+ if not sp:
100
+ return "Please authenticate with Spotify first!"
101
+
102
+ if not limit_song:
103
+ return "Please enter a number of songs"
104
+
105
+ artist_song = defaultdict(list)
106
+
107
+ res = sp.current_user_recently_played(limit_song)
108
+
109
+ items = res.get("items",[])
110
+
111
+ if items:
112
+ for item in items:
113
+ if item and item.get("track"):
114
+ track = item.get("track",{})
115
+
116
+ if (track and track.get("name",{})) or (track and track.get("artists",[])):
117
+
118
+ artist_song["artist_name"].append(track.get("artists")[0].get("name"))
119
+ artist_song["song_name"].append(track.get("name"))
120
+
121
+ else:
122
+ artist_song["artist_name"].append(None)
123
+ artist_song["song_name"].append(None)
124
+
125
+
126
+ return artist_song
127
+
128
+ # Tool
129
+ def create_playlist(id, name, description="", public=True, collaborative=False):
130
+ """
131
+ Creates a playlist for the given user.
132
+
133
+ Args:
134
+ id (str): Spotify user ID.
135
+ name (str): Name of the playlist (required).
136
+ description (str, optional): Description of the playlist. Defaults to "".
137
+ public (bool, optional): Whether the playlist is public. Defaults to True.
138
+ collaborative (bool, optional): Whether the playlist is collaborative. Defaults to False.
139
+
140
+ Keyword Args:
141
+ Additional keyword arguments are not used in this function.
142
+
143
+ Returns:
144
+ str: Success or error message.
145
+ """
146
+ if not sp:
147
+ return "Please authenticate with Spotify first!"
148
+
149
+ if not id and name:
150
+ return "Please enter id of user and playlist name"
151
+
152
+ try:
153
+ res = sp.user_playlist_create(id, name, description, public, collaborative)
154
+
155
+ if res.get("name") is not None:
156
+ return "playlist created success"
157
+ else:
158
+ return "error creating playlist"
159
+
160
+ except Exception as e:
161
+ return f"failed to create playlist error {e}"
162
+
163
+
164
+
165
+
166
+ # Tool
167
+ def get_playlist_name_and_id(limit_playlist=10):
168
+
169
+ """
170
+ Use this tool to get playlist name and id , mostly useful in tasks that needs id of playlist
171
+
172
+ it takes args like limit_playlist which is number of paylist you want to retrieve
173
+
174
+ """
175
+ playlist_name_and_id = defaultdict(list)
176
+
177
+ try:
178
+ result = sp.current_user_playlists(limit_playlist)
179
+
180
+ if result.get("items") != None:
181
+
182
+ for item in result["items"]:
183
+
184
+ if (item and item.get("name")) or (item and item.get("id")):
185
+
186
+ playlist_name_and_id["name"].append(item.get("name"))
187
+ playlist_name_and_id["id"].append(item.get("id"))
188
+
189
+ else:
190
+ playlist_name_and_id["name"].append(None)
191
+ playlist_name_and_id["id"].append(None)
192
+
193
+
194
+ return playlist_name_and_id
195
+
196
+ except Exception as e:
197
+ return f"error getting playlists name and id error {e}"
198
+
199
+
200
+
201
+ # Tool
202
+ def add_songs_to_playlist(playlist_id: str, items: str, position=None):
203
+ """
204
+ Adds a list of song URIs to the specified playlist.
205
+
206
+ Args:
207
+ playlist_id (str): The ID of the playlist.
208
+ items (str): Comma-separated Spotify track URIs or string representation of list
209
+ position (str, optional): Position to insert songs
210
+
211
+ Returns:
212
+ str: Success or error message
213
+ """
214
+ try:
215
+
216
+ if isinstance(items, str):
217
+
218
+ if items.startswith('[') and items.endswith(']'):
219
+
220
+ items = items.strip('[]').replace("'", "").replace('"', '')
221
+ song_uris = [uri.strip() for uri in items.split(',')]
222
+ else:
223
+
224
+ song_uris = [uri.strip() for uri in items.split(',')]
225
+ else:
226
+ song_uris = items
227
+
228
+ pos = int(position) if position and position.strip() else None
229
+
230
+ add_to_playlist = sp.playlist_add_items(playlist_id, song_uris, pos)
231
+
232
+ if add_to_playlist is not None:
233
+ return "success adding songs to playlist"
234
+
235
+ except Exception as e:
236
+ return f"error adding song to playlist {e}"
237
+
238
+
239
+
240
+ #Tool
241
+ def get_users_top_artists(limit_artists=5,offset=0,time_range="medium_term"):
242
+
243
+ """
244
+ Use this tool to get the top artist of the user
245
+
246
+ Args:
247
+ limit_artists (int): number of artists to retrieve
248
+ time_range (str): it can be "medium_term", "short_term" , "long_term" default is "medium_term"
249
+
250
+ Returns:
251
+ dict: defaultdict containing genres and artist_name
252
+
253
+ """
254
+ if not sp:
255
+ return "Please authenticate with Spotify first!"
256
+
257
+ if not limit_artists:
258
+ return "Please enter number of artists to retireve "
259
+
260
+ artist_and_genre = defaultdict(list)
261
+
262
+ try:
263
+ response = sp.current_user_top_artists(limit_artists, offset, time_range)
264
+
265
+ if response.get("items"):
266
+ for item in response["items"]:
267
+ genres = item.get("genres")
268
+ artist_name = item.get("name")
269
+
270
+ artist_and_genre["artist_name"].append(artist_name)
271
+ artist_and_genre["genres"].append(genres)
272
+ else:
273
+ return "failed to extract top artists please retry the tool"
274
+
275
+ return artist_and_genre
276
+
277
+ except Exception as e:
278
+ return f"error getting top artists error {e}"
279
+
280
+
281
+
282
+
283
+ #Tool
284
+ def get_user_top_tracks(limit_songs=5,time_range="medium_term",offset=0):
285
+
286
+ topTracks_and_their_artists = defaultdict(list)
287
+
288
+ try:
289
+
290
+ result = sp.current_user_top_tracks(limit_songs,offset,time_range)
291
+
292
+ if result.get("items"):
293
+ for item in result["items"]:
294
+ topTracks_and_their_artists["track_name"].append(item.get("name"))
295
+
296
+ album_artists = item.get("album", {}).get("artists", [])
297
+
298
+ if album_artists:
299
+ for artist in album_artists:
300
+
301
+ topTracks_and_their_artists["artist_name"].append(artist.get("name"))
302
+
303
+ else:
304
+ topTracks_and_their_artists["artist_name"].append(None)
305
+ else:
306
+ return "error retrieving top tracks retry the tool again"
307
+
308
+ return topTracks_and_their_artists
309
+
310
+ except Exception as e:
311
+ return f"error retieving top tracks error -{e}"
312
+
313
+
314
+
315
+ gr.Markdown("hello")
316
+
317
+
318
+ gr_mcp_tool1 = gr.Interface(fn=add_to_queue_song,inputs="text",outputs="text")
319
+ gr_mcp_tool2 = gr.Interface(fn=get_artist_and_track,inputs="text",outputs="text")
320
+ gr_mcp_tool3 = gr.Interface(fn=auth_with_spotify,inputs=None,outputs=gr.Textbox(label="Authentication Status"))
321
+ gr_mcp_tool4 = gr.Interface(fn=get_recently_played_songs,inputs=gr.Number(label="Number of Songs"),outputs=gr.JSON(label="Recently Played Songs"))
322
+ gr_mcp_tool5 = gr.Interface(
323
+ fn=create_playlist,
324
+ inputs=[
325
+ gr.Textbox(label="User ID", placeholder="Enter Spotify user ID"),
326
+ gr.Textbox(label="Playlist Name", placeholder="Enter playlist name")
327
+ ],
328
+ outputs="text")
329
+ gr_mcp_tool6 = gr.Interface(fn=get_playlist_name_and_id,inputs=gr.Number(label="number of playlists"),outputs=gr.JSON(label="playlist name and id"))
330
+
331
+ gr_mcp_tool7 = gr.Interface(
332
+ fn=add_songs_to_playlist,
333
+ inputs=[
334
+ gr.Textbox(
335
+ label="Playlist ID",
336
+ placeholder="e.g. 6PgF6BC39K31SCyMIhlNVs",
337
+ info="The Spotify playlist ID where you want to add songs"
338
+ ),
339
+ gr.Textbox(
340
+ label="Song URIs (comma separated)",
341
+ placeholder="e.g. ['spotify:track:abc123', 'spotify:track:def456', 'spotify:track:ghi789']",
342
+ info="Enter Spotify track URIs separated by commas e.g. ['spotify:track:abc123', 'spotify:track:def456', 'spotify:track:ghi789']",
343
+ lines=3
344
+ ),
345
+ gr.Textbox(
346
+ label="Position (optional)",
347
+ placeholder="e.g. 0 for beginning, leave empty for end",
348
+ info="Position to insert songs (0 = beginning, empty = end)"
349
+ )
350
+ ],
351
+ outputs=gr.Textbox(label="Result"))
352
+
353
+ gr_mcp_tool8 = gr.Interface(fn=get_users_top_artists,inputs=[gr.Number(label="number of artists to retrieve ")
354
+ ,gr.Textbox(label="time_range",info="time range has options of 'medium_term', 'short_term' , 'long_term' default is 'medium_term'")]
355
+ ,outputs=gr.JSON(label="genre and artist name"))
356
+
357
+ gr_mcp_tool9 = gr.Interface(fn=get_user_top_tracks,inputs=[gr.Number(label="number of tracks to retrieve ")
358
+ ,gr.Textbox(label="time_range",info="time range has options of 'medium_term', 'short_term' , 'long_term' default is 'medium_term'")]
359
+ ,outputs=gr.JSON(label="topTrack and artist name"))
360
+
361
+ with gr.Blocks() as app:
362
+ gr.Markdown("# 🎵 Spotify MCP Tools")
363
+ gr.Markdown("Welcome to the Spotify Music Control Panel, Below are the tools available in the Spotify MCP server.")
364
+ gr.Markdown("Due to Limitations in the Authication of the Spotify account Please Run it locally with your Spotify Developer Credentials ,checkout the Readme file to know more about the setup")
365
+
366
+
367
+ gr.TabbedInterface(
368
+ [gr_mcp_tool1, gr_mcp_tool2, gr_mcp_tool3, gr_mcp_tool4,
369
+ gr_mcp_tool5, gr_mcp_tool6, gr_mcp_tool7, gr_mcp_tool8, gr_mcp_tool9],
370
+ tab_names=[
371
+ "Add to Queue", "Get Artist & Track", "Authenticate", "Recently Played",
372
+ "Create Playlist", "Playlist Info", "Add Songs to Playlist", "Top Artists", "Top Tracks"
373
+ ]
374
+ )
375
+
376
+
377
+ app.launch(mcp_server=True)