{
  "openapi": "3.1.1",
  "info": {
    "title": "Leftover Chef API",
    "version": "1.0.0",
    "summary": "Turn leftovers and fridge odds and ends into 5 recipe ideas, using what expires first.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://leftoverchef.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Turn leftovers and fridge odds and ends into 5 recipe ideas, using what expires first.",
        "description": "List what is in your fridge and cupboard, mark what must be used soon, and set diet, equipment and time. Leftover Chef matches your ingredients against 37 flexible recipe templates (frittata, fried rice, soup, curry…) and ranks the five that use the most of what you have with the fewest missing items.\n\nMethodology:\n- Each ingredient line is normalised: lower-cased, quantities/units and words like \"leftover\", \"fresh\", \"chopped\" removed, plurals singularised, synonyms mapped (courgette → zucchini, garbanzo → chickpea…), then classified (vegetable, protein, grain, dairy…) with diet flags. \"Vegan …\", \"dairy-free …\", \"gluten-free …\" prefixes clear the matching flags.\n- Templates are editorial: each has core requirements (one of several alternatives), flexible slots (e.g. 1–4 vegetables, optional cheese), equipment alternatives and a total time. Salt, pepper, cooking oil, water and dried spices are assumed available.\n- A template is eligible if its time ≤ your maximum, you have one of its equipment options, every unmet core requirement still has a diet-compatible alternative, at least one of your ingredients is used and at most 3 items are missing.\n- Core requirements are filled first, then slots; use-soon items are always tried first and each ingredient is used once per recipe.\n- Score = 50 × share of core requirements you have + 30 × share of your use-soon items used + 3 × core requirements you have + 4 × number of your ingredients used (max 8) − 12 × missing items. Ties: fewer missing, shorter time, then template id.\n\nGeneric cooking templates, not tested recipes. Check that leftovers are still safe to eat and cook meat, fish and eggs thoroughly.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "leftover_chef",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LeftoverChefInput"
              },
              "examples": {
                "example1": {
                  "summary": "Eggs, rice and wilting veg",
                  "value": {
                    "ingredients": [
                      "4 eggs",
                      "leftover rice",
                      "half a courgette",
                      "spinach",
                      "cheddar",
                      "spring onions",
                      "soy sauce"
                    ],
                    "useSoon": [
                      "spinach",
                      "courgette"
                    ],
                    "equipment": [
                      "stove",
                      "oven"
                    ],
                    "maxMinutes": 30
                  }
                },
                "example2": {
                  "summary": "Vegan pantry dinner",
                  "value": {
                    "ingredients": [
                      "chickpeas",
                      "tinned tomatoes",
                      "onion",
                      "garlic",
                      "coconut milk",
                      "curry paste",
                      "rice",
                      "sweet potato",
                      "kale"
                    ],
                    "useSoon": [
                      "kale"
                    ],
                    "diet": [
                      "vegan"
                    ],
                    "equipment": [
                      "stove",
                      "oven"
                    ],
                    "maxMinutes": 45
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeftoverChefResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "LeftoverChefInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ingredients": {
            "title": "Ingredients you have",
            "description": "One per line, e.g. \"2 eggs\", \"leftover rice\", \"half a courgette\".",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 60
          },
          "useSoon": {
            "title": "Must use soon",
            "description": "Items close to expiry get priority (added to the ingredients automatically).",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 20
          },
          "diet": {
            "title": "Diet",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "vegetarian",
                "vegan",
                "gluten-free",
                "dairy-free"
              ]
            }
          },
          "equipment": {
            "title": "Equipment available",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "stove",
                "oven",
                "microwave",
                "blender"
              ]
            },
            "default": [
              "stove",
              "oven",
              "microwave"
            ]
          },
          "maxMinutes": {
            "title": "Maximum time",
            "type": "integer",
            "minimum": 5,
            "maximum": 240,
            "default": 45,
            "x-unit": "min"
          }
        },
        "required": [
          "ingredients"
        ]
      },
      "LeftoverChefResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "recognized",
              "recipes",
              "templatesConsidered",
              "templatesEligible"
            ],
            "properties": {
              "recognized": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "input",
                    "normalized",
                    "category",
                    "known"
                  ],
                  "properties": {
                    "input": {
                      "type": "string"
                    },
                    "normalized": {
                      "type": "string"
                    },
                    "category": {
                      "type": "string"
                    },
                    "known": {
                      "type": "boolean"
                    }
                  }
                }
              },
              "recipes": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "rank",
                    "id",
                    "name",
                    "score",
                    "timeMinutes",
                    "uses",
                    "useSoonUsed",
                    "missing",
                    "optionalExtras",
                    "steps"
                  ],
                  "properties": {
                    "rank": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "score": {
                      "type": "number"
                    },
                    "timeMinutes": {
                      "type": "integer"
                    },
                    "uses": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "useSoonUsed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "missing": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "optionalExtras": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "steps": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "templatesConsidered": {
                "type": "integer"
              },
              "templatesEligible": {
                "type": "integer"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}