mcp-lyricscom 2.0.0 → 2.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.
package/README.md CHANGED
@@ -8,17 +8,23 @@
8
8
  [![M8ven](https://m8ven.ai/badge/mcp/smeet666-mcp-lyricscom-pptp4t?variant=verified)](https://m8ven.ai/mcp/smeet666-mcp-lyricscom-pptp4t)
9
9
  [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=lyricscom&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm1jcC1seXJpY3Njb20iXX0%3D)
10
10
  [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=lyricscom&config=%7B%22name%22%3A%22lyricscom%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22mcp-lyricscom%22%5D%7D)
11
+
11
12
  <!-- m8ven-verify: 3c4d434dcafaac3c25dc86631fb1393b -->
12
13
 
13
- An [MCP](https://modelcontextprotocol.io) server for [lyrics.com](https://www.lyrics.com).
14
- Search songs by a word in their lyrics, by title, and read the full text.
15
- **No API key, no account, no configuration.**
14
+ [lyrics.com](https://www.lyrics.com) is a large public catalogue of song lyrics.
15
+ It files a song under its title, its artist, the album it appeared on and the
16
+ year, and it holds the words themselves. Its search reaches inside those words.
17
+
18
+ This server connects a chat client to that catalogue. You can search for a song
19
+ by a line you remember, search by title and artist, and read the words of one
20
+ song, a slice at a time, with the words you were looking for located in the text.
21
+ It needs no API key and no account.
16
22
 
17
- _(Version française plus bas / [French version below](#mcp-lyricscom-français))_
23
+ _[Version française](#mcp-lyricscom-français)_
18
24
 
19
25
  ---
20
26
 
21
- ## Quickstart
27
+ ## Install
22
28
 
23
29
  **One-click install**
24
30
 
@@ -44,6 +50,8 @@ claude mcp add lyricscom -- npx -y mcp-lyricscom
44
50
  }
45
51
  ```
46
52
 
53
+ Node 24 or later is required, and no environment variable has to be set.
54
+
47
55
  ### With Docker
48
56
 
49
57
  ```json
@@ -51,204 +59,207 @@ claude mcp add lyricscom -- npx -y mcp-lyricscom
51
59
  "mcpServers": {
52
60
  "lyricscom": {
53
61
  "command": "docker",
54
- "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-lyricscom:2.0.0"]
62
+ "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-lyricscom:2.0.1"]
55
63
  }
56
64
  }
57
65
  }
58
66
  ```
59
67
 
60
- `-i` keeps stdin open, which is where the protocol travels, and no `-t` is
61
- passed: a TTY rewrites the stream and breaks it. The container needs outbound
62
- HTTPS to `www.lyrics.com`, and nothing else: no volume, no port, no environment variable, no credential.
63
-
64
- That is the whole setup. There is nothing to sign up for.
65
-
66
- **Bundle, without npm**
68
+ `-i` keeps stdin open, which is where the protocol travels, and `-t` is left out
69
+ because a TTY rewrites the stream. The container needs outbound HTTPS to
70
+ `www.lyrics.com`, and nothing else: no volume, no port, no credential.
67
71
 
68
- Download `mcp-lyricscom-<version>.mcpb` from
69
- [the latest release](https://github.com/smeet666/mcp-lyricscom/releases/latest) and open
70
- it. A client that supports MCP bundles installs it on its own, with no npm and
71
- no configuration file to edit. The bundle carries its dependencies, so nothing
72
- is fetched at install time.
72
+ ### Bundle, without npm
73
73
 
74
- ## Tools
75
-
76
- | Tool | What it does | Key parameters |
77
- | --------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
78
- | `search_lyrics` | Finds songs whose **lyrics contain a word or phrase**, with the matching line as an excerpt. | `query`, `limit`, `page`, `verify`, `include_excerpt` |
79
- | `search_songs` | Finds songs **by title**, optionally narrowed by artist. | `title`, `artist`, `limit`, `page`, `match` |
80
- | `get_lyrics` | Reads the **full lyrics** of one song, by id or URL. | `id`, `url`, `max_chars`, `offset`, `highlight` |
81
-
82
- The two search tools return a lyrics.com `id` for every result; `get_lyrics` takes that id.
83
- That is the intended chain: search, then read.
74
+ Download `mcp-lyricscom-2.0.1.mcpb` from
75
+ [the latest release](https://github.com/smeet666/mcp-lyricscom/releases/latest)
76
+ and open it. A client that supports MCP bundles installs it on its own, with no
77
+ npm and no configuration file to edit. The bundle carries its dependencies, so
78
+ nothing is fetched at install time.
84
79
 
85
- ### Things worth knowing
80
+ ## What you can ask
86
81
 
87
- **Search really checks the lyrics.** lyrics.com's own search also returns title
88
- matches and loose matches. `search_lyrics` filters them out locally, using a
89
- word-boundary matcher, so a result is a song where the word genuinely appears.
90
- `coup` does not match `beaucoup`, but `enfant` does match `enfants`. Set
91
- `verify: "none"` to see the raw, unfiltered list.
82
+ - "Which song goes 'I've got a hand for you'?"
83
+ - "Find me the lyrics of Wichita Lineman by Glen Campbell."
84
+ - "Read me the second half of those words."
85
+ - "Where does the word 'lineman' appear in that song?"
86
+ - "What albums is that song on?"
92
87
 
93
- **Pagination is yours to drive.** One call fetches one page (24 rows on
94
- lyrics.com). The response carries `has_more` and `next_page`. Raising `limit`
95
- does not fetch more pages, on purpose: chaining several fetches inside a single
96
- tool call is the fastest way to get rate limited.
88
+ The ordinary path runs from a search to a reading: a row carries an `id`, and
89
+ `get_lyrics` takes that id.
97
90
 
98
- **Some songs have no lyrics.** A valid lyrics.com page can simply have no text
99
- on file. That comes back as `status: "no_lyrics"` with a successful result, not
100
- an error, so there is nothing to retry.
91
+ ## Tools
101
92
 
102
- **Rate limiting is visible, not silent.** lyrics.com answers a throttled request
103
- with an empty body rather than a normal error code. This server detects that and
104
- returns an explicit `throttled` error telling you to wait and try again. It never
105
- reports throttling as "no results found", which would be indistinguishable from a
106
- genuine answer.
93
+ | Tool | What it does |
94
+ | --------------- | ----------------------------------------------- |
95
+ | `search_lyrics` | Finds a song from a line inside its words. |
96
+ | `search_songs` | Finds songs by title, narrowed by artist. |
97
+ | `get_lyrics` | Reads the words of one song, a slice at a time. |
98
+
99
+ ### `search_lyrics`
100
+
101
+ Finds a song from words inside its lyrics. The site ranks loosely, so a match is
102
+ checked before it is served.
103
+
104
+ | Argument | Type | Required | What it does |
105
+ | ----------------- | ---------------------------------------------- | -------- | --------------------------------------- |
106
+ | `query` | string, 1 to 120 characters | yes | The line, or part of it, to look for. |
107
+ | `limit` | integer, 1 to 50, default `10` | no | Rows to serve. |
108
+ | `page` | integer, 1 to 20, default `1` | no | Which page of rows. |
109
+ | `verify` | `snippet`, `full` or `none`, default `snippet` | no | How to confirm the words really appear. |
110
+ | `include_excerpt` | boolean, default `true` | no | Carry the matching line with each row. |
111
+
112
+ `verify` decides what a row is worth. `snippet` checks the excerpt the site
113
+ already returned and costs nothing. `full` fetches up to five song pages and
114
+ checks the complete words, which is slow and can bring on rate limiting. `none`
115
+ serves what the site ranked, unchecked.
116
+
117
+ **In return:** rows carrying `id`, which `get_lyrics` takes; `title`; `artist`;
118
+ `album` and `year`, `null` where the catalogue states none; `source_url`; and
119
+ `excerpt`, the matching line. `raw_result_count` is what the site returned and
120
+ `filtered_out` how many rows the check removed, so the two together say how loose
121
+ the ranking was. `has_more` and `next_page` continue.
122
+
123
+ ### `search_songs`
124
+
125
+ Finds songs by title, narrowed by artist.
126
+
127
+ | Argument | Type | Required | What it does |
128
+ | -------- | ------------------------------------ | -------- | --------------------------------------- |
129
+ | `title` | string, 1 to 120 characters | yes | The song title, or part of it. |
130
+ | `artist` | string, up to 120 characters | no | Keep the songs credited to this artist. |
131
+ | `limit` | integer, 1 to 50, default `10` | no | Rows to serve. |
132
+ | `page` | integer, 1 to 20, default `1` | no | Which page of rows. |
133
+ | `match` | `loose` or `strict`, default `loose` | no | How closely the artist has to match. |
134
+
135
+ **In return:** the rows `search_lyrics` returns, with `artist_filter` echoing
136
+ what was asked for and `filtered_out` counting what the artist restriction
137
+ removed. `strict` keeps the artists whose name matches as written; `loose`
138
+ accepts a name written differently.
139
+
140
+ ### `get_lyrics`
141
+
142
+ Reads the words of one song. Long lyrics are served a slice at a time.
143
+
144
+ | Argument | Type | Required | What it does |
145
+ | ----------- | ------------------------------------- | ---------- | ----------------------------------------- |
146
+ | `id` | string | one of two | The song id a search row carries. |
147
+ | `url` | a lyrics.com URL | one of two | The address of the song page. |
148
+ | `max_chars` | integer, 200 to 20000, default `6000` | no | Characters of text to serve in this call. |
149
+ | `offset` | integer, 0 or more, default `0` | no | Character offset to resume from. |
150
+ | `highlight` | string, up to 120 characters | no | Words to locate inside the text. |
151
+
152
+ **In return:** `status`, reading `ok` or `no_lyrics` for a page the site holds
153
+ without words; `title`, `artist` and `source_url`; and `lyrics`, the slice
154
+ itself. The reading is described by `total_chars`, `returned_chars`, `offset`,
155
+ `next_offset` and `truncated`: pass `next_offset` back to read on, and `null`
156
+ there means the end. `line_count` counts the lines of the slice, and `highlight`
157
+ answers for each word whether it was `found` and on which `line_number`, which is
158
+ `null` when it was not.
107
159
 
108
160
  ## Configuration
109
161
 
110
- Every variable is optional. Set them in the `env` block of your MCP client config.
111
-
112
- | Variable | Default | Purpose |
113
- | ----------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------- |
114
- | `LYRICSCOM_USER_AGENT` | `mcp-lyricscom/<version> (+repo url)` | User-Agent sent to lyrics.com. See below. |
115
- | `LYRICSCOM_MIN_INTERVAL_MS` | `1100` | Minimum gap between requests. Raise it if you hit throttling. Values below 500 ms are ignored, see below. |
116
- | `LYRICSCOM_TIMEOUT_MS` | `15000` | Per-request timeout. |
117
- | `LYRICSCOM_MAX_RETRIES` | `3` | Retries on throttling and transient errors. |
118
- | `LYRICSCOM_CACHE_TTL_MS` | `900000` | In-memory page cache lifetime (15 minutes). |
119
- | `LYRICSCOM_CACHE_MAX_ENTRIES` | `200` | In-memory page cache size. |
120
- | `LYRICSCOM_LOG_LEVEL` | `error` | `silent`, `error`, `info` or `debug`. Logs go to stderr. |
121
-
122
- ```json
123
- {
124
- "mcpServers": {
125
- "lyricscom": {
126
- "command": "npx",
127
- "args": ["-y", "mcp-lyricscom"],
128
- "env": { "LYRICSCOM_MIN_INTERVAL_MS": "1500" }
129
- }
130
- }
131
- }
162
+ Every variable is optional. Set them in the `env` block of your client config.
163
+
164
+ | Variable | Default | What it does |
165
+ | ----------------------------- | -------------------- | ---------------------------------------------------------------------------------- |
166
+ | `LYRICSCOM_USER_AGENT` | the project identity | Names your application to the site, with an address where a person can be reached. |
167
+ | `LYRICSCOM_MIN_INTERVAL_MS` | `1100` | Gap between two requests, from 500 to 60000. |
168
+ | `LYRICSCOM_TIMEOUT_MS` | `15000` | Deadline for one request, from 1000 to 120000. |
169
+ | `LYRICSCOM_MAX_RETRIES` | `3` | Attempts after a transient failure, from 0 to 10. |
170
+ | `LYRICSCOM_CACHE_TTL_MS` | `900000` | How long an answer stays in memory, from 0 to 86400000. |
171
+ | `LYRICSCOM_CACHE_MAX_ENTRIES` | `200` | Answers held in memory at once, from 0 to 10000. |
172
+ | `LYRICSCOM_LOG_LEVEL` | `error` | `silent`, `error`, `info` or `debug`, written to stderr. |
173
+
174
+ A value outside its range falls back to the default, and the reason is written to
175
+ stderr.
176
+
177
+ **On the User-Agent.** This server names the project and links to its repository,
178
+ and the site serves that. It does refuse some generic tool agents outright: a
179
+ plain `curl` gets a 403. A `blocked_user_agent` error means the identity was
180
+ refused, and `LYRICSCOM_USER_AGENT` lets you set one of your choosing. What you
181
+ put there is your call and your responsibility.
182
+
183
+ ## Errors
184
+
185
+ Every failure carries one of these codes, a message, and where it helps a hint
186
+ naming the next move.
187
+
188
+ | Code | What happened | What to do |
189
+ | -------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------- |
190
+ | `not_found` | The site answered, and holds no such song. | Check the id with `search_songs`. |
191
+ | `invalid_input` | The arguments were refused before any request went out. | Read the message, which names the argument. |
192
+ | `throttled` | The site asked this client to slow down. | Wait, then call again with the same arguments. The song is still there. |
193
+ | `blocked_user_agent` | The site refused the identity this client sent. | Set `LYRICSCOM_USER_AGENT`. |
194
+ | `parse_failure` | The page loaded and the expected content was absent. | Report it at [the issue tracker](https://github.com/smeet666/mcp-lyricscom/issues). |
195
+ | `network_error` | The request did not complete. | Try again shortly. |
196
+ | `timeout` | The request passed its deadline. | Raise `LYRICSCOM_TIMEOUT_MS`, or ask for a smaller `max_chars`. |
197
+
198
+ `throttled` and `blocked_user_agent` are this server's two names for a refusal to
199
+ serve, and a caller reading several sources normalises them onto whatever it
200
+ calls rate limiting.
201
+
202
+ ## As a library
203
+
204
+ The layer reading the site is published on its own, with its pacing, its cache
205
+ and its errors, and with no protocol attached.
206
+
207
+ ```ts
208
+ import { LyricsComClient } from "mcp-lyricscom/client";
209
+
210
+ const client = new LyricsComClient();
211
+ const { data, cached } = await client.getSong({ id: "1234567" });
212
+ console.log(data.title, data.artist, cached);
132
213
  ```
133
214
 
134
- ### Avec Docker
135
-
136
- ```json
137
- {
138
- "mcpServers": {
139
- "lyricscom": {
140
- "command": "docker",
141
- "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-lyricscom:2.0.0"]
142
- }
143
- }
144
- }
145
- ```
146
-
147
- `-i` garde l'entrée standard ouverte, qui est le canal du protocole, et aucun
148
- `-t` n'est passé : un terminal réécrit le flux et le casse. Le conteneur a besoin
149
- d'un accès HTTPS sortant vers `www.lyrics.com`, et de rien d'autre :
150
- aucun volume, aucun port, aucune variable d'environnement, aucun identifiant.
151
-
152
- ### About the User-Agent
153
-
154
- This server identifies itself honestly by default, naming the project and linking
155
- to this repository. lyrics.com serves that fine today.
215
+ `search` and `getSong` each answer `{ data, cached }`, and throw an error
216
+ carrying one of the codes above. The floor between two requests holds here as
217
+ well.
156
218
 
157
- It does block some generic tool agents outright: a plain `curl/8.5.0` gets a 403.
158
- If you ever see a `blocked_user_agent` error, set `LYRICSCOM_USER_AGENT` to a
159
- value of your choosing. That override exists so you are not stuck, and what you
160
- put in it is your call and your responsibility.
219
+ ## Pacing and attribution
161
220
 
162
- ## Troubleshooting
221
+ Requests go out one at a time with at least a second between them, and the floor
222
+ of half a second holds however the server is configured. A `verify: "full"`
223
+ search fetches up to five song pages, which is the most expensive thing this
224
+ server does.
163
225
 
164
- **"throttled" errors.** lyrics.com is rate limiting you. The server already
165
- retries with backoff and slows itself down on its own, so seeing this error means
166
- those retries were exhausted. Once the site starts throttling, the window
167
- commonly lasts several minutes, not seconds: wait a minute or more before trying
168
- again, and raise `LYRICSCOM_MIN_INTERVAL_MS` if it keeps happening. A `throttled`
169
- error does not mean your query has no results.
226
+ Every result carries the artist, the title and the address of the song page. Song
227
+ lyrics are the work of their authors and publishers. This project claims no
228
+ rights over them, ships no database of them, and writes nothing to disk.
170
229
 
171
- **"blocked_user_agent" errors.** See the User-Agent section above.
230
+ This MCP server is an unofficial project, with no affiliation to lyrics.com.
172
231
 
173
- **"parse_failure" errors.** lyrics.com changed its page layout and the server
174
- could not read the response. Please [open an issue](https://github.com/smeet666/mcp-lyricscom/issues)
175
- with the query you used. The server deliberately reports this loudly instead of
176
- pretending it found nothing.
232
+ ## Privacy
177
233
 
178
- **Empty results.** If `raw_result_count` is greater than zero while `results` is
179
- empty, lyrics.com did return rows but none of them actually contain your word.
180
- Try `verify: "none"` to see them anyway.
181
-
182
- ## How it works
183
-
184
- There is no lyrics.com API. The server requests the same public pages you would
185
- open in a browser and reads them with [cheerio](https://cheerio.js.org). It
186
- fetches one page at a time, roughly one request per second, backs off when the
187
- site pushes back, and keeps a small in-memory cache so repeated questions about
188
- the same song do not hit the site again.
234
+ This server collects nothing about you and sends nothing to its author. It runs
235
+ on your machine, contacts `www.lyrics.com` and nothing else, holds its answers in memory
236
+ while it runs, and writes nothing to disk.
237
+ [PRIVACY.md](PRIVACY.md) states what a request carries and which settings change
238
+ any of it.
189
239
 
190
240
  ## Development
191
241
 
192
242
  ```bash
193
243
  npm install
194
- npm run build:fixtures # regenerate the HTML test fixtures
195
- npm test # unit tests, no network
196
- npm run typecheck
197
- npm run build
198
- LYRICSCOM_LIVE=1 npm run test:live # hits the real site, excluded from CI
199
- npm run inspector # explore the tools in the MCP Inspector
244
+ npm run build:fixtures
245
+ npm test
246
+ npm run check
200
247
  ```
201
248
 
202
- The fixtures are generated, not scraped: they reproduce lyrics.com's markup
203
- structure with placeholder text, so the parser tests are deterministic and no
204
- copyrighted lyrics live in this repository.
205
-
206
- The scraping layer (`src/lyricscom`, `src/text`) does not import the MCP SDK and
207
- is published separately as `mcp-lyricscom/client`, so it can be used as a plain
208
- library.
209
-
210
- ## Lyrics and copyright
211
-
212
- Song lyrics are copyrighted works owned by their authors and publishers. This
213
- project claims no rights over them.
214
-
215
- This server is a client. It fetches the same public lyrics.com pages you could
216
- open in a browser, on demand, one request at a time, in response to an explicit
217
- request from you or your assistant. It does not crawl the site, does not build a
218
- lyrics database, and does not write anything to disk. Pages are held in memory
219
- for a few minutes so that repeated questions do not hit the site again.
220
-
221
- Every result carries the artist, the title, and the source URL. If you display or
222
- reuse anything this server returns, keep that attribution and link back to the
223
- source page.
224
-
225
- The server honours lyrics.com's robots.txt: none of the endpoints it uses are
226
- disallowed there, and it paces itself to roughly one request per second. That
227
- pacing is enforced: `LYRICSCOM_MIN_INTERVAL_MS` is refused below 500 ms, and a
228
- lower value falls back to the standard interval with a warning on stderr. Raise
229
- it to slow the client down further.
230
-
231
- This is an unofficial project, with no affiliation to, endorsement by, or
232
- sponsorship from lyrics.com or STANDS4 Ltd. Use it in accordance with lyrics.com's
233
- terms of service and the copyright law that applies to you.
249
+ Tests run against generated fixtures and make no network request. The live suite,
250
+ `npm run test:live`, makes one request per route and runs nightly against the
251
+ site itself.
234
252
 
235
253
  ## Contributing
236
254
 
237
- Bugs, questions and ideas all belong in
238
- [the issue tracker](https://github.com/smeet666/mcp-lyricscom/issues). Pull requests
239
- are welcome; please open an issue first so we can agree on what the right
240
- answer is before you write it. [CONTRIBUTING.md](CONTRIBUTING.md) has the
241
- detail, and [SECURITY.md](SECURITY.md) covers anything exploitable.
242
-
243
- ## Support
244
-
245
- These servers are free and stay free. If one of them saved you an afternoon,
246
- you can [buy me a coffee](https://buymeacoffee.com/smeet666).
255
+ Bugs, questions and ideas belong in
256
+ [the issue tracker](https://github.com/smeet666/mcp-lyricscom/issues). Pull
257
+ requests are welcome; opening an issue first helps agree on the shape of the
258
+ change. See [CONTRIBUTING.md](CONTRIBUTING.md).
247
259
 
248
260
  ## License
249
261
 
250
- MIT. See [LICENSE](./LICENSE). The license covers this source code only, not the
251
- lyrics retrieved through it.
262
+ MIT, see [LICENSE](LICENSE). The lyrics belong to their authors and publishers.
252
263
 
253
264
  ---
254
265
 
@@ -256,11 +267,19 @@ lyrics retrieved through it.
256
267
 
257
268
  # mcp-lyricscom (français)
258
269
 
259
- Un serveur [MCP](https://modelcontextprotocol.io) pour [lyrics.com](https://www.lyrics.com).
260
- Cherchez des chansons par un mot présent dans les paroles, par titre, et lisez le
261
- texte complet. **Sans clé d'API, sans compte, sans configuration.**
270
+ _[English version](#mcp-lyricscom)_
271
+
272
+ [lyrics.com](https://www.lyrics.com) est un grand catalogue public de paroles de
273
+ chansons. Il classe une chanson sous son titre, son artiste, l'album où elle a
274
+ paru et l'année, et il contient les paroles elles-mêmes. Sa recherche va à
275
+ l'intérieur de ces paroles.
276
+
277
+ Ce serveur relie un client de conversation à ce catalogue. On peut y chercher une
278
+ chanson par un vers dont on se souvient, chercher par titre et par artiste, et
279
+ lire les paroles d'une chanson par tranches, avec les mots cherchés localisés
280
+ dans le texte. Aucune clé d'API, aucun compte.
262
281
 
263
- ## Démarrage rapide
282
+ ## Installation
264
283
 
265
284
  **Installation en un clic**
266
285
 
@@ -273,7 +292,7 @@ texte complet. **Sans clé d'API, sans compte, sans configuration.**
273
292
  claude mcp add lyricscom -- npx -y mcp-lyricscom
274
293
  ```
275
294
 
276
- **Claude Desktop, Cursor, et tout client utilisant le format de configuration standard**
295
+ **Claude Desktop, Cursor, et tout client au format de configuration standard**
277
296
 
278
297
  ```json
279
298
  {
@@ -286,152 +305,219 @@ claude mcp add lyricscom -- npx -y mcp-lyricscom
286
305
  }
287
306
  ```
288
307
 
289
- C'est toute l'installation. Il n'y a aucune inscription.
308
+ Node 24 ou plus récent est nécessaire, et aucune variable d'environnement n'est à
309
+ renseigner.
290
310
 
291
- **Bundle, sans npm**
292
-
293
- Téléchargez `mcp-lyricscom-<version>.mcpb` depuis
294
- [la dernière release](https://github.com/smeet666/mcp-lyricscom/releases/latest) et
295
- ouvrez-le. Un client compatible avec les bundles MCP l'installe seul, sans npm
296
- ni fichier de configuration à modifier. Le bundle embarque ses dépendances,
297
- donc rien n'est téléchargé à l'installation.
298
-
299
- ## Outils
300
-
301
- | Outil | Rôle | Paramètres principaux |
302
- | --------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
303
- | `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` |
304
- | `search_songs` | Trouve des chansons **par titre**, éventuellement restreintes à un artiste. | `title`, `artist`, `limit`, `page`, `match` |
305
- | `get_lyrics` | Lit les **paroles complètes** d'une chanson, par id ou par URL. | `id`, `url`, `max_chars`, `offset`, `highlight` |
306
-
307
- Les deux outils de recherche renvoient un `id` lyrics.com pour chaque résultat, et
308
- `get_lyrics` prend cet id. C'est l'enchaînement prévu : chercher, puis lire.
309
-
310
- ### Ce qu'il faut savoir
311
-
312
- **La recherche vérifie vraiment les paroles.** La recherche de lyrics.com renvoie
313
- aussi des correspondances de titre et des à-peu-près. `search_lyrics` les écarte
314
- localement, avec un matcher qui respecte les frontières de mot : un résultat est
315
- donc une chanson où le mot est réellement présent. `coup` ne matche pas
316
- `beaucoup`, mais `enfant` matche bien `enfants`. Mettez `verify: "none"` pour voir
317
- la liste brute.
318
-
319
- **C'est vous qui paginez.** Un appel récupère une page (24 lignes chez
320
- lyrics.com). La réponse porte `has_more` et `next_page`. Augmenter `limit` ne va
321
- pas chercher plus de pages, volontairement : enchaîner plusieurs requêtes dans un
322
- seul appel d'outil est le meilleur moyen de se faire limiter.
311
+ ### Avec Docker
323
312
 
324
- **Certaines chansons n'ont pas de paroles.** Une page lyrics.com valide peut
325
- simplement n'avoir aucun texte enregistré. Cela revient en `status: "no_lyrics"`
326
- avec un résultat réussi, pas une erreur : inutile de réessayer.
313
+ ```json
314
+ {
315
+ "mcpServers": {
316
+ "lyricscom": {
317
+ "command": "docker",
318
+ "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-lyricscom:2.0.1"]
319
+ }
320
+ }
321
+ }
322
+ ```
327
323
 
328
- **La limitation de débit est visible, pas silencieuse.** lyrics.com répond à une
329
- requête limitée par un corps vide plutôt que par un code d'erreur normal. Ce
330
- serveur le détecte et renvoie une erreur `throttled` explicite, qui vous dit
331
- d'attendre et de réessayer. Il ne présente jamais une limitation comme « aucun
332
- résultat », ce qui serait indiscernable d'une vraie réponse.
324
+ `-i` garde l'entrée standard ouverte, qui est le canal du protocole, et `-t` est
325
+ omis parce qu'un TTY réécrit le flux. Le conteneur a besoin d'un accès HTTPS
326
+ sortant vers `www.lyrics.com`, et de rien d'autre : aucun volume, aucun port,
327
+ aucun identifiant.
328
+
329
+ ### Bundle, sans npm
330
+
331
+ Téléchargez `mcp-lyricscom-2.0.1.mcpb` depuis
332
+ [la dernière publication](https://github.com/smeet666/mcp-lyricscom/releases/latest)
333
+ et ouvrez-le. Un client qui gère les bundles MCP l'installe seul, sans npm et
334
+ sans fichier de configuration à modifier. Le bundle emporte ses dépendances, donc
335
+ rien n'est téléchargé à l'installation.
336
+
337
+ ## Ce qu'on peut demander
338
+
339
+ - « Quelle est la chanson qui dit "I've got a hand for you" ? »
340
+ - « Trouve-moi les paroles de Wichita Lineman par Glen Campbell. »
341
+ - « Lis-moi la seconde moitié de ces paroles. »
342
+ - « Où apparaît le mot "lineman" dans cette chanson ? »
343
+ - « Sur quels albums cette chanson figure-t-elle ? »
344
+
345
+ Le chemin ordinaire va d'une recherche à une lecture : une ligne porte un `id`,
346
+ et `get_lyrics` reprend cet identifiant.
347
+
348
+ ## Les outils
349
+
350
+ | Outil | Ce qu'il fait |
351
+ | --------------- | ------------------------------------------------------ |
352
+ | `search_lyrics` | Trouve une chanson à partir d'un vers de ses paroles. |
353
+ | `search_songs` | Trouve des chansons par titre, resserrées par artiste. |
354
+ | `get_lyrics` | Lit les paroles d'une chanson, par tranches. |
355
+
356
+ ### `search_lyrics`
357
+
358
+ Trouve une chanson à partir de mots contenus dans ses paroles. Le site classe
359
+ largement, donc une correspondance est vérifiée avant d'être servie.
360
+
361
+ | Argument | Type | Requis | Ce qu'il fait |
362
+ | ----------------- | --------------------------------------------- | ------ | ---------------------------------------------- |
363
+ | `query` | chaîne, 1 à 120 caractères | oui | Le vers, ou une partie, à chercher. |
364
+ | `limit` | entier, 1 à 50, défaut `10` | non | Lignes à servir. |
365
+ | `page` | entier, 1 à 20, défaut `1` | non | Quelle page de lignes. |
366
+ | `verify` | `snippet`, `full` ou `none`, défaut `snippet` | non | Comment confirmer que les mots y figurent. |
367
+ | `include_excerpt` | booléen, défaut `true` | non | Porter le vers correspondant sur chaque ligne. |
368
+
369
+ `verify` décide de ce que vaut une ligne. `snippet` vérifie l'extrait que le site
370
+ a déjà rendu et ne coûte rien. `full` va chercher jusqu'à cinq pages de chansons
371
+ et vérifie les paroles entières, ce qui est lent et peut déclencher une
372
+ limitation. `none` sert ce que le site a classé, sans vérification.
373
+
374
+ **En retour :** des lignes portant `id`, que `get_lyrics` reprend ; `title` ;
375
+ `artist` ; `album` et `year`, `null` là où le catalogue n'indique rien ;
376
+ `source_url` ; et `excerpt`, le vers correspondant. `raw_result_count` est ce que
377
+ le site a rendu et `filtered_out` le nombre de lignes que la vérification a
378
+ retirées, si bien que les deux ensemble disent à quel point le classement était
379
+ large. `has_more` et `next_page` poursuivent.
380
+
381
+ ### `search_songs`
382
+
383
+ Trouve des chansons par titre, resserrées par artiste.
384
+
385
+ | Argument | Type | Requis | Ce qu'il fait |
386
+ | -------- | ----------------------------------- | ------ | ---------------------------------------------- |
387
+ | `title` | chaîne, 1 à 120 caractères | oui | Le titre de la chanson, ou une partie. |
388
+ | `artist` | chaîne, jusqu'à 120 caractères | non | Ne garder que les chansons de cet artiste. |
389
+ | `limit` | entier, 1 à 50, défaut `10` | non | Lignes à servir. |
390
+ | `page` | entier, 1 à 20, défaut `1` | non | Quelle page de lignes. |
391
+ | `match` | `loose` ou `strict`, défaut `loose` | non | La rigueur de la correspondance sur l'artiste. |
392
+
393
+ **En retour :** les lignes que rend `search_lyrics`, avec `artist_filter` qui
394
+ redonne ce qui a été demandé et `filtered_out` qui compte ce que la restriction
395
+ sur l'artiste a retiré. `strict` garde les artistes dont le nom correspond tel
396
+ qu'écrit ; `loose` accepte un nom écrit autrement.
397
+
398
+ ### `get_lyrics`
399
+
400
+ Lit les paroles d'une chanson. Des paroles longues sont servies par tranches.
401
+
402
+ | Argument | Type | Requis | Ce qu'il fait |
403
+ | ----------- | ---------------------------------- | ------------- | -------------------------------------------- |
404
+ | `id` | chaîne | l'un des deux | L'identifiant que porte une ligne. |
405
+ | `url` | une adresse lyrics.com | l'un des deux | L'adresse de la page de la chanson. |
406
+ | `max_chars` | entier, 200 à 20000, défaut `6000` | non | Caractères de texte à servir dans cet appel. |
407
+ | `offset` | entier, 0 ou plus, défaut `0` | non | Position en caractères où reprendre. |
408
+ | `highlight` | chaîne, jusqu'à 120 caractères | non | Des mots à localiser dans le texte. |
409
+
410
+ **En retour :** `status`, valant `ok` ou `no_lyrics` pour une page que le site
411
+ contient sans paroles ; `title`, `artist` et `source_url` ; et `lyrics`, la
412
+ tranche elle-même. La lecture est décrite par `total_chars`, `returned_chars`,
413
+ `offset`, `next_offset` et `truncated` : redonnez `next_offset` pour poursuivre,
414
+ et `null` marque la fin. `line_count` compte les lignes de la tranche, et
415
+ `highlight` répond pour chaque mot s'il a été `found` et à quel `line_number`,
416
+ `null` quand il ne l'a pas été.
333
417
 
334
418
  ## Configuration
335
419
 
336
- Toutes les variables sont optionnelles. Elles se déclarent dans le bloc `env` de
337
- la configuration de votre client MCP.
338
-
339
- | Variable | Défaut | Rôle |
340
- | ----------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
341
- | `LYRICSCOM_USER_AGENT` | `mcp-lyricscom/<version> (+url du dépôt)` | User-Agent envoyé à lyrics.com. Voir plus bas. |
342
- | `LYRICSCOM_MIN_INTERVAL_MS` | `1100` | Écart minimal entre deux requêtes. À augmenter en cas de limitation. Une valeur sous 500 ms est ignorée, voir plus bas. |
343
- | `LYRICSCOM_TIMEOUT_MS` | `15000` | Délai d'attente par requête. |
344
- | `LYRICSCOM_MAX_RETRIES` | `3` | Tentatives sur limitation et erreurs passagères. |
345
- | `LYRICSCOM_CACHE_TTL_MS` | `900000` | Durée de vie du cache mémoire (15 minutes). |
346
- | `LYRICSCOM_CACHE_MAX_ENTRIES` | `200` | Taille du cache mémoire. |
347
- | `LYRICSCOM_LOG_LEVEL` | `error` | `silent`, `error`, `info` ou `debug`. Les logs vont sur stderr. |
348
-
349
- ### À propos du User-Agent
350
-
351
- Le serveur s'identifie honnêtement par défaut, en nommant le projet et en pointant
352
- vers ce dépôt. lyrics.com l'accepte sans problème aujourd'hui.
353
-
354
- Le site bloque en revanche certains agents d'outils génériques : un simple
355
- `curl/8.5.0` reçoit un 403. Si vous voyez une erreur `blocked_user_agent`,
356
- définissez `LYRICSCOM_USER_AGENT` à la valeur de votre choix. Cette surcharge
357
- existe pour que vous ne restiez pas bloqué ; ce que vous y mettez relève de votre
358
- décision et de votre responsabilité.
420
+ Chaque variable est facultative. Elles se posent dans le bloc `env` de la
421
+ configuration du client.
422
+
423
+ | Variable | Défaut | Ce qu'elle fait |
424
+ | ----------------------------- | -------------------- | --------------------------------------------------------------------------------- |
425
+ | `LYRICSCOM_USER_AGENT` | l'identité du projet | Nomme votre application auprès du site, avec une adresse où joindre une personne. |
426
+ | `LYRICSCOM_MIN_INTERVAL_MS` | `1100` | Écart entre deux requêtes, de 500 à 60000. |
427
+ | `LYRICSCOM_TIMEOUT_MS` | `15000` | Délai d'une requête, de 1000 à 120000. |
428
+ | `LYRICSCOM_MAX_RETRIES` | `3` | Tentatives après un échec passager, de 0 à 10. |
429
+ | `LYRICSCOM_CACHE_TTL_MS` | `900000` | Durée pendant laquelle une réponse reste en mémoire, de 0 à 86400000. |
430
+ | `LYRICSCOM_CACHE_MAX_ENTRIES` | `200` | Réponses gardées en mémoire à la fois, de 0 à 10000. |
431
+ | `LYRICSCOM_LOG_LEVEL` | `error` | `silent`, `error`, `info` ou `debug`, écrit sur la sortie d'erreur. |
432
+
433
+ Une valeur hors de sa plage retombe sur le défaut, et la raison est écrite sur la
434
+ sortie d'erreur.
435
+
436
+ **À propos du User-Agent.** Ce serveur nomme le projet et renvoie vers son dépôt,
437
+ et le site le sert. Il refuse en revanche certains agents d'outils génériques :
438
+ un `curl` nu reçoit un 403. Une erreur `blocked_user_agent` signifie que
439
+ l'identité envoyée a été refusée, et `LYRICSCOM_USER_AGENT` permet d'en poser une
440
+ de votre choix. Ce que vous y mettez relève de votre décision et de votre
441
+ responsabilité.
442
+
443
+ ## Erreurs
444
+
445
+ Chaque échec porte un de ces codes, un message, et quand cela aide une indication
446
+ du geste suivant.
447
+
448
+ | Code | Ce qui s'est passé | Que faire |
449
+ | -------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------- |
450
+ | `not_found` | Le site a répondu, et n'a pas cette chanson. | Vérifiez l'identifiant avec `search_songs`. |
451
+ | `invalid_input` | Les arguments ont été refusés avant toute requête. | Lisez le message, qui nomme l'argument. |
452
+ | `throttled` | Le site demande à ce client de ralentir. | Attendez, puis rappelez avec les mêmes arguments. La chanson est toujours là. |
453
+ | `blocked_user_agent` | Le site a refusé l'identité envoyée par ce client. | Posez `LYRICSCOM_USER_AGENT`. |
454
+ | `parse_failure` | La page a chargé et le contenu attendu est absent. | Signalez-le sur [le suivi d'incidents](https://github.com/smeet666/mcp-lyricscom/issues). |
455
+ | `network_error` | La requête n'a pas abouti. | Réessayez sous peu. |
456
+ | `timeout` | La requête a dépassé son délai. | Augmentez `LYRICSCOM_TIMEOUT_MS`, ou demandez un `max_chars` plus petit. |
457
+
458
+ `throttled` et `blocked_user_agent` sont les deux noms que ce serveur donne à un
459
+ refus de servir, et un appelant qui lit plusieurs sources les ramène sur ce qu'il
460
+ appelle une limitation de débit.
461
+
462
+ ## Comme bibliothèque
463
+
464
+ La couche qui lit le site est publiée seule, avec son rythme, son cache et ses
465
+ erreurs, sans protocole attaché.
466
+
467
+ ```ts
468
+ import { LyricsComClient } from "mcp-lyricscom/client";
469
+
470
+ const client = new LyricsComClient();
471
+ const { data, cached } = await client.getSong({ id: "1234567" });
472
+ console.log(data.title, data.artist, cached);
473
+ ```
359
474
 
360
- ## Dépannage
475
+ `search` et `getSong` répondent chacun `{ data, cached }`, et lèvent une erreur
476
+ portant un des codes ci-dessus. Le plancher entre deux requêtes tient également
477
+ ici.
361
478
 
362
- **Erreurs « throttled ».** lyrics.com vous limite. Le serveur réessaie déjà avec
363
- backoff et ralentit tout seul : voir cette erreur signifie que ces tentatives ont
364
- été épuisées. Une fois la limitation déclenchée, la fenêtre dure couramment
365
- plusieurs minutes, pas quelques secondes : attendez une minute ou plus avant de
366
- réessayer, et augmentez `LYRICSCOM_MIN_INTERVAL_MS` si cela persiste. Une erreur
367
- `throttled` ne signifie pas que votre requête n'a pas de résultats.
479
+ ## Rythme et attribution
368
480
 
369
- **Erreurs « blocked_user_agent ».** Voir la section User-Agent ci-dessus.
481
+ Les requêtes partent une à une avec au moins une seconde entre elles, et le
482
+ plancher d'une demi-seconde tient quelle que soit la configuration. Une recherche
483
+ en `verify: "full"` va chercher jusqu'à cinq pages de chansons, ce qui est la
484
+ chose la plus coûteuse que fait ce serveur.
370
485
 
371
- **Erreurs « parse_failure ».** lyrics.com a changé la structure de ses pages et le
372
- serveur n'a pas su lire la réponse. Merci d'[ouvrir une issue](https://github.com/smeet666/mcp-lyricscom/issues)
373
- en indiquant la requête utilisée. Le serveur signale volontairement ce cas au lieu
374
- de faire comme s'il n'avait rien trouvé.
486
+ Chaque résultat porte l'artiste, le titre et l'adresse de la page de la chanson.
487
+ Les paroles sont l'œuvre de leurs auteurs et de leurs éditeurs. Ce projet ne
488
+ revendique aucun droit dessus, n'embarque aucune base de paroles et n'écrit rien
489
+ sur le disque.
375
490
 
376
- **Résultats vides.** Si `raw_result_count` est supérieur à zéro alors que
377
- `results` est vide, lyrics.com a bien renvoyé des lignes mais aucune ne contient
378
- réellement votre mot. Essayez `verify: "none"` pour les voir quand même.
491
+ Ce MCP est un projet non officiel, sans affiliation à lyrics.com.
379
492
 
380
- ## Fonctionnement
493
+ ## Confidentialité
381
494
 
382
- lyrics.com n'a pas d'API. Le serveur demande les mêmes pages publiques que celles
383
- que vous ouvririez dans un navigateur et les lit avec
384
- [cheerio](https://cheerio.js.org). Il récupère une page à la fois, à environ une
385
- requête par seconde, ralentit quand le site le lui demande, et garde un petit
386
- cache mémoire pour ne pas redemander deux fois la même page.
495
+ Ce serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur
496
+ votre machine, ne joint que `www.lyrics.com`, garde ses réponses en mémoire le temps qu'il
497
+ tourne, et n'écrit rien sur le disque. [PRIVACY.md](PRIVACY.md) dit ce qu'une
498
+ requête emporte et quels réglages changent cela.
387
499
 
388
500
  ## Développement
389
501
 
390
502
  ```bash
391
503
  npm install
392
- npm run build:fixtures # régénère les fixtures HTML de test
393
- npm test # tests unitaires, sans réseau
394
- npm run typecheck
395
- npm run build
396
- LYRICSCOM_LIVE=1 npm run test:live # touche le vrai site, exclu de la CI
397
- npm run inspector # explorer les outils dans le MCP Inspector
504
+ npm run build:fixtures
505
+ npm test
506
+ npm run check
398
507
  ```
399
508
 
400
- Les fixtures sont générées, pas aspirées : elles reproduisent la structure du
401
- markup de lyrics.com avec du texte de remplissage, ce qui rend les tests de
402
- parsing déterministes et évite de stocker des paroles sous droits dans ce dépôt.
403
-
404
- La couche de scraping (`src/lyricscom`, `src/text`) n'importe pas le SDK MCP et
405
- est publiée séparément sous `mcp-lyricscom/client`, utilisable comme simple
406
- bibliothèque.
407
-
408
- ## Paroles et droits d'auteur
409
-
410
- Les paroles de chansons sont des œuvres protégées, propriété de leurs auteurs et
411
- éditeurs. Ce projet ne revendique aucun droit dessus.
412
-
413
- Ce serveur est un client. Il va chercher les mêmes pages publiques de lyrics.com
414
- que celles que vous pourriez ouvrir dans un navigateur, à la demande, une requête
415
- à la fois, en réponse à une demande explicite de votre part ou de celle de votre
416
- assistant. Il ne parcourt pas le site, ne constitue aucune base de paroles, et
417
- n'écrit rien sur le disque. Les pages restent en mémoire quelques minutes pour ne
418
- pas solliciter le site inutilement.
419
-
420
- Chaque résultat porte l'artiste, le titre et l'URL source. Si vous affichez ou
421
- réutilisez ce que renvoie ce serveur, conservez cette attribution et le lien vers
422
- la page d'origine.
509
+ Les tests s'exécutent sur des fixtures engendrées et n'émettent aucune requête.
510
+ La suite en direct, `npm run test:live`, émet une requête par route et tourne
511
+ chaque nuit contre le site lui-même.
423
512
 
424
- Le serveur respecte le robots.txt de lyrics.com : aucun des endpoints qu'il
425
- utilise n'y est interdit, et il s'impose environ une requête par seconde. Ce
426
- rythme est contraint : `LYRICSCOM_MIN_INTERVAL_MS` est refusé en dessous de
427
- 500 ms, une valeur plus basse retombant sur l'intervalle standard avec un
428
- avertissement sur stderr. Augmentez-le pour ralentir davantage.
513
+ ## Contribuer
429
514
 
430
- Projet non officiel, sans affiliation à lyrics.com ni à STANDS4 Ltd, ni
431
- approbation ou parrainage de leur part. Utilisez-le dans le respect des conditions
432
- d'utilisation de lyrics.com et du droit d'auteur qui vous est applicable.
515
+ Les anomalies, les questions et les idées ont leur place dans
516
+ [le suivi d'incidents](https://github.com/smeet666/mcp-lyricscom/issues). Les
517
+ propositions de modification sont bienvenues ; ouvrir un ticket d'abord aide à
518
+ s'accorder sur la forme du changement. Voir [CONTRIBUTING.md](CONTRIBUTING.md).
433
519
 
434
520
  ## Licence
435
521
 
436
- MIT, voir [LICENSE](./LICENSE). La licence couvre uniquement le code source, pas
437
- les paroles récupérées par son intermédiaire.
522
+ MIT, voir [LICENSE](LICENSE). Les paroles appartiennent à leurs auteurs et à
523
+ leurs éditeurs.