Skip to main content
Glama

mcp-lyricscom

npm CI license MCP Registry Glama M8ven LobeHub Install in Cursor Install in VS Code

lyrics.com is a large public catalogue of song lyrics. It files a song under its title, its artist, the album it appeared on and the year, and it holds the words themselves. Its search reaches inside those words.

This server connects a chat client to that catalogue. You can search for a song by a line you remember, search by title and artist, and read the words of one song, a slice at a time, with the words you were looking for located in the text. It needs no API key and no account.

Version française


Install

One-click install

Install in Cursor Install in VS Code

Claude Code

claude mcp add lyricscom -- npx -y mcp-lyricscom

Claude Desktop, Cursor, and any client using the standard config format

{
  "mcpServers": {
    "lyricscom": {
      "command": "npx",
      "args": ["-y", "mcp-lyricscom"]
    }
  }
}

Node 24 or later is required, and no environment variable has to be set.

With Docker

{
  "mcpServers": {
    "lyricscom": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-lyricscom:2.1.0"]
    }
  }
}

-i keeps stdin open, which is where the protocol travels, and -t is left out because a TTY rewrites the stream. The container needs outbound HTTPS to www.lyrics.com, and nothing else: no volume, no port, no credential.

Bundle, without npm

Download mcp-lyricscom-2.1.0.mcpb from the latest release and open it. A client that supports MCP bundles installs it on its own, with no npm and no configuration file to edit. The bundle carries its dependencies, so nothing is fetched at install time.

Related MCP server: LyricsGenius MCP Server

What you can ask

  • "Which song goes 'I've got a hand for you'?"

  • "Find me the lyrics of Wichita Lineman by Glen Campbell."

  • "Read me the second half of those words."

  • "Where does the word 'lineman' appear in that song?"

  • "What albums is that song on?"

The ordinary path runs from a search to a reading: a row carries an id, and get_lyrics takes that id.

Tools

Tool

What it does

search_lyrics

Finds a song from a line inside its words.

search_songs

Finds songs by title, narrowed by artist.

get_lyrics

Reads the words of one song, a slice at a time.

search_lyrics

Finds a song from words inside its lyrics. The site ranks loosely, so a match is checked before it is served.

Argument

Type

Required

What it does

query

string, 1 to 120 characters

yes

The line, or part of it, to look for.

limit

integer, 1 to 50, default 10

no

Rows to serve.

page

integer, 1 to 20, default 1

no

Which page of rows.

verify

snippet, full or none, default snippet

no

How to confirm the words really appear.

include_excerpt

boolean, default true

no

Carry the matching line with each row.

verify decides what a row is worth. snippet checks the excerpt the site already returned and costs nothing. full fetches up to five song pages and checks the complete words, which is slow and can bring on rate limiting. none serves what the site ranked, unchecked.

In return: rows carrying id, which get_lyrics takes; title; artist; album and year, null where the catalogue states none; source_url; and excerpt, the matching line. raw_result_count is what the site returned and filtered_out how many rows the check removed, so the two together say how loose the ranking was. has_more and next_page continue.

search_songs

Finds songs by title, narrowed by artist.

Argument

Type

Required

What it does

title

string, 1 to 120 characters

yes

The song title, or part of it.

artist

string, up to 120 characters

no

Keep the songs credited to this artist.

limit

integer, 1 to 50, default 10

no

Rows to serve.

page

integer, 1 to 20, default 1

no

Which page of rows.

match

loose or strict, default loose

no

How closely the artist has to match.

In return: the rows search_lyrics returns, with artist_filter echoing what was asked for and filtered_out counting what the artist restriction removed. strict keeps the artists whose name matches as written; loose accepts a name written differently.

get_lyrics

Reads the words of one song. Long lyrics are served a slice at a time.

Argument

Type

Required

What it does

id

string

one of two

The song id a search row carries.

url

a lyrics.com URL

one of two

The address of the song page.

max_chars

integer, 200 to 20000, default 6000

no

Characters of text to serve in this call.

offset

integer, 0 or more, default 0

no

Character offset to resume from.

highlight

string, up to 120 characters

no

Words to locate inside the text.

