mcp-lyricscom 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 smeet666
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ This license covers the source code of this project only. Song lyrics retrieved
24
+ through this software are the property of their respective copyright holders and
25
+ are not covered by this license.
package/README.md ADDED
@@ -0,0 +1,353 @@
1
+ # mcp-lyricscom
2
+
3
+ [![npm](https://img.shields.io/npm/v/mcp-lyricscom.svg)](https://www.npmjs.com/package/mcp-lyricscom)
4
+ [![CI](https://github.com/smeet666/mcp-lyricscom/actions/workflows/ci.yml/badge.svg)](https://github.com/smeet666/mcp-lyricscom/actions/workflows/ci.yml)
5
+ [![license](https://img.shields.io/npm/l/mcp-lyricscom.svg)](./LICENSE)
6
+
7
+ An [MCP](https://modelcontextprotocol.io) server for [lyrics.com](https://www.lyrics.com).
8
+ Search songs by a word in their lyrics, by title, and read the full text.
9
+ **No API key, no account, no configuration.**
10
+
11
+ _(Version française plus bas / [French version below](#mcp-lyricscom-français))_
12
+
13
+ ---
14
+
15
+ ## Quickstart
16
+
17
+ **Claude Code**
18
+
19
+ ```bash
20
+ claude mcp add lyricscom -- npx -y mcp-lyricscom
21
+ ```
22
+
23
+ **Claude Desktop, Cursor, and any client using the standard config format**
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "lyricscom": {
29
+ "command": "npx",
30
+ "args": ["-y", "mcp-lyricscom"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ That is the whole setup. There is nothing to sign up for.
37
+
38
+ ## Tools
39
+
40
+ | Tool | What it does | Key parameters |
41
+ | --------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
42
+ | `search_lyrics` | Finds songs whose **lyrics contain a word or phrase**, with the matching line as an excerpt. | `query`, `limit`, `page`, `verify`, `include_excerpt` |
43
+ | `search_songs` | Finds songs **by title**, optionally narrowed by artist. | `title`, `artist`, `limit`, `page`, `match` |
44
+ | `get_lyrics` | Reads the **full lyrics** of one song, by id or URL. | `id`, `url`, `max_chars`, `offset`, `highlight` |
45
+
46
+ The two search tools return a lyrics.com `id` for every result; `get_lyrics` takes that id.
47
+ That is the intended chain: search, then read.
48
+
49
+ ### Things worth knowing
50
+
51
+ **Search really checks the lyrics.** lyrics.com's own search also returns title
52
+ matches and loose matches. `search_lyrics` filters them out locally, using a
53
+ word-boundary matcher, so a result is a song where the word genuinely appears.
54
+ `coup` does not match `beaucoup`, but `enfant` does match `enfants`. Set
55
+ `verify: "none"` to see the raw, unfiltered list.
56
+
57
+ **Pagination is yours to drive.** One call fetches one page (24 rows on
58
+ lyrics.com). The response carries `has_more` and `next_page`. Raising `limit`
59
+ does not fetch more pages, on purpose: chaining several fetches inside a single
60
+ tool call is the fastest way to get rate limited.
61
+
62
+ **Some songs have no lyrics.** A valid lyrics.com page can simply have no text
63
+ on file. That comes back as `status: "no_lyrics"` with a successful result, not
64
+ an error, so there is nothing to retry.
65
+
66
+ **Rate limiting is visible, not silent.** lyrics.com answers a throttled request
67
+ with an empty body rather than a normal error code. This server detects that and
68
+ returns an explicit `throttled` error telling you to wait and try again. It never
69
+ reports throttling as "no results found", which would be indistinguishable from a
70
+ genuine answer.
71
+
72
+ ## Configuration
73
+
74
+ Every variable is optional. Set them in the `env` block of your MCP client config.
75
+
76
+ | Variable | Default | Purpose |
77
+ | ----------------------------- | ------------------------------------- | ------------------------------------------------------------- |
78
+ | `LYRICSCOM_USER_AGENT` | `mcp-lyricscom/<version> (+repo url)` | User-Agent sent to lyrics.com. See below. |
79
+ | `LYRICSCOM_MIN_INTERVAL_MS` | `1100` | Minimum gap between requests. Raise it if you hit throttling. |
80
+ | `LYRICSCOM_TIMEOUT_MS` | `15000` | Per-request timeout. |
81
+ | `LYRICSCOM_MAX_RETRIES` | `3` | Retries on throttling and transient errors. |
82
+ | `LYRICSCOM_CACHE_TTL_MS` | `900000` | In-memory page cache lifetime (15 minutes). |
83
+ | `LYRICSCOM_CACHE_MAX_ENTRIES` | `200` | In-memory page cache size. |
84
+ | `LYRICSCOM_LOG_LEVEL` | `error` | `silent`, `error`, `info` or `debug`. Logs go to stderr. |
85
+
86
+ ```json
87
+ {
88
+ "mcpServers": {
89
+ "lyricscom": {
90
+ "command": "npx",
91
+ "args": ["-y", "mcp-lyricscom"],
92
+ "env": { "LYRICSCOM_MIN_INTERVAL_MS": "1500" }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### About the User-Agent
99
+
100
+ This server identifies itself honestly by default, naming the project and linking
101
+ to this repository. lyrics.com serves that fine today.
102
+
103
+ It does block some generic tool agents outright: a plain `curl/8.5.0` gets a 403.
104
+ If you ever see a `blocked_user_agent` error, set `LYRICSCOM_USER_AGENT` to a
105
+ value of your choosing. That override exists so you are not stuck, and what you
106
+ put in it is your call and your responsibility.
107
+
108
+ ## Troubleshooting
109
+
110
+ **"throttled" errors.** lyrics.com is rate limiting you. The server already
111
+ retries with backoff and slows itself down on its own, so seeing this error means
112
+ those retries were exhausted. Once the site starts throttling, the window
113
+ commonly lasts several minutes, not seconds: wait a minute or more before trying
114
+ again, and raise `LYRICSCOM_MIN_INTERVAL_MS` if it keeps happening. A `throttled`
115
+ error does not mean your query has no results.
116
+
117
+ **"blocked_user_agent" errors.** See the User-Agent section above.
118
+
119
+ **"parse_failure" errors.** lyrics.com changed its page layout and the server
120
+ could not read the response. Please [open an issue](https://github.com/smeet666/mcp-lyricscom/issues)
121
+ with the query you used. The server deliberately reports this loudly instead of
122
+ pretending it found nothing.
123
+
124
+ **Empty results.** If `raw_result_count` is greater than zero while `results` is
125
+ empty, lyrics.com did return rows but none of them actually contain your word.
126
+ Try `verify: "none"` to see them anyway.
127
+
128
+ ## How it works
129
+
130
+ There is no lyrics.com API. The server requests the same public pages you would
131
+ open in a browser and reads them with [cheerio](https://cheerio.js.org). It
132
+ fetches one page at a time, roughly one request per second, backs off when the
133
+ site pushes back, and keeps a small in-memory cache so repeated questions about
134
+ the same song do not hit the site again.
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ npm install
140
+ npm run build:fixtures # regenerate the HTML test fixtures
141
+ npm test # unit tests, no network
142
+ npm run typecheck
143
+ npm run build
144
+ LYRICSCOM_LIVE=1 npm run test:live # hits the real site, excluded from CI
145
+ npm run inspector # explore the tools in the MCP Inspector
146
+ ```
147
+
148
+ The fixtures are generated, not scraped: they reproduce lyrics.com's markup
149
+ structure with placeholder text, so the parser tests are deterministic and no
150
+ copyrighted lyrics live in this repository.
151
+
152
+ The scraping layer (`src/lyricscom`, `src/text`) does not import the MCP SDK and
153
+ is published separately as `mcp-lyricscom/client`, so it can be used as a plain
154
+ library.
155
+
156
+ ## Lyrics and copyright
157
+
158
+ Song lyrics are copyrighted works owned by their authors and publishers. This
159
+ project claims no rights over them.
160
+
161
+ This server is a client. It fetches the same public lyrics.com pages you could
162
+ open in a browser, on demand, one request at a time, in response to an explicit
163
+ request from you or your assistant. It does not crawl the site, does not build a
164
+ lyrics database, and does not write anything to disk. Pages are held in memory
165
+ for a few minutes so that repeated questions do not hit the site again.
166
+
167
+ Every result carries the artist, the title, and the source URL. If you display or
168
+ reuse anything this server returns, keep that attribution and link back to the
169
+ source page.
170
+
171
+ The server honours lyrics.com's robots.txt: none of the endpoints it uses are
172
+ disallowed there. It rate limits itself by default. Please do not lower
173
+ `LYRICSCOM_MIN_INTERVAL_MS` for bulk use.
174
+
175
+ This is an unofficial project, with no affiliation to, endorsement by, or
176
+ sponsorship from lyrics.com or STANDS4 Ltd. Use it in accordance with lyrics.com's
177
+ terms of service and the copyright law that applies to you.
178
+
179
+ ## License
180
+
181
+ MIT. See [LICENSE](./LICENSE). The license covers this source code only, not the
182
+ lyrics retrieved through it.
183
+
184
+ ---
185
+
186
+ <a name="mcp-lyricscom-français"></a>
187
+
188
+ # mcp-lyricscom (français)
189
+
190
+ Un serveur [MCP](https://modelcontextprotocol.io) pour [lyrics.com](https://www.lyrics.com).
191
+ Cherchez des chansons par un mot présent dans les paroles, par titre, et lisez le
192
+ texte complet. **Sans clé d'API, sans compte, sans configuration.**
193
+
194
+ ## Démarrage rapide
195
+
196
+ **Claude Code**
197
+
198
+ ```bash
199
+ claude mcp add lyricscom -- npx -y mcp-lyricscom
200
+ ```
201
+
202
+ **Claude Desktop, Cursor, et tout client utilisant le format de configuration standard**
203
+
204
+ ```json
205
+ {
206
+ "mcpServers": {
207
+ "lyricscom": {
208
+ "command": "npx",
209
+ "args": ["-y", "mcp-lyricscom"]
210
+ }
211
+ }
212
+ }
213
+ ```
214
+
215
+ C'est toute l'installation. Il n'y a aucune inscription.
216
+
217
+ ## Outils
218
+
219
+ | Outil | Rôle | Paramètres principaux |
220
+ | --------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
221
+ | `search_lyrics` | Trouve les chansons dont les **paroles contiennent un mot ou une phrase**, avec la ligne correspondante en extrait. | `query`, `limit`, `page`, `verify`, `include_excerpt` |
222
+ | `search_songs` | Trouve des chansons **par titre**, éventuellement restreintes à un artiste. | `title`, `artist`, `limit`, `page`, `match` |
223
+ | `get_lyrics` | Lit les **paroles complètes** d'une chanson, par id ou par URL. | `id`, `url`, `max_chars`, `offset`, `highlight` |
224
+
225
+ Les deux outils de recherche renvoient un `id` lyrics.com pour chaque résultat, et
226
+ `get_lyrics` prend cet id. C'est l'enchaînement prévu : chercher, puis lire.
227
+
228
+ ### Ce qu'il faut savoir
229
+
230
+ **La recherche vérifie vraiment les paroles.** La recherche de lyrics.com renvoie
231
+ aussi des correspondances de titre et des à-peu-près. `search_lyrics` les écarte
232
+ localement, avec un matcher qui respecte les frontières de mot : un résultat est
233
+ donc une chanson où le mot est réellement présent. `coup` ne matche pas
234
+ `beaucoup`, mais `enfant` matche bien `enfants`. Mettez `verify: "none"` pour voir
235
+ la liste brute.
236
+
237
+ **C'est vous qui paginez.** Un appel récupère une page (24 lignes chez
238
+ lyrics.com). La réponse porte `has_more` et `next_page`. Augmenter `limit` ne va
239
+ pas chercher plus de pages, volontairement : enchaîner plusieurs requêtes dans un
240
+ seul appel d'outil est le meilleur moyen de se faire limiter.
241
+
242
+ **Certaines chansons n'ont pas de paroles.** Une page lyrics.com valide peut
243
+ simplement n'avoir aucun texte enregistré. Cela revient en `status: "no_lyrics"`
244
+ avec un résultat réussi, pas une erreur : inutile de réessayer.
245
+
246
+ **La limitation de débit est visible, pas silencieuse.** lyrics.com répond à une
247
+ requête limitée par un corps vide plutôt que par un code d'erreur normal. Ce
248
+ serveur le détecte et renvoie une erreur `throttled` explicite, qui vous dit
249
+ d'attendre et de réessayer. Il ne présente jamais une limitation comme « aucun
250
+ résultat », ce qui serait indiscernable d'une vraie réponse.
251
+
252
+ ## Configuration
253
+
254
+ Toutes les variables sont optionnelles. Elles se déclarent dans le bloc `env` de
255
+ la configuration de votre client MCP.
256
+
257
+ | Variable | Défaut | Rôle |
258
+ | ----------------------------- | ----------------------------------------- | -------------------------------------------------------------------- |
259
+ | `LYRICSCOM_USER_AGENT` | `mcp-lyricscom/<version> (+url du dépôt)` | User-Agent envoyé à lyrics.com. Voir plus bas. |
260
+ | `LYRICSCOM_MIN_INTERVAL_MS` | `1100` | Écart minimal entre deux requêtes. À augmenter en cas de limitation. |
261
+ | `LYRICSCOM_TIMEOUT_MS` | `15000` | Délai d'attente par requête. |
262
+ | `LYRICSCOM_MAX_RETRIES` | `3` | Tentatives sur limitation et erreurs passagères. |
263
+ | `LYRICSCOM_CACHE_TTL_MS` | `900000` | Durée de vie du cache mémoire (15 minutes). |
264
+ | `LYRICSCOM_CACHE_MAX_ENTRIES` | `200` | Taille du cache mémoire. |
265
+ | `LYRICSCOM_LOG_LEVEL` | `error` | `silent`, `error`, `info` ou `debug`. Les logs vont sur stderr. |
266
+
267
+ ### À propos du User-Agent
268
+
269
+ Le serveur s'identifie honnêtement par défaut, en nommant le projet et en pointant
270
+ vers ce dépôt. lyrics.com l'accepte sans problème aujourd'hui.
271
+
272
+ Le site bloque en revanche certains agents d'outils génériques : un simple
273
+ `curl/8.5.0` reçoit un 403. Si vous voyez une erreur `blocked_user_agent`,
274
+ définissez `LYRICSCOM_USER_AGENT` à la valeur de votre choix. Cette surcharge
275
+ existe pour que vous ne restiez pas bloqué ; ce que vous y mettez relève de votre
276
+ décision et de votre responsabilité.
277
+
278
+ ## Dépannage
279
+
280
+ **Erreurs « throttled ».** lyrics.com vous limite. Le serveur réessaie déjà avec
281
+ backoff et ralentit tout seul : voir cette erreur signifie que ces tentatives ont
282
+ été épuisées. Une fois la limitation déclenchée, la fenêtre dure couramment
283
+ plusieurs minutes, pas quelques secondes : attendez une minute ou plus avant de
284
+ réessayer, et augmentez `LYRICSCOM_MIN_INTERVAL_MS` si cela persiste. Une erreur
285
+ `throttled` ne signifie pas que votre requête n'a pas de résultats.
286
+
287
+ **Erreurs « blocked_user_agent ».** Voir la section User-Agent ci-dessus.
288
+
289
+ **Erreurs « parse_failure ».** lyrics.com a changé la structure de ses pages et le
290
+ serveur n'a pas su lire la réponse. Merci d'[ouvrir une issue](https://github.com/smeet666/mcp-lyricscom/issues)
291
+ en indiquant la requête utilisée. Le serveur signale volontairement ce cas au lieu
292
+ de faire comme s'il n'avait rien trouvé.
293
+
294
+ **Résultats vides.** Si `raw_result_count` est supérieur à zéro alors que
295
+ `results` est vide, lyrics.com a bien renvoyé des lignes mais aucune ne contient
296
+ réellement votre mot. Essayez `verify: "none"` pour les voir quand même.
297
+
298
+ ## Fonctionnement
299
+
300
+ lyrics.com n'a pas d'API. Le serveur demande les mêmes pages publiques que celles
301
+ que vous ouvririez dans un navigateur et les lit avec
302
+ [cheerio](https://cheerio.js.org). Il récupère une page à la fois, à environ une
303
+ requête par seconde, ralentit quand le site le lui demande, et garde un petit
304
+ cache mémoire pour ne pas redemander deux fois la même page.
305
+
306
+ ## Développement
307
+
308
+ ```bash
309
+ npm install
310
+ npm run build:fixtures # régénère les fixtures HTML de test
311
+ npm test # tests unitaires, sans réseau
312
+ npm run typecheck
313
+ npm run build
314
+ LYRICSCOM_LIVE=1 npm run test:live # touche le vrai site, exclu de la CI
315
+ npm run inspector # explorer les outils dans le MCP Inspector
316
+ ```
317
+
318
+ Les fixtures sont générées, pas aspirées : elles reproduisent la structure du
319
+ markup de lyrics.com avec du texte de remplissage, ce qui rend les tests de
320
+ parsing déterministes et évite de stocker des paroles sous droits dans ce dépôt.
321
+
322
+ La couche de scraping (`src/lyricscom`, `src/text`) n'importe pas le SDK MCP et
323
+ est publiée séparément sous `mcp-lyricscom/client`, utilisable comme simple
324
+ bibliothèque.
325
+
326
+ ## Paroles et droits d'auteur
327
+
328
+ Les paroles de chansons sont des œuvres protégées, propriété de leurs auteurs et
329
+ éditeurs. Ce projet ne revendique aucun droit dessus.
330
+
331
+ Ce serveur est un client. Il va chercher les mêmes pages publiques de lyrics.com
332
+ que celles que vous pourriez ouvrir dans un navigateur, à la demande, une requête
333
+ à la fois, en réponse à une demande explicite de votre part ou de celle de votre
334
+ assistant. Il ne parcourt pas le site, ne constitue aucune base de paroles, et
335
+ n'écrit rien sur le disque. Les pages restent en mémoire quelques minutes pour ne
336
+ pas solliciter le site inutilement.
337
+
338
+ Chaque résultat porte l'artiste, le titre et l'URL source. Si vous affichez ou
339
+ réutilisez ce que renvoie ce serveur, conservez cette attribution et le lien vers
340
+ la page d'origine.
341
+
342
+ Le serveur respecte le robots.txt de lyrics.com : aucun des endpoints qu'il
343
+ utilise n'y est interdit. Il s'auto-limite par défaut. Merci de ne pas abaisser
344
+ `LYRICSCOM_MIN_INTERVAL_MS` pour un usage en masse.
345
+
346
+ Projet non officiel, sans affiliation à lyrics.com ni à STANDS4 Ltd, ni
347
+ approbation ou parrainage de leur part. Utilisez-le dans le respect des conditions
348
+ d'utilisation de lyrics.com et du droit d'auteur qui vous est applicable.
349
+
350
+ ## Licence
351
+
352
+ MIT, voir [LICENSE](./LICENSE). La licence couvre uniquement le code source, pas
353
+ les paroles récupérées par son intermédiaire.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node