> ## Documentation Index
> Fetch the complete documentation index at: https://docs.skinai.co/llms.txt
> Use this file to discover all available pages before exploring further.

# analyze_ingredients

> Explain the ingredients on a product label.

Matches an ingredient list against Skan's ingredient database and returns what each ingredient is and what it is commonly used for. Send either the label text as one string, or up to 50 names as an array. Passing a scan (`run_id` and `access_token`) raises the allowance. Nothing is personalised to the scan.

* **Fuzzy matches below 0.8 confidence** are returned as `matched: false`, so invented or misspelled names aren't reported as a real ingredient.
* **An unmatched entry** means only that Skan didn't recognise the text. It says nothing about whether the ingredient is safe or approved.

This is cosmetic reference information, not a safety assessment.

**Allowance:** 10 per scan, or 5 per day with no scan.

**Common errors:** `invalid_input`, `catalog_unavailable`, `rate_limited`.

|             |                                                                      |
| ----------- | -------------------------------------------------------------------- |
| Tool        | `analyze_ingredients`                                                |
| Listed on   | `/mcp` (guest) and `/mcp/account` (OAuth)                            |
| Annotations | `readOnlyHint: false` `destructiveHint: false` `openWorldHint: true` |

## Input

Sent as `params.arguments` of `tools/call`. Unknown keys are rejected with `invalid_input`.

<ParamField body="run_id" type="string">
  Optional: the person's scan, which raises the free allowance.

  Format `uuid`.
</ParamField>

<ParamField body="access_token" type="string">
  Pattern `^[a-f0-9]{64}$`.
</ParamField>

<ParamField body="ingredients" type="string | string[]" required>
  Max length 6000. Max 50 items.
</ParamField>

## Output

Returned as `structuredContent`, and as the same object serialised in `content[0].text`. On failure the result has `isError: true` and an [error body](/errors) instead.

<ResponseField name="items" type="object[]" required>
  Max 50 items.

  <Expandable title="item">
    <ResponseField name="input_text" type="string" required />

    <ResponseField name="matched" type="boolean" required />

    <ResponseField name="match_confidence" type="number" required>
      Range 0–1.
    </ResponseField>

    <ResponseField name="match_type" type="enum<string> | null" required>
      One of `exact_name`, `exact_alternative`, `fuzzy_name`, `fuzzy_alternative`.
    </ResponseField>

    <ResponseField name="name" type="string | null" required />

    <ResponseField name="description" type="string | null" required />

    <ResponseField name="what_it_does" type="string[]" required>
      Max 8 items.
    </ResponseField>

    <ResponseField name="benefits" type="string[]" required>
      Max 8 items.
    </ResponseField>

    <ResponseField name="concerns" type="string[]" required>
      Max 8 items.
    </ResponseField>

    <ResponseField name="ingredient_type" type="string[]" required>
      Max 4 items.
    </ResponseField>

    <ResponseField name="suitable_for_time" type="string | null" required />
  </Expandable>
</ResponseField>

<ResponseField name="summary" type="object" required>
  <Expandable title="properties">
    <ResponseField name="total" type="integer" required>
      Range 0–9007199254740991.
    </ResponseField>

    <ResponseField name="matched" type="integer" required>
      Range 0–9007199254740991.
    </ResponseField>

    <ResponseField name="unmatched" type="integer" required>
      Range 0–9007199254740991.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="entitlement" type="object" required>
  <Expandable title="properties">
    <ResponseField name="tier" type="&#x22;free_guest&#x22;" required />

    <ResponseField name="used" type="integer" required>
      Range 0–9007199254740991.
    </ResponseField>

    <ResponseField name="included" type="integer" required>
      Range 0–9007199254740991.
    </ResponseField>

    <ResponseField name="remaining" type="integer" required>
      Range 0–9007199254740991.
    </ResponseField>

    <ResponseField name="upgrade_required" type="boolean" required />

    <ResponseField name="more_in_app" type="string" required />
  </Expandable>
</ResponseField>

<ResponseField name="unmatched_meaning" type="string" required />

<ResponseField name="limitations" type="string" required />

