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
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Navidrome MCP Server - Listening History Tools
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 { z } from 'zod';
19
+ import { logger } from '../utils/logger.js';
20
+ import { transformSongsToDTO, transformAlbumsToDTO, transformArtistsToDTO } from '../transformers/song-transformer.js';
21
+ import { DEFAULT_VALUES } from '../constants/defaults.js';
22
+ // Helper function to parse duration from MM:SS format to seconds
23
+ function parseDuration(durationFormatted) {
24
+ const parts = durationFormatted.split(':');
25
+ if (parts.length === 2) {
26
+ const minutes = parseInt(parts[0] || '0', 10);
27
+ const seconds = parseInt(parts[1] || '0', 10);
28
+ return minutes * 60 + seconds;
29
+ }
30
+ return 0;
31
+ }
32
+ const RecentlyPlayedSchema = z.object({
33
+ limit: z.number().min(1).max(500).optional().default(DEFAULT_VALUES.RECENTLY_PLAYED_LIMIT),
34
+ offset: z.number().min(0).optional().default(0),
35
+ timeRange: z.enum(['today', 'week', 'month', 'all']).optional().default('all'),
36
+ });
37
+ export async function listRecentlyPlayed(client, args) {
38
+ const { limit = 20, offset = 0, timeRange = 'all' } = RecentlyPlayedSchema.parse(args);
39
+ logger.info(`Getting recently played songs (${timeRange})`);
40
+ // For now, we'll get songs sorted by addedDate (when added to library) as a proxy for recently played
41
+ // The API doesn't appear to support playDate filtering reliably
42
+ const response = await client.request(`/song?_sort=addedDate&_order=DESC&_start=${offset}&_end=${offset + limit}`);
43
+ // Transform using proper transformer
44
+ const songs = transformSongsToDTO(response);
45
+ // Filter songs that have been played at least once, if available
46
+ const playedSongs = songs.filter(song => !song.playCount || song.playCount > 0);
47
+ const tracks = playedSongs.slice(0, limit).map((song) => {
48
+ const track = {
49
+ id: song.id,
50
+ title: song.title,
51
+ artist: song.artist,
52
+ album: song.album,
53
+ playCount: song.playCount || 0,
54
+ duration: parseDuration(song.durationFormatted),
55
+ // Note: Real lastPlayed timestamps are not available from the API
56
+ // Only including lastPlayed if we actually have play date data (which we don't currently)
57
+ };
58
+ // Only add lastPlayed if we have actual play date data
59
+ // Currently, the API doesn't provide reliable play date information
60
+ // so we omit this field rather than provide misleading data
61
+ return track;
62
+ });
63
+ return {
64
+ timeRange,
65
+ count: tracks.length,
66
+ tracks,
67
+ };
68
+ }
69
+ const MostPlayedSchema = z.object({
70
+ type: z.enum(['songs', 'albums', 'artists']).optional().default('songs'),
71
+ limit: z.number().min(1).max(500).optional().default(DEFAULT_VALUES.MOST_PLAYED_LIMIT),
72
+ offset: z.number().min(0).optional().default(0),
73
+ minPlayCount: z.number().min(1).optional().default(1),
74
+ });
75
+ export async function listMostPlayed(client, args) {
76
+ const { type = 'songs', limit = 20, offset = 0, minPlayCount = 1 } = MostPlayedSchema.parse(args);
77
+ logger.info(`Getting most played ${type} with minPlayCount: ${minPlayCount}`);
78
+ const endpoint = type === 'songs' ? '/song' : type === 'albums' ? '/album' : '/artist';
79
+ // Fetch more items to account for filtering by minPlayCount
80
+ // We'll fetch 3x the requested amount to ensure we have enough after filtering
81
+ const fetchLimit = limit * 3;
82
+ const response = await client.request(`${endpoint}?_sort=playCount&_order=DESC&_start=${offset}&_end=${offset + fetchLimit}`);
83
+ // Transform using the appropriate transformer
84
+ let transformedItems;
85
+ if (type === 'songs') {
86
+ const songs = transformSongsToDTO(response);
87
+ transformedItems = songs.map(song => ({
88
+ id: song.id,
89
+ title: song.title,
90
+ artist: song.artist,
91
+ album: song.album,
92
+ playCount: song.playCount || 0
93
+ }));
94
+ }
95
+ else if (type === 'albums') {
96
+ const albums = transformAlbumsToDTO(response);
97
+ transformedItems = albums.map(album => ({
98
+ id: album.id,
99
+ name: album.name,
100
+ artist: album.artist,
101
+ songCount: album.songCount,
102
+ playCount: album.playCount || 0
103
+ }));
104
+ }
105
+ else {
106
+ const artists = transformArtistsToDTO(response);
107
+ transformedItems = artists.map(artist => ({
108
+ id: artist.id,
109
+ name: artist.name,
110
+ albumCount: artist.albumCount,
111
+ songCount: artist.songCount,
112
+ playCount: artist.playCount || 0
113
+ }));
114
+ }
115
+ // Filter by minPlayCount and limit results
116
+ const filteredItems = transformedItems
117
+ .filter(item => (item.playCount || 0) >= minPlayCount)
118
+ .slice(0, limit);
119
+ return {
120
+ type,
121
+ minPlayCount,
122
+ count: filteredItems.length,
123
+ items: filteredItems,
124
+ };
125
+ }
126
+ //# sourceMappingURL=listening-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listening-history.js","sourceRoot":"","sources":["../../src/tools/listening-history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Navidrome MCP Server - Lyrics Tools
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 { z } from 'zod';
19
+ import type { LyricsDTO } from '../types/index.js';
20
+ import type { Config } from '../config.js';
21
+ /**
22
+ * Schema for getting lyrics
23
+ */
24
+ export declare const GetLyricsArgsSchema: z.ZodObject<{
25
+ title: z.ZodString;
26
+ artist: z.ZodString;
27
+ album: z.ZodOptional<z.ZodString>;
28
+ durationMs: z.ZodOptional<z.ZodNumber>;
29
+ id: z.ZodOptional<z.ZodString>;
30
+ }, z.core.$strip>;
31
+ /**
32
+ * Get lyrics for a song
33
+ */
34
+ export declare function getLyrics(config: Config, args: unknown): Promise<LyricsDTO>;
35
+ //# sourceMappingURL=lyrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lyrics.d.ts","sourceRoot":"","sources":["../../src/tools/lyrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;iBAM9B,CAAC;AA4JH;;GAEG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAyEjF"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Navidrome MCP Server - Lyrics Tools
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 { z } from 'zod';
19
+ import { ErrorFormatter } from '../utils/error-formatter.js';
20
+ /**
21
+ * Schema for getting lyrics
22
+ */
23
+ export const GetLyricsArgsSchema = z.object({
24
+ title: z.string(),
25
+ artist: z.string(),
26
+ album: z.string().optional(),
27
+ durationMs: z.number().min(0).optional(),
28
+ id: z.string().optional()
29
+ });
30
+ /**
31
+ * Parse LRC format synced lyrics into structured format
32
+ */
33
+ function parseSyncedLyrics(lrcText) {
34
+ const lines = [];
35
+ const lrcRegex = /\[(\d{2}):(\d{2})\.(\d{2})\](.+)/g;
36
+ let match;
37
+ while ((match = lrcRegex.exec(lrcText)) !== null) {
38
+ const [, minutesStr, secondsStr, centisecondsStr, textStr] = match;
39
+ if (!minutesStr || !secondsStr || !centisecondsStr || !textStr)
40
+ continue;
41
+ const minutes = parseInt(minutesStr, 10);
42
+ const seconds = parseInt(secondsStr, 10);
43
+ const centiseconds = parseInt(centisecondsStr, 10);
44
+ const timeMs = (minutes * 60 + seconds) * 1000 + centiseconds * 10;
45
+ const text = textStr.trim();
46
+ if (text) {
47
+ lines.push({ timeMs, text });
48
+ }
49
+ }
50
+ return lines;
51
+ }
52
+ /**
53
+ * Try to get lyrics using exact match
54
+ */
55
+ async function tryExactMatch(params, config) {
56
+ try {
57
+ const url = new URL('/api/get', config.lrclibBase);
58
+ if (params.id) {
59
+ url.searchParams.set('id', params.id);
60
+ }
61
+ else {
62
+ url.searchParams.set('track_name', params.title);
63
+ url.searchParams.set('artist_name', params.artist);
64
+ if (params.album)
65
+ url.searchParams.set('album_name', params.album);
66
+ if (params.durationMs)
67
+ url.searchParams.set('duration', String(Math.round(params.durationMs / 1000)));
68
+ }
69
+ const response = await fetch(url.toString(), {
70
+ headers: {
71
+ 'User-Agent': config.lrclibUserAgent || 'Navidrome-MCP/1.0',
72
+ 'Accept': 'application/json'
73
+ }
74
+ });
75
+ if (response.status === 404) {
76
+ return null;
77
+ }
78
+ if (!response.ok) {
79
+ throw new Error(ErrorFormatter.httpRequest('LRCLIB API', response));
80
+ }
81
+ return await response.json();
82
+ }
83
+ catch {
84
+ // If exact match fails, return null to try search
85
+ return null;
86
+ }
87
+ }
88
+ /**
89
+ * Search for lyrics and find best match
90
+ */
91
+ async function searchLyrics(params, config) {
92
+ try {
93
+ const url = new URL('/api/search', config.lrclibBase);
94
+ url.searchParams.set('query', `${params.title} ${params.artist}`);
95
+ if (params.durationMs) {
96
+ url.searchParams.set('duration', String(Math.round(params.durationMs / 1000)));
97
+ }
98
+ const response = await fetch(url.toString(), {
99
+ headers: {
100
+ 'User-Agent': config.lrclibUserAgent || 'Navidrome-MCP/1.0',
101
+ 'Accept': 'application/json'
102
+ }
103
+ });
104
+ if (!response.ok) {
105
+ throw new Error(ErrorFormatter.httpRequest('LRCLIB search API', response));
106
+ }
107
+ const results = await response.json();
108
+ if (!results || results.length === 0) {
109
+ return null;
110
+ }
111
+ // Find best match
112
+ // 1. Prefer exact artist and title match
113
+ // 2. If duration provided, prefer within 3% tolerance
114
+ const titleLower = params.title.toLowerCase();
115
+ const artistLower = params.artist.toLowerCase();
116
+ const durationSec = params.durationMs ? params.durationMs / 1000 : null;
117
+ let bestMatch = null;
118
+ let bestScore = -1;
119
+ for (const result of results) {
120
+ let score = 0;
121
+ // Check title match
122
+ if (result.trackName?.toLowerCase() === titleLower) {
123
+ score += 10;
124
+ }
125
+ else if (result.trackName?.toLowerCase().includes(titleLower)) {
126
+ score += 5;
127
+ }
128
+ // Check artist match
129
+ if (result.artistName?.toLowerCase() === artistLower) {
130
+ score += 10;
131
+ }
132
+ else if (result.artistName?.toLowerCase().includes(artistLower)) {
133
+ score += 5;
134
+ }
135
+ // Check duration match (within 3% tolerance)
136
+ if (durationSec && result.duration) {
137
+ const tolerance = durationSec * 0.03;
138
+ const diff = Math.abs(result.duration - durationSec);
139
+ if (diff <= tolerance) {
140
+ score += 5;
141
+ }
142
+ }
143
+ if (score > bestScore) {
144
+ bestScore = score;
145
+ bestMatch = result;
146
+ }
147
+ }
148
+ return bestMatch;
149
+ }
150
+ catch {
151
+ return null;
152
+ }
153
+ }
154
+ /**
155
+ * Get lyrics for a song
156
+ */
157
+ export async function getLyrics(config, args) {
158
+ const params = GetLyricsArgsSchema.parse(args);
159
+ try {
160
+ // Try exact match first
161
+ let lyricsData = await tryExactMatch(params, config);
162
+ // If no exact match, try searching
163
+ if (!lyricsData) {
164
+ lyricsData = await searchLyrics(params, config);
165
+ }
166
+ // If still no match, return empty lyrics
167
+ if (!lyricsData) {
168
+ const result = {
169
+ track: {
170
+ title: params.title,
171
+ artist: params.artist,
172
+ ...(params.album ? { album: params.album } : {}),
173
+ ...(params.durationMs ? { durationMs: params.durationMs } : {})
174
+ },
175
+ isInstrumental: false,
176
+ provider: 'lrclib',
177
+ attribution: {
178
+ url: 'https://lrclib.net',
179
+ license: 'community-sourced'
180
+ }
181
+ };
182
+ return result;
183
+ }
184
+ // Parse synced lyrics if available
185
+ let syncedLines;
186
+ if (lyricsData.syncedLyrics) {
187
+ syncedLines = parseSyncedLyrics(lyricsData.syncedLyrics);
188
+ }
189
+ const result = {
190
+ track: {
191
+ title: lyricsData.trackName || params.title,
192
+ artist: lyricsData.artistName || params.artist
193
+ },
194
+ isInstrumental: Boolean(lyricsData.instrumental),
195
+ provider: 'lrclib',
196
+ attribution: {
197
+ url: 'https://lrclib.net',
198
+ license: 'community-sourced'
199
+ }
200
+ };
201
+ // Add optional fields only if they have values
202
+ const album = lyricsData.albumName || params.album;
203
+ if (album) {
204
+ result.track.album = album;
205
+ }
206
+ const durationMs = lyricsData.duration ? lyricsData.duration * 1000 : params.durationMs;
207
+ if (durationMs) {
208
+ result.track.durationMs = durationMs;
209
+ }
210
+ if (syncedLines && syncedLines.length > 0) {
211
+ result.synced = syncedLines;
212
+ }
213
+ if (lyricsData.plainLyrics) {
214
+ result.unsynced = lyricsData.plainLyrics;
215
+ }
216
+ return result;
217
+ }
218
+ catch (error) {
219
+ throw new Error(ErrorFormatter.toolExecution('getLyrics', error));
220
+ }
221
+ }
222
+ //# sourceMappingURL=lyrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lyrics.js","sourceRoot":"","sources":["../../src/tools/lyrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Navidrome MCP Server - Media Library Tools
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 { NavidromeClient } from '../client/navidrome-client.js';
19
+ import type { Config } from '../config.js';
20
+ import type { SongDTO, AlbumDTO, ArtistDTO, GenreDTO, PlaylistDTO } from '../types/index.js';
21
+ export declare function listAlbums(client: NavidromeClient, args: unknown): Promise<{
22
+ albums: AlbumDTO[];
23
+ total: number;
24
+ offset: number;
25
+ limit: number;
26
+ }>;
27
+ export declare function listArtists(client: NavidromeClient, args: unknown): Promise<{
28
+ artists: ArtistDTO[];
29
+ total: number;
30
+ offset: number;
31
+ limit: number;
32
+ }>;
33
+ export declare function listGenres(_client: NavidromeClient, config: Config, args: unknown): Promise<{
34
+ genres: GenreDTO[];
35
+ total: number;
36
+ offset: number;
37
+ limit: number;
38
+ }>;
39
+ export declare function getSong(client: NavidromeClient, args: unknown): Promise<SongDTO>;
40
+ export declare function getAlbum(client: NavidromeClient, args: unknown): Promise<AlbumDTO>;
41
+ export declare function getArtist(client: NavidromeClient, args: unknown): Promise<ArtistDTO>;
42
+ export declare function getSongPlaylists(client: NavidromeClient, args: unknown): Promise<{
43
+ playlists: PlaylistDTO[];
44
+ songId: string;
45
+ }>;
46
+ //# sourceMappingURL=media-library.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-library.d.ts","sourceRoot":"","sources":["../../src/tools/media-library.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAa3C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAU7F,wBAAsB,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAChF,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAyBD;AAGD,wBAAsB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACjF,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAyBD;AAoBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACjG,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAoDD;AAGD,wBAAsB,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAWtF;AAGD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAWxF;AAGD,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAW1F;AAGD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACtF,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CA6BD"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Navidrome MCP Server - Media Library Tools
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 crypto from 'crypto';
19
+ import { logger } from '../utils/logger.js';
20
+ import { transformAlbumsToDTO, transformArtistsToDTO, transformPlaylistsToDTO, transformToSongDTO, transformToAlbumDTO, transformToArtistDTO, } from '../transformers/song-transformer.js';
21
+ import { AlbumPaginationSchema, ArtistPaginationSchema, GenrePaginationSchema, IdSchema, GetSongPlaylistsSchema, } from '../schemas/index.js';
22
+ // List Albums
23
+ export async function listAlbums(client, args) {
24
+ const params = AlbumPaginationSchema.parse(args);
25
+ try {
26
+ const queryParams = new URLSearchParams({
27
+ _start: params.offset.toString(),
28
+ _end: (params.offset + params.limit).toString(),
29
+ _sort: params.sort,
30
+ _order: params.order,
31
+ });
32
+ const rawAlbums = await client.request(`/album?${queryParams.toString()}`);
33
+ const albums = transformAlbumsToDTO(rawAlbums);
34
+ return {
35
+ albums,
36
+ total: albums.length,
37
+ offset: params.offset,
38
+ limit: params.limit,
39
+ };
40
+ }
41
+ catch (error) {
42
+ throw new Error(`Failed to fetch albums: ${error instanceof Error ? error.message : 'Unknown error'}`);
43
+ }
44
+ }
45
+ // List Artists
46
+ export async function listArtists(client, args) {
47
+ const params = ArtistPaginationSchema.parse(args);
48
+ try {
49
+ const queryParams = new URLSearchParams({
50
+ _start: params.offset.toString(),
51
+ _end: (params.offset + params.limit).toString(),
52
+ _sort: params.sort,
53
+ _order: params.order,
54
+ });
55
+ const rawArtists = await client.request(`/artist?${queryParams.toString()}`);
56
+ const artists = transformArtistsToDTO(rawArtists);
57
+ return {
58
+ artists,
59
+ total: artists.length,
60
+ offset: params.offset,
61
+ limit: params.limit,
62
+ };
63
+ }
64
+ catch (error) {
65
+ throw new Error(`Failed to fetch artists: ${error instanceof Error ? error.message : 'Unknown error'}`);
66
+ }
67
+ }
68
+ /**
69
+ * Create Subsonic API authentication parameters for genres
70
+ */
71
+ function createSubsonicAuthForGenres(config) {
72
+ const salt = crypto.randomBytes(16).toString('hex');
73
+ const token = crypto.createHash('md5').update(config.navidromePassword + salt).digest('hex');
74
+ return new URLSearchParams({
75
+ u: config.navidromeUsername,
76
+ t: token,
77
+ s: salt,
78
+ v: '1.16.1',
79
+ c: 'NavidromeMCP',
80
+ f: 'json',
81
+ });
82
+ }
83
+ // List Genres
84
+ export async function listGenres(_client, config, args) {
85
+ const params = GenrePaginationSchema.parse(args);
86
+ try {
87
+ // Use direct fetch to Subsonic API (not through our client since it adds /api prefix)
88
+ const authParams = createSubsonicAuthForGenres(config);
89
+ const subsonicUrl = `${config.navidromeUrl}/rest/getGenres?${authParams.toString()}`;
90
+ const subsonicResponse = await fetch(subsonicUrl);
91
+ if (!subsonicResponse.ok) {
92
+ throw new Error(`Subsonic API request failed: ${subsonicResponse.status} ${subsonicResponse.statusText}`);
93
+ }
94
+ const response = await subsonicResponse.json();
95
+ // Extract genres from Subsonic response structure
96
+ const subsonicGenres = response?.['subsonic-response']?.genres?.genre || [];
97
+ // Transform Subsonic genre format to our DTO
98
+ const allGenres = subsonicGenres.map((genre) => ({
99
+ id: genre.value || '', // Subsonic uses 'value' for genre name as ID
100
+ name: genre.value || '',
101
+ songCount: genre.songCount || 0,
102
+ albumCount: genre.albumCount || 0,
103
+ }));
104
+ // Apply pagination manually since Subsonic getGenres doesn't support it
105
+ const startIndex = params.offset;
106
+ const endIndex = Math.min(startIndex + params.limit, allGenres.length);
107
+ const paginatedGenres = allGenres.slice(startIndex, endIndex);
108
+ return {
109
+ genres: paginatedGenres,
110
+ total: allGenres.length,
111
+ offset: params.offset,
112
+ limit: params.limit,
113
+ };
114
+ }
115
+ catch (error) {
116
+ throw new Error(`Failed to fetch genres: ${error instanceof Error ? error.message : 'Unknown error'}`);
117
+ }
118
+ }
119
+ // Get Song by ID
120
+ export async function getSong(client, args) {
121
+ const params = IdSchema.parse(args);
122
+ try {
123
+ const rawSong = await client.request(`/song/${params.id}`);
124
+ return transformToSongDTO(rawSong);
125
+ }
126
+ catch (error) {
127
+ throw new Error(`Failed to fetch song: ${error instanceof Error ? error.message : 'Unknown error'}`);
128
+ }
129
+ }
130
+ // Get Album by ID
131
+ export async function getAlbum(client, args) {
132
+ const params = IdSchema.parse(args);
133
+ try {
134
+ const rawAlbum = await client.request(`/album/${params.id}`);
135
+ return transformToAlbumDTO(rawAlbum);
136
+ }
137
+ catch (error) {
138
+ throw new Error(`Failed to fetch album: ${error instanceof Error ? error.message : 'Unknown error'}`);
139
+ }
140
+ }
141
+ // Get Artist by ID
142
+ export async function getArtist(client, args) {
143
+ const params = IdSchema.parse(args);
144
+ try {
145
+ const rawArtist = await client.request(`/artist/${params.id}`);
146
+ return transformToArtistDTO(rawArtist);
147
+ }
148
+ catch (error) {
149
+ throw new Error(`Failed to fetch artist: ${error instanceof Error ? error.message : 'Unknown error'}`);
150
+ }
151
+ }
152
+ // Get Playlists containing a song
153
+ export async function getSongPlaylists(client, args) {
154
+ const params = GetSongPlaylistsSchema.parse(args);
155
+ try {
156
+ const rawPlaylists = await client.request(`/song/${params.songId}/playlists`);
157
+ // Workaround: This specific endpoint returns JSON data but with text/plain content-type
158
+ // So we need to parse it manually if it's a string
159
+ let playlistData = rawPlaylists;
160
+ if (typeof rawPlaylists === 'string') {
161
+ try {
162
+ playlistData = JSON.parse(rawPlaylists);
163
+ }
164
+ catch (parseError) {
165
+ logger.error('Failed to parse playlist data:', parseError);
166
+ playlistData = [];
167
+ }
168
+ }
169
+ const playlists = transformPlaylistsToDTO(playlistData);
170
+ return {
171
+ playlists,
172
+ songId: params.songId,
173
+ };
174
+ }
175
+ catch (error) {
176
+ throw new Error(`Failed to fetch song playlists: ${error instanceof Error ? error.message : 'Unknown error'}`);
177
+ }
178
+ }
179
+ //# sourceMappingURL=media-library.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-library.js","sourceRoot":"","sources":["../../src/tools/media-library.ts"],"names":[],"mappings":"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"}