# find_sunglasses

Filter the covered catalog by a reference size with per-dimension tolerances and hard conditions (polarized, maker-labeled bridge fit, brand, optional offer conditions). Every condition is evaluated per candidate as pass/fail/unknown/conflict/stale with evidence.

- REST: `POST https://sunglassesguides.com/api/v1/search` (OpenAPI operationId `find_sunglasses`)
- MCP: tool `find_sunglasses` on `https://sunglassesguides.com/mcp`
- Read-only: yes. It never changes anything outside this service.
- Market / units: US, USD, millimetres. Schema version 1.0.
- Support: implemented=true, data_covered=partial, externally_verified=false. Offer conditions are implemented but no offer source is connected, so they return NO_OFFER_COVERAGE in production.

## When to use

- The user has a size (e.g. 52-18-140) and wants alternatives.
- The user needs frames that are confirmed polarized and/or maker-labeled low bridge fit.
- One call usually answers the task: it returns size differences, evidence and detail links.

## When not to use

- To identify which exact frame the user owns (use resolve_sunglasses).
- To compare 2-4 known variants field by field (use compare_sunglasses).

## Input

JSON body:

| Name | Type | Required | Range | Meaning |
|---|---|---|---|---|
| `reference` | object | no |  | Reference size to compare against (e.g. from the user's current glasses). Fields: `lens_width_mm` (integer 30-80), `bridge_width_mm` (integer 8-30), `temple_length_mm` (integer 100-170). |
| `reference_text` | string | no | max 40 chars | Reference size as printed, e.g. "52-18-140". Parsed to reference. |
| `reference_variant_id` | string | no |  | Use a known catalog variant's size as the reference. Requires expected_data_version if it came from an earlier call. |
| `tolerance_mm` | object | no |  | Allowed absolute difference per dimension in mm. Defaults (disclosed in normalized_constraints): lens 2, bridge 2, temple 5. Fields: `lens_width` (number 0-10), `bridge_width` (number 0-10), `temple_length` (number 0-20). |
| `require` | object | no |  |  Fields: `polarized` (boolean), `bridge_fit` (low_bridge_fit/standard_fit/high_bridge_fit/universal_fit/unmapped), `brands` (array), `max_item_price_usd` (number), `max_landed_total_usd` (number), `in_stock` (boolean), `ship_to_zip` (string). |
| `market` | string | no | max 8 chars | Market code. Only "US" is supported; anything else returns 422 UNSUPPORTED_MARKET. |
| `currency` | string | no | max 8 chars | Currency code. Only "USD" is supported. |
| `limit` | integer | no | min 1, max 20 | Matches per page. Default 5, max 20. |
| `cursor` | string | no | max 400 chars | Opaque cursor from a previous page of the SAME query. |
| `include_undecided` | boolean | no |  | Also list up to 5 candidates that could not be decided (unknown/conflicting/stale evidence). Default true. |
| `expected_data_version` | string | no |  | data_version returned by an earlier call. If the catalog changed since, the call fails with DATA_VERSION_CHANGED instead of mixing old and new facts. |


Default tolerances (disclosed in `normalized_constraints.defaults_applied`): lens ±2 mm, bridge ±2 mm, temple ±5 mm. Every hard condition is evaluated per candidate as pass / fail / unknown / conflict / stale. Only all-pass candidates are in `matches`. A definite fail on any condition makes a candidate a non-match even if other fields are unknown. All offer conditions must be met by one single offer (`qualifying_offer_id`).

## Output

The standard envelope (see [REST conventions](https://sunglassesguides.com/docs/rest.md)): `request_id`, `success`, `schema_version`, `data_version`, `data_mode`, `generated_at`, `request`, `result`, `sources`, `freshness`, `cache`, `next_actions`, and `error` on failure.

status MATCHES_FOUND, NO_MATCH_IN_COVERED_CATALOG, INSUFFICIENT_EVIDENCE, NO_OFFER_COVERAGE, OUT_OF_SCOPE or EMPTY_CATALOG; normalized_constraints; non-overlapping counts; matches (only all-pass candidates); undecided candidates with blocking conditions; relaxation suggestions that require user confirmation.

`result.status` values: `MATCHES_FOUND`, `NO_MATCH_IN_COVERED_CATALOG`, `INSUFFICIENT_EVIDENCE`, `NO_OFFER_COVERAGE`, `OUT_OF_SCOPE`, `EMPTY_CATALOG`. `success:true` means the request was processed; it does not mean something matched.

## Sources and time

Each field carries `field_status`, `source_ids` and evidence excerpts; `sources[]` lists each cited source once with `retrieved_at`. `checked_at` is per record. See [freshness](https://sunglassesguides.com/docs/freshness.md).

## Permissions

Anonymous read-only trial without a key; optional API key for higher quotas. See [auth and quotas](https://sunglassesguides.com/docs/auth-and-quotas.md).

## Examples

### Success example (live result on data_version c1.o0)

```sh
curl -sS -X POST https://sunglassesguides.com/api/v1/search \
  -H 'Content-Type: application/json' \
  -d '{"reference_text":"52-18-140","require":{"polarized":true,"bridge_fit":"low_bridge_fit"},"limit":3}'
```

Result summary:

```json
{
  "status": "MATCHES_FOUND",
  "message": "4 variant(s) meet every hard condition. 17 more could not be decided because evidence is unknown, conflicting or stale.",
  "counts": {
    "published_variants": 81,
    "candidates_in_scope": 81,
    "evaluated": 81,
    "matched": 4,
    "excluded_definite_fail": 60,
    "undecided": {
      "unknown": 17,
      "conflict": 0,
      "stale": 0,
      "total": 17
    },
    "spec_matched_without_offer_coverage": null,
    "categories_overlap": false,
    "note": "Each candidate is counted in exactly one of matched / excluded_definite_fail / undecided. A definite fail on any hard condition wins over unknowns."
  },
  "matches": [
    {
      "rank": 1,
      "variant_id": "mauijim-658-02",
      "display_name": "Ihupani Asian Fit Matte Black / Neutral Grey",
      "size_distance_mm": 8
    },
    {
      "rank": 2,
      "variant_id": "mauijim-r658-04",
      "display_name": "Ihupani Asian Fit Matte Burgundy / Maui Rose®",
      "size_distance_mm": 8
    },
    {
      "rank": 3,
      "variant_id": "mauijim-659-02",
      "display_name": "'ōpiopio Asian Fit Matte Black with Interior White Gradient Effect / Neutral Grey",
      "size_distance_mm": 9
    }
  ]
}
```

### No-match example

```sh
curl -sS -X POST https://sunglassesguides.com/api/v1/search \
  -H 'Content-Type: application/json' \
  -d '{"reference":{"lens_width_mm":45,"bridge_width_mm":25,"temple_length_mm":120},"require":{"polarized":true}}'
```

```json
{
  "status": "INSUFFICIENT_EVIDENCE",
  "message": "No variant is confirmed to meet every hard condition. 3 candidate(s) lack evidence (unknown/conflicting/stale) for at least one condition; 78 definitely fail."
}
```

### Failure example (invalid input -> HTTP 422)

```sh
curl -sS -X POST https://sunglassesguides.com/api/v1/search \
  -H 'Content-Type: application/json' \
  -d '{"reference":{"lens_width_mm":5,"bridge_width_mm":18}}'
```

Returns `success:false`, `error.code` `VALIDATION_FAILED` with `error.details` per field.

### MCP call

```json
{
  "method": "tools/call",
  "params": {
    "name": "find_sunglasses",
    "arguments": {
      "reference_text": "52-18-140",
      "require": {
        "polarized": true,
        "bridge_fit": "low_bridge_fit"
      },
      "limit": 3
    }
  }
}
```

## Related tools

- [compare_sunglasses](https://sunglassesguides.com/docs/compare_sunglasses.md)
- [get_sunglasses_variant](https://sunglassesguides.com/docs/get_sunglasses_variant.md)
- [get_sunglasses_offers](https://sunglassesguides.com/docs/get_sunglasses_offers.md)
