API & MCP documentation
Check any domain for Universal Commerce Protocol support, find the stores an agent can actually transact with and search their live catalogues, then shop at the store directly — from the independent record that re-checks every verified store daily. Read-only and keyless.
Quickstart
Add the UCP Checker MCP server to any MCP-capable agent. It gets check-domain, list-stores, search-all and five more tools in its tray; resolving a store to buy from is four calls.
claude mcp add --transport http ucp-checker https://ucpchecker.com/mcp
{
"mcpServers": {
"ucp-checker": {
"url": "https://ucpchecker.com/mcp"
}
}
}{
"mcpServers": {
"ucp-checker": {
"type": "streamable-http",
"url": "https://ucpchecker.com/mcp"
}
}
}https://ucpchecker.com/mcp
{
"servers": {
"ucp-checker": {
"type": "http",
"url": "https://ucpchecker.com/mcp"
}
}
}That is the whole setup. What the server exposes is under MCP server; no MCP client, use the REST API or ARD search.
CLI
No agent, no MCP client — just a terminal or a CI job. ucp-check is a zero-dependency Node client over the REST API below: it renders the verdict the API returns and turns it into an exit code. It never scores locally, so a check from CI says exactly what the status page says.
npx ucp-check your-store.com
| Exit | Meaning |
|---|---|
0 | verified |
1 | not verified — not_detected, invalid, blocked, unreachable, pending |
2 | usage error |
3 | API unreachable or rate limited |
--cached reads the last observed state without crawling (use it on every push; run the live check on deploy), --json prints the response as served, --quiet is exit-code only. Source and issues: github.com/ucpchecker/ucp-check · npm.
MCP server
One streamable-HTTP server at https://ucpchecker.com/mcp. It exposes the 8 tools documented below, read straight from the server class so this page cannot drift from what it serves. Read-only tools are open at every tier; the cart tools are for signed or token callers once enforcement is on. The server also publishes a machine-readable card at /.well-known/mcp/server-card.json.
The resolution flow — list-stores (or search-all) → search-catalog → get-product-details → search-policies, then discover-store to see what the merchant itself exposes. There is no server-side session. UCP Checker is read-only: it resolves and measures, it never carts, checks out or takes payment — you transact with the merchant.
Authentication & rate limits
Every read endpoint is open: no API key, no signup. Limits are per IP and per tier:
| Tier | How | MCP calls | search-all |
|---|---|---|---|
anonymous | Nothing — just connect | 30/min · 2,000/day | 12/min |
signed | Sign requests with Web Bot Auth (RFC 9421); we resolve your Signature-Agent key directory | 120/min | 20/min |
token | Authorization: Bearer <key> — keys are issued to partners on request | 300/min | 60/min |
REST: POST /api/v1/check is 30/min and 200/day per IP (it crawls); other endpoints 60/min and 1,000/day. ARD endpoints share the REST limiter. Over the limit you get 429 with Retry-After. Our own outbound requests are signed too — verify them against /.well-known/http-message-signatures-directory.
Guide — build a shopping agent
The minimum viable agent is six steps. With the MCP server connected, steps 1–5 are single tool calls; without it, the same flow works against any store's own UCP profile and MCP endpoint. The long-form version, with code, is at /agents.
| # | Step | How |
|---|---|---|
| 01 | Pick a target store | Use list-stores (or POST /ard/search) for a verified store with an MCP transport and a high score — or browse the directory. |
| 02 | Fetch its UCP profile | GET https://{domain}/.well-known/ucp. check-domain does this for you and returns status, version, capabilities and transports. |
| 03 | Parse capabilities and the MCP endpoint | The profile's services block names the transport and endpoint; capabilities tell you whether cart and checkout are declared. discover-store lists the live tools. |
| 04 | Connect an MCP client | Streamable HTTP to the endpoint the profile declares — never a guessed path. UCP binds the tool names (search_catalog, lookup_catalog, get_product) and every tool call carries meta["ucp-agent"] naming your platform profile. Most read tools are public; some stores gate write tools behind auth. |
| 05 | Search the catalogue | search-catalog (one store) or search-all (the fleet) return titles, prices and variant IDs; get-product-details returns a product's full declared detail. |
| 06 | Hand off to the merchant | UCP Checker is read-only: it resolves and measures, it never carts or checks out. Once you have picked a store, transact with that store's own endpoint. |
To test the whole journey against a live store with a real model — every tool call recorded — use the UCP Playground.
Guide — methodology & the UCP score
Every verified store is re-checked daily from the outside in: we fetch its profile as an agent would, validate it against the current spec, read its bot policies and surface signals, and record what we observed. The score summarises that record. The full method — crawler behaviour, response classification, validation rules, refresh schedule — is on the methodology page; this is the part you need to read a number.
| Category | Weight | What it measures |
|---|---|---|
discoveryAgent Discovery | 30% | Can agents find and reach this store? HTTPS on the profile endpoint, reachability, an agent-friendly robots.txt, and surface signals such as llms.txt and a sitemap. |
conformanceUCP Conformance | 40% | Does the profile validate against the spec? Validity is weighted 3× — an invalid profile cannot score above ~50 here. Declaring a known spec version and clean required fields count. |
capabilitiesCapability Coverage | 30% | What can an agent actually do here? Declared transports (REST / MCP / A2A), checkout, payment handlers, and breadth of capabilities; functional probes raise it when they pass. |
score = discovery × 0.30 + conformance × 0.40 + capabilities × 0.30 , rounded to the nearest integer. A blocked or unreachable store scores 0 on discovery; an undetected profile caps it.
| Grade | Score | Reads as |
|---|---|---|
A | 85–100 | Agent-ready — valid profile, strong discovery, broad capability coverage |
B | 70–84 | Solid — minor warnings or one weak category; agents can still transact |
C | 50–69 | Partial — profile works but missing capabilities or surface signals |
D | 30–49 | Weak — profile reachable but invalid or near-empty |
F | 0–29 | Failing — blocked, unreachable, or no profile detected |
The score is a summary of observed conformance, not a ranking and not a trust rating. In ARD results it travels as metadata.ucpScore, deliberately separate from the relevance score.
Guide — score model versioning
A reading is only useful if today's number means the same thing as last quarter's. So the score model is versioned, history is never rescored, and every number says which model produced it. Drift is not forbidden — unlabelled drift is.
| Axis | Versions | Current | Consumer rule |
|---|---|---|---|
contract | The shape of an API response — which fields exist and what they mean | v1.0.0 | Pin the MAJOR |
model | How the score was computed — weights, grade bands, category definitions, the check set | v1.0.0 (since 2026-08-25) | Compare only within a MAJOR |
| Bump | When | Comparable? |
|---|---|---|
MAJOR | Weights, grade bands or category definitions change | No — scores are not comparable across a major |
MINOR | A check is added, removed or retuned inside a category | Yes, frame holds; the series is annotated at the boundary |
PATCH | The model now computes what it always intended (a check that silently never fired) | Yes, flagged |
MAJOR — a stable field is removed, renamed or changes meaning. MINOR — anything additive, including check-catalogue membership. PATCH — documentation or spec-only corrections. Stable within a major: domain, status, score, grade, category_scores keys, check ids, model_version, contract_version.
Never rescore history. A point in the series reflects the model in force when it was observed; backfilling today's model onto old dates would assert scores merchants never had. Announce every model change on the blog and the methodology page before it ships. Gate CI on check ids or a threshold, not on "all required checks pass" — the required set can grow on a minor.
Check & resolve
check-domainread-only| Name | Type | Description |
|---|---|---|
domainrequired | string | The domain to check for UCP support (e.g., "shopify.com", "example.com"). Protocol, www prefix, and paths are automatically removed. |
list-storesread-only| Name | Type | Description |
|---|---|---|
platform | string | Restrict to one platform, e.g. "wix", "shopify", "woocommerce". |
category | string | Optional. Limit to the deeply audited stores in one category (e.g., "footwear"); see list-categories. Most browsing is better without it. |
limit | integer | Maximum number of stores to return (default: 10, max: 50) |
list-categoriesread-onlyNone.
discover-storeread-only| Name | Type | Description |
|---|---|---|
domainrequired | string | The store domain to discover (e.g., "allbirds.com") |
Read catalogs & policies
search-catalogread-only| Name | Type | Description |
|---|---|---|
domainrequired | string | The store domain (e.g., "allbirds.com", "gymshark.com"). Must be a UCP-enabled Shopify store. |
queryrequired | string | Product search term (e.g., "tree topper", "wool runners", "hoodie") |
country | string | ISO country code for localized pricing/inventory (e.g., "US", "GB", "CA"). Optional — defaults to store default. |
search-allread-only| Name | Type | Description |
|---|---|---|
queryrequired | string | Product search term (e.g., "running shoes", "hoodie") |
category | string | Optional. Limit the fan-out to the deeply audited stores in one category (e.g., "footwear"); see list-categories. Most queries search better without it. |
country | string | ISO country code for localized pricing (e.g., "US", "GB"). Optional. |
maxStores | integer | Maximum number of stores to search (default: 5, max: 10) |
get-product-detailsread-only| Name | Type | Description |
|---|---|---|
domainrequired | string | The store domain (e.g., "allbirds.com") |
productIdrequired | string | The product ID from search-catalog results (e.g., "gid://shopify/Product/123456") |
search-policiesread-only| Name | Type | Description |
|---|---|---|
domainrequired | string | The store domain (e.g., "allbirds.com") |
queryrequired | string | Policy search term (e.g., "return policy", "shipping", "refund window", "privacy policy") |
Discovery — Agentic Resource Discovery
UCP Checker is an ARD v0.91 discovery service — the federated standard agents use to ask "what's available for this task?" before invoking anything. Every result is a verified storefront as a standard catalogue entry: identifier (urn:air:{domain}:ucp:storefront), url (the store's /.well-known/ucp), capabilities, metadata (category, platform, grade, ucpScore, mcpEndpoint) and a trustManifest whose attestations link the store's public conformance record. Ask for type: ["application/mcp-server-card+json"] and stores with an MCP endpoint come back as inline server cards.
/ard/searchAgentic Resource Discovery §5.3.2. Returns ARD catalogue entries — one per verified store — each with the store's UCP profile URL, declared capabilities, MCP endpoint and a trustManifest linking its public conformance record. Ranking is lexical and deterministic: each query word counts by how rare it is across the index (a word few stores carry says more than one most do), a category synonym counts at 0.7 of a direct match, singular and plural match each other, accented letters match their base letters, and the query as a phrase in a store's name is a full match. Stores scoring below 30 are not returned, so an empty `results` list means no strong match — try the product type alone, or go to a store you know. Equal scores are ordered by how prominent the query words are on the store's observed shelf, then evidence, then UCP score, then own domain before platform subdomain. A query reaches its first 50 results. `score` is relevance only; the UCP score is in `metadata.ucpScore`.
| Name | In | Type | Description |
|---|---|---|---|
query.textrequired | body | string | What the agent needs, e.g. "running shoes". |
query.filter | body | object | Field → array of values. Supported: type, tags, capabilities, publisher, metadata.category, metadata.platform, metadata.country, metadata.grade, metadata.tier, metadata.transports, metadata.evidence, trustManifest.identityType, trustManifest.attestations.type. Unknown fields → 400. |
federation | body | string | auto (default) | referrals | none. We do not proxy upstream; auto and referrals return the GitHub and Hugging Face finders as referrals. |
pageSize | body | integer | Default 10, max 100 (larger values are capped). Pages stop at the first 50 results. |
pageToken | body | string | From the previous response. |
curl -s -X POST https://ucpchecker.com/ard/search \
-H 'Content-Type: application/json' \
-d '{"query": {"text": "running shoes",
"filter": {"metadata.platform": ["shopify"]}},
"federation": "none", "pageSize": 5}'200{ results: [entry + score + source], referrals?, pageToken? } — results may be empty (no strong match)400INVALID_ARGUMENT — missing text, body not a JSON object, unsupported filter field, bad token405METHOD_NOT_ALLOWED — search is POST429RATE_LIMIT_EXCEEDED — honour Retry-After/ard/exploreARD §5.3.3. Same query model as search, including the relevance floor; returns bucket counts over the matched stores for the requested fields instead of entries — "which categories are there?", "how many stores per platform?". Without query text, counts cover every listed store.
| Name | In | Type | Description |
|---|---|---|---|
query | body | object | text and/or filter, as for search. |
resultType.facets[]required | body | array | { field, limit?, minCount? } — field from the supported list. |
curl -s -X POST https://ucpchecker.com/ard/explore \
-H 'Content-Type: application/json' \
-d '{"resultType": {"facets": [{"field": "metadata.category", "limit": 10},
{"field": "metadata.platform"}]}}'200{ resultType: "facets", facets: { field: { buckets: [{value, count}], otherCount? } } }400INVALID_ARGUMENT — missing resultType.facets, unsupported field, body not a JSON object429RATE_LIMIT_EXCEEDED — honour Retry-After/ard/agentsARD §5.3.4. No relevance ranking — sorted, filtered, paged. Built for developer portals and crawlers. Pages stop at the first 50 entries; the index is browsable, not exportable. Responses are cached at the edge for up to 15 minutes, so a store verified a moment ago may take that long to appear.
| Name | In | Type | Description |
|---|---|---|---|
filter | query | string | EBNF-lite, e.g. type = 'application/mcp-server-card+json' AND updatedAfter > '2026-01-01'. Fields: displayName, type, publisherId, createdAfter, updatedAfter. |
orderBy | query | string | displayName | updatedAt, optionally ASC|DESC. |
pageSize | query | integer | Default 20, max 100. |
pageToken | query | string | From the previous response. |
curl -s "https://ucpchecker.com/ard/agents?pageSize=20&orderBy=updatedAt%20DESC"
200{ items: [entry], total, pageToken? }400INVALID_ARGUMENT — malformed filter or orderBy, or a pageToken past the result limit429RATE_LIMIT_EXCEEDED — honour Retry-AfterREST API
The same record over plain HTTP. All responses are JSON; the request and response shapes are published as JSON Schema at /api/v1/schema.
Observed, not asserted. Every record carries two timestamps: observed_at — when we last actually saw the store (any status) — and attempted_at — when we last tried. outcome says what the latest attempt told us; only observed refreshes status. When a store's edge rate-limits our crawler the attempt is recorded, observed is false, and the record you get is the last observation, not a new verdict. Treat observed_at as the freshness of the data.
/api/v1/checkFetches and validates /.well-known/ucp right now, records the result, and returns the domain's status. Use GET /api/v1/status/{domain} when you only need the current record. If the store's edge rate-limits the attempt the response is still 200: `observed` is false, `status` and every other field are the last observed state, and a Retry-After header (and `retry_after_s`) says when to try again — an attempt that observed nothing never changes a verdict.
| Name | In | Type | Description |
|---|---|---|---|
domainrequired | body | string | Domain to check, e.g. "example.com". Scheme, www and path are stripped. |
curl -s -X POST https://ucpchecker.com/api/v1/check \
-H 'Content-Type: application/json' \
-d '{"domain": "allbirds.com"}'200merchant_status — { data: { domain, status, ucp_version, manifest_url, observed_at, attempted_at, outcome, observed, retry_after_s, last_checked_at } }422Invalid domain429Rate limited — 30/min, 200/day per IP; Retry-After set/api/v1/status/{domain}What we last observed: status, declared UCP version, the profile URL, and when it was observed (`observed_at`). `attempted_at` and `outcome` say whether a more recent attempt observed nothing (for example, the store's edge rate-limited us) — `observed: false` means the record is older than the last attempt. No crawl is triggered.
| Name | In | Type | Description |
|---|---|---|---|
domainrequired | path | string | Bare domain, e.g. "allbirds.com". |
curl -s https://ucpchecker.com/api/v1/status/allbirds.com
200merchant_status404Never checked — POST /api/v1/check first42960/min, 1,000/day per IP/api/v1/schemaDraft 2020-12. The same schema is declared as the com.ucpchecker.api service in our own /.well-known/ucp, so conformance tooling can resolve it.
curl -s https://ucpchecker.com/api/v1/schema
200JSON Schema documentSkills
Self-contained instruction documents for coding agents, served live at /.well-known/agent-skills/index.json (with sha256 digests) so the current spec version never bakes into an agent's memory. Paste the URL into your agent, or fetch it over MCP as a resource.
ucp-agent-readyskill-mdPublish or repair a Universal Commerce Protocol (UCP) profile so AI shopping agents can discover, read and transact with a store, verified against UCP Checker's independent validator. Use when asked to "make this store agent-ready", "add UCP", "fix our /.well-known/ucp", "improve our UCP score", or when a UCP check reports errors or warnings. Drives a check → fix → re-check loop over the keyless UCP Checker API.
https://ucpchecker.com/.well-known/agent-skills/ucp-agent-ready/SKILL.md
ucp-checkerskill-mdFind stores an AI agent can actually transact with, and check any domain's Universal Commerce Protocol (UCP) status, using the UCP Checker resolver. Use when choosing a store that supports agentic checkout, checking whether a merchant is agent-ready, or comparing stores by UCP conformance. Read-only: it resolves and measures, it never carts or checks out. Designed for agents evaluating merchants before acting.
https://ucpchecker.com/.well-known/agent-skills/ucp-checker/SKILL.md
Well-known files
Everything an agent needs to discover and verify this server without being told about it.
/.well-known/ard.jsonARD capability profile (canonical path since ARD v0.91): the registry entry, the MCP server card, the API and the skills./.well-known/ai-catalog.jsonThe same profile at the predecessor path, for consumers that still read it./.well-known/mcp/server-card.jsonMachine-readable card for the MCP server, reflected from the server class./.well-known/agent-skills/index.jsonSkills index (schemas.agentskills.io discovery 0.2.0) with sha256 digests./.well-known/http-message-signatures-directoryOur Web Bot Auth (RFC 9421) signing keys — verify our crawler's signatures against this./.well-known/ucpOur own UCP profile, declaring the com.ucpchecker.api service./llms.txtPlain-text guide for language models. Errors
ARD errors follow the spec's error object — a top-level errorCode and message — and every failure under /ard, including the ones the server raises itself, is JSON: { "errorCode": "INVALID_ARGUMENT", "message": "…", "error": { "code": "INVALID_ARGUMENT", "message": "…" } }. The nested error object is the shape this endpoint shipped with; it stays for existing clients, and new ones should read errorCode.
| HTTP | Where | Meaning |
|---|---|---|
400 | ARD | INVALID_ARGUMENT — missing query.text, a body that is not a JSON object, unsupported filter/facet field, bad pageToken (or one past the 50-result limit), bad orderBy. |
404 | ARD | NOT_FOUND — no such ARD endpoint; the profile is at /.well-known/ard.json. |
405 | ARD | METHOD_NOT_ALLOWED — e.g. GET /ard/search; search and explore are POST. |
404 | REST | Domain never checked — run POST /api/v1/check. |
422 | REST | Validation failed (not a domain). |
429 | all | Rate limited; honour Retry-After. On ARD the code is RATE_LIMIT_EXCEEDED. |
500 | ARD | INTERNAL_ERROR — nothing about the failure is exposed in the message. |
| MCP | tools | Tool errors come back as an isError result with a plain-language message — an unknown tool name is a JSON-RPC error, not a 500. |
Versioning
The API contract is v1.0.0. The REST response envelope and the MCP tool names are stable within a major version; new tools and new optional fields are additive. The UCP score model is versioned separately from the API — a score change is a model change, never a silent one, and is announced on the blog and in the methodology. Nothing is removed without notice.
| Date | Change |
|---|---|
2026-09-15 | Removed: the MCP ucp://stores/verified resource. Use list-stores to browse (up to 50, filter by platform), search-all or POST /ard/search to find stores. POST /ard/explore no longer facets on publisher (it is still a filter). |
2026-09-01 | Additive: observed_at, attempted_at, outcome, observed, retry_after_s on merchant_status (REST), observed_at on the MCP ucp://stores/verified resource, ARD updatedAt now the last observation, last_observed_at + last_outcome columns appended to the CC-BY dataset. last_checked_at is kept as an alias of observed_at. POST /api/v1/check sends Retry-After when the live attempt observed nothing. |
