navidrome-mcp 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +397 -0
  3. package/dist/capabilities.d.ts +20 -0
  4. package/dist/capabilities.d.ts.map +1 -0
  5. package/dist/capabilities.js +25 -0
  6. package/dist/capabilities.js.map +1 -0
  7. package/dist/client/auth-manager.d.ts +27 -0
  8. package/dist/client/auth-manager.d.ts.map +1 -0
  9. package/dist/client/auth-manager.js +54 -0
  10. package/dist/client/auth-manager.js.map +1 -0
  11. package/dist/client/navidrome-client.d.ts +28 -0
  12. package/dist/client/navidrome-client.d.ts.map +1 -0
  13. package/dist/client/navidrome-client.js +84 -0
  14. package/dist/client/navidrome-client.js.map +1 -0
  15. package/dist/config.d.ts +41 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +132 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/constants/defaults.d.ts +29 -0
  20. package/dist/constants/defaults.d.ts.map +1 -0
  21. package/dist/constants/defaults.js +37 -0
  22. package/dist/constants/defaults.js.map +1 -0
  23. package/dist/constants/timeouts.d.ts +66 -0
  24. package/dist/constants/timeouts.d.ts.map +1 -0
  25. package/dist/constants/timeouts.js +66 -0
  26. package/dist/constants/timeouts.js.map +1 -0
  27. package/dist/index.d.ts +20 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +70 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/resources/index.d.ts +21 -0
  32. package/dist/resources/index.d.ts.map +1 -0
  33. package/dist/resources/index.js +83 -0
  34. package/dist/resources/index.js.map +1 -0
  35. package/dist/schemas/common.d.ts +68 -0
  36. package/dist/schemas/common.d.ts.map +1 -0
  37. package/dist/schemas/common.js +69 -0
  38. package/dist/schemas/common.js.map +1 -0
  39. package/dist/schemas/index.d.ts +21 -0
  40. package/dist/schemas/index.d.ts.map +1 -0
  41. package/dist/schemas/index.js +24 -0
  42. package/dist/schemas/index.js.map +1 -0
  43. package/dist/schemas/pagination.d.ts +153 -0
  44. package/dist/schemas/pagination.d.ts.map +1 -0
  45. package/dist/schemas/pagination.js +93 -0
  46. package/dist/schemas/pagination.js.map +1 -0
  47. package/dist/schemas/validation.d.ts +147 -0
  48. package/dist/schemas/validation.d.ts.map +1 -0
  49. package/dist/schemas/validation.js +143 -0
  50. package/dist/schemas/validation.js.map +1 -0
  51. package/dist/tools/handlers/lastfm-handlers.d.ts +5 -0
  52. package/dist/tools/handlers/lastfm-handlers.d.ts.map +1 -0
  53. package/dist/tools/handlers/lastfm-handlers.js +143 -0
  54. package/dist/tools/handlers/lastfm-handlers.js.map +1 -0
  55. package/dist/tools/handlers/lyrics-handlers.d.ts +5 -0
  56. package/dist/tools/handlers/lyrics-handlers.d.ts.map +1 -0
  57. package/dist/tools/handlers/lyrics-handlers.js +51 -0
  58. package/dist/tools/handlers/lyrics-handlers.js.map +1 -0
  59. package/dist/tools/handlers/playlist-handlers.d.ts +5 -0
  60. package/dist/tools/handlers/playlist-handlers.d.ts.map +1 -0
  61. package/dist/tools/handlers/playlist-handlers.js +316 -0
  62. package/dist/tools/handlers/playlist-handlers.js.map +1 -0
  63. package/dist/tools/handlers/queue-handlers.d.ts +5 -0
  64. package/dist/tools/handlers/queue-handlers.d.ts.map +1 -0
  65. package/dist/tools/handlers/queue-handlers.js +135 -0
  66. package/dist/tools/handlers/queue-handlers.js.map +1 -0
  67. package/dist/tools/handlers/radio-handlers.d.ts +5 -0
  68. package/dist/tools/handlers/radio-handlers.d.ts.map +1 -0
  69. package/dist/tools/handlers/radio-handlers.js +323 -0
  70. package/dist/tools/handlers/radio-handlers.js.map +1 -0
  71. package/dist/tools/handlers/registry.d.ts +26 -0
  72. package/dist/tools/handlers/registry.d.ts.map +1 -0
  73. package/dist/tools/handlers/registry.js +77 -0
  74. package/dist/tools/handlers/registry.js.map +1 -0
  75. package/dist/tools/handlers/search-handlers.d.ts +5 -0
  76. package/dist/tools/handlers/search-handlers.d.ts.map +1 -0
  77. package/dist/tools/handlers/search-handlers.js +125 -0
  78. package/dist/tools/handlers/search-handlers.js.map +1 -0
  79. package/dist/tools/handlers/tag-handlers.d.ts +5 -0
  80. package/dist/tools/handlers/tag-handlers.d.ts.map +1 -0
  81. package/dist/tools/handlers/tag-handlers.js +155 -0
  82. package/dist/tools/handlers/tag-handlers.js.map +1 -0
  83. package/dist/tools/handlers/user-preferences-handlers.d.ts +5 -0
  84. package/dist/tools/handlers/user-preferences-handlers.d.ts.map +1 -0
  85. package/dist/tools/handlers/user-preferences-handlers.js +155 -0
  86. package/dist/tools/handlers/user-preferences-handlers.js.map +1 -0
  87. package/dist/tools/index.d.ts +19 -0
  88. package/dist/tools/index.d.ts.map +1 -0
  89. package/dist/tools/index.js +20 -0
  90. package/dist/tools/index.js.map +1 -0
  91. package/dist/tools/lastfm-discovery.d.ts +107 -0
  92. package/dist/tools/lastfm-discovery.d.ts.map +1 -0
  93. package/dist/tools/lastfm-discovery.js +238 -0
  94. package/dist/tools/lastfm-discovery.js.map +1 -0
  95. package/dist/tools/library.d.ts +31 -0
  96. package/dist/tools/library.d.ts.map +1 -0
  97. package/dist/tools/library.js +272 -0
  98. package/dist/tools/library.js.map +1 -0
  99. package/dist/tools/listening-history.d.ts +51 -0
  100. package/dist/tools/listening-history.d.ts.map +1 -0
  101. package/dist/tools/listening-history.js +126 -0
  102. package/dist/tools/listening-history.js.map +1 -0
  103. package/dist/tools/lyrics.d.ts +35 -0
  104. package/dist/tools/lyrics.d.ts.map +1 -0
  105. package/dist/tools/lyrics.js +222 -0
  106. package/dist/tools/lyrics.js.map +1 -0
  107. package/dist/tools/media-library.d.ts +46 -0
  108. package/dist/tools/media-library.d.ts.map +1 -0
  109. package/dist/tools/media-library.js +179 -0
  110. package/dist/tools/media-library.js.map +1 -0
  111. package/dist/tools/playlist-management.d.ts +80 -0
  112. package/dist/tools/playlist-management.d.ts.map +1 -0
  113. package/dist/tools/playlist-management.js +384 -0
  114. package/dist/tools/playlist-management.js.map +1 -0
  115. package/dist/tools/queue-management.d.ts +48 -0
  116. package/dist/tools/queue-management.d.ts.map +1 -0
  117. package/dist/tools/queue-management.js +83 -0
  118. package/dist/tools/queue-management.js.map +1 -0
  119. package/dist/tools/radio-discovery.d.ts +95 -0
  120. package/dist/tools/radio-discovery.d.ts.map +1 -0
  121. package/dist/tools/radio-discovery.js +352 -0
  122. package/dist/tools/radio-discovery.js.map +1 -0
  123. package/dist/tools/radio-validation.d.ts +49 -0
  124. package/dist/tools/radio-validation.d.ts.map +1 -0
  125. package/dist/tools/radio-validation.js +463 -0
  126. package/dist/tools/radio-validation.js.map +1 -0
  127. package/dist/tools/radio.d.ts +38 -0
  128. package/dist/tools/radio.d.ts.map +1 -0
  129. package/dist/tools/radio.js +315 -0
  130. package/dist/tools/radio.js.map +1 -0
  131. package/dist/tools/search.d.ts +54 -0
  132. package/dist/tools/search.d.ts.map +1 -0
  133. package/dist/tools/search.js +165 -0
  134. package/dist/tools/search.js.map +1 -0
  135. package/dist/tools/tags.d.ts +63 -0
  136. package/dist/tools/tags.d.ts.map +1 -0
  137. package/dist/tools/tags.js +260 -0
  138. package/dist/tools/tags.js.map +1 -0
  139. package/dist/tools/test.d.ts +50 -0
  140. package/dist/tools/test.d.ts.map +1 -0
  141. package/dist/tools/test.js +121 -0
  142. package/dist/tools/test.js.map +1 -0
  143. package/dist/tools/user-preferences.d.ts +73 -0
  144. package/dist/tools/user-preferences.d.ts.map +1 -0
  145. package/dist/tools/user-preferences.js +231 -0
  146. package/dist/tools/user-preferences.js.map +1 -0
  147. package/dist/transformers/song-transformer.d.ts +122 -0
  148. package/dist/transformers/song-transformer.d.ts.map +1 -0
  149. package/dist/transformers/song-transformer.js +232 -0
  150. package/dist/transformers/song-transformer.js.map +1 -0
  151. package/dist/types/core.d.ts +147 -0
  152. package/dist/types/core.d.ts.map +1 -0
  153. package/dist/types/core.js +19 -0
  154. package/dist/types/core.js.map +1 -0
  155. package/dist/types/index.d.ts +23 -0
  156. package/dist/types/index.d.ts.map +1 -0
  157. package/dist/types/index.js +19 -0
  158. package/dist/types/index.js.map +1 -0
  159. package/dist/types/lyrics.d.ts +58 -0
  160. package/dist/types/lyrics.d.ts.map +1 -0
  161. package/dist/types/lyrics.js +19 -0
  162. package/dist/types/lyrics.js.map +1 -0
  163. package/dist/types/playlists.d.ts +150 -0
  164. package/dist/types/playlists.d.ts.map +1 -0
  165. package/dist/types/playlists.js +19 -0
  166. package/dist/types/playlists.js.map +1 -0
  167. package/dist/types/radio.d.ts +192 -0
  168. package/dist/types/radio.d.ts.map +1 -0
  169. package/dist/types/radio.js +19 -0
  170. package/dist/types/radio.js.map +1 -0
  171. package/dist/types/tags.d.ts +72 -0
  172. package/dist/types/tags.d.ts.map +1 -0
  173. package/dist/types/tags.js +19 -0
  174. package/dist/types/tags.js.map +1 -0
  175. package/dist/utils/cache.d.ts +32 -0
  176. package/dist/utils/cache.d.ts.map +1 -0
  177. package/dist/utils/cache.js +84 -0
  178. package/dist/utils/cache.js.map +1 -0
  179. package/dist/utils/error-formatter.d.ts +108 -0
  180. package/dist/utils/error-formatter.d.ts.map +1 -0
  181. package/dist/utils/error-formatter.js +161 -0
  182. package/dist/utils/error-formatter.js.map +1 -0
  183. package/dist/utils/logger.d.ts +33 -0
  184. package/dist/utils/logger.d.ts.map +1 -0
  185. package/dist/utils/logger.js +52 -0
  186. package/dist/utils/logger.js.map +1 -0
  187. package/dist/utils/message-manager.d.ts +74 -0
  188. package/dist/utils/message-manager.d.ts.map +1 -0
  189. package/dist/utils/message-manager.js +134 -0
  190. package/dist/utils/message-manager.js.map +1 -0
  191. package/dist/utils/version.d.ts +5 -0
  192. package/dist/utils/version.d.ts.map +1 -0
  193. package/dist/utils/version.js +19 -0
  194. package/dist/utils/version.js.map +1 -0
  195. package/package.json +78 -0