<AccordionGroup>
  <Accordion title="Description the model receives">
    ```text theme={null}
    Look up a skincare product's ingredient list in Skan's ingredient database and return what each matched ingredient is and what it is commonly used for. Send the label text, or up to 50 individual names. An unmatched entry means only that Skan did not recognise the text; never present it as unknown, unsafe or unapproved. Check entitlement.upgrade_required before you describe the result: when it is true the free allowance is spent and `items` is empty for that reason, so say the allowance is used up and point the person at the Skan app — do not tell them Skan recognised none of their ingredients, and do not start a scan to reset it. This is cosmetic reference information, not medical advice, not a safety assessment, and not a judgement about a specific person's skin. Completes in one call; it is not a background job.
    ```
  </Accordion>

  <Accordion title="Input JSON Schema">
    ```json theme={null}
    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "properties": {
        "run_id": {
          "description": "Optional: the person's scan, which raises the free allowance.",
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "access_token": {
          "type": "string",
          "pattern": "^[a-f0-9]{64}$"
        },
        "ingredients": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 6000,
              "description": "A product's ingredient list as printed on the label. Must not be empty."
            },
            {
              "maxItems": 50,
              "type": "array",
              "items": {
                "type": "string",
                "maxLength": 120
              },
              "description": "Individual ingredient names. At least one, at most 50."
            }
          ]
        }
      },
      "required": [
        "ingredients"
      ],
      "additionalProperties": false
    }
    ```
  </Accordion>

  <Accordion title="Output JSON Schema">
    ```json theme={null}
    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "properties": {
        "items": {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "input_text": {
                "type": "string"
              },
              "matched": {
                "type": "boolean"
              },
              "match_confidence": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "match_type": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "exact_name",
                      "exact_alternative",
                      "fuzzy_name",
                      "fuzzy_alternative"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "what_it_does": {
                "maxItems": 8,
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "benefits": {
                "maxItems": 8,
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "concerns": {
                "maxItems": 8,
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "ingredient_type": {
                "maxItems": 4,
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "suitable_for_time": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "input_text",
              "matched",
              "match_confidence",
              "match_type",
              "name",
              "description",
              "what_it_does",
              "benefits",
              "concerns",
              "ingredient_type",
              "suitable_for_time"
            ],
            "additionalProperties": false
          }
        },
        "summary": {
          "type": "object",
          "properties": {
            "total": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            "matched": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            "unmatched": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            }
          },
          "required": [
            "total",
            "matched",
            "unmatched"
          ],
          "additionalProperties": false
        },
        "entitlement": {
          "type": "object",
          "properties": {
            "tier": {
              "type": "string",
              "const": "free_guest"
            },
            "used": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            "included": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            "remaining": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            "upgrade_required": {
              "type": "boolean"
            },
            "more_in_app": {
              "type": "string"
            }
          },
          "required": [
            "tier",
            "used",
            "included",
            "remaining",
            "upgrade_required",
            "more_in_app"
          ],
          "additionalProperties": false
        },
        "unmatched_meaning": {
          "type": "string"
        },
        "limitations": {
          "type": "string"
        }
      },
      "required": [
        "items",
        "summary",
        "entitlement",
        "unmatched_meaning",
        "limitations"
      ],
      "additionalProperties": false
    }
    ```
  </Accordion>
</AccordionGroup>

<RequestExample>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0", "id": 6, "method": "tools/call",
    "params": {
      "name": "analyze_ingredients",
      "arguments": { "ingredients": ["Niacinamide", "Xyzzyl Glowamide"] }
    }
  }
  ```
</RequestExample>

<ResponseExample>
  ```json Response (structuredContent, description shortened) theme={null}
  {
    "items": [
      {
        "input_text": "Niacinamide",
        "matched": true,
        "match_confidence": 1,
        "match_type": "exact_name",
        "name": "Niacinamide",
        "description": "Niacinamide, a potent form of vitamin B3, is celebrated for its wide-ranging benefits in skincare. …",
        "what_it_does": ["Smoothing"],
        "benefits": ["Anti-Aging", "Soothing", "Fades Dark Spots", "Controls Oil Production", "Pore Minimizer", "Brightening"],
        "concerns": [],
        "ingredient_type": ["Antioxidant", "Humectant", "Niacinamide"],
        "suitable_for_time": "both"
      },
      {
        "input_text": "Xyzzyl Glowamide",
        "matched": false,
        "match_confidence": 0.56,
        "match_type": null,
        "name": null,
        "description": null,
        "what_it_does": [],
        "benefits": [],
        "concerns": [],
        "ingredient_type": [],
        "suitable_for_time": null
      }
    ],
    "summary": { "total": 2, "matched": 1, "unmatched": 1 },
    "entitlement": { "tier": "free_guest", "used": 1, "included": 5, "remaining": 4, "upgrade_required": false, "more_in_app": "…" },
    "unmatched_meaning": "An unmatched entry means only that Skan's ingredient database did not recognise that text. …",
    "limitations": "Cosmetic and informational only. This is not medical advice, not a diagnosis, and not a safety assessment. …"
  }
  ```
</ResponseExample>
