Skip to content

Products from JSON LD

neodigm edited this page Oct 18, 2026 · 2 revisions

Products from JSON-LD

Most WebMCP guidance asks you to write tools. This page is about the case where you have already written them and do not know it.

If your site sells things, it almost certainly carries schema.org Product markup — the JSON-LD block search engines read to understand your catalogue. It is structured, it is typed, it uses a published vocabulary, and somebody is already keeping it current because search rewards that. It is, in other words, a product API that has been sitting in your <head> for years with no consumer.

<machvive-webmcp-products></machvive-webmcp-products>

That is the integration. One tag, no backend, no new data, nothing to keep in sync.


What an agent gets

Tool
search_products Free text plus category, brand, availability, minPrice, maxPrice, limit, offset
get_product One product in full by SKU, MPN or GTIN, including its original JSON-LD
list_product_facets The categories, brands, stock states and price range present on the page

All three are read-only. They search and read; they never add to a cart, place an order or submit anything. That is a deliberate boundary, and it is what makes the component safe to publish automatically and why it holds no credentials. If you want an agent to do something, that is a tool you write — see Magnet Integration for a human-in-the-loop example.

The filter values come from your data

This is the part worth understanding, because it is what separates a usable tool from one an agent fumbles.

category, brand and availability are declared in the schema as enums built from the products actually on the page. An agent asking for category: "Actuators" when your data says "HVAC Actuators" gets nothing and has no way to find out why. With an enum, it can read the valid values straight off the schema — or call list_product_facets and get them explicitly.

The same property gives you a working UI for free: the inspector renders an enum as a <select>, so your filters become dropdowns of real values with no configuration at all. Change the catalogue, call load(), and the dropdowns follow.

It reads the markup you actually have

Real JSON-LD is correct-but-inconsistent, and a reader that handles only the tidy shape works on a fixture and fails on the web. The normalizer therefore walks the document recursively rather than looking for known containers, and copes with:

  • @graph, ItemList / itemListElement / ListItem, a bare array, a lone Product, or a Product nested somewhere nobody anticipated
  • @type as a string, an array, or a full URL
  • brand, category and seller as a plain string or a node
  • image as a URL, an array, or an ImageObject
  • offers as an object or an array — where the first entry is often a stub carrying only a url, and taking it blindly reports the product as unpriced
  • AggregateOffer, reported at its low price, because "from $198" is actionable and a midpoint nobody can pay is not
  • every gtin flavour collapsed into one field
  • prices as decorated strings ("$1,299.00")
  • availability and itemCondition arriving as https://schema.org/InStock rather than a word, which must be reduced or the filter is unusable
  • a malformed block, which is skipped rather than taking the page's other blocks down with it — pages carry generated blocks beside a hand-written one, and the hand-written one has the trailing comma

The same product appearing in both an ItemList and a BreadcrumbList is deduplicated, keeping whichever copy is richer.

Attributes and API

Attribute
src Optional JSON-LD URL. Omit to read the page's own markup
no-tool Read the data but publish nothing
theme light or dark
const el = document.querySelector('machvive-webmcp-products');

el.products;        // the normalized catalogue
el.facets;          // categories, brands, availability, price range
await el.load();    // re-read and republish after the page's JSON-LD changes

One tool set per page however many elements you place; the first owns it and hands over if it is removed. All three tools share one registration signal, so removing the owning element withdraws them together.

The normalizer on its own

It is exported independently, because it is useful without the element — in a build step, a fixture, or simply to see what an agent would make of a page:

import {
  collectProducts, normalizeProduct, facetsOf, readDocumentJsonLd
} from '@machfivetechchicago/machvive-webmcp-ai/webmcp-products';

const products = collectProducts(readDocumentJsonLd(document));
console.log(facetsOf(products));

Sizing, and why results are capped

A tool result lands in an agent's context window. A catalogue of three thousand products returned in one call is not a helpful answer; it is an expensive way to fail.

So search_products returns a compact summary — name, sku, brand, category, price, currency, availability, url — capped at 50 with a default of 10, and an explicit truncated flag. The flag matters: an agent that cannot distinguish a page from a complete answer will confidently report "there are 10 products". Page through with offset, and use get_product when the full record and the original JSON-LD are actually wanted.

See also

Clone this wiki locally