mcp-metacritic
This server provides read-only access to Metacritic data for films, shows, and games, with no API key or account required. You can:
Search titles by keyword and get compact results with critic and audience scores.
Retrieve detailed entry information, including critic score (out of 100), audience score (out of 10), genre, release, awards, production companies, networks, streaming links (with tracking redirects unwrapped), and more. Long descriptions support pagination.
Read individual reviews from critics or users, filterable by sentiment (positive, neutral, negative), with review excerpts, publication names, and links to original articles.
Browse rankings by critic score (all-time best), recency (newest), or popularity (trending), with optional genre filtering and approximate pagination.
Scores use different scales (Metascore out of 100, audience score out of 10) and should not be averaged directly. Reviews are a fixed sample, not the full list. Errors are always explicitly returned, never silently empty.
Provides tools for searching and retrieving Metacritic data, including films, shows, and games, with critic and audience scores, individual reviews, and browsing rankings.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-metacriticSearch for the movie 'Inception' and show its Metascore."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-metacritic
Metacritic gathers what critics and audiences said about films, television series and video games. Each entry carries the year, the age rating, the genres, and two scores of its own: the Metascore, a weighted average of the professional reviews, and the user score, out of ten, from the people who signed up to rate it. Under each entry sit the reviews themselves, with the publication that ran them and the quoted line.
This server connects a chat client to that catalogue. You can search for a title, read its entry with its scores and its details, browse a catalogue by score, recency or popularity, and read the reviews of one title, filtered by critic or audience and by how favourable they were. It needs no API key and no account.
Install
One-click install
Claude Code
claude mcp add metacritic -- npx -y mcp-metacriticClaude Desktop, Cursor, and any client using the standard config format
{
"mcpServers": {
"metacritic": {
"command": "npx",
"args": ["-y", "mcp-metacritic"]
}
}
}Node 24 or later is required, and no environment variable has to be set.
With Docker
{
"mcpServers": {
"metacritic": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-metacritic:2.0.1"]
}
}
}-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
backend.metacritic.com, and nothing else: no volume, no port, no credential.
Bundle, without npm
Download mcp-metacritic-2.0.1.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: moviefinder-mcp
What you can ask
"What did critics make of The Matrix?"
"Read me a few negative reviews of that game."
"What are the best-reviewed horror films?"
"How does the user score compare to the Metascore?"
"What came out recently that reviewed well?"
The ordinary path runs from a search to an entry: a row carries a slug and a
kind, and get_title and get_reviews take both together.
Tools
Tool | What it does |
| Finds films, series and games by title. |
| Reads one entry, its scores and its details. |
| Reads the reviews of one entry, by source and by sentiment. |
| Lists a catalogue by score, recency or popularity. |
A title is addressed by its slug together with its kind, since the same slug
can name a film and a game.
search_titles
Finds films, series and games by title.
Argument | Type | Required | What it does |
| string, at least 1 character | yes | A title, or part of one. |
|
| no | Which catalogue to search. |
| integer, 1 to 50, default | no | Rows to serve. |
In return: rows carrying slug and kind, which get_title and
get_reviews take together; title; year; release_date; rating, the age
rating as published; metascore; user_score, which the search route publishes
for no row and which is null on every one of them; and source_url. A score the
site has not computed is null, never 0: on a scale that starts at zero the
two would be indistinguishable, and a title with too few reviews carries none.
get_title
Reads one entry. The heavier parts are asked for rather than served by default, and each one beyond the default costs a request.
Argument | Type | Required | What it does |
| string, at least 1 character | yes | The identifier a row carries. |
|
| yes | Which catalogue it belongs to. |
| array of | no | Which parts to return. |
| integer, 200 to 20000, default | no | How much of the description to serve. |
| integer, 0 or more, default | no | Where to resume the description. |
In return: the entry a search row carries, plus description, tagline,
genres, duration_minutes and imdb_id, each null where the page states
nothing. total_chars, returned_chars and offset describe the slice of the
description served.
get_reviews
Reads the reviews of one entry.
Argument | Type | Required | What it does |
| string, at least 1 character | yes | The identifier a row carries. |
|
| yes | Which catalogue it belongs to. |
|
| no | Whose reviews to read. |
|
| no | How favourable a review has to be. |
| integer, 1 to 50, default | no | Reviews to serve. |
| integer, 0 or more, default | no | Reviews to skip, for paging. |
In return: reviews, each with its quote as published, its score, the
max that score is out of, which is 100 for a critic and 10 for a user, and the
publication that ran it. Name the publication when quoting a review.
total_available counts every review Metacritic holds from that source for the
entry, whatever sentiment was asked for, since the sentiment sorts the sample
this server received rather than the request that fetched it. next_offset
continues.
browse_titles
Lists a catalogue.
Argument | Type | Required | What it does |
|
| no | Which catalogue to list. |
|
| no | How the rows are ordered. |
| string | no | A single genre name, such as |
| integer, 1 to 50, default | no | Rows to serve. |
| integer, 0 or more, default | no | Rows to skip, for paging. |
In return: the rows search_titles returns, with total_available,
offset, next_offset and the kind, sort and genre the listing was read
under.
Two scores, two things measured
The Metascore is a weighted average of professional reviews, out of 100. The user
score is the average of what registered members gave, out of 10. They measure
different populations on different scales, and a title can carry one and not the
other. Read each with the max its reviews state, and report a missing score as
missing.
Configuration
Every variable is optional. Set them in the env block of your client config.
Variable | Default | What it does |
| the project identity | Names your application to the site, with an address where a person can be reached. |
|
| Gap between two requests, from 500 to 60000. |
|
| Deadline for one request, from 1000 to 120000. |
|
| Attempts after a transient failure, from 0 to 10. |
|
| How long a catalogue entry stays in memory, from 0 to 604800000. |
|
| How long scores and reviews stay in memory, from 0 to 86400000. |
|
| Answers held in memory at once, from 0 to 10000. |
|
|
|
Scores move as reviews come in, especially around a release, so they are held for an hour where a catalogue entry is held for a day. A value outside its range falls back to the default, and the reason is written to stderr.
Errors
Every failure carries one of six codes, a message, and where it helps a hint naming the next move.
Code | What happened | What to do |
| The site answered, and holds no such entry. | Check the slug and the kind with |
| The arguments were refused before any request went out. | Read the message, which names the argument. |
| The site asked this client to slow down. | Wait the number of seconds the hint names and call again with the same arguments. The entry is still there. |
| The answer arrived in a shape this client cannot read. | Report it at the issue tracker. |
| The request did not complete. | Try again shortly. |
| The request passed its deadline. | Raise |
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 { McClient } from "mcp-metacritic/client";
const client = new McClient();
const { data, cached } = await client.getDetail("movie", "the-matrix");
console.log(data.title, data.metascore, cached);Each read answers { data, cached }, and throws an error carrying one of the six
codes. 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. The User-Agent always
ends with the project identity and an address where a person can be reached.
Every result carries the address of the Metacritic page, and every quoted review carries the publication that ran it. The reviews belong to their authors and to the publications that ran them.
This MCP server is an unofficial project, with no affiliation to Metacritic.
Privacy
This server collects nothing about you and sends nothing to its author. It runs
on your machine, contacts backend.metacritic.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 checkTests 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 scores and the reviews belong to Metacritic and to the publications it quotes.
mcp-metacritic (français)
Metacritic rassemble ce que la critique et le public ont dit des films, des séries et des jeux vidéo. Chaque fiche porte l'année, la classification par âge, les genres, et deux notes qui lui sont propres : le Metascore, moyenne pondérée des critiques professionnelles, et la note des utilisateurs, sur dix, donnée par les inscrits. Sous chaque fiche se trouvent les critiques elles-mêmes, avec la publication qui les a signées et la phrase citée.
Ce serveur relie un client de conversation à ce catalogue. On peut y chercher un titre, lire sa fiche avec ses notes et ses détails, parcourir un catalogue par note, par fraîcheur ou par popularité, et lire les critiques d'un titre, filtrées par source et par tonalité. Aucune clé d'API, aucun compte.
Installation
Installation en un clic
Claude Code
claude mcp add metacritic -- npx -y mcp-metacriticClaude Desktop, Cursor, et tout client au format de configuration standard
{
"mcpServers": {
"metacritic": {
"command": "npx",
"args": ["-y", "mcp-metacritic"]
}
}
}Node 24 ou plus récent est nécessaire, et aucune variable d'environnement n'est à renseigner.
Avec Docker
{
"mcpServers": {
"metacritic": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-metacritic:2.0.1"]
}
}
}-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 backend.metacritic.com, et de rien d'autre : aucun volume, aucun
port, aucun identifiant.
Bundle, sans npm
Téléchargez mcp-metacritic-2.0.1.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
« Qu'a pensé la critique de Matrix ? »
« Lis-moi quelques critiques négatives de ce jeu. »
« Quels sont les films d'horreur les mieux notés ? »
« Comment la note du public se compare-t-elle au Metascore ? »
« Qu'est-il sorti récemment qui a été bien reçu ? »
Le chemin ordinaire va d'une recherche à une fiche : une ligne porte un slug et
un kind, et get_title comme get_reviews reprennent les deux ensemble.
Les outils
Outil | Ce qu'il fait |
| Trouve des films, des séries et des jeux par leur titre. |
| Lit une fiche, ses notes et ses détails. |
| Lit les critiques d'une fiche, par source et par tonalité. |
| Liste un catalogue par note, par fraîcheur ou par popularité. |
Un titre s'adresse par son slug accompagné de son kind, un même slug pouvant
nommer un film et un jeu.
search_titles
Trouve des films, des séries et des jeux par leur titre.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, au moins 1 caractère | oui | Un titre, ou une partie. |
|
| non | Le catalogue où chercher. |
| entier, 1 à 50, défaut | non | Lignes à servir. |
En retour : des lignes portant slug et kind, que get_title et
get_reviews reprennent ensemble ; title ; year ; release_date ; rating,
la classification par âge telle que publiée ; metascore ; user_score, que la
route de recherche ne publie pour aucune ligne et qui y vaut null partout ; et
source_url. Une note que le site n'a pas calculée vaut null, jamais 0 :
sur une échelle qui commence à zéro les deux seraient indiscernables, et un titre
avec trop peu de critiques n'en porte aucune.
get_title
Lit une fiche. Les parties lourdes se demandent au lieu d'être servies par défaut, et chacune au-delà du défaut coûte une requête.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, au moins 1 caractère | oui | L'identifiant d'une ligne. |
|
| oui | Le catalogue dont il relève. |
| tableau de | non | Les parties à rendre. |
| entier, 200 à 20000, défaut | non | La longueur de description à servir. |
| entier, 0 ou plus, défaut | non | Où reprendre la description. |
En retour : la fiche que porte une ligne de recherche, plus description,
tagline, genres, duration_minutes et imdb_id, chacun null là où la page
n'indique rien. total_chars, returned_chars et offset décrivent la tranche
de description servie.
get_reviews
Lit les critiques d'une fiche.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, au moins 1 caractère | oui | L'identifiant d'une ligne. |
|
| oui | Le catalogue dont il relève. |
|
| non | De qui lire les critiques. |
|
| non | La tonalité exigée d'une critique. |
| entier, 1 à 50, défaut | non | Critiques à servir. |
| entier, 0 ou plus, défaut | non | Critiques à sauter, pour paginer. |
En retour : reviews, chacune avec sa quote telle que publiée, son
score, le max sur lequel cette note est donnée, qui vaut 100 pour un critique
et 10 pour un utilisateur, et la publication qui l'a signée. Nommez la
publication quand vous citez une critique. total_available compte toutes les
critiques que Metacritic tient de cette source pour cette fiche, quelle que soit
la tonalité demandée, puisque la tonalité trie l'échantillon reçu et non la
requête qui l'a cherché. next_offset poursuit.
browse_titles
Liste un catalogue.
Argument | Type | Requis | Ce qu'il fait |
|
| non | Le catalogue à lister. |
|
| non | L'ordre des lignes. |
| chaîne | non | Un seul nom de genre, comme |
| entier, 1 à 50, défaut | non | Lignes à servir. |
| entier, 0 ou plus, défaut | non | Lignes à sauter, pour paginer. |
En retour : les lignes que rend search_titles, avec total_available,
offset, next_offset et les kind, sort et genre sous lesquels la liste a
été lue.
Deux notes, deux choses mesurées
Le Metascore est une moyenne pondérée des critiques professionnelles, sur 100. La
note des utilisateurs est la moyenne de ce qu'ont donné les membres inscrits, sur 10. Elles mesurent des populations différentes sur des échelles différentes, et
un titre peut porter l'une sans l'autre. Lisez chacune avec le max que ses
critiques indiquent, et rapportez une note absente comme absente.
Configuration
Chaque variable est facultative. Elles se posent dans le bloc env de la
configuration du client.
Variable | Défaut | Ce qu'elle fait |
| l'identité du projet | Nomme votre application auprès du site, avec une adresse où joindre une personne. |
|
| Écart entre deux requêtes, de 500 à 60000. |
|
| Délai d'une requête, de 1000 à 120000. |
|
| Tentatives après un échec passager, de 0 à 10. |
|
| Durée pendant laquelle une fiche reste en mémoire, de 0 à 604800000. |
|
| Durée pendant laquelle les notes et critiques restent en mémoire, de 0 à 86400000. |
|
| Réponses gardées en mémoire à la fois, de 0 à 10000. |
|
|
|
Les notes bougent au fil des critiques, surtout autour d'une sortie, donc elles sont gardées une heure là où une fiche l'est un jour. Une valeur hors de sa plage retombe sur le défaut, et la raison est écrite sur la sortie d'erreur.
Erreurs
Chaque échec porte un des six codes, un message, et quand cela aide une indication du geste suivant.
Code | Ce qui s'est passé | Que faire |
| Le site a répondu, et n'a pas cette fiche. | Vérifiez le slug et le type avec |
| Les arguments ont été refusés avant toute requête. | Lisez le message, qui nomme l'argument. |
| Le site demande à ce client de ralentir. | Attendez les secondes indiquées et rappelez avec les mêmes arguments. La fiche est toujours là. |
| La réponse est arrivée dans une forme illisible ici. | Signalez-le sur le suivi d'incidents. |
| La requête n'a pas abouti. | Réessayez sous peu. |
| La requête a dépassé son délai. | Augmentez |
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 { McClient } from "mcp-metacritic/client";
const client = new McClient();
const { data, cached } = await client.getDetail("movie", "the-matrix");
console.log(data.title, data.metascore, cached);Chaque lecture répond { data, cached }, et lève une erreur portant un des six
codes. 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. Le
User-Agent se termine toujours par l'identité du projet et une adresse où
joindre une personne.
Chaque résultat porte l'adresse de la page Metacritic, et chaque critique citée porte la publication qui l'a signée. Les critiques appartiennent à leurs auteurs et aux publications qui les ont publiées.
Ce MCP est un projet non officiel, sans affiliation à Metacritic.
Confidentialité
Ce serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur
votre machine, ne joint que backend.metacritic.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 checkLes 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 notes et les critiques appartiennent à Metacritic et aux publications qu'il cite.
Available Tools
4 toolsbrowse_titlesBrowse rankingsARead-onlyIdempotent
List films, shows or games by score, by release date or by current popularity. Use this when there is no specific title to look up: best rated, newest, what people are looking at now. Filter by genre with a single name such as Horror, Comedy or Action. Sorting by score returns the all-time ranking, which is dominated by older titles: combine with a genre to narrow it. Scores come back on their own scales, 100 for critics and 10 for users. Paging is approximate: Metacritic does not order tied entries stably, so an entry can appear on two consecutive pages. Deduplicate by slug rather than counting rows.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Which catalogue to list. | movie |
| sort | No | 'score' is the critic ranking, 'recent' is by release date, 'popular' is current attention. | score |
| genre | No | Single genre name, such as Horror. Omit for all genres. | |
| limit | No | How many rows to return. | |
| offset | No | How many rows to skip, for paging. |
Output Schema
| Name | Required | Description |
|---|---|---|
| kind | Yes | |
| sort | Yes | |
| genre | Yes | |
| notes | Yes | |
| offset | Yes | |
| results | Yes | |
| next_offset | Yes | Pass as 'offset' for the next page. Tied entries are not ordered stably upstream, so deduplicate by slug across pages. |
| total_available | Yes | How many entries match upstream, which is the size of what this samples. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool as read-only and idempotent, but the description adds critical behavioral detail: scores arrive on different scales (100 vs 10), paging can be unstable for tied entries, and deduplication should be done by slug rather than row count. This goes well beyond what annotations or schema convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: purpose, usage context, caveats about ranking composition, score scales, paging instability, and deduplication are all essential. It is front-loaded with the main action and use case.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only listing tool with an output schema, the description covers all necessary operational context: when to use it, how to narrow results, what the ranking means, and how to handle paging anomalies. Nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the parameters are already fully documented in the schema. The description reinforces genre usage and sort meanings but does not meaningfully add new parameter-level information beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb ('List') and resource ('films, shows or games'), plus the three ranking dimensions. It distinguishes itself from the sibling lookup tools by explicitly saying 'when there is no specific title to look up.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explains when to use the tool versus looking up a specific title, and gives concrete guidance: combine with a genre to narrow the all-time ranking, expect approximate paging, and deduplicate by slug. This is actionable and prevents misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_reviewsRead reviewsARead-onlyIdempotent
Read individual reviews of a Metacritic entry, from critics or from users. Get the slug and kind from search_titles first. Filter with 'sentiment' to read only what praised or panned it, which Metacritic groups itself. Metacritic returns a fixed sample rather than the full list, so 'total_available' is usually far larger than what comes back, and there is no way to page past the sample. Critic reviews carry the publication and a link to the original article: quote them with both. Critic scores run to 100 and user scores to 10, so do not average the two together.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | Which catalogue the entry belongs to. Pass it back with the slug. | |
| slug | Yes | Identifier from search_titles, such as 'the-matrix'. | |
| limit | No | How many reviews to return from the sample Metacritic publishes. | |
| offset | No | How many of the sampled reviews to skip. The sample itself cannot be paged past. | |
| source | No | 'critic' is the professional press, 'user' is the audience. | critic |
| sentiment | No | Which slice to read. Metacritic groups reviews this way itself. | all |
Output Schema
| Name | Required | Description |
|---|---|---|
| kind | Yes | |
| slug | Yes | |
| notes | Yes | |
| offset | Yes | |
| source | Yes | |
| reviews | Yes | |
| sentiment | Yes | |
| source_url | Yes | Metacritic page these reviews belong to. Cite it when an article link is missing. |
| next_offset | Yes | Pass as 'offset' for the next page. |
| total_available | Yes | How many reviews Metacritic counts in total. Usually far more than it serves here. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations, the description discloses critical behaviors: Metacritic returns a fixed sample, total_available is misleading, pagination cannot go past the sample, and critic vs. user scores use different scales. It also notes that critic reviews include publication and link metadata, which affects how results should be quoted. This is substantial added value.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence carries unique information: purpose, prerequisite, sentiment behavior, sample limitation, citation requirement, and score-scale warning. It is front-loaded with the core purpose before caveats, so an agent can quickly determine relevance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter tool with an output schema, the description covers the required parameters, the prerequisite dependency, filtering semantics, result limitations, and how to handle critic vs. user data. No essential operational detail seems missing, and the output schema can carry the return-shape burden.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds non-obvious semantics: sentiment maps to Metacritic's own grouping, offset only skips within the published sample and cannot extend it, and the score scales differ by source. These enrich the parameter meanings beyond the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Read') with a precise resource ('individual reviews of a Metacritic entry') and names the two review sources (critics or users). It clearly differentiates from siblings like search_titles and browse_titles, which are discovery tools, whereas this is a content retrieval tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells the agent to obtain slug and kind from search_titles first, which is exactly the right prerequisite. It does not explicitly contrast against get_title or browse_titles, but the dependency chain and the filtering guidance make the intended usage clear without exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_titleRead an entryARead-onlyIdempotent
Read one Metacritic entry by slug and kind, both from search_titles. Sections are opt-in because a full entry is large: 'basic' and 'scores' are the default and cover most questions. 'scores' fetches the critic and audience breakdowns, each with its own scale, so never compare the two numbers directly. Sections gate the payload: asking for 'scores' alone returns no description, and 'basic' alone returns no scores. 'networks' lists the broadcasters of a show; 'where_to_watch' costs an extra request and covers films and shows only. A long description paginates: when 'truncated' is true, call again with 'offset' set to 'next_offset'.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | Which catalogue the entry belongs to. Pass it back with the slug. | |
| slug | Yes | Identifier from search_titles, such as 'the-matrix'. | |
| offset | No | Where to resume the description. | |
| sections | No | Which parts to return. Each extra section beyond the default costs a request. | |
| max_chars | No | Character budget for the description. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| title | Yes | |
| awards | No | One tally per ceremony. Metacritic publishes no per-category detail. |
| genres | Yes | |
| offset | Yes | |
| imdb_id | Yes | IMDb identifier, usable to cross-reference other sources. |
| tagline | Yes | |
| networks | No | Broadcasters, for shows. |
| truncated | Yes | |
| production | No | |
| user_score | Yes | |
| description | Yes | |
| next_offset | Yes | Pass as 'offset' to read the rest. |
| total_chars | Yes | Length of the full description. |
| critic_score | Yes | |
| returned_chars | Yes | |
| where_to_watch | No | |
| duration_minutes | Yes | Runtime in minutes for a film, or the length of a typical episode for a show. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses several behaviors beyond the readOnly/idempotent annotations: sections gate the payload, 'scores alone returns no description', 'where_to_watch costs an extra request', and pagination via 'truncated'/'next_offset'. It also adds the critical caveat that critic and audience scales are not comparable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is five dense sentences, each carrying actionable information; core purpose is front-loaded before section behavior and pagination. There is no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and readOnly/idempotent annotations already covering safety, the description supplies the remaining needed operational context: what sections return, request cost, media-type scope, and pagination. An agent has enough to call and iterate on this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though the schema already documents all parameters, the description enriches the sections parameter by explaining the default, the payload-gating effects of each section, and the extra-request cost. It also explains offset/'next_offset' pagination, which the schema does not capture.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with a specific verb and resource ('Read one Metacritic entry by slug and kind') and ties identifiers to search_titles, so an agent knows exactly what the call returns. It does not explicitly contrast itself with sibling tools such as get_reviews or browse_titles, so the differentiation is clear but not fully spelled out.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete selection guidance for sections ('basic and scores are the default and cover most questions'), warns about extra cost for where_to_watch, and notes media-type limitations. It does not explicitly state 'use this instead of get_reviews/browse_titles', so sibling routing is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_titlesSearch MetacriticARead-onlyIdempotent
Search Metacritic for films, shows and games by title. Returns one compact row per match, carrying the critic Metascore, so a question about the critical verdict needs no second call. Search rows carry no audience score: use get_title for that, or browse_titles, which does return it. Use the slug and kind with get_title for the full entry, or with get_reviews for what critics wrote. There is no paging: 'limit' is the only lever, and results always start from the most relevant match. This searches titles only. It cannot find an entry from a plot detail, a person or a studio.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Restrict results to one catalogue. Metacritic searches all three at once. | any |
| limit | No | How many rows to return. | |
| query | Yes | Title or part of one, for example 'the matrix'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| query | Yes | |
| results | Yes | |
| total_available | Yes | How many entries Metacritic counted. For a query of several words it counts entries matching any one of them, so the number is far larger than the useful matches and should not be read as a match count. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as read-only, idempotent, and non-destructive, and the description adds valuable behavioral detail: no audience score in rows, no paging, results always start from the most relevant match, and title-only matching. This goes well beyond what annotations alone convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Each sentence earns its place: the description covers output shape, missing data, sibling routing, paging behavior, and search scope without redundancy. It is dense but well-structured and front-loaded with the core function.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Backed by a full input schema, an output schema, and strong annotations, the description still contributes deep context: return fields, absence of audience score, sibling usage, slug/kind handoff, no-paging behavior, and title-only limitation. An agent has everything needed to select and invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaning beyond the schema by clarifying that limit is the only paging lever, that results start from most relevant matches, and that kind restricts to one catalogue while Metacritic searches all three. This is helpful but not exhaustive, hence 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search Metacritic for films, shows and games by title') and describes the compact row output carrying the critic Metascore. It explicitly differentiates itself from siblings by noting it searches titles only, not plot details, people, or studios.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing guidance: use get_title or browse_titles for audience score, get_title for the full entry, and get_reviews for critic text. It also warns that there is no paging and that limit is the only control, so the agent knows the exact operational constraints.
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.
4 tool updates
v2.0.1- Changed
browse_titles19 fields changed- added
Input schema / properties / offset / maximumAdded value: +9007199254740991 - added
Output schema / properties / genre / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / genre / typeRemoved value: -[ - "string", - "null" -] - changed
Output schema / properties / next_offset / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / offset / maximumAdded value: +9007199254740991 - added
Output schema / properties / offset / minimumAdded value: +-9007199254740991 - added
Output schema / properties / results / items / properties / id / maximumAdded value: +9007199254740991 - added
Output schema / properties / results / items / properties / id / minimumAdded value: +-9007199254740991 - added
Output schema / properties / results / items / properties / metascore / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / metascore / typeRemoved value: -[ - "number", - "null" -] - added
Output schema / properties / results / items / properties / rating / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / rating / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / results / items / properties / release_date / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / release_date / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / results / items / properties / user_score / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / user_score / typeRemoved value: -[ - "number", - "null" -] - changed
Output schema / properties / results / items / properties / year / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / total_available / maximumAdded value: +9007199254740991 - added
Output schema / properties / total_available / minimumAdded value: +-9007199254740991
- Changed
get_reviews18 fields changed- added
Input schema / properties / offset / maximumAdded value: +9007199254740991 - changed
Output schema / properties / next_offset / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / offset / maximumAdded value: +9007199254740991 - added
Output schema / properties / offset / minimumAdded value: +-9007199254740991 - added
Output schema / properties / reviews / items / properties / author / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / reviews / items / properties / author / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / reviews / items / properties / date / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / reviews / items / properties / date / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / reviews / items / properties / publication / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / reviews / items / properties / publication / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / reviews / items / properties / quote / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / reviews / items / properties / quote / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / reviews / items / properties / score / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - removed
Output schema / properties / reviews / items / properties / score / typeRemoved value: -[ - "number", - "null" -] - added
Output schema / properties / reviews / items / properties / url / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / reviews / items / properties / url / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / total_available / maximumAdded value: +9007199254740991 - added
Output schema / properties / total_available / minimumAdded value: +-9007199254740991
- Changed
get_title33 fields changed- added
Input schema / properties / offset / maximumAdded value: +9007199254740991 - changed
Output schema / properties / awards / items / properties / nominations / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - changed
Output schema / properties / awards / items / properties / wins / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - changed
Output schema / properties / critic_score / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "max": { - "description": "Scale this score is on: 100 for critics, 10 for users.", - "type": "number" - }, - "negative_count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ] - }, - "neutral_count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ] - }, - "positive_count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ] - }, - "review_count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ] - }, - "score": { - "type": [ - "number", - "null" - ] - }, - "sentiment": { - "description": "Metacritic's own wording for the verdict.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "score", - "max", - "review_count", - "positive_count", - "neutral_count", - "negative_count", - "sentiment" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "max": { + "description": "Scale this score is on: 100 for critics, 10 for users.", + "type": "number" + }, + "negative_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "neutral_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "positive_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "review_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "score": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "sentiment": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Metacritic's own wording for the verdict." + } + }, + "required": [ + "score", + "max", + "review_count", + "positive_count", + "neutral_count", + "negative_count", + "sentiment" + ], + "type": "object" + }, + { + "type": "null" + } +] - added
Output schema / properties / description / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / description / typeRemoved value: -[ - "string", - "null" -] - changed
Output schema / properties / duration_minutes / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / imdb_id / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / imdb_id / typeRemoved value: -[ - "string", - "null" -] - changed
Output schema / properties / next_offset / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / offset / maximumAdded value: +9007199254740991 - added
Output schema / properties / offset / minimumAdded value: +-9007199254740991 - changed
Output schema / properties / production / items / properties / id / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / returned_chars / maximumAdded value: +9007199254740991 - added
Output schema / properties / returned_chars / minimumAdded value: +-9007199254740991 - added
Output schema / properties / tagline / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / tagline / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / title / properties / id / maximumAdded value: +9007199254740991 - added
Output schema / properties / title / properties / id / minimumAdded value: +-9007199254740991 - added
Output schema / properties / title / properties / metascore / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - removed
Output schema / properties / title / properties / metascore / typeRemoved value: -[ - "number", - "null" -] - added
Output schema / properties / title / properties / rating / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / title / properties / rating / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / title / properties / release_date / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / title / properties / release_date / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / title / properties / user_score / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - removed
Output schema / properties / title / properties / user_score / typeRemoved value: -[ - "number", - "null" -] - changed
Output schema / properties / title / properties / year / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / total_chars / maximumAdded value: +9007199254740991 - added
Output schema / properties / total_chars / minimumAdded value: +-9007199254740991 - changed
Output schema / properties / user_score / anyOfPrevious value: -[ - { - "$ref": "#/properties/critic_score/anyOf/0" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "max": { + "description": "Scale this score is on: 100 for critics, 10 for users.", + "type": "number" + }, + "negative_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "neutral_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "positive_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "review_count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "score": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "sentiment": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Metacritic's own wording for the verdict." + } + }, + "required": [ + "score", + "max", + "review_count", + "positive_count", + "neutral_count", + "negative_count", + "sentiment" + ], + "type": "object" + }, + { + "type": "null" + } +] - added
Output schema / properties / where_to_watch / items / properties / url / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / where_to_watch / items / properties / url / typeRemoved value: -[ - "string", - "null" -]
- Changed
search_titles13 fields changed- added
Output schema / properties / results / items / properties / id / maximumAdded value: +9007199254740991 - added
Output schema / properties / results / items / properties / id / minimumAdded value: +-9007199254740991 - added
Output schema / properties / results / items / properties / metascore / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / metascore / typeRemoved value: -[ - "number", - "null" -] - added
Output schema / properties / results / items / properties / rating / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / rating / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / results / items / properties / release_date / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / release_date / typeRemoved value: -[ - "string", - "null" -] - added
Output schema / properties / results / items / properties / user_score / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - removed
Output schema / properties / results / items / properties / user_score / typeRemoved value: -[ - "number", - "null" -] - changed
Output schema / properties / results / items / properties / year / anyOfPrevious value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } +] - added
Output schema / properties / total_available / maximumAdded value: +9007199254740991 - added
Output schema / properties / total_available / minimumAdded value: +-9007199254740991
4 tool updates
v1.0.0- First observed
browse_titles - First observed
get_reviews - First observed
get_title - First observed
search_titles
TDQS
Scored across 4 tools
Each tool targets a distinct operation: keyword title lookup, full entry retrieval, review reading, and browse/filter lists. Although search_titles and browse_titles both return title rows, their triggers are clearly separated by whether a specific title is known.
All tool names follow a consistent verb_noun snake_case pattern: search_titles, get_title, get_reviews, and browse_titles. The naming style is uniform and predictable.
Four tools is well-scoped for the server's purpose, covering search, browsing, detail retrieval, and reviews without redundancy. Each tool earns its place in the workflow.
The set covers the core Metacritic query lifecycle: searching titles, browsing by score/date/popularity, fetching full entries, and reading reviews. Documented limitations such as fixed review samples are platform constraints rather than missing tool operations.
Maintenance
Related MCP Connectors
Unlock a world of television with the TV Maze MCP server. Effortlessly search for shows by name or
OMDb MCP — IMDB-derived movie / TV / episode data (BYO key)
Reddit MCP server: search posts, subreddit feeds, comments & user profiles as JSON. No API key.
TheGamesDB MCP — wraps TheGamesDB API (thegamesdb.net), a community
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceA comprehensive MCP server that enables users to interact with Letterboxd for searching films, viewing member data, and performing actions like rating or reviewing movies. It supports private data access and automated browser actions for managing watchlists, diaries, and custom lists.1-
- FlicenseAqualityDmaintenanceAn MCP server that wraps The Movie Database (TMDB) API, enabling search for movies and TV shows, retrieval of movie details, recommendations, similar movies, trending content, streaming providers, and movie discovery.8-
- FlicenseAqualityDmaintenanceAn MCP server that wraps the TMDB API, enabling search of movies and TV shows, retrieval of details, trending titles, recommendations, and streaming provider information.8-
- AlicenseAqualityCmaintenanceMCP server for the OMDb API - search movies, series, and episodes from any MCP-compatible client.5MIT