eatmundo
Cross-cuisine dish equivalence with shared-ingredient evidence: 1,956 dishes, 26 cuisines.
我该使用它吗
质量与安全性
发现(1)
- LOW在 compare_dishes 中
基于对工具定义和协议合规性的自动分析。
上下文开销
这是每次将服务器的工具加载到模型上下文窗口时所消耗的大致 token 数。数值越高,可用于其他任务的注意力就越少。
安装
一键安装
将以下内容添加到你的 `claude_desktop_config.json` 文件中:
{
"mcpServers": {
"dishes": {
"url": "https://www.eatmundo.com/api/mcp"
}
}
}远程端点
https://www.eatmundo.com/api/mcpstreamable-http它能做什么
工具清单
工具(6)
🟢find_similar_dishes(dish, country, min_score, limit, lang)
Find dishes from other cuisines that are equivalent to a given dish, from eatmundo's scored equivalence graph (11,467 edges over 1,956 recipes from 26 countries). Every edge is evidence-based - shared canonical ingredients - not a language-model guess, and every edge crosses country lines. Use it for "what is the Greek counterpart of lahmacun, and why".
输入模式
{
"type": "object",
"properties": {
"dish": {
"type": "string",
"minLength": 2,
"description": "Dish to start from: an eatmundo slug (e.g. \"lahmacun\") or a plain name (\"rice pudding\"). Names are resolved through site search; check resolved_by in the result."
},
"country": {
"description": "Restrict matches to one cuisine, ISO 3166-1 alpha-2 (e.g. \"GR\"). 26 countries are covered.",
"type": "string",
"minLength": 2,
"maxLength": 2
},
"min_score": {
"default": 0,
"description": "Minimum match score. Defaults to 0 on purpose: 320 dishes only have below-threshold nearest matches, and a default of 50 would silently return nothing for them.",
"type": "integer",
"minimum": 0,
"maximum": 100
},
"limit": {
"default": 5,
"description": "How many matches to return, best score first. The default of 5 is enough to see whether a cuisine has one clear counterpart or several competing ones.",
"type": "integer",
"minimum": 1,
"maximum": 20
},
"lang": {
"default": "en",
"description": "Language for titles, ingredient names and URLs.",
"type": "string",
"enum": [
"en",
"tr",
"de",
"fr",
"es",
"it",
"el"
]
}
},
"required": [
"dish"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}输出模式
{
"type": "object",
"properties": {
"source": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url"
],
"additionalProperties": false
},
"resolved_by": {
"type": "string",
"enum": [
"slug",
"slug_other_language",
"text_search"
]
},
"total_matches": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"matches": {
"type": "array",
"items": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
},
"score": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"below_threshold": {
"type": "boolean"
},
"same_form": {
"type": "boolean"
},
"shared_ingredients": {
"type": "array",
"items": {
"type": "string"
}
},
"shared_count": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"compare_url": {
"type": [
"string",
"null"
]
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url",
"score",
"below_threshold",
"same_form",
"shared_ingredients",
"shared_count",
"compare_url"
],
"additionalProperties": false
}
},
"score_basis": {
"type": "string"
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"attribution": {
"type": "string"
}
},
"required": [
"source",
"resolved_by",
"total_matches",
"matches",
"score_basis",
"notes",
"attribution"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}🟢search_dishes(query, country, category, form, ingredients, ...)
Search 1,956 dishes from 26 cuisines by text, country, category, preparation technique, or canonical ingredient. Every ingredient in the corpus is linked to a canonical entity, so an ingredient filter also catches translations and spelling variants. Use it to get a slug for the other tools.
输入模式
{
"type": "object",
"properties": {
"query": {
"description": "Free text; needs at least 2 characters to take effect.",
"type": "string"
},
"country": {
"description": "ISO 3166-1 alpha-2 cuisine filter.",
"type": "string",
"minLength": 2,
"maxLength": 2
},
"category": {
"description": "Dish type. DRINK covers both alcoholic and non-alcoholic; DESSERT is separate from FOOD, so a query for a sweet dish returns nothing under FOOD.",
"type": "string",
"enum": [
"FOOD",
"DESSERT",
"DRINK"
]
},
"form": {
"description": "Preparation technique. One of: soup, stew, stir-fry, grill-roast, fritter, dumpling, stuffed-vegetable, flatbread-pastry, noodle, rice, cold-salad, dip-spread, pickle-ferment, egg, sandwich-wrap, porridge-mash, casserole-bake, cured-preserved, composed-plate, meat-salad, pancake-batter, marinated-cooked, syrup-pastry, fried-dough-sweet, cake, cookie, custard-pudding, sweet-pie-tart, fruit-dessert, frozen, confection, sweet-porridge, glutinous-dumpling, sweet-bread, layered-assembly, filled-pastry, sweet-pancake, starch-gel, hot-infusion, cold-cooler, juice, smoothie-shake, hot-milk-drink, fermented-drink, alcoholic-mixed, alcoholic-brew.",
"type": "string"
},
"ingredients": {
"description": "Canonical ingredient names (e.g. [\"tomato\",\"garlic\"]). A dish matches if it has ANY of them, but results are ranked by how many it has. Unrecognised names are reported back, not ignored.",
"maxItems": 10,
"type": "array",
"items": {
"type": "string"
}
},
"limit": {
"default": 10,
"description": "Results per page (max 25).",
"type": "integer",
"minimum": 1,
"maximum": 25
},
"offset": {
"default": 0,
"description": "Rows to skip for paging. Pass back the `next_offset` from the previous response; it is null when there is no further page.",
"type": "integer",
"minimum": 0,
"maximum": 5000
},
"lang": {
"default": "en",
"description": "Language for titles, ingredient names and URLs.",
"type": "string",
"enum": [
"en",
"tr",
"de",
"fr",
"es",
"it",
"el"
]
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}输出模式
{
"type": "object",
"properties": {
"total": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"offset": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"next_offset": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
},
"description": {
"type": "string"
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url",
"description"
],
"additionalProperties": false
}
},
"unresolved_ingredients": {
"type": "array",
"items": {
"type": "string"
}
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"attribution": {
"type": "string"
}
},
"required": [
"total",
"offset",
"next_offset",
"items",
"unresolved_ingredients",
"notes",
"attribution"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}🟢get_recipe(slug, lang)
Full recipe for one dish: steps, FAQ, image credit, and ingredients with PARSED quantities (amount, unit, and a normalised base in grams or millilitres where the quantity is measurable). Count-based lines like "3 onions" carry a gram estimate only where a per-item weight is known.
输入模式
{
"type": "object",
"properties": {
"slug": {
"type": "string",
"minLength": 2,
"description": "Recipe slug in any of the 7 languages, or a dish name (resolved by search)."
},
"lang": {
"default": "en",
"description": "Language for titles, ingredient names and URLs.",
"type": "string",
"enum": [
"en",
"tr",
"de",
"fr",
"es",
"it",
"el"
]
}
},
"required": [
"slug"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}输出模式
{
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
},
"description": {
"type": "string"
},
"calories": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"servings": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"steps": {
"type": "array",
"items": {
"type": "string"
}
},
"faq": {
"type": "array",
"items": {
"type": "object",
"properties": {
"question": {
"type": "string"
},
"answer": {
"type": "string"
}
},
"required": [
"question",
"answer"
],
"additionalProperties": false
}
},
"ingredients": {
"type": "array",
"items": {
"type": "object",
"properties": {
"canonical": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"quantity_text": {
"type": [
"string",
"null"
]
},
"amount": {
"type": [
"number",
"null"
]
},
"unit": {
"type": [
"string",
"null"
]
},
"unit_class": {
"description": "MASS, VOLUME, COUNT or VAGUE. VAGUE can appear even when quantity_text shows a number: trailing phrases like \"for serving\" or \"for frying\" mark a line as an amount we cannot pin down (0.5% of lines). Do not infer a quantity from the text when this is VAGUE.",
"type": [
"string",
"null"
]
},
"base": {
"type": [
"number",
"null"
]
},
"base_unit": {
"type": [
"string",
"null"
]
},
"grams_estimate": {
"type": [
"number",
"null"
]
}
},
"required": [
"canonical",
"name",
"quantity_text",
"amount",
"unit",
"unit_class",
"base",
"base_unit",
"grams_estimate"
],
"additionalProperties": false
}
},
"image": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"ai_generated": {
"type": "boolean"
},
"attribution": {
"anyOf": [
{
"type": "object",
"properties": {
"author": {
"type": [
"string",
"null"
]
},
"license": {
"type": [
"string",
"null"
]
},
"license_url": {
"type": [
"string",
"null"
]
},
"source_url": {
"type": [
"string",
"null"
]
}
},
"required": [
"author",
"license",
"license_url",
"source_url"
],
"additionalProperties": false
},
{
"type": "null"
}
]
}
},
"required": [
"url",
"ai_generated",
"attribution"
],
"additionalProperties": false
},
"match_count": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"resolved_by": {
"type": "string",
"enum": [
"slug",
"slug_other_language",
"text_search"
]
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"attribution": {
"type": "string"
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url",
"description",
"calories",
"servings",
"steps",
"faq",
"ingredients",
"image",
"match_count",
"resolved_by",
"notes",
"attribution"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}🟢compare_dishes(a, b, lang)
Compare two dishes ingredient by ingredient: shared items, what is unique to each, a match score, and - for the 848 pairs that have one - an editorial comparison paragraph in the requested language. Works for any two dishes in the corpus, whether or not they are an edge in the equivalence graph.
输入模式
{
"type": "object",
"properties": {
"a": {
"type": "string",
"minLength": 2,
"description": "First dish: slug or name."
},
"b": {
"type": "string",
"minLength": 2,
"description": "Second dish: slug or name."
},
"lang": {
"default": "en",
"description": "Language for titles, ingredient names and URLs.",
"type": "string",
"enum": [
"en",
"tr",
"de",
"fr",
"es",
"it",
"el"
]
}
},
"required": [
"a",
"b"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}输出模式
{
"type": "object",
"properties": {
"a": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url"
],
"additionalProperties": false
},
"b": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url"
],
"additionalProperties": false
},
"score": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"score_basis": {
"type": "string"
},
"is_graph_edge": {
"type": "boolean"
},
"stored_score": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"below_threshold": {
"type": "boolean"
},
"same_form": {
"type": "boolean"
},
"shared_ingredients": {
"type": "array",
"items": {
"type": "string"
}
},
"only_a": {
"type": "array",
"items": {
"type": "string"
}
},
"only_b": {
"type": "array",
"items": {
"type": "string"
}
},
"excluded_generics": {
"type": "array",
"items": {
"type": "string"
},
"description": "Pantry staples deliberately excluded from BOTH the score and the ingredient lists above, because they match almost everything (flour/water/oil/salt/yeast for food, water/salt/sugar for drinks). They are present in the dishes - they are just not evidence of similarity."
},
"paragraph": {
"type": [
"string",
"null"
]
},
"compare_url": {
"type": [
"string",
"null"
]
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"attribution": {
"type": "string"
}
},
"required": [
"a",
"b",
"score",
"score_basis",
"is_graph_edge",
"stored_score",
"below_threshold",
"same_form",
"shared_ingredients",
"only_a",
"only_b",
"excluded_generics",
"paragraph",
"compare_url",
"notes",
"attribution"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}🟢weekly_menu(days, mode, country, exclude_countries, exclude, ...)
Build a menu of up to 7 dinners, either one cuisine per day ("world") or all from one country. Honours diet exclusions (pork, beef, meat, fish, dairy, egg, alcohol), vegetarian/vegan/pescatarian requirements, a prep-time ceiling and a budget mode that reuses ingredients. When a constraint cannot be met it is relaxed and the relaxation is reported, never hidden.
输入模式
{
"type": "object",
"properties": {
"days": {
"default": 7,
"description": "How many days to plan, 1-7.",
"type": "integer",
"minimum": 1,
"maximum": 7
},
"mode": {
"default": "world",
"description": "\"world\" = a different cuisine each day; \"single\" = one country.",
"type": "string",
"enum": [
"world",
"single"
]
},
"country": {
"description": "Required when mode is \"single\".",
"type": "string",
"minLength": 2,
"maxLength": 2
},
"exclude_countries": {
"description": "Cuisines to keep out of the plan, ISO 3166-1 alpha-2. Only meaningful when mode is \"world\"; excluding too many leaves the generator with too few cuisines to fill the week.",
"maxItems": 30,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 2
}
},
"exclude": {
"description": "Ingredient attributes to avoid entirely: pork, beef, meat, fish, dairy, egg, alcohol.",
"type": "array",
"items": {
"type": "string",
"enum": [
"pork",
"beef",
"meat",
"fish",
"dairy",
"egg",
"alcohol"
]
}
},
"require": {
"description": "Diet every dish must satisfy. This is a WHITELIST and differs from `exclude`, which only removes single ingredient attributes: \"vegan\" is not the same as excluding meat.",
"type": "string",
"enum": [
"vegetarian",
"vegan",
"pescatarian"
]
},
"time_limit": {
"description": "Max prep time in minutes; closed set (30/45/60). The generator relaxes it automatically if too few cuisines qualify, and reports that in relaxations.",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"budget": {
"default": false,
"description": "Favour weeks that reuse ingredients across days.",
"type": "boolean"
},
"include_extras": {
"default": true,
"description": "Add 2 desserts and 2 drinks (never alcoholic).",
"type": "boolean"
},
"seed": {
"description": "Same seed + same options = same menu.",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"lang": {
"default": "en",
"description": "Language for titles, ingredient names and URLs.",
"type": "string",
"enum": [
"en",
"tr",
"de",
"fr",
"es",
"it",
"el"
]
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}输出模式
{
"type": "object",
"properties": {
"days": {
"type": "array",
"items": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
},
"day": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url",
"day"
],
"additionalProperties": false
}
},
"desserts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url"
],
"additionalProperties": false
}
},
"drinks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"title": {
"type": "string"
},
"country": {
"type": "string"
},
"country_name": {
"type": "string"
},
"category": {
"type": "string"
},
"form": {
"type": [
"string",
"null"
]
},
"prep_time_minutes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"url": {
"type": "string"
}
},
"required": [
"slug",
"title",
"country",
"country_name",
"category",
"form",
"prep_time_minutes",
"url"
],
"additionalProperties": false
}
},
"shopping_list": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ingredient": {
"type": "string"
},
"used_in": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"dishes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"ingredient",
"used_in",
"dishes"
],
"additionalProperties": false
}
},
"pantry_assumed": {
"type": "array",
"items": {
"type": "string"
}
},
"relaxations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"what": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"what",
"reason"
],
"additionalProperties": false
}
},
"stats": {
"type": "object",
"properties": {
"countries": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"pool_size": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"time_limit": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"seed": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"countries",
"pool_size",
"time_limit",
"seed"
],
"additionalProperties": false
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"attribution": {
"type": "string"
}
},
"required": [
"days",
"desserts",
"drinks",
"shopping_list",
"pantry_assumed",
"relaxations",
"stats",
"notes",
"attribution"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}🟢substitute_ingredient(ingredient, context, limit, lang)
Find what can replace a single ingredient, optionally under a dietary constraint (vegan, vegetarian, halal, dairy-free, pescatarian). Each result is marked "substitute" (same dish) or "adaptation" (result is a meaningfully different dish) - most swaps forced by a diet exclusion are adaptations, and the tool says so rather than overselling a swap as if the dish stayed the same. Precomputed and model-verified, not generated on the fly.
输入模式
{
"type": "object",
"properties": {
"ingredient": {
"type": "string",
"minLength": 1,
"description": "Ingredient name in any of the 7 site languages, or its English canonical form (e.g. \"pork belly\", \"domuz göbeği\", \"Schweinebauch\")."
},
"context": {
"description": "Dietary constraint the substitute must satisfy. Omit for the single best general substitute with no diet filter. Other constraints (gluten-free, allergen-free) are not supported - the corpus has no gluten/allergen data, so the tool would have to guess, and it does not.",
"type": "string",
"enum": [
"vegan",
"vegetarian",
"halal",
"dairy-free",
"pescatarian"
]
},
"limit": {
"default": 3,
"description": "Max substitutes to return, best first.",
"type": "integer",
"minimum": 1,
"maximum": 5
},
"lang": {
"default": "en",
"description": "Language for titles, ingredient names and URLs.",
"type": "string",
"enum": [
"en",
"tr",
"de",
"fr",
"es",
"it",
"el"
]
}
},
"required": [
"ingredient"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}输出模式
{
"type": "object",
"properties": {
"ingredient": {
"type": "string",
"description": "Resolved canonical (English) ingredient name."
},
"ingredient_label": {
"type": "string",
"description": "Ingredient name translated into the requested language."
},
"context": {
"type": [
"string",
"null"
]
},
"substitutes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ingredient": {
"type": "string"
},
"label": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"substitute",
"adaptation"
],
"description": "\"substitute\": swapping keeps the same dish. \"adaptation\": the result is a meaningfully different dish (e.g. pork belly -> beef short ribs for halal - still a rich braise, but not the same recipe). Never claim \"substitute\" when it is not one."
},
"reason": {
"type": [
"string",
"null"
]
}
},
"required": [
"ingredient",
"label",
"kind",
"reason"
],
"additionalProperties": false
}
},
"declined": {
"description": "Set instead of an empty substitutes array explaining why there are none - a missing reason is a bug, not a deliberate silence.",
"type": [
"string",
"null"
]
},
"resolved_by": {
"type": "string",
"enum": [
"exact",
"search"
]
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"attribution": {
"type": "string"
}
},
"required": [
"ingredient",
"ingredient_label",
"context",
"substitutes",
"declined",
"resolved_by",
"notes",
"attribution"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}社区
证据