In return: status, reading ok or no_lyrics for a page the site holds without words; title, artist and source_url; and lyrics, the slice itself. The reading is described by total_chars, returned_chars, offset, next_offset and truncated: pass next_offset back to read on, and null there means the end. line_count counts the lines of the slice, and highlight answers for each word whether it was found and on which line_number, which is null when it was not.

Configuration

Every variable is optional. Set them in the env block of your client config.

Variable

Default

What it does

LYRICSCOM_USER_AGENT

the project identity

Names your application to the site, with an address where a person can be reached.

LYRICSCOM_MIN_INTERVAL_MS

1100

Gap between two requests, from 500 to 60000.

LYRICSCOM_TIMEOUT_MS

15000

Deadline for one request, from 1000 to 120000.

LYRICSCOM_MAX_RETRIES

3

Attempts after a transient failure, from 0 to 10.

LYRICSCOM_CACHE_TTL_MS

900000

How long an answer stays in memory, from 0 to 86400000.

LYRICSCOM_CACHE_MAX_ENTRIES

200

Answers held in memory at once, from 0 to 10000.

LYRICSCOM_LOG_LEVEL

error

silent, error, info or debug, written to stderr.

A value outside its range falls back to the default, and the reason is written to stderr.

On the User-Agent. This server names the project and links to its repository, and the site serves that. It does refuse some generic tool agents outright: a plain curl gets a 403. A blocked_user_agent error means the identity was refused, and LYRICSCOM_USER_AGENT lets you set one of your choosing. What you put there is your call and your responsibility.

Errors

Every failure carries one of these codes, a message, and where it helps a hint naming the next move.

Code

What happened

What to do

not_found

The site answered, and holds no such song.

Check the id with search_songs.

invalid_input

The arguments were refused before any request went out.

Read the message, which names the argument.

throttled

The site asked this client to slow down.

Wait, then call again with the same arguments. The song is still there.

blocked_user_agent

The site refused the identity this client sent.

Set LYRICSCOM_USER_AGENT.

parse_failure

The page loaded and the expected content was absent.

Report it at the issue tracker.

network_error

The request did not complete.

Try again shortly.

timeout

The request passed its deadline.

Raise LYRICSCOM_TIMEOUT_MS, or ask for a smaller max_chars.

throttled and blocked_user_agent are this server's two names for a refusal to serve, and a caller reading several sources normalises them onto whatever it calls rate limiting.

As a library

The layer reading the site is published on its own, with its pacing, its cache and its errors, and with no protocol attached.

import { LyricsComClient } from "mcp-lyricscom/client";

const client = new LyricsComClient();
const { data, cached } = await client.getSong({ id: "1234567" });
console.log(data.title, data.artist, cached);

search and getSong each answer { data, cached }, and throw an error carrying one of the codes above. The floor between two requests holds here as well.

Pacing and attribution

Requests go out one at a time with at least a second between them, and the floor of half a second holds however the server is configured. A verify: "full" search fetches up to five song pages, which is the most expensive thing this server does.

Every result carries the artist, the title and the address of the song page. Song lyrics are the work of their authors and publishers. This project claims no rights over them, ships no database of them, and writes nothing to disk.

This MCP server is an unofficial project, with no affiliation to lyrics.com.

Privacy

This server collects nothing about you and sends nothing to its author. It runs on your machine, contacts www.lyrics.com and nothing else, holds its answers in memory while it runs, and writes nothing to disk. PRIVACY.md states what a request carries and which settings change any of it.

Development

npm install
npm run build:fixtures
npm test
npm run check

Tests run against generated fixtures and make no network request. The live suite, npm run test:live, makes one request per route and runs nightly against the site itself.

Contributing

Bugs, questions and ideas belong in the issue tracker. Pull requests are welcome; opening an issue first helps agree on the shape of the change. See CONTRIBUTING.md.

License

MIT, see LICENSE. The lyrics belong to their authors and publishers.


mcp-lyricscom (français)

English version

lyrics.com est un grand catalogue public de paroles de chansons. Il classe une chanson sous son titre, son artiste, l'album où elle a paru et l'année, et il contient les paroles elles-mêmes. Sa recherche va à l'intérieur de ces paroles.