package/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # Navidrome MCP Server
2
+
3
+ Transform your Navidrome music server with an AI-powered music assistant. This MCP (Model Context Protocol) server enables Claude, ChatGPT, and other AI assistants to interact with your personal music library through natural language, offering intelligent playlist creation, music discovery, and library management.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Why Navidrome MCP?](#why-navidrome-mcp)
8
+ - [Features](#features)
9
+ - [Music Library Management](#-music-library-management)
10
+ - [Intelligent Playlist Creation](#-intelligent-playlist-creation)
11
+ - [Personalized Music Discovery](#-personalized-music-discovery)
12
+ - [Smart Radio Management](#-smart-radio-management)
13
+ - [Analytics & Insights](#-analytics--insights)
14
+ - [Installation](#installation)
15
+ - [Prerequisites](#prerequisites)
16
+ - [Quick Setup](#quick-setup)
17
+ - [Configure Claude Desktop](#configure-claude-desktop)
18
+ - [Configure ChatGPT Desktop](#configure-chatgpt-desktop)
19
+ - [Powerful Usage Examples](#powerful-usage-examples)
20
+ - [Available Tools](#available-tools)
21
+ - [Troubleshooting](#troubleshooting)
22
+ - [Development](#development)
23
+ - [License](#license)
24
+
25
+ ## Why Navidrome MCP?
26
+
27
+ Imagine having an AI assistant that truly understands your music taste. This MCP server enables AI assistants to:
28
+
29
+ - **Analyze your listening patterns** and create perfectly curated playlists
30
+ - **Discover hidden gems** in your library based on your mood or activity
31
+ - **Build custom radio stations** from your favorite tracks
32
+ - **Find music similar to what you love** using Last.fm's recommendation engine
33
+ - **Discover internet radio stations** from around the world with advanced filtering
34
+ - **Get synchronized lyrics** with millisecond-precision timestamps for your favorite tracks
35
+ - **Manage your library** with simple conversational commands
36
+
37
+ This isn't just another music tool – it's your personal music curator powered by AI.
38
+
39
+ ## Features
40
+
41
+ ### 🎵 Music Library Management
42
+
43
+ * **Intelligent Browsing**: Navigate songs, albums, artists, and genres with smart filtering
44
+ * **Deep Search**: Full-text search across all metadata or targeted searches for specific content
45
+ * **Rich Metadata**: Access detailed information about tracks, albums, and artists
46
+ * **Tag Analysis**: Explore and analyze metadata tags (genre, composer, label, year, and more)
47
+ * **Clean Responses**: Optimized data transfer with only essential fields (~10 properties vs 50+ raw)
48
+
49
+ ### 🎶 Intelligent Playlist Creation
50
+
51
+ * **AI Assistant Integration**: Enables AI assistants to analyze your taste and build themed playlists
52
+ * **Smart Management**: Create, update, and organize playlists conversationally
53
+ * **Flexible Track Addition**: Add songs by ID, entire albums, artist discographies, or specific discs
54
+ * **Dynamic Reordering**: Rearrange tracks with simple commands
55
+ * **Cross-Reference**: Find which playlists contain specific songs
56
+
57
+ ### 🎼 Personalized Music Discovery
58
+
59
+ * **Listening Data Access**: Provides listening history data for AI assistants to understand your preferences
60
+ * **Similar Artist/Track Finding**: Discover music similar to your favorites via Last.fm
61
+ * **Artist Deep Dives**: Get biographies, popular tracks, and related artists
62
+ * **Global Trends**: Browse worldwide music charts and trending genres
63
+ * **Library Analysis Tools**: Access data to help AI assistants find overlooked tracks in your library
64
+
65
+ ### 📻 Smart Radio Management
66
+
67
+ * **Stream Validation**: Test radio URLs before adding to avoid broken streams
68
+ * **Format Detection**: Automatic detection of MP3, AAC, OGG, FLAC streams
69
+ * **Metadata Extraction**: Pull station info from SHOUTcast/Icecast headers
70
+ * **Custom Station Creation**: Build radio stations from your collection
71
+ * **One-Time Setup Tips**: Smart contextual help that appears only when needed
72
+
73
+ ### 🌍 Internet Radio Discovery
74
+
75
+ * **Global Station Database**: Access thousands of internet radio stations worldwide via Radio Browser
76
+ * **Advanced Filtering**: Search by genre, country, language, codec, bitrate, and more
77
+ * **Automatic Validation**: Discovered stations are tested for availability
78
+ * **Quality Control**: Filter out broken stations and focus on high-quality streams
79
+ * **Popularity Metrics**: Discover stations by vote count and listener engagement
80
+ * **Station Management**: Tools for AI assistants to add discovered stations to your collection
81
+
82
+ ### 🎤 Synchronized Lyrics
83
+
84
+ * **Time-Synchronized Lyrics**: Get lyrics with millisecond-precision timestamps for line-by-line timing
85
+ * **Dual Format Support**: Access both time-synced and plain text lyrics
86
+ * **Community-Powered**: Lyrics sourced from LRCLIB's community database
87
+ * **Smart Matching**: Automatic matching by title, artist, album, and duration
88
+ * **No API Keys Required**: Free lyrics access without registration
89
+
90
+ ### 📊 Analytics & Insights
91
+
92
+ * **Listening Patterns**: Understand your music habits with play statistics
93
+ * **Taste Evolution**: Track how your preferences change over time
94
+ * **Most/Least Played**: Discover your true favorites and forgotten tracks
95
+ * **Genre Distribution**: Access data about your library's composition for analysis
96
+ * **Recommendation Data**: Provides data for AI assistants to generate suggestions based on your history
97
+
98
+ ### ⭐ Preference Management
99
+
100
+ * **Star System**: Mark favorites for quick access
101
+ * **5-Star Ratings**: Rate content for better recommendations
102
+ * **Queue Control**: Manage playback queues across devices
103
+ * **Data Access for Organization**: Provides preference data for AI assistants to help organize your collection
104
+
105
+ ## Installation
106
+
107
+ ### Prerequisites
108
+
109
+ * **Node.js 20+** ([Download here](https://nodejs.org/))
110
+ * **pnpm** package manager ([Install instructions](https://pnpm.io/installation))
111
+ * **Running Navidrome server** with your music library
112
+ * **Claude Desktop** or **ChatGPT Desktop** (or any MCP-compatible client)
113
+
114
+ ### Quick Setup
115
+
116
+ #### 1. Clone and Build
117
+
118
+ ```bash
119
+ git clone https://github.com/Blakeem/Navidrome-MCP.git
120
+ cd navidrome-mcp
121
+ pnpm install
122
+ pnpm build
123
+ ```
124
+
125
+ ### Configure Claude Desktop
126
+
127
+ Find your configuration file:
128
+ * **Windows**: `%APPDATA%/Claude/claude_desktop_config.json`
129
+ * **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
130
+ * **Linux**: `~/.config/Claude/claude_desktop_config.json`
131
+
132
+ Add the Navidrome MCP server:
133
+
134
+ ```json
135
+ {
136
+ "mcpServers": {
137
+ "navidrome": {
138
+ "command": "node",
139
+ "args": ["/absolute/path/to/navidrome-mcp/dist/index.js"],
140
+ "env": {
141
+ "NAVIDROME_URL": "http://your-server:4533",
142
+ "NAVIDROME_USERNAME": "your_username",
143
+ "NAVIDROME_PASSWORD": "your_password",
144
+ "LASTFM_API_KEY": "your_api_key", // Get your own at https://www.last.fm/api/account/create
145
+ "RADIO_BROWSER_USER_AGENT": "Navidrome-MCP/1.0 (+https://github.com/Blakeem/Navidrome-MCP)",
146
+ "LYRICS_PROVIDER": "lrclib",
147
+ "LRCLIB_USER_AGENT": "Navidrome-MCP/1.0 (+https://github.com/Blakeem/Navidrome-MCP)"
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ **Important**:
155
+ - Use absolute paths (full path from root)
156
+ - Get a free Last.fm API key at [Last.fm](https://www.last.fm/api) (optional - enables music discovery)
157
+ - Radio Browser integration requires a User-Agent string (enables station discovery)
158
+ - Lyrics integration works without API keys (LRCLIB is free)
159
+ - Features are automatically enabled/disabled based on available configuration
160
+ - Restart Claude Desktop after saving
161
+
162
+ ### Configure ChatGPT Desktop
163
+
164
+ 1. Open **ChatGPT Desktop**
165
+ 2. Go to **Settings → Connectors**
166
+ 3. Click **Create** and add:
167
+ - **Command**: `node`
168
+ - **Args**: `/absolute/path/to/navidrome-mcp/dist/index.js`
169
+ - **Environment variables**: Same as above
170
+ 4. Save and restart
171
+
172
+ ## Powerful Usage Examples
173
+
174
+ ### 🎯 Smart Discovery & Playlist Creation
175
+
176
+ * **"Look at my most played artists, find what albums I'm missing from their discographies, and tell me which ones are most popular"**
177
+ * **"Create a 'Best of Pink Floyd' playlist using Last.fm's top tracks data, but only with songs I actually own"**
178
+ * **"Analyze my recently played songs, find similar tracks in my library I haven't played in 6 months, and create a rediscovery playlist"**
179
+ * **"Build me a playlist mixing my top 10 most played songs with similar tracks from artists I own but rarely listen to"**
180
+
181
+ ### 🔍 Collection Gap Analysis
182
+
183
+ * **"Show me which albums are missing from my top 5 most played artists and rank them by Last.fm popularity"**
184
+ * **"Find artists where I only own singles or compilations, not their main albums"**
185
+ * **"Identify my favorite genres, then show me highly-rated albums in those genres that I don't own"**
186
+ * **"Look at artists similar to my favorites and tell me which ones I already have in my library but never play"**
187
+
188
+ ### 📻 Radio Station Maintenance
189
+
190
+ * **"Go through all my existing radio stations, validate each one, and remove the broken ones"**
191
+ * **"Here are 10 jazz radio URLs I found online - test them all and add only the working ones with good bitrates"**
192
+ * **"Find the top-voted jazz and classical stations worldwide, validate them, and add the best 5 of each genre"**
193
+ * **"Check all my radio stations and replace any broken ones with similar working stations"**
194
+
195
+ ### 🎼 Advanced Playlist Automation
196
+
197
+ * **"Create a 'Hidden Gems' playlist with 5-star rated songs that have less than 5 plays"**
198
+ * **"Build weekly playlists based on what I listened to most each month of last year"**
199
+ * **"Make a 'Complete Artist Journey' playlist with one top track from each album of my top 10 artists, in chronological order"**
200
+ * **"Generate mood playlists by analyzing my listening patterns: what I play in mornings vs evenings vs weekends"**
201
+
202
+ ### 📊 Listening Insights & Organization
203
+
204
+ * **"Analyze my jazz collection: show play counts, ratings, and find which albums I've never fully listened to"**
205
+ * **"Find duplicate songs across different albums and create a cleanup list"**
206
+ * **"Show me my listening evolution: which genres I've moved away from and which I'm playing more"**
207
+ * **"Identify 'one-hit wonders' in my library - artists where I only play one song repeatedly"**
208
+
209
+ ## Available Tools
210
+
211
+ ### 🔧 Core System
212
+
213
+ | Tool | Description |
214
+ |------|-------------|
215
+ | `test_connection` | Verify Navidrome server connectivity and feature status |
216
+
217
+ ### 📚 Library Management
218
+
219
+ | Tool | Description |
220
+ |------|-------------|
221
+ | `list_songs` | Browse songs with filtering and sorting |
222
+ | `list_albums` | Browse albums with metadata |
223
+ | `list_artists` | Browse artists with statistics |
224
+ | `list_genres` | View all music genres in library |
225
+ | `get_song` | Detailed song information |
226
+ | `get_album` | Detailed album information |
227
+ | `get_artist` | Detailed artist information |
228
+
229
+ ### 🔍 Search & Discovery
230
+
231
+ | Tool | Description |
232
+ |------|-------------|
233
+ | `search_all` | Search across all content types |
234
+ | `search_songs` | Search for specific songs |
235
+ | `search_albums` | Search for albums |
236
+ | `search_artists` | Search for artists |
237
+ | `get_similar_artists` | Find similar artists (Last.fm) |
238
+ | `get_similar_tracks` | Find similar tracks (Last.fm) |
239
+ | `get_artist_info` | Artist biography and tags |
240
+ | `get_trending_music` | Global music trends |
241
+
242
+ ### 🎵 Playlist Operations
243
+
244
+ | Tool | Description |
245
+ |------|-------------|
246
+ | `list_playlists` | View all playlists |
247
+ | `create_playlist` | Create new playlist |
248
+ | `update_playlist` | Update playlist metadata |
249
+ | `delete_playlist` | Remove playlist |
250
+ | `get_playlist_tracks` | Get playlist contents |
251
+ | `add_tracks_to_playlist` | Add songs/albums/artists |
252
+ | `batch_add_tracks_to_playlist` | Batch add multiple sets of tracks |
253
+ | `remove_tracks_from_playlist` | Remove specific tracks |
254
+ | `reorder_playlist_track` | Rearrange track order |
255
+
256
+ ### ⭐ Ratings & Favorites
257
+
258
+ | Tool | Description |
259
+ |------|-------------|
260
+ | `star_item` | Mark as favorite |
261
+ | `unstar_item` | Remove from favorites |
262
+ | `set_rating` | Set 0-5 star rating |
263
+ | `list_starred_items` | View favorites |
264
+ | `list_top_rated` | View highest rated items |
265
+
266
+ ### 📊 Analytics & History
267
+
268
+ | Tool | Description |
269
+ |------|-------------|
270
+ | `list_recently_played` | View recent listening activity |
271
+ | `list_most_played` | Find most played content |
272
+ | `get_queue` | View playback queue |
273
+ | `set_queue` | Set playback queue |
274
+
275
+ ### 📻 Radio Management
276
+
277
+ | Tool | Description |
278
+ |------|-------------|
279
+ | `validate_radio_stream` | Test stream URL validity |
280
+ | `list_radio_stations` | View all stations |
281
+ | `create_radio_station` | Add new station (with optional validation*) |
282
+ | `batch_create_radio_stations` | Batch add multiple stations (with optional validation*) |
283
+ | `play_radio_station` | Start radio playback |
284
+ | `discover_radio_stations` | Find internet radio stations globally |
285
+ | `get_radio_filters` | Get available search filters (genres, countries, etc.) |
286
+ | `get_station_by_uuid` | Get detailed station information |
287
+ | `click_station` | Register play click for popularity metrics |
288
+ | `vote_station` | Vote for a radio station |
289
+
290
+ *Note: Both `create_radio_station` and `batch_create_radio_stations` support an optional `validateBeforeAdd` parameter that will test stream URLs before adding them to Navidrome.
291
+
292
+ ### 🎤 Lyrics & Timestamps
293
+
294
+ | Tool | Description |
295
+ |------|-------------|
296
+ | `get_lyrics` | Get synchronized and plain text lyrics |
297
+
298
+ ### 🏷️ Metadata & Tags
299
+
300
+ | Tool | Description |
301
+ |------|-------------|
302
+ | `list_tags` | Browse all metadata tags |
303
+ | `search_by_tags` | Search by specific tags |
304
+ | `get_tag_distribution` | Analyze tag usage |
305
+
306
+ ## Troubleshooting
307
+
308
+ ### Common Issues
309
+
310
+ **Connection Problems**
311
+ - Verify Navidrome server is running
312
+ - Check URL includes protocol (http:// or https://)
313
+ - Ensure credentials are correct
314
+ - Test with `curl` or browser first
315
+
316
+ **macOS Specific**
317
+ - See [macOS Troubleshooting Guide](docs/MACOS_TROUBLESHOOTING.md)
318
+ - Common issue: Node.js path not found
319
+ - Solution: Create symlinks or use full paths
320
+
321
+ **Configuration Issues**
322
+ - Use absolute paths in config files
323
+ - Validate JSON syntax (no trailing commas)
324
+ - Check environment variables are set
325
+ - Restart client after changes
326
+
327
+ ### Limitations
328
+
329
+ **Playback Control**: This MCP server manages your library and queues but doesn't directly control playback. Use your Navidrome client app for play/pause/skip.
330
+
331
+ **Recently Played**: Navidrome doesn't provide last-played timestamps, only play counts and completion status.
332
+
333
+ **Queue Management**: Works with Subsonic-compatible clients that support jukebox mode.
334
+
335
+ ## Development
336
+
337
+ ### Setup for Contributors
338
+
339
+ ```bash
340
+ # Clone and setup
341
+ git clone https://github.com/Blakeem/Navidrome-MCP.git
342
+ cd navidrome-mcp
343
+ cp .env.example .env
344
+ # Edit .env with your credentials
345
+
346
+ # Development
347
+ pnpm dev # Hot reload mode
348
+ pnpm test # Run tests
349
+ pnpm lint # Check code style
350
+ pnpm typecheck # Type checking
351
+ ```
352
+
353
+ ### Testing with MCP Inspector
354
+
355
+ ```bash
356
+ # Build first
357
+ pnpm build
358
+
359
+ # Web UI testing
360
+ npx @modelcontextprotocol/inspector node dist/index.js
361
+
362
+ # CLI testing
363
+ npx @modelcontextprotocol/inspector --cli node dist/index.js \
364
+ --method tools/call \
365
+ --tool-name search_all \
366
+ --tool-arg query="jazz"
367
+ ```
368
+
369
+ ### Project Structure
370
+
371
+ ```
372
+ navidrome-mcp/
373
+ ├── src/ # TypeScript source
374
+ ├── dist/ # Compiled JavaScript
375
+ ├── docs/ # Documentation
376
+ ├── tests/ # Test suites
377
+ └── CLAUDE.md # AI assistant instructions
378
+ ```
379
+
380
+ ## License
381
+
382
+ ### Code: AGPL-3.0
383
+
384
+ Source code is licensed under GNU Affero General Public License v3.0. See [LICENSE](LICENSE).
385
+
386
+ ### Documentation: CC-BY-SA-4.0
387
+
388
+ Documentation is licensed under Creative Commons Attribution-ShareAlike 4.0 International.
389
+
390
+ ## Support
391
+
392
+ - **Issues**: [GitHub Issues](https://github.com/Blakeem/Navidrome-MCP/issues)
393
+ - **Discussions**: [GitHub Discussions](https://github.com/Blakeem/Navidrome-MCP/discussions)
394
+
395
+ ---
396
+
397
+ **Built with ❤️ for the Navidrome community**
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Navidrome MCP Server - Capabilities Configuration
3
+ * Copyright (C) 2025
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published
7
+ * by the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ import type { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
19
+ export declare const MCP_CAPABILITIES: ServerCapabilities;
20
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,eAAO,MAAM,gBAAgB,EAAE,kBAM9B,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Navidrome MCP Server - Capabilities Configuration
3
+ * Copyright (C) 2025
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published
7
+ * by the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ export const MCP_CAPABILITIES = {
19
+ tools: {},
20
+ resources: {
21
+ subscribe: false,
22
+ listChanged: false,
23
+ },
24
+ };
25
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,CAAC,MAAM,gBAAgB,GAAuB;IAClD,KAAK,EAAE,EAAE;IACT,SAAS,EAAE;QACT,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,KAAK;KACnB;CACF,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Navidrome MCP Server - Authentication Manager
3
+ * Copyright (C) 2025
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published
7
+ * by the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ import type { Config } from '../config.js';
19
+ export declare class AuthManager {
20
+ private token;
21
+ private tokenExpiry;
22
+ private config;
23
+ constructor(config: Config);
24
+ authenticate(): Promise<void>;
25
+ getToken(): Promise<string>;
26
+ }
27
+ //# sourceMappingURL=auth-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../../src/client/auth-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAIpB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB7B,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;CAWlC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Navidrome MCP Server - Authentication Manager
3
+ * Copyright (C) 2025
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published
7
+ * by the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ import { logger } from '../utils/logger.js';
19
+ import { ErrorFormatter } from '../utils/error-formatter.js';
20
+ export class AuthManager {
21
+ token = null;
22
+ tokenExpiry = null;
23
+ config;
24
+ constructor(config) {
25
+ this.config = config;
26
+ }
27
+ async authenticate() {
28
+ const response = await fetch(`${this.config.navidromeUrl}/auth/login`, {
29
+ method: 'POST',
30
+ headers: { 'Content-Type': 'application/json' },
31
+ body: JSON.stringify({
32
+ username: this.config.navidromeUsername,
33
+ password: this.config.navidromePassword,
34
+ }),
35
+ });
36
+ if (!response.ok) {
37
+ throw new Error(ErrorFormatter.authentication(`${response.status} ${response.statusText}`));
38
+ }
39
+ const data = (await response.json());
40
+ this.token = data.token;
41
+ this.tokenExpiry = new Date(Date.now() + this.config.tokenExpiry * 1000); // Convert seconds to milliseconds
42
+ logger.debug('Authentication successful');
43
+ }
44
+ async getToken() {
45
+ if (!this.token || !this.tokenExpiry || this.tokenExpiry <= new Date()) {
46
+ await this.authenticate();
47
+ }
48
+ if (!this.token) {
49
+ throw new Error(ErrorFormatter.authentication('token not available after authentication'));
50
+ }
51
+ return this.token;
52
+ }
53
+ }
54
+ //# sourceMappingURL=auth-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../../src/client/auth-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,MAAM,OAAO,WAAW;IACd,KAAK,GAAkB,IAAI,CAAC;IAC5B,WAAW,GAAgB,IAAI,CAAC;IAChC,MAAM,CAAS;IAEvB,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,aAAa,EAAE;YACrE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB;gBACvC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB;aACxC,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC9F,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,kCAAkC;QAC5G,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;YACvE,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,0CAA0C,CAAC,CAAC,CAAC;QAC7F,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Navidrome MCP Server - API Client
3
+ * Copyright (C) 2025
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published
7
+ * by the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ import type { Config } from '../config.js';
19
+ export declare class NavidromeClient {
20
+ private authManager;
21
+ private baseUrl;
22
+ private config;
23
+ constructor(config: Config);
24
+ initialize(): Promise<void>;
25
+ request<T>(endpoint: string, options?: RequestInit): Promise<T>;
26
+ subsonicRequest(endpoint: string, params?: Record<string, string>): Promise<unknown>;
27
+ }
28
+ //# sourceMappingURL=navidrome-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navidrome-client.d.ts","sourceRoot":"","sources":["../../src/client/navidrome-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAK3C,qBAAa,eAAe;IAC1B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAMpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAkCnE,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAyB/F"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Navidrome MCP Server - API Client
3
+ * Copyright (C) 2025
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published
7
+ * by the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ import { AuthManager } from './auth-manager.js';
19
+ import { logger } from '../utils/logger.js';
20
+ import { ErrorFormatter } from '../utils/error-formatter.js';
21
+ export class NavidromeClient {
22
+ authManager;
23
+ baseUrl;
24
+ config;
25
+ constructor(config) {
26
+ this.baseUrl = config.navidromeUrl;
27
+ this.authManager = new AuthManager(config);
28
+ this.config = config;
29
+ }
30
+ async initialize() {
31
+ await this.authManager.authenticate();
32
+ logger.info('Navidrome client initialized');
33
+ }
34
+ async request(endpoint, options = {}) {
35
+ const token = await this.authManager.getToken();
36
+ const defaultHeaders = {
37
+ 'X-ND-Authorization': `Bearer ${token}`,
38
+ };
39
+ // Only set Content-Type for non-GET requests
40
+ if (options.method && options.method !== 'GET') {
41
+ defaultHeaders['Content-Type'] = 'application/json';
42
+ }
43
+ const response = await fetch(`${this.baseUrl}/api${endpoint}`, {
44
+ ...options,
45
+ headers: {
46
+ ...defaultHeaders,
47
+ ...options.headers,
48
+ },
49
+ });
50
+ if (!response.ok) {
51
+ const errorText = await response.text();
52
+ throw new Error(ErrorFormatter.httpRequest('navidrome API', response, errorText));
53
+ }
54
+ // Handle different content types
55
+ const contentType = response.headers.get('content-type');
56
+ if (contentType && contentType.includes('application/json')) {
57
+ return response.json();
58
+ }
59
+ else {
60
+ return response.text();
61
+ }
62
+ }
63
+ async subsonicRequest(endpoint, params = {}) {
64
+ // Build Subsonic REST API parameters
65
+ const queryParams = new URLSearchParams({
66
+ u: this.config.navidromeUsername,
67
+ p: this.config.navidromePassword,
68
+ v: '1.16.1',
69
+ c: 'navidrome-mcp',
70
+ f: 'json',
71
+ ...params,
72
+ });
73
+ const response = await fetch(`${this.baseUrl}/rest${endpoint}?${queryParams}`);
74
+ if (!response.ok) {
75
+ throw new Error(ErrorFormatter.subsonicApi(response));
76
+ }
77
+ const data = await response.json();
78
+ if (data['subsonic-response']?.status !== 'ok') {
79
+ throw new Error(ErrorFormatter.subsonicResponse(data['subsonic-response']?.error?.message));
80
+ }
81
+ return data['subsonic-response'];
82
+ }
83
+ }
84
+ //# sourceMappingURL=navidrome-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navidrome-client.js","sourceRoot":"","sources":["../../src/client/navidrome-client.ts"],"names":[],"mappings":"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"}