All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Spotify error bodies no longer leak into MCP responses.
StartPlaybackAsyncandActivateDeviceAsyncinSpotifyStreamer.cswere writing Spotify's raw HTTP error JSON to stdout viaConsole.WriteLine; because that JSON appears before the CLI's own final response, MCP's parser was picking up the leak (e.g.{"error":{"status":404,"message":"Not found."}}) instead of the CLI's structured{success:false, message:...}output. Rerouted to stderr. - MCP
parseJsonOutputprefers the last balanced JSON in stdout (lfm-mcp-release/server-core.js), not the first. Any pre-payload stdout noise (Spotify errors, progress lines, future leaks) is now harmless as long as the CLI writes its structured response last. lfm_play_nowno longer requires a manuallfm_activate_deviceround-trip when the target Spotify device is idle.PlayNowFromUrisAsyncnow callsEnsurePlaybackActiveAsyncbefore starting playback, mirroring the patternQueueFromUrisAsynchas always used. Eliminates the recurring NO_ACTIVE_DEVICE friction.
lfm config spotify-auth— authorize or re-authorize Spotify on demand, starting a fresh (and now tracked) 6-month authorization. Prints the next re-auth due date and the commands to propagate the token to other machines. Every reauth error message has advertised this command since 1.11.0; now it exists.lfm config import <file> --spotify-auth— import only the Spotify authorization (client credentials, refresh token, authorization date), leaving machine-specific settings (Sonos URL, default device/player) untouched. Intended for syncing a fresh authorization to other laptops via the OneDrive-synced staging config.- Spotify authorization expiry tracking — new
SpotifyConfig.AuthorizedAtstamp set on every OAuth code exchange.lfm api-status(text and--json) now reports authorization status, the ~6-month re-auth due date, and warns inside the final 14 days. Verified against Spotify's 2026-06-18 policy: expiry is absolute from the original authorization, not from last refresh. ./deploy-to-spark.sh --config-only— push config.json and recreate the container without a source sync or image rebuild; the fast path for the 6-monthly re-auth ritual.
- Rotated refresh tokens are now persisted — the refresh path previously discarded any new refresh token Spotify returned; per the documented contract it is now saved (without resetting the authorization clock).
- Transient refresh failures no longer force a full interactive OAuth flow — network blips now surface as retryable errors that keep the stored token.
- Bare
code=...paste during the OAuth flow no longer crashes withUriFormatException. - Stale
lfm spotify authreferences corrected tolfm config spotify-auth.
- Bearer-token authentication implemented — enforced on
/mcpwhenAUTH_TOKENis set (timing-safe comparison, 401 otherwise);/healthstays open. PreviouslyAUTH_TOKENwas advertised by the entrypoint and docs but never read. Runs open when unset (required until claude.ai custom connectors' request-headers beta is broadly available). - CORS hardened — dropped
credentials: trueand the*wildcard; browser cross-origin requests are denied by default (ALLOWED_ORIGINSempty). MCP clients send no Origin header and are unaffected. - Removed
error.stackfrom 500 response bodies; committed bearer token removed from.mcp.json(now${LFM_AUTH_TOKEN}env expansion);.env.examplerewritten to match the implemented auth (the GitHub-OAuth design it described was never built).
lfm_recommendations,lfm_toptracks,lfm_mixtape, andlfm_recent_tracksnow pass structured CLI errors through (spotify_reauth_requiredetc.) instead of masking them withsuccess: trueand empty results- Reauth
actionguidance now describes the real recovery ritual (re-auth on a laptop, push config to the server)
lfm album-trackscommand /lfm_album_tracksMCP tool — canonical Spotify tracklist for an album, returning track number, disc number, duration, and per-track artist attribution- Plugs the well-known LLM blind spot on album track positions (the "Tom The Model is the closer" failure mode)
- Uses
GET /v1/albums/{id}/trackswith pagination vianextfor albums >50 tracks - Same disambiguation contract as
lfm_play_now(multiple-versions error +exactMatchretry) - Updated
lfm-guidelines.mdto route canonical-position questions to the new tool, keepinglfm_check verbosefor scrobble-coverage questions
- Spotify reauthentication handling (preparing for Spotify's 2026-07-20 6-month refresh-token expiry policy)
- New
SpotifyReauthRequiredExceptionthrown specifically on Spotify'sinvalid_grantresponse EnsureValidAccessTokenAsyncdistinguishes interactive CLI from headless MCP viaConsole.IsInputRedirected: interactive falls through to OAuth prompt as before; headless throws a clean structured error instead of deadlocking onConsole.ReadLine- Dead refresh tokens are now discarded from saved config on confirmed
invalid_grant(per Spotify's "discard expired tokens" guidance) PlayCommandemits structured{ errorCode: "spotify_reauth_required", action: "..." }JSON for MCP consumption
- New
- Album disambiguation is now case-insensitive — flipped all 5 album exact-match sites from
StringComparison.OrdinaltoOrdinalIgnoreCase, matching the existing playlist-disambiguation convention. ("Out of Season" now resolves to Spotify's "Out Of Season" without the user needing to mirror Spotify's title-case.) - Primary build target is now win-arm64 (laptop upgraded to ARM as of 2026-06)
- New tool:
lfm_album_tracks(33 tools total) executeLfmCommandnow passes structured-JSON stdout through to the MCP layer on non-zero CLI exit, surfacing handled-error JSON (multiple album versions, Spotify reauth required) instead of swallowing it as a generic "command failed" message
- Complete Spotify API Migration - All breaking changes from Spotify's Feb 2026 API update now addressed ahead of March 9 deadline
- Playlist delete endpoint migrated from
DELETE /v1/playlists/{id}/followerstoDELETE /v1/me/library?uris=(note:urismust be a query parameter, not request body — undocumented by Spotify) - Playlist object field rename from
trackstoitemsinSpotifyPlaylistItemmodel (SpotifyModels.cs) - Previously completed: playlist creation endpoint (
/me/playlists) and add tracks endpoint (/items)
- Playlist delete endpoint migrated from
- Spotify endpoint changes apply to MCP tools that create playlists and manage tracks
- No new tools or tool changes — all 32 tools remain the same
- Play Playlists by Name - New
lfm playlistcommand to play Spotify playlists- Fuzzy name matching for easy playlist discovery
- Exact match mode with
--exact-matchflag for disambiguation - Works with both Spotify and Sonos players
- Supports
--player,--device,--room,--jsonoptions
- List All Playlists - New
lfm playlistscommand- Shows all user playlists with track counts
- Indicates owned vs followed playlists
- JSON output support for programmatic use
- New MCP Tools - Added 2 new tools for playlist management (30 tools total)
lfm_play_playlist- Play user playlists by name with disambiguation supportlfm_get_playlists- Get list of all user playlists with metadata
- Two-Phase Disambiguation - Same pattern as albums for consistent UX
- Discovery phase: Fuzzy search shows all matching playlists
- Exact match phase: Filter for specific playlist when multiple matches found
- Interface Extensions
IPlaylistStreamer: AddedSearchPlaylistByNameAsync()andPlayPlaylistAsync()ISonosStreamer: AddedPlayPlaylistAsync()
- New Models -
PlaylistSearchResultfor disambiguation support - URI Format Handling
- Spotify:
spotify:playlist:{id} - Sonos:
spotify:user:spotify:playlist:{id}(required format)
- Spotify:
- Updated all documentation with playlist features (README, QUICKSTART, MCP_SETUP)
- Updated MCP tool counts (28 → 30 tools)
- Added playlist usage examples and command references
- Updated CLAUDE.md session notes with implementation details
- Commands:
PlaylistCommand,PlaylistsCommand - Command Builders:
PlaylistCommandBuilder,PlaylistsCommandBuilder - Search Strategy: Reuses existing
GetUserPlaylistsAsync()with client-side filtering - Design Decision: User playlists only (no public/Spotify playlists for simplicity)
- GitHub Release Automation - Fixed automated release process to enable installation scripts
- Ensured GitHub Actions workflow runs on tag push
- Creates proper GitHub Release with all platform binaries
- Fixes installation scripts that depend on GitHub Releases
- User Documentation Suite - Comprehensive documentation for end users
- Created INSTALL.md - Platform-specific installation guide
- Created QUICKSTART.md - 5-minute getting started guide
- Created MCP_SETUP.md - Claude Code/Desktop integration setup
- Created TROUBLESHOOTING.md - Common issues reference
- Updated README.md to user-friendly landing page
- Created RELEASE.md - Release process documentation
- Repository Cleanup - Removed legacy files and improved organization
- Removed lfm-mcp-prototype directory (2,061 files including node_modules)
- Removed old planning docs and test files from git
- Moved development docs to docs/ folder
- Updated .gitignore to prevent re-adding legacy files
1.5.0 - 2025-10-17
- "Listen Before You Speak" methodology - Core workflow guidance for LLM interactions
- Emphasizes data → think → more data → think → narrative pattern
- Prevents starting narratives before completing research phase
- "Using lfm_recommendations - LLM Reasoning First" - Guidelines for proper tool usage
- Emphasizes LLM musical knowledge over algorithmic recommendations
- Clear guidance on when NOT to use recommendations (well-known artists)
- Proper workflow: LLM reasoning first, optionally supplement with tool
- "Playback State Awareness" - Mandatory workflow before playback suggestions
- Trigger words identification ("I've queued", "Want me to queue", etc.)
- Required state checks: recent_tracks + current_track before suggesting playback
- Response templates for data-informed suggestions
- Sonos Integration - Full playback support via node-sonos-http-api
- Unified play command supporting both Spotify and Sonos
- Room management and device routing
- Config-driven defaults (DefaultPlayer, DefaultRoom)
- Recent Tracks Feature - Temporal listening history with chronological ordering
- Most recent first ordering
- Configurable hours lookback parameter
- Perfect for detecting listening patterns
- Spotify Playback Controls - Comprehensive playback management
- Play now / queue functionality
- Pause, resume, skip commands
- Current track status
- Device activation support
- Album Disambiguation - Handle tracks on multiple albums
- Detect and report multiple album versions
- Allow track + album parameter combination
- User preference for studio vs live/greatest hits versions
- API Status Checker - Diagnose Last.fm connectivity issues
- Health status for multiple endpoints
- Verbose mode with HTTP details
- JSON output for monitoring/automation
- Parallel API Processing - Batch processing for deep searches
- Configurable concurrent API calls (default: 5)
- Intelligent rate limiting (1 second between batches)
- Significant speedup for artist-tracks/artist-albums deep searches
- MCP Parser - Fixed position-based JSON extraction for array-root responses
- Correctly handles both object-root and array-root JSON structures
- Helper functions: extractArray() and extractObject() with fallback logic
- Token Optimization - ~50% reduction in MCP response tokens
- Added compact helper functions (compactAlbum, compactArtist, compactTrack)
- Strip URLs and MBIDs that LLMs can't use
- Applied to lfm_albums, lfm_artists, lfm_tracks handlers
- Spotify Authentication - Improved resilience
- Changed StartPlaybackAsync signature from string to List
- Ensures atomic album playback (all tracks in one API call)
- Prevents race conditions in track ordering
- Documentation Refactoring - Split CLAUDE.md into focused files
- Created SESSION_HISTORY.md - 7 archived sessions
- Created IMPLEMENTATION_NOTES.md - Technical reference
- Created LESSONS_LEARNED.md - Debugging patterns and best practices
- CLAUDE.md reduced by 53% while maintaining current work focus
- Album Queueing - Fixed missing/reordered tracks in Spotify album playback
- Changed from 12 separate API calls to ONE atomic call
- Supports up to 100 URIs in one request
- Depth Parameter Clarification - Corrected understanding in guidelines
- Depth = popularity ranking (top N by play count), NOT chronological
- Updated guidelines to recommend deep:true as default
- Cache Control - Added support for cache control in recent command
- Respects cache expiry settings
- Configurable throttling behavior
1.4.0 - Previous Release
- Enhanced cache system with comprehensive management
- Comprehensive parameter validation and API improvements
- Tag filtering system for recommendations
- Recommendation diversity controls (totalArtists/totalTracks)
- Artist-specific track and album search
- Last.fm autocorrect support for artist name matching
- Service layer architecture refactoring
- Date range support across all commands
- Unicode symbol support with auto-detection
- API throttling configuration (default: 200ms)
1.0.0 - Initial Release
- Core Last.fm API integration
- Basic commands: artists, tracks, albums
- File-based caching system
- Cross-platform support (Windows, Linux, WSL)
- Configuration management
- Spotify basic integration