Ce serveur relie un client de conversation à ce catalogue. On peut y chercher une chanson par un vers dont on se souvient, chercher par titre et par artiste, et lire les paroles d'une chanson par tranches, avec les mots cherchés localisés dans le texte. Aucune clé d'API, aucun compte.

Installation

Installation en un clic

Install in Cursor Install in VS Code

Claude Code

claude mcp add lyricscom -- npx -y mcp-lyricscom

Claude Desktop, Cursor, et tout client au format de configuration standard

{
  "mcpServers": {
    "lyricscom": {
      "command": "npx",
      "args": ["-y", "mcp-lyricscom"]
    }
  }
}

Node 24 ou plus récent est nécessaire, et aucune variable d'environnement n'est à renseigner.

Avec Docker

{
  "mcpServers": {
    "lyricscom": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-lyricscom:2.1.0"]
    }
  }
}

-i garde l'entrée standard ouverte, qui est le canal du protocole, et -t est omis parce qu'un TTY réécrit le flux. Le conteneur a besoin d'un accès HTTPS sortant vers www.lyrics.com, et de rien d'autre : aucun volume, aucun port, aucun identifiant.

Bundle, sans npm

Téléchargez mcp-lyricscom-2.1.0.mcpb depuis la dernière publication et ouvrez-le. Un client qui gère les bundles MCP l'installe seul, sans npm et sans fichier de configuration à modifier. Le bundle emporte ses dépendances, donc rien n'est téléchargé à l'installation.

Ce qu'on peut demander

  • « Quelle est la chanson qui dit "I've got a hand for you" ? »

  • « Trouve-moi les paroles de Wichita Lineman par Glen Campbell. »

  • « Lis-moi la seconde moitié de ces paroles. »

  • « Où apparaît le mot "lineman" dans cette chanson ? »

  • « Sur quels albums cette chanson figure-t-elle ? »

Le chemin ordinaire va d'une recherche à une lecture : une ligne porte un id, et get_lyrics reprend cet identifiant.

Les outils

Outil

Ce qu'il fait

search_lyrics

Trouve une chanson à partir d'un vers de ses paroles.

search_songs

Trouve des chansons par titre, resserrées par artiste.

get_lyrics

Lit les paroles d'une chanson, par tranches.

search_lyrics

Trouve une chanson à partir de mots contenus dans ses paroles. Le site classe largement, donc une correspondance est vérifiée avant d'être servie.

Argument

Type

Requis

Ce qu'il fait

query

chaîne, 1 à 120 caractères

oui

Le vers, ou une partie, à chercher.

limit

entier, 1 à 50, défaut 10

non

Lignes à servir.

page

entier, 1 à 20, défaut 1

non

Quelle page de lignes.

verify

snippet, full ou none, défaut snippet

non

Comment confirmer que les mots y figurent.

include_excerpt

booléen, défaut true

non

Porter le vers correspondant sur chaque ligne.

verify décide de ce que vaut une ligne. snippet vérifie l'extrait que le site a déjà rendu et ne coûte rien. full va chercher jusqu'à cinq pages de chansons et vérifie les paroles entières, ce qui est lent et peut déclencher une limitation. none sert ce que le site a classé, sans vérification.

En retour : des lignes portant id, que get_lyrics reprend ; title ; artist ; album et year, null là où le catalogue n'indique rien ; source_url ; et excerpt, le vers correspondant. raw_result_count est ce que le site a rendu et filtered_out le nombre de lignes que la vérification a retirées, si bien que les deux ensemble disent à quel point le classement était large. has_more et next_page poursuivent.

search_songs

Trouve des chansons par titre, resserrées par artiste.

Argument

Type

Requis

Ce qu'il fait

title

chaîne, 1 à 120 caractères

oui

Le titre de la chanson, ou une partie.

artist

chaîne, jusqu'à 120 caractères

non

Ne garder que les chansons de cet artiste.

limit

entier, 1 à 50, défaut 10

non

Lignes à servir.

page

entier, 1 à 20, défaut 1

non

Quelle page de lignes.

match

loose ou strict, défaut loose

non

La rigueur de la correspondance sur l'artiste.

En retour : les lignes que rend search_lyrics, avec artist_filter qui redonne ce qui a été demandé et filtered_out qui compte ce que la restriction sur l'artiste a retiré. strict garde les artistes dont le nom correspond tel qu'écrit ; loose accepte un nom écrit autrement.

