Transport: MCP Streamable HTTP (legacy SSE also available). All tools return JSON. No HTML rendering or JS execution required at any step.
Endpoint: https://papyrasai.com/mcp Transport: streamable-http Legacy: https://papyrasai.com/mcp/sse (sse) Protocol: Model Context Protocol (MCP)
See /.well-known/mcp.json for a machine-readable manifest of this same information.
Status: not yet enforced. An API-key check against a registered-client table is planned but not live as of this writing. Do not assume this endpoint is permanently open — future calls may require an API key passed as a header. Treat current unauthenticated access as provisional.
Hybrid structured + semantic search. Pass query for free-text search; combine with
structured filters to narrow results. Prefer natural phrases over single bare keywords —
short queries (e.g. "patio") retrieve less accurately than descriptive phrases
(e.g. "pressure washer for patio"), since the embedding model has more context
to place longer queries precisely.
| Field | Type | Default | Notes |
|---|---|---|---|
| query | string | null | Free-text search, embedded and matched semantically |
| category | string | null | Exact match against normalized category |
| brand | string | null | Case-insensitive match |
| min_price / max_price | number | null | Applied to sale price if present, else list price |
| intent_tag / use_case_tag / audience_tag | string | null | Matches against AI-generated tag arrays |
| availability | string | in_stock | "all" for every status, or one of: in_stock / out_of_stock / preorder / discontinued |
| limit | integer | 20 | Max 100 |
| offset | integer | 0 | Pagination |
{
"results": [
{
"id": "uuid",
"global_sku": "string",
"name": "string",
"brand": "string",
"category_path": "string",
"price": "12.99",
"sale_price": "9.99",
"currency": "USD",
"availability": "in_stock",
"affiliate_url": "https://...",
"image_urls": ["https://..."],
"intent_tags": ["...", "...", "..."],
"use_case_tags": ["...", "...", "..."],
"audience_tags": ["...", "...", "..."],
"explanation_short": "1-sentence relevance summary"
}
],
"count": 20,
"limit": 20,
"offset": 0,
"query_embedded": true
}
Full detail for a single product by UUID. Returns regardless of stock status — this is a direct lookup, not a discovery search, so an already-known product ID still resolves even if it has since gone out of stock.
Input: { "product_id": "uuid" }
Output: same fields as search_products, plus description, gtin, material,
color, size, explanation_long, carousel_label, is_gift_friendly,
is_best_price_flag, price_percentile, rating_score
Fetch up to 100 products by ID in a single call.
Input: { "product_ids": ["uuid", "uuid", ...] }
Output: { "results": [...], "not_found": ["uuid", ...] }
Related/similar/accessory/bundle/alternative products for a given ID.
Input: { "product_id": "uuid" }
Output: { "results": [...] }
Currently returns an empty result set for all products — the underlying relationship data has not been generated yet. This is expected behavior, not an error.
Sequential pagination through the entire catalog, ordered by creation date. Use for bulk export rather than targeted search.
Input: { "limit": 20, "offset": 0, "availability": null }
Output: { "results": [...], "limit": 20, "offset": 0 }
sale_price is null when no discount is active; compare against price when present.intent_tags, use_case_tags, audience_tags,
explanation_short) are LLM-generated per product, not manually curated — treat as
strong signal, not ground truth.