diff --git a/server.py b/server.py index 324ba2b..f7353e3 100644 --- a/server.py +++ b/server.py @@ -8,6 +8,10 @@ from mcp.types import Tool, TextContent import spotify_client + +def _dbg(msg: str) -> None: + print(f"[spotify-mcp] {msg}", file=sys.stderr, flush=True) + server = Server("spotify-mcp") @@ -112,6 +116,7 @@ async def handle_list_tools() -> list[Tool]: @server.call_tool() async def handle_call_tool(name: str, arguments: dict | None) -> list[TextContent]: arguments = arguments or {} + _dbg(f"tool={name!r} args={arguments!r}") try: if name == "list_playlists": result = await asyncio.to_thread(spotify_client.list_playlists) @@ -145,8 +150,11 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[TextConten else: raise ValueError(f"Unknown tool: {name}") except Exception as e: + _dbg(f"tool={name!r} raised {type(e).__name__}: {e}") return [TextContent(type="text", text=f"Error: {e}")] + result_len = len(result) if isinstance(result, list) else "n/a" + _dbg(f"tool={name!r} returning {result_len} items") return [TextContent(type="text", text=json.dumps(result, ensure_ascii=False, indent=2))] diff --git a/spotify_client.py b/spotify_client.py index e56b8d4..2f226cb 100644 --- a/spotify_client.py +++ b/spotify_client.py @@ -1,11 +1,16 @@ import os import pathlib +import sys from dotenv import load_dotenv import spotipy from spotipy.oauth2 import SpotifyOAuth from spotipy.cache_handler import CacheFileHandler + +def _dbg(msg: str) -> None: + print(f"[spotify-mcp] {msg}", file=sys.stderr, flush=True) + load_dotenv() _SCOPES = " ".join([ @@ -72,25 +77,41 @@ def list_playlists() -> list[dict]: def get_playlist_tracks(playlist_id: str) -> list[dict]: + _dbg(f"get_playlist_tracks called with playlist_id={playlist_id!r}") sp = get_client() results = [] + page = 0 response = sp.playlist_items(playlist_id, additional_types=["track"]) while response: - for item in response["items"]: - track = item.get("track") - if not track or track.get("type") != "track": + items = response.get("items") or [] + total = response.get("total") + _dbg(f" page {page}: total={total}, items_in_page={len(items)}, next={bool(response.get('next'))}") + for idx, item in enumerate(items): + # Spotify API returns the track under "item" key (newer API) or "track" key (older) + track = item.get("item") or item.get("track") + if track is None: + _dbg(f" item[{idx}]: track=None (local/null track), skipping") continue + track_type = track.get("type") + track_name = track.get("name", "") + track_uri = track.get("uri", "") + if track_type != "track": + _dbg(f" item[{idx}]: SKIPPED type={track_type!r} name={track_name!r} uri={track_uri!r}") + continue + _dbg(f" item[{idx}]: OK type={track_type!r} name={track_name!r}") 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"], + "popularity": track.get("popularity"), "uri": track["uri"], "added_at": item["added_at"], }) + page += 1 response = sp.next(response) if response["next"] else None + _dbg(f"get_playlist_tracks returning {len(results)} tracks") return results