Content
# Catalog Attribute Normalizer
Clean, normalized product attributes and taxonomy mappings from messy multi-source catalogs — titles,
descriptions, images in; consistent attributes, units, and category mappings out. For merchant-ops
teams and feed-tool developers wrangling a Shopify export, a supplier CSV, and a marketplace scrape that
each spell "size" or "material" differently.
## What it does
- Accepts a batch of raw products (`title`, `description`, `raw_attributes`), returns normalized
attributes (canonicalized sizes/colors/units) plus category mappings for whichever taxonomies you
request (Google Product Taxonomy, Shopify, Amazon).
- Works as an MCP server (`normalize_catalog` tool) or a plain HTTPS API.
### Example (MCP tool call)
```json
{
"tool": "normalize_catalog",
"input": {
"products": [{ "title": "...", "description": "...", "raw_attributes": { "size": "Lrg", "Color": "Navy Blue" } }],
"target_taxonomies": ["google", "shopify"]
}
}
```
```json
{
"results": [
{
"schema_version": "2.0",
"source_title": "...",
"category_paths": {
"google": { "path": ["Apparel & Accessories", "Clothing", "Shirts & Tops"], "leaf_id": null, "confidence": 0.9 },
"shopify": { "path": ["Apparel & Accessories", "Clothing", "Tops"], "leaf_id": "aa-3-1", "confidence": 0.85 }
},
"attributes": {
"size": { "value": "L", "provenance": "canonicalized" },
"color": { "value": "navy", "provenance": "canonicalized" }
}
}
]
}
```
`attributes` is keyed by a controlled vocabulary (`size`, `color`, `material`, `gender`, `sleeve_length` —
unrecognized keys are dropped, not passed through under a model-chosen name). Each value's `provenance`
is `"canonicalized"` when it came from your own `raw_attributes` input for that product (deterministic
cleanup only, no recall) or `"extracted"` when the model inferred it from the title/description and it
wasn't in your input — treat `"extracted"` values as a suggestion, the same way you'd treat a
low-confidence `category_paths` entry.
**Attributes are reliable by construction** — canonicalizing values already in your input, not recall.
**Category classification is retrieval-grounded**, not recalled from memory: candidates are retrieved
from the real, current Google and Shopify taxonomy files and offered to the model as suggestions, so a
`leaf_id` almost always names a node that actually exists — measured against a 12-product evaluation set,
**22/24 (91.7%) exact path + leaf-ID matches**. Still treat `confidence` and a `null` `leaf_id` as
"worth a quick check," not a guarantee — a `leaf_id` is only ever returned when the path independently
verifies against the real taxonomy file, so a `null` there is an honest "check this" signal, never a
fabricated ID. Amazon has no comprehensive public taxonomy reference file to retrieve candidates from,
so it stays best-effort (recall from memory) rather than retrieval-grounded.
## Pricing
- **Free** — 500 products/month, no card required.
- **Pay-as-you-go** — $0.01/product, no minimum.
- **Pro** — $29/month for 5,000 products (~$0.0058/product effective).
## Getting a key
The MCP server is live at `https://acjlabs-catalog-normalizer.acjlabs.workers.dev/mcp`. Free-tier keys
self-serve — `POST /v1/signup` with `{ "email": "you@example.com" }` returns your key directly in the
response, good for 500 products/month, no card required. **Store it immediately — it is shown once and
cannot be recovered if lost** (re-signup for a new one). Paid keys provision the same way via a
one-time claim link once Pro/pay-as-you-go billing is live.
## Connecting it to an MCP client
With Claude Code:
```bash
claude mcp add --transport http catalog-normalizer \
https://acjlabs-catalog-normalizer.acjlabs.workers.dev/mcp \
--header "Authorization: Bearer YOUR_API_KEY"
```
Any other MCP client that supports a remote HTTP server with a custom header (Cursor, Cline, VS Code,
etc.) works the same way: point it at the URL above with an `Authorization: Bearer YOUR_API_KEY`
header. A gateway or scanner that can only forward the raw key value (no `Bearer` prefix) also works —
both forms authenticate. Tool discovery (`initialize`/`tools/list`) doesn't require a key at all; only
calling `normalize_catalog` does.
## npm client
Prefer a typed function over hand-rolling MCP JSON-RPC calls? `@acjlabs/catalog-attribute-normalizer-client`
(`client/`, not published yet — npm publish is a gated founder action) wraps the `normalize_catalog`
tool call:
```ts
import { createCatalogNormalizerClient } from "@acjlabs/catalog-attribute-normalizer-client";
const client = createCatalogNormalizerClient({
baseUrl: "https://acjlabs-catalog-normalizer.acjlabs.workers.dev",
apiKey: "...",
});
const results = await client.normalizeCatalog(products, ["google", "shopify"]);
```
## Why not just use a category classifier?
Category classifiers (ProductCategorization.com, ProductClassifier.com) tell you *what* a product is.
They don't touch the messier problem: standardizing *attributes* across sources that each spell them
differently. The one vendor doing the broader job, DataWeave, is enterprise sales-led — no self-serve,
no public price. See the [full comparison](docs/comparison.md), or read
[why "clean product data" is actually two different problems](docs/attribute-normalization-vs-classification.md)
(canonicalization vs. classification, and why they fail differently).
## Source availability & support
This repository hosts the documentation for the hosted service. The service implementation is not open source. Bug reports and feature requests are welcome in this repo's Issues; you can also reach us at <contact@acjlabs.com>.