Seven real shoes, all labelled men's US 9:
Nike 263 mm Salomon 264 mm Converse 265 mm ASICS 268 mm Adidas 270 mm Birkenstock 270 mm New Balance 270 mm
Seven millimetres of spread on an identical label.
It gets worse. I read Kith's live catalog the way a shopping agent would. Every US-family product uses the same 3–18 size axis regardless of whether it's tagged mens, wmns, or both — and most are tagged both. So a label that just says "7" is ambiguous by roughly 15 mm inside a single brand, wider than the spread across all seven brands.
The catalog structurally cannot tell you which. Neither can the agent shopping on your behalf. That fact only exists on the buyer's side of the transaction.
Every agentic commerce protocol shipped in the last eighteen months standardises the merchant — UCP and ACP for the shopping journey, AP2 for payment authorization, x402 for settlement. None of them model the buyer past a payment instrument and a shipping address.
Fit Check is the smallest useful version of the buyer's half.
Apparel returns run around 25% (31% on shoes), size and fit cause roughly half to two-thirds of them, and each costs a merchant about $30. The person's half of the transaction is missing, and it's expensive.
What it does You enter shoes you already own that fit — never millimetres. Brands and sizes, which sneaker shoppers know from memory. Those statements resolve to a foot length range in millimetres, which is the canonical stored value. Brand labels resolve against that length. Labels never convert directly to other labels. Any shopping agent can read the profile, write to it, and check products against it through WebMCP tools registered on the page. The server reads any store's public UCP catalog. Merchants install nothing and don't know it exists. When a verdict is wrong, you say so, and the profile updates. It sharpens across stores instead of being rebuilt at each one.
Verdicts are deliberately not binary:
fits · size_up · size_down · between_sizes · no_size out_of_stock · unmapped_brand · unknown
Three design decisions that matter more than they sound:
Rounding is always up. A shoe shorter than your foot doesn't fit; slightly long is wearable. A property test sweeps 240–300 mm across all seven brands asserting no recommendation is ever shorter than the foot.
Conflicting statements produce a question, not an average. If your Nike 9 and your Converse 9.5 disagree by more than a half size, the tool says so and asks which fits better. Only the person can answer that, and answering it improves the profile permanently. That's the collaboration: the agent finds the contradiction, the human resolves it.
unmapped_brand is a real answer. Veja appears on Stomping Ground and there's no mapping for it. Saying so beats guessing — especially for the people this serves best, who have been burned by confident sizing widgets before. Nine tools registered on document.modelContext, with navigator.modelContext as a fallback. readOnlyHint on the read-shaped tools, untrustedContentHint on anything returning merchant catalog text.
The architecture is inverted on purpose. WebMCP tools are scoped to a document — an agent on a merchant's page cannot see tools registered by another origin, and cross-tab visibility is unspecified. So Fit Check is the page the agent works on, and the server reaches out to stores. The merchant is never in the loop, which is also what makes the portability claim true rather than aspirational. There's no integration to sell, no widget to install, and no store that has to say yes.
UCP. The server fetches a store's /.well-known/ucp, negotiates capabilities, and reads the catalog over MCP. Everything below came from live stores, not fixtures:
Store Protocol Size data Brand coverage kith.com 2026-08-25 100% clean variant.options, option Size 95% stompingground.myshopify.com 2026-08-25 100% clean, option Shoe Size 80% knifewear-inc.myshopify.com 2026-08-25 no size axis at all n/a — degrades to unknown
The knife shop is included deliberately: a store sharing none of the assumptions, which the layer negotiates with, reads, and correctly reports it has nothing to say about.
Three tiers of data access, degrading gracefully:
find_shoe — one query across multiple UCP stores in parallel, verdicts attached, ~0.9s warm check_fit — authoritative single-product read with the full option matrix check_labels — no store, no catalog, no protocol. The agent reads the size dropdown off any page and hands over the labels. Works on stores with no UCP manifest at all.
Sizing data. Brand-to-foot-length mappings for Nike, Adidas, New Balance, Converse, ASICS, Birkenstock and Salomon come from a read-only sample shared by Sizely / Size AI for this submission. Their guidance shaped the design: resolve on foot length rather than labels, read Birkenstock via EU because its own US labels are internally inconsistent, alias Jordan to Nike, and leave out the women's rows they didn't trust rather than invent them.
Challenges
Version mismatch is silent. UCP negotiation intersects capabilities by name and version, and a capability with no mutual version is dropped rather than erroring. The symptom is "this store has no catalog search," not "you advertised the wrong date." Two stores on the same platform were four months apart, and Kith upgraded mid-week. The platform profile advertises every version it can parse.
There is no size filter. Standard UCP search filters are categories and price only. Fit has to be filtered client-side after retrieval — which is exactly why this layer needs to exist.
A bug that looked right. I added a 10 mm toe allowance to recommendations. Plausible, and wrong: the dataset's foot_length_mm already means "the foot this size fits," so the manufacturer's toe room was being counted twice and every recommendation moved up a size. It surfaced because recommend_size and check_fit disagreed on the same profile. Now there's a round-trip test for all seven brands: a profile built solely from " 9 fits" must return US 9 from both paths.
A bug only an agent could find. I ran the project through Google's WebMCP Evals CLI against the live deployment. add_fit_statement passed; check_labels immediately after returned "no foot-length estimate yet." The mutation was resolving before the estimate it implied had landed. A human typing never hits that gap — an agent calling two tools back to back hits it every time. The mutations now await the recompute, and the error message names what's actually missing instead of sending an agent in a loop.
What I learned
Consistent parameter naming across a tool surface is a discovery requirement, not a style preference. check_fit took store_domain while search_catalog took domain. An agent priming on one tool called the other with the wrong key and read it as broken. Nothing was internally inconsistent — the vocabulary just drifted between siblings.
Catalogs cannot supply gender, and no parsing recovers it. This is different from a fact that's badly labelled but present. It's the clearest example I found of something that only exists on the buyer's side, and it's the whole argument for the profile.
Honest verdicts beat confident ones. The audience this serves best — wide feet, half sizes, one foot larger than the other — has been failed by confident sizing tools before. unmapped_brand and "these two don't agree, which fits better?" read as trustworthy in a way a guess never would.
What's next
Width grades are the obvious next field: they're in the data, three of seven brands offer them, and they matter most to the people this serves.
Longer term, the right home for a buyer profile is probably the browser — held like autofill, queryable by any page's agent, with no app in between. The spec isn't there yet; tools are document-scoped and cross-tab isolation is still maturing.
But the schema outlives whoever holds it. Someone has to decide that the canonical value is foot length rather than a label, that both feet are recorded separately, that statements carry provenance, and that an unresolvable brand says so. That's the part this is really proposing.
The mechanism generalises past shoes. Any category where the reference data is public and the person's half is missing — bike geometry against inseam, knives against hand and board — has the same shape.
Built With
- model-context-protocol
- node.js
- react
- shopify
- typescript
- ucp
- vercel
- vite
- webmcp
Log in or sign up for Devpost to join the conversation.