get_lyrics

Lit les paroles d'une chanson. Des paroles longues sont servies par tranches.

Argument

Type

Requis

Ce qu'il fait

id

chaîne

l'un des deux

L'identifiant que porte une ligne.

url

une adresse lyrics.com

l'un des deux

L'adresse de la page de la chanson.

max_chars

entier, 200 à 20000, défaut 6000

non

Caractères de texte à servir dans cet appel.

offset

entier, 0 ou plus, défaut 0

non

Position en caractères où reprendre.

highlight

chaîne, jusqu'à 120 caractères

non

Des mots à localiser dans le texte.

En retour : status, valant ok ou no_lyrics pour une page que le site contient sans paroles ; title, artist et source_url ; et lyrics, la tranche elle-même. La lecture est décrite par total_chars, returned_chars, offset, next_offset et truncated : redonnez next_offset pour poursuivre, et null marque la fin. line_count compte les lignes de la tranche, et highlight répond pour chaque mot s'il a été found et à quel line_number, null quand il ne l'a pas été.

Configuration

Chaque variable est facultative. Elles se posent dans le bloc env de la configuration du client.

Variable

Défaut

Ce qu'elle fait

LYRICSCOM_USER_AGENT

l'identité du projet

Nomme votre application auprès du site, avec une adresse où joindre une personne.

LYRICSCOM_MIN_INTERVAL_MS

1100

Écart entre deux requêtes, de 500 à 60000.

LYRICSCOM_TIMEOUT_MS

15000

Délai d'une requête, de 1000 à 120000.

LYRICSCOM_MAX_RETRIES

3

Tentatives après un échec passager, de 0 à 10.

LYRICSCOM_CACHE_TTL_MS

900000

Durée pendant laquelle une réponse reste en mémoire, de 0 à 86400000.

LYRICSCOM_CACHE_MAX_ENTRIES

200

Réponses gardées en mémoire à la fois, de 0 à 10000.

LYRICSCOM_LOG_LEVEL

error

silent, error, info ou debug, écrit sur la sortie d'erreur.

Une valeur hors de sa plage retombe sur le défaut, et la raison est écrite sur la sortie d'erreur.

À propos du User-Agent. Ce serveur nomme le projet et renvoie vers son dépôt, et le site le sert. Il refuse en revanche certains agents d'outils génériques : un curl nu reçoit un 403. Une erreur blocked_user_agent signifie que l'identité envoyée a été refusée, et LYRICSCOM_USER_AGENT permet d'en poser une de votre choix. Ce que vous y mettez relève de votre décision et de votre responsabilité.

Erreurs

Chaque échec porte un de ces codes, un message, et quand cela aide une indication du geste suivant.

Code

Ce qui s'est passé

Que faire

not_found

Le site a répondu, et n'a pas cette chanson.

Vérifiez l'identifiant avec search_songs.

invalid_input

Les arguments ont été refusés avant toute requête.

Lisez le message, qui nomme l'argument.

throttled

Le site demande à ce client de ralentir.

Attendez, puis rappelez avec les mêmes arguments. La chanson est toujours là.

blocked_user_agent

Le site a refusé l'identité envoyée par ce client.

Posez LYRICSCOM_USER_AGENT.

parse_failure

La page a chargé et le contenu attendu est absent.

Signalez-le sur le suivi d'incidents.

network_error

La requête n'a pas abouti.

Réessayez sous peu.

timeout

La requête a dépassé son délai.

Augmentez LYRICSCOM_TIMEOUT_MS, ou demandez un max_chars plus petit.

throttled et blocked_user_agent sont les deux noms que ce serveur donne à un refus de servir, et un appelant qui lit plusieurs sources les ramène sur ce qu'il appelle une limitation de débit.

Comme bibliothèque

La couche qui lit le site est publiée seule, avec son rythme, son cache et ses erreurs, sans protocole attaché.

import { LyricsComClient } from "mcp-lyricscom/client";

const client = new LyricsComClient();
const { data, cached } = await client.getSong({ id: "1234567" });
console.log(data.title, data.artist, cached);

