mcp-recipes
A no-API-key MCP server that reads recipes from six sites (Marmiton, Ptitchef, Supertoinette, Wikibooks Cookbook, BBC Good Food, Pequerecetas) and renders them in one shape.
search_recipes— search all six sources at once with one free-text dish or ingredient; returns merged rows with source-taggedids, plus per-source status, counts, and derived wordings.get_recipe— read one recipe in full from any source by itsid: ingredients, steps, yield, times, nutrition, tips, equipment; optionally rescale to a serving count, and pick which sections to return.compare_recipes— put several sources' versions of the same dish side by side at equal servings, listing what differs without ranking them.scale_ingredients— rescale any ingredient list offline (by factor, or from/to servings) in French, English, or Spanish, with kitchen-sensible rounding.Understands refusal and comparison nuance — never sums counts across sources, distinguishes a failed source from an empty one, keeps units in their original system, and flags subscriber-only content rather than reconstructing it.
Allows searching and reading recipes from the Wikibooks Cookbook, including rescaling ingredient quantities and comparing recipes across sources.
Click on "Deploy 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-recipessearch for a recipe for tarte tatin and scale to 6"
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-recipes
Recipes live on many sites, and each one writes them its own way: a French cooking site publishes in French, with its own measures and its own idea of what a serving is, and a wiki cookbook in English, with equipment lists and prose the first has no field for. Asking a question of one of them answers about one of them.
This server reads six. Three publish in French, Marmiton, Ptitchef and Supertoinette; two in English, the Wikibooks Cookbook and BBC Good Food; and one in Spanish, Pequerecetas. You can search them all with one question, read a recipe from any of them in one shape, put several versions of the same dish side by side, and rescale any ingredient list. It needs no API key and no account.
Install
One-click install
Claude Code
claude mcp add recipes -- npx -y mcp-recipesClaude Desktop, Cursor, and any client using the standard config format
{
"mcpServers": {
"recipes": {
"command": "npx",
"args": ["-y", "mcp-recipes"]
}
}
}Node 24 or later is required, and no environment variable has to be set.
With Docker
{
"mcpServers": {
"recipes": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-recipes:4.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.marmiton.org, api.wikimedia.org, www.ptitchef.com,
www.bbcgoodfood.com, www.supertoinette.com and www.pequerecetas.com, and
nothing else: no volume, no port, no credential.
Bundle, without npm
Download mcp-recipes-4.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: mealplan-mcp
What you can ask
"Find me carbonara recipes, from wherever you can."
"Compare the French and English versions of that dish."
"Read the second one for eight people."
"Which of them uses cream?"
"Scale this list from my notebook by 1.5."
The ordinary path runs from a search to a reading: a row carries an id naming
its source, and get_recipe takes it.
The sources
Source | Site | Language |
|
| French |
| Wikibooks Cookbook | English |
|
| French |
|
| English |
|
| French |
|
| Spanish |
A row's id names its source, so an identifier read from one answer goes back to
the right site. Counts are never added across sources, and a source that
failed is reported as having failed rather than as having found nothing.
Each site is read at its own pace: two of them ask for three seconds between requests, and a setting posted for all of them can only make this server more patient than the slowest asks for.
Two of these sites file something other than a recipe at the address a recipe
lives at. The Wikibooks Cookbook keeps pages about an ingredient beside the
recipes using it, and Pequerecetas publishes articles that gather recipes. A
search says so, and get_recipe says what it read off the page.
Tools
Tool | What it does |
| Searches every source with one question. |
| Reads one recipe from any source, in one shape. |
| Puts several versions of the same dish side by side. |
| Rescales any ingredient list, with no request to any site. |
search_recipes
Searches every source with one question.
Argument | Type | Required | What it does |
| string, 1 to 200 characters | yes | The dish or the ingredient to look for. |
| integer, 1 to 25, default | no | Rows to keep from each source. |
| array of source ids | no | Ask these sources alone. |
| boolean, default | no | Also send each source shorter wordings derived from the question. |
In return: results, rows carrying id, which get_recipe takes;
source and source_name saying which site published the row; title; url;
image_url; and an excerpt where the source offers one. per_source gives one
report per site with its status, reading answered or failed, the count it
contributed, and its reported_total alongside reported_total_means, which
says what that number counts on that site. names_the_dish says how many of that
site's rows carry the dish in their title, out of count: a search index answers
the words it is handed, so a site can offer rows and none of them be the dish.
order says in words how the list was built.
get_recipe
Reads one recipe from any source, in one shape.
Argument | Type | Required | What it does |
| string, 1 to 500 characters | yes | The identifier a row carries, such as |
| integer, 1 to 500 | no | Rescale the ingredients to this many servings. |
| array of | no | Which parts to return. |
| integer, 1 to 100, default | no | Steps to serve. |
| integer, 1 to 500, default | no | Recipes and headings to return from an address that gathers recipes. |
| integer, 80 to 4000, default | no | Characters kept per step. |
In return: kind says what the address held. A recipe answer carries
recipe and no collection; a collection answer carries collection and no
recipe, and is an article gathering other recipes, with the headings it is
built from and the recipes it points at, each of them readable with
get_recipe.
A recipe comes in the shape every source is rendered into, whichever
published it: its title, its address, its ingredients with each line's scaling
and is_equipment,
its steps, and the sections asked for. A field one source publishes and another
has no notion of comes back absent rather than invented. rest_minutes carries a
resting time from a source that prints one apart, and is in no other time here.
steps_as_one_block says when a source published its method as one block of
prose rather than as steps. withheld names a part a source keeps for its
subscribers, which is a part the page has rather than a part that could not be
read. scaling_summary counts the lines four ways, and the four add up to the
list. Raise max_step_chars when a step was cut mid-sentence.
compare_recipes
Puts several versions of the same dish side by side.
Argument | Type | Required | What it does |
| string, 1 to 200 characters | yes | The dish to compare. |
| integer, 1 to 500 | no | Rescale every version to this many servings. |
| array of | no | Which parts to return per version. |
| integer, 1 to 100, default | no | Steps to serve per version. |
| integer, 80 to 4000, default | no | Characters kept per step. |
| array of source ids | no | Compare these sources alone. |
In return: versions, one recipe per source that answered, all rescaled to
the same number of servings so their quantities can be read against each other,
and differences, what separates them. per_source reports each site as a
search does.
scale_ingredients
Rescales any ingredient list, with no request to any site.
Argument | Type | Required | What it does |
| array of 1 to 200 lines | yes | The lines to rescale. |
| number, up to 1000 | one of two | The multiplier to apply. |
| integer, 1 to 500 | one of two | How many servings the list is written for. |
| integer, 1 to 500 | one of two | How many servings are wanted. |
|
| no | How each line is read. |
Pass factor, or the from_servings and to_servings pair. auto reads each
line on its own, which is what a list holding more than one language needs; naming a
language reads every line that way.
In return: the rescaled lines in the shape get_recipe returns, each with
its scaling.
Rescaling the quantities
A quantity is stated in the unit that suits it, so a line can come back in a
different unit from the one the recipe used: 200 g multiplied by twenty reads
4 kg.
How finely an ingredient can be divided depends on what it is. A baguette can be
cut in two, in three or in four; an egg cannot be shared out. A quantity landing
between the two is rounded, and the rescaled recipe then departs a little from
the proportions of the original. The line carries rounded, and its note says
what was done.
The sources write their quantities in their own languages, and a line is read in the language it was written in. The figures are this server's arithmetic, so say they were recomputed when you show them.
What an answer states about the sources
Every answer accounts for each source separately. A site that failed, one nobody asked, and one that answered with nothing are three different things, and they are reported as three. A total stays beside the source that published it, with what that source counts when it says it: one site counts a whole category, another counts the rows it served, and a third publishes no total at all.
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 sites, with an address where a person can be reached. |
|
| Gap between two requests to one site, from 500 to 60000. |
|
| Deadline for one request, from 1000 to 120000. |
|
| Attempts after a transient failure, from 0 to 8. |
|
| How long an answer stays in memory, from 0 to 86400000. |
|
| Answers held in memory at once, from 1 to 5000. |
|
|
|
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 |
| A source answered, and holds no such recipe. | Check the identifier with |
| The arguments were refused before any request went out. | Read the message, which names the argument. |
| A source asked this client to slow down. | Wait, then call again with the same arguments. The recipe is still there. |
| A page loaded and the expected content was absent. | Report it at the issue tracker. |
| The request did not complete. | Try again shortly. |
| The request passed its deadline. | Raise |
A source that failed is reported per source rather than failing the whole answer, so one silent site never hides the other.
As a library
The layer reading the sites is published on its own, with its pacing, its cache and its errors, and with no protocol attached.
import { RecipesClient } from "mcp-recipes/client";
const client = new RecipesClient();
const { rows, reports } = await client.searchRecipes("carbonara", 3);
console.log(
rows.length,
reports.map((report) => report.status),
);
const { recipe } = await client.getRecipe(rows[0].id);searchRecipes(query, limitPerSource, sources?, options?) answers
{ rows, reports }: one report per source, saying whether it answered and what
its own count measured, so a source that failed is never read as a source that
holds nothing. getRecipe(id) answers { recipe, cached, read }, and throws an
error carrying one of the six codes. client.profiles lists the sources the
build registers.
The scaler is published separately at mcp-recipes/scale, and works offline on
any list:
import { scaleIngredients } from "mcp-recipes/scale";
scaleIngredients(["200 g de harina", "4 oeufs", "1 cup milk"], { factor: 2 });Each site keeps its own pace, and the floors hold here as well.
Pacing and attribution
Each site is paced on its own, one request at a time. Every site holds a floor
of its own that no configuration goes under: half a second for Marmiton and the
Cookbook, one second for Ptitchef and BBC Good Food, three seconds for
Supertoinette and Pequerecetas. A setting posted for every source can raise a
site's spacing and never lower it. Asking every site at once therefore costs each of them one request,
never two. The User-Agent always ends with the project identity and an address
where a person can be reached.
Every row carries the address of the recipe's own page and the name of the site that published it. The Cookbook pages are published under CC BY-SA 4.0, which asks that what is built on them be shared under the same licence. Marmiton, Ptitchef, BBC Good Food, Supertoinette and Pequerecetas state no terms on a recipe page, and their recipes belong to those sites and to the cooks who wrote them. Silence is not a grant, so credit the site and link the page you took a recipe from.
A recipe BBC Good Food keeps for its subscribers comes back without its ingredients and its method, named as a recipe held back, with the address of its page. This server does not reconstruct what that site chose to sell.
Two figures the sites publish are not repeated here. A difficulty is a word each site writes its own way, on no scale any of them publishes, so it sits on no axis along which two versions could be put. A cost is a price in euros on one site and a rank inside its own list on another, and one field holding both would invite them to be compared.
This MCP server is an unofficial project, with no affiliation to any of the sites it reads.
Privacy
This server collects nothing about you and sends nothing to its author. It runs
on your machine, contacts www.marmiton.org, api.wikimedia.org,
www.ptitchef.com, www.bbcgoodfood.com, www.supertoinette.com and
www.pequerecetas.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
sites themselves.
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 recipes belong to the sites that published them and to their authors.
mcp-recipes (français)
Les recettes vivent sur beaucoup de sites, et chacun les écrit à sa façon : un site de cuisine français publie en français, avec ses mesures et son idée de ce qu'est une part, et un wiki de cuisine en anglais, avec des listes de matériel et une prose pour lesquelles le premier n'a aucun champ. Poser une question à l'un d'eux répond au sujet de l'un d'eux.
Ce serveur en lit six. Trois publient en français, Marmiton, Ptitchef et Supertoinette ; deux en anglais, le Cookbook des Wikibooks et BBC Good Food ; un en espagnol, Pequerecetas. On peut chercher dans les six avec une seule question, lire une recette de n'importe lequel sous une seule forme, mettre plusieurs versions d'un même plat côte à côte, et adapter n'importe quelle liste d'ingrédients. Aucune clé d'API, aucun compte.
Installation
Installation en un clic
Claude Code
claude mcp add recipes -- npx -y mcp-recipesClaude Desktop, Cursor, et tout client au format de configuration standard
{
"mcpServers": {
"recipes": {
"command": "npx",
"args": ["-y", "mcp-recipes"]
}
}
}Node 24 ou plus récent est nécessaire, et aucune variable d'environnement n'est à renseigner.
Avec Docker
{
"mcpServers": {
"recipes": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-recipes:4.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.marmiton.org, api.wikimedia.org, www.ptitchef.com,
www.bbcgoodfood.com, www.supertoinette.com et www.pequerecetas.com, et de
rien d'autre : aucun volume, aucun port, aucun identifiant.
Bundle, sans npm
Téléchargez mcp-recipes-4.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
« Trouve-moi des recettes de carbonara, d'où que tu puisses. »
« Compare les versions française et anglaise de ce plat. »
« Lis-moi la seconde pour huit personnes. »
« Laquelle utilise de la crème ? »
« Multiplie par 1,5 cette liste de mon carnet. »
Le chemin ordinaire va d'une recherche à une lecture : une ligne porte un id
qui nomme sa source, et get_recipe le reprend.
Les sources
Source | Site | Langue |
|
| français |
| Cookbook Wikibooks | anglais |
|
| français |
|
| anglais |
|
| français |
|
| espagnol |
L'id d'une ligne nomme sa source, donc un identifiant lu dans une réponse
retourne vers le bon site. Les comptes ne sont jamais additionnés entre
sources, et une source qui a échoué est rapportée comme ayant échoué plutôt que
comme n'ayant rien trouvé.
Chaque site est lu à son propre rythme : deux d'entre eux demandent trois secondes entre deux requêtes, et un réglage posé pour tous ne peut que rendre ce serveur plus patient que ce que le plus lent demande.
Deux de ces sites rangent autre chose qu'une recette à l'adresse où vit une
recette. Le Cookbook des Wikibooks garde des pages sur un ingrédient à côté des
recettes qui l'emploient, et Pequerecetas publie des articles qui rassemblent
des recettes. Une recherche le dit, et get_recipe dit ce qu'il a lu sur la
page.
Les outils
Outil | Ce qu'il fait |
| Cherche dans toutes les sources avec une seule question. |
| Lit une recette de n'importe quelle source, sous une seule forme. |
| Met plusieurs versions d'un même plat côte à côte. |
| Adapte n'importe quelle liste d'ingrédients, sans requête. |
search_recipes
Cherche dans toutes les sources avec une seule question.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 1 à 200 caractères | oui | Le plat ou l'ingrédient cherché. |
| entier, 1 à 25, défaut | non | Lignes à garder de chaque source. |
| tableau d'identifiants de source | non | N'interroger que ces sources. |
| booléen, défaut | non | Envoyer aussi à chaque source des formulations plus courtes tirées de la question. |
En retour : results, des lignes portant id, que get_recipe reprend ;
source et source_name qui disent quel site a publié la ligne ; title ;
url ; image_url ; et un excerpt là où la source en propose un.
per_source donne un rapport par site avec son status, valant answered ou
failed, le count qu'il a fourni, et son reported_total accompagné de
reported_total_means, qui dit ce que ce nombre compte sur ce site.
names_the_dish dit combien des lignes de ce site portent le plat dans leur
titre, sur count : un index de recherche répond aux mots qu'on lui tend, donc
un site peut rendre des lignes dont aucune n'est le plat. order dit en mots
comment la liste a été bâtie.
get_recipe
Lit une recette de n'importe quelle source, sous une seule forme.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 1 à 500 caractères | oui | L'identifiant d'une ligne, tel que |
| entier, 1 à 500 | non | Adapte les ingrédients à ce nombre de parts. |
| tableau de | non | Les parties à rendre. |
| entier, 1 à 500, défaut | non | Recettes et intertitres rendus pour une adresse qui rassemble des recettes. |
| entier, 1 à 100, défaut | non | Étapes à servir. |
| entier, 80 à 4000, défaut | non | Caractères gardés par étape. |
En retour : kind dit ce que l'adresse portait. Une réponse recipe porte
recipe et pas de collection ; une réponse collection porte collection et
pas de recipe, et c'est un article rassemblant d'autres recettes, avec les
headings dont il est bâti et les recipes vers lesquelles il pointe, chacune
lisible par get_recipe.
Une recette vient dans la forme où toutes les sources sont rendues, quelle
que soit celle qui l'a publiée : son titre, son adresse, ses ingrédients avec le
scaling et l'is_equipment de chaque ligne, ses étapes, et les parties
demandées. Un champ qu'une
source publie et dont une autre n'a pas la notion revient absent plutôt
qu'inventé. rest_minutes porte le temps de repos d'une source qui l'imprime à
part, et il n'entre dans aucun autre temps rendu ici. steps_as_one_block dit
quand une source a publié sa méthode d'un seul bloc de prose plutôt qu'en étapes.
scaling_summary compte les lignes de quatre façons, et les quatre font le
total de la liste. withheld nomme la partie qu'une source réserve à ses abonnés, qui est une
partie que la page porte et non une partie illisible. Augmentez max_step_chars
quand une étape a été coupée au milieu d'une phrase.
compare_recipes
Met plusieurs versions d'un même plat côte à côte.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 1 à 200 caractères | oui | Le plat à comparer. |
| entier, 1 à 500 | non | Adapte chaque version à ce nombre de parts. |
| tableau de | non | Les parties à rendre par version. |
| entier, 1 à 100, défaut | non | Étapes à servir par version. |
| entier, 80 à 4000, défaut | non | Caractères gardés par étape. |
| tableau d'ids de source | non | Ne compare que ces sources. |
En retour : versions, une recette par source ayant répondu, toutes adaptées
au même nombre de parts pour que leurs quantités se lisent l'une contre l'autre,
et differences, ce qui les sépare. per_source rapporte chaque site comme le
fait une recherche.
scale_ingredients
Adapte n'importe quelle liste d'ingrédients, sans requête à aucun site.
Argument | Type | Requis | Ce qu'il fait |
| tableau de 1 à 200 lignes | oui | Les lignes à adapter. |
| nombre, jusqu'à 1000 | l'un des deux | Le multiplicateur à appliquer. |
| entier, 1 à 500 | l'un des deux | Le nombre de parts de la liste d'origine. |
| entier, 1 à 500 | l'un des deux | Le nombre de parts voulu. |
|
| non | Comment chaque ligne est lue. |
Passez factor, ou le couple from_servings et to_servings. auto lit chaque
ligne pour elle-même, ce dont a besoin une liste portant plusieurs langues ;
nommer une langue lit toutes les lignes ainsi.
En retour : les lignes adaptées dans la forme que rend get_recipe, chacune
avec son scaling et son is_equipment, vrai pour une ligne qui nomme un outil
et qu'on laisse telle quelle. scaled_count, rounded_count, unscaled_count
et equipment_count font le total des lignes envoyées.
L'adaptation des quantités
Une quantité est exprimée dans l'unité qui lui convient. Après adaptation, une
ligne peut donc apparaître dans une autre unité que celle de la recette : 200 g
multipliés par vingt donnent 4 kg.
La finesse à laquelle un ingrédient se coupe dépend de sa nature. Une baguette se
coupe en deux, en trois ou en quatre ; un oeuf ne se partage pas. Une quantité
qui tombe entre les deux est donc arrondie, et la recette adaptée s'écarte alors
un peu des proportions de l'originale. La ligne porte rounded, et sa note dit
ce qui a été fait.
Les sources écrivent leurs quantités dans leur propre langue, et une ligne est lue dans la langue où elle a été écrite. Les chiffres sont l'arithmétique de ce serveur, donc dites qu'ils ont été recalculés quand vous les montrez.
Ce qu'une réponse dit des sources
Chaque réponse rend compte de chaque source séparément. Un site qui a échoué, un que personne n'a interrogé et un qui a répondu vide sont trois choses différentes, et elles sont rapportées comme trois. Un total reste à côté de la source qui l'a publié, avec ce que cette source compte en le disant : l'un compte une catégorie entière, un autre compte les lignes qu'il a servies, et un troisième ne publie aucun total.
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 des sites, avec une adresse où joindre une personne. |
|
| Écart entre deux requêtes vers un même site, de 500 à 60000. |
|
| Délai d'une requête, de 1000 à 120000. |
|
| Tentatives après un échec passager, de 0 à 8. |
|
| Durée pendant laquelle une réponse reste en mémoire, de 0 à 86400000. |
|
| Réponses gardées en mémoire à la fois, de 1 à 5000. |
|
|
|
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 |
| Une source a répondu, et n'a pas cette recette. | Vérifiez l'identifiant avec |
| Les arguments ont été refusés avant toute requête. | Lisez le message, qui nomme l'argument. |
| Une source demande à ce client de ralentir. | Attendez, puis rappelez avec les mêmes arguments. La recette est toujours là. |
| Une page a chargé et le contenu attendu est absent. | 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 |
Une source qui échoue est rapportée source par source plutôt que de faire échouer toute la réponse, donc un site silencieux n'en cache jamais un autre.
Comme bibliothèque
La couche qui lit les sites est publiée seule, avec son rythme, son cache et ses erreurs, sans protocole attaché.
import { RecipesClient } from "mcp-recipes/client";
const client = new RecipesClient();
const { rows, reports } = await client.searchRecipes("carbonara", 3);
console.log(
rows.length,
reports.map((report) => report.status),
);
const { recipe } = await client.getRecipe(rows[0].id);searchRecipes(query, limitPerSource, sources?, options?) répond
{ rows, reports } : un rapport par source, disant si elle a répondu et ce que
son propre compte mesure, pour qu'une source en échec ne se lise jamais comme une
source qui ne détient rien. getRecipe(id) répond { recipe, cached, read }, et
lève une erreur portant un des six codes. client.profiles énumère les sources
que cette construction enregistre.
La mise à l'échelle est publiée à part, sous mcp-recipes/scale, et travaille
hors ligne sur n'importe quelle liste :
import { scaleIngredients } from "mcp-recipes/scale";
scaleIngredients(["200 g de harina", "4 oeufs", "1 cup milk"], { factor: 2 });Chaque site garde son propre rythme, et les planchers tiennent également ici.
Rythme et attribution
Chaque site est cadencé pour lui-même, une requête à la fois. Chaque site tient
un plancher qui lui est propre et sous lequel aucune configuration ne descend :
une demi-seconde pour Marmiton et le Cookbook, une seconde pour Ptitchef et BBC
Good Food, trois secondes pour Supertoinette et Pequerecetas. Un réglage posé
pour toutes les sources peut élargir l'écart d'un site, jamais le réduire. Les interroger toutes à la fois coûte donc à chacune une requête,
jamais deux. Le User-Agent se termine toujours par l'identité du projet et une
adresse où joindre une personne.
Chaque ligne porte l'adresse de la page de la recette et le nom du site qui l'a publiée. Les pages du Cookbook sont publiées sous CC BY-SA 4.0, qui demande que ce qu'on bâtit dessus soit partagé sous la même licence. Marmiton, Ptitchef, BBC Good Food, Supertoinette et Pequerecetas n'énoncent aucune condition sur une page de recette, et leurs recettes appartiennent à ces sites et aux cuisiniers qui les ont écrites. Le silence n'est pas une autorisation : créditez le site et liez la page d'où vient la recette.
Une recette que BBC Good Food réserve à ses abonnés revient sans ses ingrédients ni sa méthode, nommée comme une recette retenue, avec l'adresse de sa page. Ce serveur ne reconstitue pas ce que ce site a choisi de vendre.
Deux chiffres que les sites publient ne sont pas repris ici. Une difficulté est un mot que chaque site écrit à sa façon, sur aucune échelle publiée : elle ne siège sur aucun axe le long duquel deux versions se compareraient. Un coût est un prix en euros sur un site et un rang dans sa propre liste sur un autre, et un seul champ portant les deux inviterait à les comparer.
Ce MCP est un projet non officiel, sans affiliation à aucun des sites qu'il lit.
Confidentialité
Ce serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur
votre machine, ne joint que www.marmiton.org, api.wikimedia.org,
www.ptitchef.com, www.bbcgoodfood.com, www.supertoinette.com et
www.pequerecetas.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 les sites eux-mêmes.
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 recettes appartiennent aux sites qui les ont publiées et à leurs auteurs.
Available Tools
4 toolscompare_recipesOne dish, as each source writes itARead-onlyIdempotent
Take a dish and show how each source writes it, side by side. Each source's closest match is read in full, and all of them can be rescaled to the same number of servings so the ingredient lists stand comparison. The answer states what differs and leaves it there: the quantities each asks for, what each measures in, what each yields, and which fields each source publishes at all. No version is ranked above another. Some sources carry reader ratings and some have no author and no rating by nature, so there is no score they share. When only one source answers, the answer names that source and shows its version, so a single version is read as the one it is.
| Name | Required | Description | Default |
|---|---|---|---|
| dish | Yes | The dish to compare, in any of the languages the sources publish in. | |
| sources | No | Sources to compare, by id. Left out, every source is read, which is the point of this tool. Name two or three to put particular traditions side by side. The ids are the ones 'per_source' reports. | |
| sections | No | Which parts of each version to return. Several full recipes is a lot of text. | |
| servings | No | Rescale every version to this many, which is what makes the lists comparable. | |
| max_steps | No | Steps to return per version. | |
| max_step_chars | No | Characters kept per step. Raise it only if a step was cut mid-sentence. |
Output Schema
| Name | Required | Description |
|---|---|---|
| dish | Yes | |
| notes | Yes | |
| versions | Yes | |
| per_source | Yes | |
| differences | Yes | What differs between the versions, stated as fact. No version is ranked. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, open-world and non-destructive, so safety is covered. The description adds genuinely non-obvious behavior: no version is ranked, the sources share no comparable score, the result enumerates what differs and which fields each source publishes, and a lone source yields its version plainly. It doesn't touch cost/latency of reading several full recipes, though it does flag that sections defaults to ingredients because 'several full recipes is a lot of text.'
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?
Purpose is front-loaded and the sentences are ordered from what the tool does to how results behave. A few clauses are ornamental or circular, notably 'so a single version is read as the one it is,' which could be cut without loss.
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, the description needn't explain the return shape, and it still characterizes the response contents (quantities, units, yields, published fields) and the no-ranking contract. The single-source fallback is covered, so an agent has enough to call it confidently.
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 schema already carries parameter meaning. The description restates the servings rescaling rationale, which the schema also states, and adds nothing about sources ids or section selection beyond what the properties document — a baseline 3.
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 opening sentence gives a concrete verb and resource — take a dish and show how each source writes it, side by side — which is clearly distinct from get_recipe (one version) and scale_ingredients (numeric scaling). An agent can route among the siblings on the strength of the first line alone.
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 the multi-source default ('Left out, every source is read, which is the point of this tool') and the narrowing case ('Name two or three to put particular traditions side by side'), plus the degenerate single-source outcome. It never explicitly names search_recipes or get_recipe as the alternative to pick instead, so the routing is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recipeRead one recipe, optionally rescaledARead-onlyIdempotent
Read one recipe in full: its ingredients, its steps, what it yields, and whatever times, rating and nutrition its source publishes. 'id' must come from search_recipes. It names the source, so this reads the right one without guessing; an identifier no source would have minted is refused, because sending it anywhere would answer about the wrong dish. Pass 'servings' to rescale. Quantities land where a kitchen can follow them: an egg stays whole, anything that pours or cuts can halve, a small measurement moves to a smaller unit before it is rounded, and anything unmultipliable comes back flagged. A page that states no number of servings comes back as published and says so, because dividing by a yield nobody wrote would answer for a number of people the page never claimed. Read 'kind' first. Some sources publish articles gathering other recipes at the same kind of address as a recipe, and such an answer carries 'collection' with the recipes it points at and no 'recipe' at all: there is nothing to cook from that page, and the recipes it lists are read with get_recipe. A part this answer holds nothing for says which of two things happened: the page showed no sign of it, or this server failed to read what the page carries. An empty ingredient list is never evidence that an ingredient is absent from the dish. 'sections' decides what comes back, and 'sections_omitted' names what was left out: a field belonging to an omitted section is empty because nobody asked for it, never because the page states nothing. A field a source does not publish is null, never zero. Credit the source and link the url when you repeat any of it.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | From search_recipes, such as 'marmiton:44078', 'pequerecetas:paella-de-marisco' or 'cookbook:Cookbook:Carbonara'. Two sources address a recipe by a bare number, so spell an id with its source. | |
| sections | No | Which parts to return. A full recipe is a lot of text, so this defaults to two. | |
| servings | No | Rescale to this many. Left out, the quantities come back as published. | |
| max_steps | No | Steps to return. The answer says how many more there are. | |
| max_gathered | No | Recipes and headings to return from an address that gathers recipes. The answer says how many the article holds. | |
| max_step_chars | No | Characters kept per step. Raise it only if a step was cut mid-sentence. |
Output Schema
| Name | Required | Description |
|---|---|---|
| kind | Yes | What the address held. 'recipe' comes with 'recipe' and no 'collection'; 'collection' comes with 'collection' and no 'recipe', and is an article gathering other recipes, with no ingredients and no method of its own. |
| notes | Yes | |
| recipe | No | Present when 'kind' is 'recipe'. |
| collection | No | Present when 'kind' is 'collection'. |
| id_read_as | Yes | How a raw identifier was routed, when it was not spelled with its source. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses substantial behavior: invalid ids are refused, rescaling preserves whole eggs and flags unmultipliable quantities, missing serving counts are returned as published, collection pages return no recipe, empty sections distinguish 'not present' from 'server failed to read', and null never means zero. This is unusually rich behavioral context for a read-only tool.
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 front-loaded with the purpose and then moves through id, rescaling, kind, sections, and null semantics in a logical order. It is long and occasionally circuitous, but nearly every sentence carries distinct operational information rather than repeating schema or annotations.
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?
Given six parameters, full schema coverage, an output schema, and read-only annotations, the description covers the remaining behavioral and semantic gaps an agent needs: source-qualified ids, rescaling edge cases, collection pages, omitted sections, null versus zero, and attribution expectations.
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, but the description adds real meaning for key parameters: id provenance and source naming, servings rescaling rules, and sections/sections_omitted semantics. It does not add prose about max_steps, max_gathered, or max_step_chars, leaving those to 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 opens with a specific verb and resource: 'Read one recipe in full', then enumerates what is returned (ingredients, steps, yield, times, rating, nutrition). It clearly distinguishes this from search_recipes by requiring an id from that tool, and it explains the collection-page case where get_recipe is still the reader.
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 clear usage context: the id must come from search_recipes, 'kind' should be read first, collection results point to recipes that are read with get_recipe, and 'servings' triggers rescaling. It does not explicitly compare against sibling tools like scale_ingredients or compare_recipes, so it stops short of full when/when-not/alternatives guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scale_ingredientsScale an ingredient list, in any language it readsARead-onlyIdempotent
Multiply a list of ingredient lines, in French, in English, in Spanish, or in a list holding more than one of them. Give either 'factor', or 'from_servings' and 'to_servings' and the factor is worked out from them. Quantities land where a kitchen can follow them: an egg stays whole because half of one is not something a cook takes out of the shell, while anything that pours, weighs or cuts can halve, a spoonful shrinks into the smaller spoon before it is rounded, and a pinch keeps whatever size a hand gives it while its count is multiplied. Every line comes back with 'scaling': 'scaled' when the arithmetic landed on the exact product, 'rounded' when something had to move for the line to stay usable, 'unscaled' when the line carries nothing to multiply. A rounded line says what it was rounded from and in which direction. No quantity is converted between measuring systems: grams stay grams and cups stay cups, because a conversion changes what the recipe said.
| Name | Required | Description | Default |
|---|---|---|---|
| factor | No | What to multiply by. Give exactly one of: 'factor', or both 'from_servings' and 'to_servings'. | |
| language | No | 'auto' reads each line on its own, which is what a list holding more than one language needs. Name a language to read every line that way. | auto |
| ingredients | Yes | The lines as written, one ingredient each, such as '200 g de farine' or '3 eggs'. | |
| to_servings | No | What it should serve. Give it with 'from_servings', and without 'factor'. | |
| from_servings | No | What the list serves now. Give it with 'to_servings', and without 'factor'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| factor | Yes | What every quantity was multiplied by. |
| language | Yes | How the lines were read. |
| ingredients | Yes | |
| scaled_count | Yes | Lines whose arithmetic came out exact. |
| rounded_count | Yes | Lines whose value moved to stay usable. |
| unscaled_count | Yes | Lines carrying nothing that can be multiplied, tools excepted. |
| equipment_count | Yes | Lines naming a tool, which are never multiplied: a recipe made for more people uses the same pan. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description goes well beyond them: it explains rounding policy per ingredient class (whole eggs, pourables, spoonfuls, pinches), the three 'scaling' statuses with direction of rounding, and the deliberate no-unit-conversion rule. That is exactly the behavioral context an agent needs to predict output.
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?
Purpose and input modes are front-loaded, then behavior, then the unit-conversion caveat. The kitchen imagery (egg, pinch, spoon) is longer than strictly necessary but each clause carries a distinct rounding rule, so little is pure waste.
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, the description need not explain return shapes, yet it still documents the 'scaling' status vocabulary and rounding provenance. For a 5-parameter, language-aware transformation tool, nothing an agent needs to call it correctly 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 coverage is 100%, so the baseline is 3; the description still adds value by stating the mutual exclusivity of 'factor' versus the servings pair and clarifying that the factor is derived in the second case. It largely restates the schema's own wording, so it does not fully clear the bar for a 5.
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 ('Multiply a list of ingredient lines') plus the language scope, which no sibling (search_recipes, get_recipe, compare_recipes) covers. An agent can immediately tell this is the scaling tool rather than a 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?
Gives clear operating context: supply either 'factor' or the 'from_servings'/'to_servings' pair, and name a language or let 'auto' read each line. It never names an alternative tool or an exclusion case, but the operation is unambiguous enough that sibling routing is not really at stake.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_recipesSearch every recipe source at onceARead-onlyIdempotent
Search every recipe source this server reads, at the same time, for a dish or an ingredient, and get one merged list. Each row carries the id get_recipe takes, and that id names the source it came from, so nothing has to be guessed afterwards. The sources are written in different languages and count their own results differently, so 'per_source' says what each one answered, what its own number means, and names any that failed. A short list is never evidence of what exists. Not every row opens onto a recipe. Some sources file pages about an ingredient beside the recipes using it, and some publish articles that gather recipes; 'per_source' says in each source's own words what one of its rows can be. Only get_recipe tells them apart, and it says what it read off the page. The query goes to each source's own search as free text. There is no filtering: a word naming a diet, a time or a calorie count matches only where that source's index happens to carry it. Ask in a whole sentence if that is the question. These indexes answer the words they are handed, so a sentence is also sent as the words naming the dish and as the dish word alone, and the rows are the union; 'per_source' lists every wording and what it returned. What the question says the recipe must not hold is set aside from those shorter wordings rather than searched for, and named back in the notes: a negation, an allergy stated as one, a diet named in one word, and the number of people at the table. No source filters on any of it, so open a row with get_recipe and read the ingredient list before calling it suitable. A condition is read with the food on whichever side of it the sentence put one, so 'allergique aux noix' and 'peanut allergy' both name the nut. Where a sentence puts a food on neither side, the notes say a condition was stated and that its food was not read, because naming the wrong word would hide a dish and search for the food being avoided at once. Rows are interleaved one source at a time, since the sources share no score that could order them against each other.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | A dish or an ingredient, in any of the languages the sources publish in. | |
| fan_out | No | Whether a question may also be sent in shorter wordings derived from it. On, because these indexes answer the words they are handed: a question written as a sentence comes back empty from a corpus holding several of the dish. Turn it off to send exactly the words typed; 'per_source' then names the wordings that were withheld. | |
| sources | No | Sources to ask, by id. Left out, they are all asked, which is the point of this tool. The ids are the ones 'per_source' reports. | |
| limit_per_source | No | Rows to take from each source, so one source cannot fill the whole list. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| order | Yes | How the list was built, in words. |
| query | Yes | |
| results | Yes | |
| per_source | Yes | |
| result_count | Yes | Rows in this answer, across every source. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, which already indicate a safe read-only operation, the description discloses substantial behavior: per-source reporting, language and counting differences, failures, mixed row types, free-text querying, negation handling, and interleaved rows. These details materially affect how an agent should interpret and trust the results.
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 purpose is front-loaded and most sentences carry useful behavioral detail, but the description is a dense wall of prose with repeated references to 'per_source' and convoluted phrasing. It is appropriately complete for a complex tool but would be easier to scan with structure and trimming.
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?
Given the open-world search behavior, the lack of filtering, the mixed row types, and the presence of an output schema, this description covers everything an agent needs to call and interpret the tool correctly. It explains limitations and directs follow-up verification through get_recipe.
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, but the description adds real meaning: it explains why fan_out exists, that omitted sources means all are queried, why limit_per_source prevents one source from dominating, and that query text is sent as free text without filtering. It does not systematically define every parameter, but it goes beyond the schema's own descriptions.
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 opening sentence gives a specific verb and resource: search every recipe source simultaneously and return one merged list. It also distinguishes the result from get_recipe by noting that each row carries the id get_recipe takes, so the search-vs-fetch boundary is clear.
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?
The description explicitly routes the agent to get_recipe for disambiguating rows and reading ingredient lists before judging suitability. It does not compare this tool against the other siblings, scale_ingredients and compare_recipes, so the alternative guidance is strong but incomplete.
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
v4.1.0- Changed
compare_recipes39 fields changed- added
Output schema / properties / per_source / items / properties / names_the_dishAdded value: +{ + "description": "How many of this source's rows in this answer carry the dish in their title, out of 'count'. A row is what the source returned for the words it was handed, and one of these indexes answers any wording with something, so zero means the source offered rows and none of them is the dish.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - removed
Output schema / properties / per_source / items / properties / reported_total_means / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / per_source / items / properties / reported_total_means / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / per_source / items / properties / wordings / items / properties / not_run_because / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / per_source / items / properties / wordings / items / properties / not_run_because / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / per_source / items / requiredPrevious value: -[ - "source", - "name", - "status", - "count", - "reported_total", - "reported_total_means", - "skipped", - "cached", - "preferred_by_name", - "wordings", - "error", - "read" -]New value: +[ + "source", + "name", + "status", + "count", + "reported_total", + "reported_total_means", + "skipped", + "cached", + "preferred_by_name", + "names_the_dish", + "wordings", + "error", + "read" +] - removed
Output schema / properties / versions / items / properties / author / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / author / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / versions / items / properties / category / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / category / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / versions / items / properties / image_url / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / image_url / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / versions / items / properties / ingredients / items / properties / amount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / ingredients / items / properties / amount / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / versions / items / properties / ingredients / items / properties / amount_max / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / ingredients / items / properties / amount_max / typeAdded value: +[ + "number", + "null" +] - added
Output schema / properties / versions / items / properties / ingredients / items / properties / is_equipmentAdded value: +{ + "description": "True for a line naming a tool, which some sites write among the ingredients. Such a line is never multiplied: a recipe made for more people uses the same pan.", + "type": "boolean" +} - changed
Output schema / properties / versions / items / properties / ingredients / items / properties / language / enumPrevious value: -[ - "fr", - "en" -]New value: +[ + "fr", + "en", + "es" +] - changed
Output schema / properties / versions / items / properties / ingredients / items / properties / scaling / descriptionPrevious value: -"'scaled' means the arithmetic landed on the exact product, 'rounded' that something had to move for the line to stay usable or that the line carries a second quantity whose match is the page's claim, 'unscaled' that there is nothing to multiply."New value: +"'scaled' means the arithmetic landed on the exact product, 'rounded' that something had to move for the line to stay usable or that the line carries a second quantity whose match is the page's claim, 'unscaled' that the line was left as given. Read 'is_equipment' beside it: a tool line is 'unscaled' whether or not it carries a figure." - removed
Output schema / properties / versions / items / properties / ingredients / items / properties / unit / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / ingredients / items / properties / unit / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / versions / items / properties / ingredients / items / requiredPrevious value: -[ - "text", - "original", - "scaling", - "amount", - "amount_max", - "unit", - "language" -]New value: +[ + "text", + "original", + "scaling", + "amount", + "amount_max", + "unit", + "language", + "is_equipment" +] - changed
Output schema / properties / versions / items / properties / language / enumPrevious value: -[ - "fr", - "en" -]New value: +[ + "fr", + "en", + "es" +] - changed
Output schema / properties / versions / items / properties / rating / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "count": { - "anyOf": [ - { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "How many readers rated it, when the source says." - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "The top of the scale. A rating read without its scale means nothing." - }, - "value": { - "type": "number" - } - }, - "required": [ - "value", - "count", - "max" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "How many readers rated it, when the source says." + }, + "max": { + "description": "The top of the scale. A rating read without its scale means nothing.", + "type": [ + "number", + "null" + ] + }, + "value": { + "type": "number" + } + }, + "required": [ + "value", + "count", + "max" + ], + "type": "object" + }, + { + "type": "null" + } +] - added
Output schema / properties / versions / items / properties / scaling_summary / descriptionAdded value: +"The four counts add up to the number of ingredient lines." - added
Output schema / properties / versions / items / properties / scaling_summary / properties / equipment_countAdded value: +{ + "description": "Lines naming a tool, which some sites write among the ingredients.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - changed
Output schema / properties / versions / items / properties / scaling_summary / requiredPrevious value: -[ - "scaled_count", - "rounded_count", - "unscaled_count" -]New value: +[ + "scaled_count", + "rounded_count", + "unscaled_count", + "equipment_count" +] - removed
Output schema / properties / versions / items / properties / steps_as_one_block / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / steps_as_one_block / typeAdded value: +[ + "boolean", + "null" +] - removed
Output schema / properties / versions / items / properties / yield / properties / factor / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / yield / properties / factor / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / versions / items / properties / yield / properties / original_count / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / yield / properties / original_count / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / versions / items / properties / yield / properties / original_max / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / yield / properties / original_max / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / versions / items / properties / yield / properties / original_text / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / yield / properties / original_text / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / versions / items / properties / yield / properties / unit / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / versions / items / properties / yield / properties / unit / typeAdded value: +[ + "string", + "null" +]
- Changed
get_recipe41 fields changed- changed
Input schema / properties / id / descriptionPrevious value: -"From search_recipes, such as 'marmiton:44078', 'goodfood:recipes/carbonara' or 'cookbook:Cookbook:Carbonara'. Two sources address a recipe by a bare number, so spell an id with its source."New value: +"From search_recipes, such as 'marmiton:44078', 'pequerecetas:paella-de-marisco' or 'cookbook:Cookbook:Carbonara'. Two sources address a recipe by a bare number, so spell an id with its source." - added
Input schema / properties / max_gatheredAdded value: +{ + "default": 30, + "description": "Recipes and headings to return from an address that gathers recipes. The answer says how many the article holds.", + "maximum": 500, + "minimum": 1, + "type": "integer" +} - added
Output schema / properties / collectionAdded value: +{ + "additionalProperties": false, + "description": "Present when 'kind' is 'collection'.", + "properties": { + "attribution": { + "description": "Show this, with the url, when repeating anything from here.", + "type": "string" + }, + "gathered_count": { + "description": "Recipes the article points at, whether or not they are all listed here.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "headings": { + "description": "The headings the article is built from, in the order it prints them, up to 'max_gathered'.", + "items": { + "type": "string" + }, + "type": "array" + }, + "id": { + "type": "string" + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "recipes": { + "description": "The recipes it points at, each readable with get_recipe, up to 'max_gathered'. 'gathered_count' says how many the article holds.", + "items": { + "additionalProperties": false, + "properties": { + "excerpt": { + "description": "The gloss or matching passage the source offered, when it offered one.", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "Pass this to get_recipe. It names its source, so nothing has to be guessed.", + "type": "string" + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "source": { + "description": "Which source published this row.", + "type": "string" + }, + "source_name": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "description": "The recipe's own page. Show this when citing it.", + "type": "string" + } + }, + "required": [ + "id", + "source", + "source_name", + "title", + "url", + "image_url", + "excerpt" + ], + "type": "object" + }, + "type": "array" + }, + "source": { + "type": "string" + }, + "source_name": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "source", + "source_name", + "title", + "url", + "image_url", + "headings", + "recipes", + "gathered_count", + "attribution" + ], + "type": "object" +} - removed
Output schema / properties / id_read_as / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / id_read_as / typeAdded value: +[ + "string", + "null" +] - added
Output schema / properties / kindAdded value: +{ + "description": "What the address held. 'recipe' comes with 'recipe' and no 'collection'; 'collection' comes with 'collection' and no 'recipe', and is an article gathering other recipes, with no ingredients and no method of its own.", + "enum": [ + "recipe", + "collection" + ], + "type": "string" +} - added
Output schema / properties / recipe / descriptionAdded value: +"Present when 'kind' is 'recipe'." - removed
Output schema / properties / recipe / properties / author / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / author / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / recipe / properties / category / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / category / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / recipe / properties / image_url / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / image_url / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / recipe / properties / ingredients / items / properties / amount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / ingredients / items / properties / amount / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / recipe / properties / ingredients / items / properties / amount_max / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / ingredients / items / properties / amount_max / typeAdded value: +[ + "number", + "null" +] - added
Output schema / properties / recipe / properties / ingredients / items / properties / is_equipmentAdded value: +{ + "description": "True for a line naming a tool, which some sites write among the ingredients. Such a line is never multiplied: a recipe made for more people uses the same pan.", + "type": "boolean" +} - changed
Output schema / properties / recipe / properties / ingredients / items / properties / language / enumPrevious value: -[ - "fr", - "en" -]New value: +[ + "fr", + "en", + "es" +] - changed
Output schema / properties / recipe / properties / ingredients / items / properties / scaling / descriptionPrevious value: -"'scaled' means the arithmetic landed on the exact product, 'rounded' that something had to move for the line to stay usable or that the line carries a second quantity whose match is the page's claim, 'unscaled' that there is nothing to multiply."New value: +"'scaled' means the arithmetic landed on the exact product, 'rounded' that something had to move for the line to stay usable or that the line carries a second quantity whose match is the page's claim, 'unscaled' that the line was left as given. Read 'is_equipment' beside it: a tool line is 'unscaled' whether or not it carries a figure." - removed
Output schema / properties / recipe / properties / ingredients / items / properties / unit / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / ingredients / items / properties / unit / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / recipe / properties / ingredients / items / requiredPrevious value: -[ - "text", - "original", - "scaling", - "amount", - "amount_max", - "unit", - "language" -]New value: +[ + "text", + "original", + "scaling", + "amount", + "amount_max", + "unit", + "language", + "is_equipment" +] - changed
Output schema / properties / recipe / properties / language / enumPrevious value: -[ - "fr", - "en" -]New value: +[ + "fr", + "en", + "es" +] - changed
Output schema / properties / recipe / properties / rating / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "count": { - "anyOf": [ - { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "How many readers rated it, when the source says." - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "The top of the scale. A rating read without its scale means nothing." - }, - "value": { - "type": "number" - } - }, - "required": [ - "value", - "count", - "max" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "How many readers rated it, when the source says." + }, + "max": { + "description": "The top of the scale. A rating read without its scale means nothing.", + "type": [ + "number", + "null" + ] + }, + "value": { + "type": "number" + } + }, + "required": [ + "value", + "count", + "max" + ], + "type": "object" + }, + { + "type": "null" + } +] - added
Output schema / properties / recipe / properties / scaling_summary / descriptionAdded value: +"The four counts add up to the number of ingredient lines." - added
Output schema / properties / recipe / properties / scaling_summary / properties / equipment_countAdded value: +{ + "description": "Lines naming a tool, which some sites write among the ingredients.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - changed
Output schema / properties / recipe / properties / scaling_summary / requiredPrevious value: -[ - "scaled_count", - "rounded_count", - "unscaled_count" -]New value: +[ + "scaled_count", + "rounded_count", + "unscaled_count", + "equipment_count" +] - removed
Output schema / properties / recipe / properties / steps_as_one_block / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / steps_as_one_block / typeAdded value: +[ + "boolean", + "null" +] - removed
Output schema / properties / recipe / properties / yield / properties / factor / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / yield / properties / factor / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / recipe / properties / yield / properties / original_count / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / yield / properties / original_count / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / recipe / properties / yield / properties / original_max / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / yield / properties / original_max / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / recipe / properties / yield / properties / original_text / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / yield / properties / original_text / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / recipe / properties / yield / properties / unit / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / recipe / properties / yield / properties / unit / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / requiredPrevious value: -[ - "recipe", - "id_read_as", - "notes" -]New value: +[ + "kind", + "id_read_as", + "notes" +]
- Changed
scale_ingredients16 fields changed- changed
Input schema / properties / ingredients / descriptionPrevious value: -"The lines as written, one ingredient each, such as '200 g de farine' or '3 eggs'. One line, not a whole recipe."New value: +"The lines as written, one ingredient each, such as '200 g de farine' or '3 eggs'." - changed
Input schema / properties / language / descriptionPrevious value: -"'auto' reads each line on its own, which is what a list holding both languages needs. Name a language to read every line that way."New value: +"'auto' reads each line on its own, which is what a list holding more than one language needs. Name a language to read every line that way." - changed
Input schema / properties / language / enumPrevious value: -[ - "auto", - "fr", - "en" -]New value: +[ + "auto", + "fr", + "en", + "es" +] - added
Output schema / properties / equipment_countAdded value: +{ + "description": "Lines naming a tool, which are never multiplied: a recipe made for more people uses the same pan.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - removed
Output schema / properties / ingredients / items / properties / amount / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / ingredients / items / properties / amount / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / ingredients / items / properties / amount_max / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / ingredients / items / properties / amount_max / typeAdded value: +[ + "number", + "null" +] - added
Output schema / properties / ingredients / items / properties / is_equipmentAdded value: +{ + "description": "True for a line naming a tool, which some sites write among the ingredients. Such a line is never multiplied: a recipe made for more people uses the same pan.", + "type": "boolean" +} - changed
Output schema / properties / ingredients / items / properties / language / enumPrevious value: -[ - "fr", - "en" -]New value: +[ + "fr", + "en", + "es" +] - changed
Output schema / properties / ingredients / items / properties / scaling / descriptionPrevious value: -"'scaled' means the arithmetic landed on the exact product, 'rounded' that something had to move for the line to stay usable or that the line carries a second quantity whose match is the page's claim, 'unscaled' that there is nothing to multiply."New value: +"'scaled' means the arithmetic landed on the exact product, 'rounded' that something had to move for the line to stay usable or that the line carries a second quantity whose match is the page's claim, 'unscaled' that the line was left as given. Read 'is_equipment' beside it: a tool line is 'unscaled' whether or not it carries a figure." - removed
Output schema / properties / ingredients / items / properties / unit / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / ingredients / items / properties / unit / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / ingredients / items / requiredPrevious value: -[ - "text", - "original", - "scaling", - "amount", - "amount_max", - "unit", - "language" -]New value: +[ + "text", + "original", + "scaling", + "amount", + "amount_max", + "unit", + "language", + "is_equipment" +] - changed
Output schema / properties / unscaled_count / descriptionPrevious value: -"Lines carrying nothing that can be multiplied."New value: +"Lines carrying nothing that can be multiplied, tools excepted." - changed
Output schema / requiredPrevious value: -[ - "factor", - "language", - "ingredients", - "scaled_count", - "rounded_count", - "unscaled_count", - "notes" -]New value: +[ + "factor", + "language", + "ingredients", + "scaled_count", + "rounded_count", + "unscaled_count", + "equipment_count", + "notes" +]
- Changed
search_recipes10 fields changed- added
Output schema / properties / per_source / items / properties / names_the_dishAdded value: +{ + "description": "How many of this source's rows in this answer carry the dish in their title, out of 'count'. A row is what the source returned for the words it was handed, and one of these indexes answers any wording with something, so zero means the source offered rows and none of them is the dish.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - removed
Output schema / properties / per_source / items / properties / reported_total_means / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / per_source / items / properties / reported_total_means / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / per_source / items / properties / wordings / items / properties / not_run_because / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / per_source / items / properties / wordings / items / properties / not_run_because / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / per_source / items / requiredPrevious value: -[ - "source", - "name", - "status", - "count", - "reported_total", - "reported_total_means", - "skipped", - "cached", - "preferred_by_name", - "wordings", - "error", - "read" -]New value: +[ + "source", + "name", + "status", + "count", + "reported_total", + "reported_total_means", + "skipped", + "cached", + "preferred_by_name", + "names_the_dish", + "wordings", + "error", + "read" +] - removed
Output schema / properties / results / items / properties / excerpt / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / excerpt / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / results / items / properties / image_url / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / image_url / typeAdded value: +[ + "string", + "null" +]
2 tool updates
v3.0.1- Changed
compare_recipes5 fields changed- added
Input schema / properties / sourcesAdded value: +{ + "description": "Sources to compare, by id. Left out, every source is read, which is the point of this tool. Name two or three to put particular traditions side by side. The ids are the ones 'per_source' reports.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / versions / items / properties / rest_minutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Time the recipe stands, from a source that publishes it apart. It is in no other time here, and belongs to no other source's cooking time. Null where the source publishes none, which is not a dish that needs no rest." +} - added
Output schema / properties / versions / items / properties / steps_as_one_blockAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Whether the source publishes the method as one block of prose rather than as steps of its own. Null where the source says neither, so a single entry is not read as step one of several." +} - added
Output schema / properties / versions / items / properties / withheldAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "parts": { + "description": "Which parts, named as 'ingredients' or 'method'.", + "items": { + "type": "string" + }, + "type": "array" + }, + "why": { + "description": "What the source says about keeping them back.", + "type": "string" + } + }, + "required": [ + "parts", + "why" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "A part the source published nothing of because it keeps it for its subscribers. Null from a source that withholds nothing, which is a different statement from an empty list." +} - changed
Output schema / properties / versions / items / requiredPrevious value: -[ - "id", - "source", - "source_name", - "language", - "title", - "url", - "image_url", - "yield", - "ingredients", - "steps", - "prep_minutes", - "cook_minutes", - "total_minutes", - "category", - "author", - "rating", - "nutrition", - "equipment", - "tips", - "license", - "attribution", - "sections_returned", - "sections_omitted", - "scaling_summary" -]New value: +[ + "id", + "source", + "source_name", + "language", + "title", + "url", + "image_url", + "yield", + "ingredients", + "steps", + "prep_minutes", + "cook_minutes", + "total_minutes", + "rest_minutes", + "steps_as_one_block", + "withheld", + "category", + "author", + "rating", + "nutrition", + "equipment", + "tips", + "license", + "attribution", + "sections_returned", + "sections_omitted", + "scaling_summary" +]
- Changed
get_recipe5 fields changed- changed
Input schema / properties / id / descriptionPrevious value: -"From search_recipes, such as 'marmiton:44078' or 'cookbook:Cookbook:Carbonara'."New value: +"From search_recipes, such as 'marmiton:44078', 'goodfood:recipes/carbonara' or 'cookbook:Cookbook:Carbonara'. Two sources address a recipe by a bare number, so spell an id with its source." - added
Output schema / properties / recipe / properties / rest_minutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Time the recipe stands, from a source that publishes it apart. It is in no other time here, and belongs to no other source's cooking time. Null where the source publishes none, which is not a dish that needs no rest." +} - added
Output schema / properties / recipe / properties / steps_as_one_blockAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Whether the source publishes the method as one block of prose rather than as steps of its own. Null where the source says neither, so a single entry is not read as step one of several." +} - added
Output schema / properties / recipe / properties / withheldAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "parts": { + "description": "Which parts, named as 'ingredients' or 'method'.", + "items": { + "type": "string" + }, + "type": "array" + }, + "why": { + "description": "What the source says about keeping them back.", + "type": "string" + } + }, + "required": [ + "parts", + "why" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "A part the source published nothing of because it keeps it for its subscribers. Null from a source that withholds nothing, which is a different statement from an empty list." +} - changed
Output schema / properties / recipe / requiredPrevious value: -[ - "id", - "source", - "source_name", - "language", - "title", - "url", - "image_url", - "yield", - "ingredients", - "steps", - "prep_minutes", - "cook_minutes", - "total_minutes", - "category", - "author", - "rating", - "nutrition", - "equipment", - "tips", - "license", - "attribution", - "sections_returned", - "sections_omitted", - "scaling_summary" -]New value: +[ + "id", + "source", + "source_name", + "language", + "title", + "url", + "image_url", + "yield", + "ingredients", + "steps", + "prep_minutes", + "cook_minutes", + "total_minutes", + "rest_minutes", + "steps_as_one_block", + "withheld", + "category", + "author", + "rating", + "nutrition", + "equipment", + "tips", + "license", + "attribution", + "sections_returned", + "sections_omitted", + "scaling_summary" +]
2 tool updates
v1.3.0- Changed
compare_recipes4 fields changed- added
Output schema / properties / per_source / items / properties / preferred_by_nameAdded value: +{ + "description": "Whether this source's rows were arranged so the ones whose title names the dish come first. An order over one source's own rows, never a score against another source.", + "type": "boolean" +} - added
Output schema / properties / per_source / items / properties / readAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "error": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "hint": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "read", + "failed" + ], + "type": "string" + } + }, + "required": [ + "status", + "error" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "What became of the row this source offered, once it was opened. Null when no row was opened, which is what a search that did not answer and a search holding nothing both come to. A search and a read are two moments, and this is the second one." +} - added
Output schema / properties / per_source / items / properties / wordingsAdded value: +{ + "description": "Every wording this source was sent or held back from, in the order tried. A question written as a sentence is also sent in wordings derived from it, because these indexes answer the words they are handed. Counts here belong to one wording each and are never added.", + "items": { + "additionalProperties": false, + "properties": { + "added": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Rows it returned that no earlier wording had." + }, + "count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Rows this wording returned. Null when it did not run." + }, + "derivation": { + "description": "How this wording was derived from the question.", + "type": "string" + }, + "error": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "hint": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" + }, + { + "type": "null" + } + ] + }, + "not_run_because": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Why it was held back. Null when it ran." + }, + "query": { + "description": "What was sent to this source, or would have been.", + "type": "string" + }, + "ran": { + "type": "boolean" + } + }, + "required": [ + "query", + "derivation", + "ran", + "count", + "added", + "not_run_because", + "error" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / requiredPrevious value: -[ - "source", - "name", - "status", - "count", - "reported_total", - "reported_total_means", - "skipped", - "cached", - "error" -]New value: +[ + "source", + "name", + "status", + "count", + "reported_total", + "reported_total_means", + "skipped", + "cached", + "preferred_by_name", + "wordings", + "error", + "read" +]
- Changed
search_recipes5 fields changed- added
Input schema / properties / fan_outAdded value: +{ + "default": true, + "description": "Whether a question may also be sent in shorter wordings derived from it. On, because these indexes answer the words they are handed: a question written as a sentence comes back empty from a corpus holding several of the dish. Turn it off to send exactly the words typed; 'per_source' then names the wordings that were withheld.", + "type": "boolean" +} - added
Output schema / properties / per_source / items / properties / preferred_by_nameAdded value: +{ + "description": "Whether this source's rows were arranged so the ones whose title names the dish come first. An order over one source's own rows, never a score against another source.", + "type": "boolean" +} - added
Output schema / properties / per_source / items / properties / readAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "error": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "hint": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "read", + "failed" + ], + "type": "string" + } + }, + "required": [ + "status", + "error" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "What became of the row this source offered, once it was opened. Null when no row was opened, which is what a search that did not answer and a search holding nothing both come to. A search and a read are two moments, and this is the second one." +} - added
Output schema / properties / per_source / items / properties / wordingsAdded value: +{ + "description": "Every wording this source was sent or held back from, in the order tried. A question written as a sentence is also sent in wordings derived from it, because these indexes answer the words they are handed. Counts here belong to one wording each and are never added.", + "items": { + "additionalProperties": false, + "properties": { + "added": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Rows it returned that no earlier wording had." + }, + "count": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Rows this wording returned. Null when it did not run." + }, + "derivation": { + "description": "How this wording was derived from the question.", + "type": "string" + }, + "error": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "hint": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" + }, + { + "type": "null" + } + ] + }, + "not_run_because": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Why it was held back. Null when it ran." + }, + "query": { + "description": "What was sent to this source, or would have been.", + "type": "string" + }, + "ran": { + "type": "boolean" + } + }, + "required": [ + "query", + "derivation", + "ran", + "count", + "added", + "not_run_because", + "error" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / requiredPrevious value: -[ - "source", - "name", - "status", - "count", - "reported_total", - "reported_total_means", - "skipped", - "cached", - "error" -]New value: +[ + "source", + "name", + "status", + "count", + "reported_total", + "reported_total_means", + "skipped", + "cached", + "preferred_by_name", + "wordings", + "error", + "read" +]
4 tool updates
v1.0.0- First observed
compare_recipes - First observed
get_recipe - First observed
scale_ingredients - First observed
search_recipes
TDQS
Scored across 4 tools
The four tools have mostly distinct purposes: search discovers recipes, get_recipe reads one in full, scale_ingredients scales arbitrary ingredient lines, and compare_recipes compares versions across sources. There is some overlap because get_recipe can also rescale via 'servings' and compare_recipes can rescale multiple recipes, so an agent may briefly hesitate between scaling pathways. Overall boundaries are clear enough but not perfectly clean.
All tool names follow a consistent snake_case verb_noun pattern: search_recipes, get_recipe, scale_ingredients, compare_recipes. The verbs are distinct and the resource nouns are predictable. No naming inconsistencies are present.
Four tools is well-scoped for a read-only recipe aggregation server. Each tool earns its place by covering discovery, full reading, scaling, and cross-source comparison without redundant endpoints. The surface is small but appropriately focused.
The core recipe lifecycle is covered: search across sources, read a full recipe, scale ingredients, and compare source versions. Minor gaps remain, such as no explicit source-listing tool or dedicated browsing/filtering operation, though search's 'per_source' reporting mitigates this. Agents can work around these gaps.
Maintenance
Related MCP Connectors
One MCP server for all web data. Scrape, search, crawl, and AI research in one install.
- mcpOAuthcom.zomato
An MCP server that exposes functionalities to use Zomato's services.
Unlock the power of food transparency with our Open Food Facts MCP server. Easily look up any food
MCP server for Meitre restaurant reservations.
Related MCP Servers
- AlicenseBqualityDmaintenanceAn MCP server that enables AI-powered recipe generation and transformation using natural language, supporting dietary restrictions, allergies, and nutritional goals.24 npmMIT
- AlicenseBqualityCmaintenanceMCP server for meal planning and grocery list generation, enabling recipe storage, meal plan creation, and automated grocery lists with ignored ingredients.82MIT
- AlicenseAqualityDmaintenanceMCP server that provides cooking recipe lookup, classification, and weekly meal planning based on dietary restrictions and allergies.5MIT
- AlicenseAqualityBmaintenanceMCP server that searches French recipes from Marmiton, reads ingredients and steps, and rescales quantities to any number of servings without requiring an API key.3222 npm1MIT