Skip to main content
Glama
smeet666

mcp-recipes

Search every recipe source at once

search_recipes
Read-onlyIdempotent

Search every recipe source at once for a dish or ingredient and get one merged list with source IDs for get_recipe to open.

Instructions

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.

Input Schema

NameRequiredDescriptionDefault
queryYesA dish or an ingredient, in any of the languages the sources publish in.
fan_outNoWhether 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.
sourcesNoSources 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_sourceNoRows to take from each source, so one source cannot fill the whole list.

Output Schema

NameRequiredDescriptionDefault
notesYes
orderYesHow the list was built, in words.
queryYes
resultsYes
per_sourceYes
result_countYesRows in this answer, across every source.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed10 schema fields changedv4.1.0
    • addedOutput schema / properties / per_source / items / properties / names_the_dish
      Added 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"
      +}
    • removedOutput schema / properties / per_source / items / properties / reported_total_means / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / per_source / items / properties / reported_total_means / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / per_source / items / properties / wordings / items / properties / not_run_because / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / per_source / items / properties / wordings / items / properties / not_run_because / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • changedOutput schema / properties / per_source / items / required
      Previous 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"
      +]
    • removedOutput schema / properties / results / items / properties / excerpt / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / results / items / properties / excerpt / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / results / items / properties / image_url / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / results / items / properties / image_url / type
      Added value: +[
      +  "string",
      +  "null"
      +]
  2. Changed5 schema fields changedv1.3.0
    • addedInput schema / properties / fan_out
      Added 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"
      +}
    • addedOutput schema / properties / per_source / items / properties / preferred_by_name
      Added 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"
      +}
    • addedOutput schema / properties / per_source / items / properties / read
      Added 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."
      +}
    • addedOutput schema / properties / per_source / items / properties / wordings
      Added 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"
      +}
    • changedOutput schema / properties / per_source / items / required
      Previous 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"
      +]
  3. First observedv1.0.0

TDQS

A4.5/5.0
Behavior5/5

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.

Conciseness3/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.