search et getSong répondent chacun { data, cached }, et lèvent une erreur portant un des codes ci-dessus. Le plancher entre deux requêtes tient également ici.

Rythme et attribution

Les requêtes partent une à une avec au moins une seconde entre elles, et le plancher d'une demi-seconde tient quelle que soit la configuration. Une recherche en verify: "full" va chercher jusqu'à cinq pages de chansons, ce qui est la chose la plus coûteuse que fait ce serveur.

Chaque résultat porte l'artiste, le titre et l'adresse de la page de la chanson. Les paroles sont l'œuvre de leurs auteurs et de leurs éditeurs. Ce projet ne revendique aucun droit dessus, n'embarque aucune base de paroles et n'écrit rien sur le disque.

Ce MCP est un projet non officiel, sans affiliation à lyrics.com.

Confidentialité

Ce serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur votre machine, ne joint que www.lyrics.com, garde ses réponses en mémoire le temps qu'il tourne, et n'écrit rien sur le disque. PRIVACY.md dit ce qu'une requête emporte et quels réglages changent cela.

Développement

npm install
npm run build:fixtures
npm test
npm run check

Les tests s'exécutent sur des fixtures engendrées et n'émettent aucune requête. La suite en direct, npm run test:live, émet une requête par route et tourne chaque nuit contre le site lui-même.

Contribuer

Les anomalies, les questions et les idées ont leur place dans le suivi d'incidents. Les propositions de modification sont bienvenues ; ouvrir un ticket d'abord aide à s'accorder sur la forme du changement. Voir CONTRIBUTING.md.

Licence

MIT, voir LICENSE. Les paroles appartiennent à leurs auteurs et à leurs éditeurs.

Available Tools

3 tools
get_lyricsGet full lyricsA
Read-onlyIdempotent

Fetch the full lyrics of one song from lyrics.com, given the song id or URL returned by search_lyrics or search_songs. Lyrics can be long, so the response is truncated by default: check 'truncated' and call again with 'offset' set to 'next_offset' to continue reading. Some lyrics.com pages legitimately have no lyrics on file; those come back with status "no_lyrics" and an empty lyrics field, which is a valid answer and not an error, so do not retry them. Always cite 'source_url' and the artist when showing lyrics to a user.

NameRequiredDescriptionDefault
idNolyrics.com numeric song id, as returned by the search tools. Preferred over 'url'.
urlNoFull lyrics.com song URL. Only www.lyrics.com URLs are accepted. Ignored when 'id' is given.
offsetNoCharacter offset to resume from, for lyrics longer than max_chars.
highlightNoOptional word. When set, the response reports whether it appears in the lyrics and on which line.
max_charsNoMaximum characters of lyrics text to return in this call.

Output Schema

NameRequiredDescription
idYes
notesYes
titleYes
artistYes
lyricsYesSlice of the lyrics text. Empty when status is 'no_lyrics'.
offsetYes
sourceYes
statusYes
highlightYes
truncatedYes
line_countYes
source_urlYes
attributionYesReady-to-display credit line.
next_offsetYes
total_charsYes
returned_charsYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only/idempotent/open-world, so the description goes beyond them usefully: it discloses the default truncation behavior, the pagination continuation contract, and the legitimate 'no_lyrics' + empty-field response that must not be retried. That last point is the kind of non-obvious behavior that prevents an agent from looping on a valid empty result.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then pagination, then the no_lyrics caveat, then the citation rule — each sentence addresses a distinct failure mode. No restatement of the title or annotations, and no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Five optional parameters, an output schema, and full annotation coverage are all accounted for; the description supplies exactly the pieces structured fields cannot — continuation protocol, empty-result semantics, and attribution policy. An agent could call this correctly on the first attempt.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so all five parameters are already documented including the id-over-url preference and the max_chars default. The description adds cross-parameter workflow semantics the schema cannot express — the truncated/next_offset relationship and the offset continuation loop — which lifts it above the baseline 3, though it does not explain highlight or max_chars bounds.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Fetch the full lyrics of one song from lyrics.com') and explicitly names the inputs' provenance ('id or URL returned by search_lyrics or search_songs'). This makes it immediately distinguishable from the two search siblings, which return ids rather than lyrics.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes from the siblings into this tool, prescribes the truncation workflow ('check truncated and call again with offset set to next_offset'), and states a when-NOT-to-retry rule for the no_lyrics status. It also states a post-condition obligation (cite source_url and artist). Nothing about when/how to call is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_lyricsSearch lyrics by wordA
Read-onlyIdempotent

