Fabric documentation

UCP catalog

Search every Fabric brand's approved products from a shopping agent.

Fabric's UCP catalog is one Universal Commerce Protocol endpoint that answers catalog searches across every brand Fabric serves. Each product names the brand that sells it and links to that brand's store, where the shopper buys.

Note

The catalog answers on https://app.staging.fabric.inc while it is in staging. The paths below stay the same when it moves to production.

Discovery

The business profile is at /.well-known/ucp. It needs no credentials and is cached for 5 minutes.

curl https://app.staging.fabric.inc/.well-known/ucp

It declares UCP 2026-08-25 (with 2026-04-08 under supported_versions), the dev.ucp.shopping.catalog.search and dev.ucp.shopping.catalog.lookup capabilities, and one dev.ucp.shopping service over mcp whose endpoint is /api/ucp/mcp. It declares no cart, checkout or order capability, and no payment handler.

Identifying your agent

There is no API key. Every tool call carries your agent's UCP profile URL in meta["ucp-agent"].profile, as it does with any UCP business:

  • The URL must be https on the default port, publicly reachable, and must not redirect. Its query string is ignored.
  • The profile must declare a UCP version and at least one catalog capability.
  • Fabric fetches it once and reuses it for 10 minutes.

Calling the catalog

The endpoint speaks MCP over streamable HTTP, statelessly: every request is a single POST with Accept: application/json, text/event-stream. Batches are refused.

ToolDoes
search_catalogSearches all brands, or the ones in catalog.filters.business
lookup_catalogReturns up to 100 products by id
get_productReturns one product by id
curl -X POST https://app.staging.fabric.inc/api/ucp/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "search_catalog",
      "arguments": {
        "meta": { "ucp-agent": { "profile": "https://agent.example.com/.well-known/ucp" } },
        "catalog": {
          "query": "waterproof hiking boots",
          "filters": { "business": ["alpenglowsports.com"] },
          "pagination": { "limit": 10 }
        }
      }
    }
  }'

The UCP result is in result.structuredContent, and the same JSON is in result.content[0].text.

  • catalog.query is at most 256 characters, and each filter value at most 128.
  • catalog.filters.business takes one brand domain or a list of up to 20. Leave it out to search every brand.
  • pagination.limit defaults to 10 and is capped at 100. A search with a query returns the best matches in one page; a search without one pages with pagination.cursor.
  • Price filters (catalog.filters.price.min and .max) are in minor units of each product's own currency.

What a product carries

  • id names the brand and the SKU, for example gid://fabric/Product/alpenglowsports.com/BOOT-7. Pass it back to lookup_catalog or get_product unchanged.
  • url is the product page on the brand's store. That is where the shopper buys.
  • variants[].seller holds the brand's name and a link to its store, and metadata.business_domain holds its domain.
  • variants[].price is the brand's price from its last catalog import, in the brand's currency.
  • variants[].availability is always { "available": true, "status": "in_stock" }. Fabric does not hold live stock, so check the brand's store before promising delivery.

Only products each brand has approved for publishing are served. A product without a price is left out.

Errors

A refused caller gets a JSON-RPC error with code -32001 and the reason in data.code:

data.codeHTTPMeaning
invalid_profile_url200No profile URL in meta, or it is not https on the default port
profile_unreachable422The profile could not be fetched, or Fabric has fetched too many profiles for now (the message says which)
invalid_profile200The profile is not a UCP profile, or declares no catalog capability
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32001,
    "message": "A UCP agent profile is required: send meta[\"ucp-agent\"].profile.",
    "data": { "code": "invalid_profile_url" }
  }
}

A tool that does not exist, such as create_cart, returns a tool result with isError: true. So does a malformed call; its text starts with the JSON-RPC code, for example -32602: ....

A request body over 64 KB returns 413. When the catalog is busy, a call returns 503 with Retry-After: 1.

Rate limits

Counted perLimit
All callers together1,200 requests a minute, and at most 40 a second on any one server
Client IP30 requests a minute, 600 an hour
Agent profile host300 tool calls a minute, 10,000 an hour
Profile fetches, per profile URL20 an hour
Profile fetches, all callers together60 a minute

A profile Fabric has already fetched successfully is not held to the fetch limits.

Every successful response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. Over a limit, the endpoint returns 429 with Retry-After in seconds. The MCP handshake (initialize, tools/list) counts toward the first two.

Note

Your agent is identified by the profile URL it sends; requests are not signed yet. Its budget is counted per profile host, and shared by everyone who presents a profile on that host.