Search lyrics.com for songs whose lyrics contain a given word or phrase. Returns matching songs with artist, title, a short excerpt of the line where the word appears, and the lyrics.com id and URL needed to fetch the full text with get_lyrics. Use this when someone remembers a fragment of lyrics but not the song, or wants songs mentioning a word or theme. One call returns one page of lyrics.com results; to see more, increase 'page' rather than 'limit'. lyrics.com also returns loose and title-only matches, so results are filtered locally to keep only songs where the word genuinely appears in the lyrics. Set 'verify' to "none" to see the raw, unfiltered list.

NameRequiredDescriptionDefault
pageNoResult page on lyrics.com. Each page holds up to 24 raw results.
limitNoMaximum songs to return from this page.
queryYesWord or short phrase to look for inside the lyrics, for example 'autrefois'.
verifyNoHow to confirm the query really appears in the lyrics. 'snippet' checks the excerpt lyrics.com already returned and costs nothing. 'full' fetches up to 5 song pages and checks the complete lyrics, which is slow and can trigger rate limiting. 'none' disables filtering.snippet
include_excerptNoInclude the matching lyric line excerpt in each result.

Output Schema

NameRequiredDescription
pageYes
notesYesCaveats worth knowing, such as a cache hit or partial verification.
queryYes
sourceYes
resultsYes
has_moreYes
next_pageYes
filtered_outYesRows dropped because the query was not found in their lyrics.
raw_result_countYesRows lyrics.com returned before local filtering and deduplication.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/idempotent annotations, it discloses non-obvious behavior: lyrics.com returns loose and title-only matches that are filtered locally, 'full' verification can trigger rate limiting, and one call yields only one page. It does not quantify paging limits or error behavior, but the annotations already carry the safety profile and the description adds substantial operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core action and return shape, then adds usage trigger, pagination guidance, filtering caveat, and the verify escape hatch. Five sentences, no filler, each adding distinct decision-relevant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-parameter read tool with an output schema and full annotation coverage, the description supplies everything needed to call it correctly: triggering condition, pagination strategy, filtering semantics, and rate-limit risk. Return-value detail can be deferred to the output schema, so nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema coverage the baseline is 3, and the description adds real meaning on top: it explains the page-vs-limit tradeoff ('increase page rather than limit') and the semantics of verify='none' as raw unfiltered output. It leaves the numeric caps to the schema, which is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Search lyrics.com for songs whose lyrics contain a given word or phrase') and spells out the return payload: artist, title, excerpt, lyrics.com id and URL. It explicitly links to the sibling get_lyrics as the follow-up for full text, so the agent can distinguish it from both siblings without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives the exact triggering scenario ('someone remembers a fragment of lyrics but not the song') and names the alternative (get_lyrics) for fetching full text. It also resolves the operational ambiguity between 'page' and 'limit' and explains when to use verify='none', which is guidance an agent could not infer elsewhere.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_songsSearch songs by titleA
Read-onlyIdempotent

Find songs on lyrics.com by their title. Returns candidate songs with artist, album, year, and the lyrics.com id and URL. Use this when someone names a song and you need its id to call get_lyrics, or to tell covers and versions by different artists apart. An optional 'artist' narrows the list. lyrics.com searches titles and lyrics together, so results are ranked and filtered locally by title match; use match="strict" to keep only genuine title matches.

NameRequiredDescriptionDefault
pageNo
limitNo
matchNo'strict' keeps only results whose title matches the query as a whole title, ignoring suffixes such as '(Remastered)'. 'loose' keeps anything lyrics.com returned, best match first.loose
titleYesSong title, or part of it.
artistNoOptional artist name, matched case-insensitively against each result.

Output Schema

NameRequiredDescription
pageYes
notesYes
titleYes
sourceYes
resultsYes
has_moreYes
next_pageYes
filtered_outYes
artist_filterYes
raw_result_countYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/openWorld, so the bar is lower, and the description still adds real behavioral context: lyrics.com searches titles and lyrics together, results are ranked and filtered locally by title, and match="strict" changes that filtering. It does not discuss pagination limits or result-set size, keeping it below a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences, front-loaded with purpose and return shape before the usage and filtering details; every sentence carries information. It runs slightly long and packs multiple ideas into the final sentence, but nothing is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, and the description still summarizes the return fields; the mutation/read profile is handled by annotations; the one non-obvious parameter behavior is explained. An agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 60%, and the description compensates by explaining the effect of 'artist' (narrows the list) and 'match' (strict keeps only whole-title matches, ignoring suffixes like '(Remastered)') in terms of behavior rather than restating the enum. 'page' and 'limit' get no mention, which is the only gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Find songs on lyrics.com by their title') and enumerates the returned fields (artist, album, year, id, URL). It is clearly distinguishable from get_lyrics (retrieval by id) and search_lyrics (lyric-text search).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger ('when someone names a song and you need its id to call get_lyrics') and a secondary use case (telling covers/versions apart by artist). It does not state when to prefer the sibling search_lyrics instead, which is the one remaining routing gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv2.1.0
    • Changedget_lyrics5 fields changed
      • removedOutput schema / properties / artist / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / artist / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / highlight / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "found": {
        -        "type": "boolean"
        -      },
        -      "line": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ]
        -      },
        -      "line_number": {
        -        "anyOf": [
        -          {
        -            "maximum": 9007199254740991,
        -            "minimum": -9007199254740991,
        -            "type": "integer"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ]
        -      },
        -      "word": {
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "word",
        -      "found",
        -      "line_number",
        -      "line"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "found": {
        +        "type": "boolean"
        +      },
        +      "line": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "line_number": {
        +        "anyOf": [
        +          {
        +            "maximum": 9007199254740991,
        +            "minimum": -9007199254740991,
        +            "type": "integer"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ]
        +      },
        +      "word": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "word",
        +      "found",
        +      "line_number",
        +      "line"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / title / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / title / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedsearch_lyrics4 fields changed
      • removedOutput schema / properties / results / items / properties / album / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / album / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / excerpt / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / excerpt / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedsearch_songs6 fields changed
      • removedOutput schema / properties / artist_filter / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / artist_filter / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / album / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / album / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / excerpt / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / excerpt / type
        Added value: +[
        +  "string",
        +  "null"
        +]
  2. 3 tool updatesv2.0.0
    • Changedget_lyrics15 fields changed
      • addedInput schema / properties / offset / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / artist / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / artist / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • changedOutput schema / properties / highlight / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "found": {
        -        "type": "boolean"
        -      },
        -      "line": {
        -        "type": [
        -          "string",
        -          "null"
        -        ]
        -      },
        -      "line_number": {
        -        "anyOf": [
        -          {
        -            "type": "integer"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ]
        -      },
        -      "word": {
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "word",
        -      "found",
        -      "line_number",
        -      "line"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "found": {
        +        "type": "boolean"
        +      },
        +      "line": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ]
        +      },
        +      "line_number": {
        +        "anyOf": [
        +          {
        +            "maximum": 9007199254740991,
        +            "minimum": -9007199254740991,
        +            "type": "integer"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ]
        +      },
        +      "word": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "word",
        +      "found",
        +      "line_number",
        +      "line"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / line_count / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / line_count / minimum
        Added value: +-9007199254740991
      • changedOutput schema / properties / next_offset / anyOf
        Previous value: -[
        -  {
        -    "type": "integer"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "maximum": 9007199254740991,
        +    "minimum": -9007199254740991,
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / offset / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / offset / minimum
        Added value: +-9007199254740991
      • addedOutput schema / properties / returned_chars / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / returned_chars / minimum
        Added value: +-9007199254740991
      • addedOutput schema / properties / title / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / title / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / total_chars / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / total_chars / minimum
        Added value: +-9007199254740991
    • Changedsearch_lyrics12 fields changed
      • addedOutput schema / properties / filtered_out / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / filtered_out / minimum
        Added value: +-9007199254740991
      • changedOutput schema / properties / next_page / anyOf
        Previous value: -[
        -  {
        -    "type": "integer"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "maximum": 9007199254740991,
        +    "minimum": -9007199254740991,
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / page / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / page / minimum
        Added value: +-9007199254740991
      • addedOutput schema / properties / raw_result_count / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / raw_result_count / minimum
        Added value: +-9007199254740991
      • addedOutput schema / properties / results / items / properties / album / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / results / items / properties / album / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / results / items / properties / excerpt / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / results / items / properties / excerpt / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • changedOutput schema / properties / results / items / properties / year / anyOf
        Previous value: -[
        -  {
        -    "type": "integer"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "maximum": 9007199254740991,
        +    "minimum": -9007199254740991,
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
    • Changedsearch_songs14 fields changed
      • addedOutput schema / properties / artist_filter / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / artist_filter / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / filtered_out / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / filtered_out / minimum
        Added value: +-9007199254740991
      • changedOutput schema / properties / next_page / anyOf
        Previous value: -[
        -  {
        -    "type": "integer"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "maximum": 9007199254740991,
        +    "minimum": -9007199254740991,
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / page / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / page / minimum
        Added value: +-9007199254740991
      • addedOutput schema / properties / raw_result_count / maximum
        Added value: +9007199254740991
      • addedOutput schema / properties / raw_result_count / minimum
        Added value: +-9007199254740991
      • addedOutput schema / properties / results / items / properties / album / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / results / items / properties / album / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • addedOutput schema / properties / results / items / properties / excerpt / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / results / items / properties / excerpt / type
        Removed value: -[
        -  "string",
        -  "null"
        -]
      • changedOutput schema / properties / results / items / properties / year / anyOf
        Previous value: -[
        -  {
        -    "type": "integer"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "maximum": 9007199254740991,
        +    "minimum": -9007199254740991,
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
  3. 1 tool updatev1.4.0
    • Changedget_lyrics2 fields changed
      • addedOutput schema / properties / notes
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "status",
        -  "id",
        -  "title",
        -  "artist",
        -  "source_url",
        -  "lyrics",
        -  "total_chars",
        -  "returned_chars",
        -  "offset",
        -  "next_offset",
        -  "truncated",
        -  "line_count",
        -  "highlight",
        -  "attribution",
        -  "source"
        -]New value: +[
        +  "status",
        +  "id",
        +  "title",
        +  "artist",
        +  "source_url",
        +  "lyrics",
        +  "total_chars",
        +  "returned_chars",
        +  "offset",
        +  "next_offset",
        +  "truncated",
        +  "line_count",
        +  "highlight",
        +  "attribution",
        +  "source",
        +  "notes"
        +]
  4. 3 tool updatesv1.0.0
    • First observedget_lyrics
    • First observedsearch_lyrics
    • First observedsearch_songs

TDQS

A4.5/5.0

Scored across 3 tools

Disambiguation4/5

search_lyrics and search_songs are the only pair at risk of confusion since lyrics.com conflates title and lyric matching, but the descriptions clearly separate 'fragment of lyrics' from 'named song title' use cases. get_lyrics is unambiguously a fetch-by-id tool.

Naming Consistency5/5

All three tools follow a consistent snake_case verb_noun pattern (search_lyrics, search_songs, get_lyrics). Search verbs are used uniformly and the fetch verb is distinct.

Tool Count4/5

Three tools is on the lean side, but for the narrow lyrics-lookup domain each has a clear role: find by lyric, find by title, fetch full text. No padding or redundancy.

Completeness4/5

The core workflow (locate a song by lyric or title, then retrieve its full text with pagination) is fully covered, including edge cases like no_lyrics. Minor gaps exist for artist/album-centric browsing, but agents can work around these via title searches.

Maintenance

ActivityActive
ResponsivenessLimited data

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server for Spotify control and synchronized lyrics retrieval that enables playback management, queue navigation, and music search capabilities. It also features perception tools for real-time track analysis, including BPM, key detection, and timestamped lyrics.
    107 npm
    3
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server that enables AI assistants to search and retrieve music data (songs, lyrics, playlists, albums, artists) from Netease Cloud Music.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for Suno music generation API that enables generating lyrics and custom songs with style tags, model selection, and automatic polling for task completion.
    2
    MIT