DEV Community

Cover image for Implementing UCP on PrestaShop 8/9: the complete guide
Mohammed R.
Mohammed R.

Posted on • Originally published at fondouk.dev

Implementing UCP on PrestaShop 8/9: the complete guide

AI agent commerce surfaces are coming. Google, Shopify and Walmart published UCP (Universal Commerce Protocol, Apache 2.0); Anthropic pushes MCP. For a PrestaShop store the question is no longer whether but how to become readable by these agents. Here's how we did it — decision by decision, with the real code from Fondouk.

The golden rule: implement the spec, not a blog post

UCP is a versioned spec (date YYYY-MM-DD) with JSON Schemas. We target the stable release 2026-04-08 and validate every response against the published schemas — not an internal copy. That's what guarantees a third-party agent understands us.

The discovery manifest lives at /.well-known/ucp. An agent reads it, finds the endpoint and the capabilities, then queries the store — nothing hard-coded:

{
  "ucp": {
    "version": "2026-04-08",
    "services": {
      "dev.ucp.shopping": [
        { "version": "2026-04-08", "transport": "rest",
          "endpoint": "https://demo.fondouk.dev/fondouk/ucp",
          "schema": "https://ucp.dev/2026-04-08/services/shopping/rest.openapi.json" },
        { "version": "2026-04-08", "transport": "mcp",
          "endpoint": "https://demo.fondouk.dev/fondouk/ucp/mcp",
          "schema": "https://ucp.dev/2026-04-08/services/shopping/mcp.json" }
      ]
    },
    "capabilities": {
      "dev.ucp.shopping.catalog.search": [{ "version": "2026-04-08",
        "schema": "https://ucp.dev/2026-04-08/capabilities/shopping/catalog/search.json" }],
      "dev.ucp.shopping.catalog.lookup": [{ "version": "2026-04-08",
        "schema": "https://ucp.dev/2026-04-08/capabilities/shopping/catalog/lookup.json" }]
    },
    "payment_handlers": {}
  }
}
Enter fullscreen mode Exit fullscreen mode

Decision #1 — ORM/Presenter, not the webservice

PrestaShop's /api webservice returns raw data: base price, no computed tax-inclusive price, no discounts applied, stock in a separate call (N+1). Reproducing the storefront price would mean re-implementing the price engine — a source of divergence.

So we use the internal classes (Product::getPriceStatic, StockAvailable, SpecificPrice). The single most important detail is that the price is computed in an anonymous-visitor context — this is the security model:

// Does the shop display tax-included prices? (group/shop setting)
$displayTax = (Product::getTaxCalculationMethod() === PS_TAX_INC);

$price = Product::getPriceStatic(
    $idProduct,
    $displayTax,                 // tax-incl or tax-excl, per the shop's display setting
    $idProductAttribute,
    $decimals,
    null,
    false,
    /* usereduc */ true,
    1,
    false,
    /* id_customer */ 0,         // anonymous — no customer
    null,
    null,
    $specificPriceOutput,
    true,
    true,
    $context,
    /* use_customer_price */ false  // never a customer-specific price
);
// group = PS_UNIDENTIFIED_GROUP → public specific prices apply,
// B2B / group prices never do. What an anonymous visitor sees, nothing more.
Enter fullscreen mode Exit fullscreen mode

That single call, with id_customer = 0 and use_customer_price = false, is why a B2B price can never leak through an agent request.

Decision #2 — /.well-known/ routing across PS8 and PS9

The front office is migrated to Symfony in neither PS8 nor PS9 (the FrontKernel is experimental). The portable mechanism is the legacy ModuleFrontController + moduleRoutes hook. Only the manifest needs the well-known path; the REST/MCP endpoints live under a module base:

public function hookModuleRoutes()
{
    $mod = ['fc' => 'module', 'module' => 'fondouk'];
    return [
        'module-fondouk-ucp' => [
            'rule' => '.well-known/ucp', 'keywords' => [],
            'controller' => 'ucp', 'params' => $mod,
        ],
        'module-fondouk-catalog-search' => [
            'rule' => 'fondouk/ucp/catalog/search', 'keywords' => [],
            'controller' => 'catalog', 'params' => $mod + ['action' => 'search'],
        ],
        // … lookup, product, mcp, llms.txt
    ];
}
Enter fullscreen mode Exit fullscreen mode

Prerequisite: URL rewriting. Known trap: a physical .well-known/ directory (created by Let's Encrypt) short-circuits Apache via the .htaccess -d rule. We detect and surface it; a static-file fallback is opt-in.

The pivot: a decoupled core

The most important piece isn't the module — it's the Capability Graph: a neutral pivot format (versioned JSON Schema) with zero PrestaShop dependency. The module feeds it by introspection; adapters project it into UCP (REST, MCP). Here's the shape a variant takes in the graph — note prices is an array (multi-currency) and quantity is marked internal, never serialized to an agent:

"variant": {
  "required": ["id", "prices", "availability"],
  "properties": {
    "id":    { "type": "string" },
    "prices": { "type": "array", "items": { "required": ["price"],
      "properties": { "price": { "$ref": "#/$defs/money" }, "list_price": { "$ref": "#/$defs/money" } } } },
    "availability": { "required": ["available"], "properties": {
      "available": { "type": "boolean" }, "status": { "type": "string" },
      "quantity": { "type": "integer", "description": "Internal — NEVER exposed by adapters." } } }
  }
}
Enter fullscreen mode Exit fullscreen mode

A platform that can produce this graph inherits every adapter for free. That's the whole point of the pivot.

What an agent actually gets

Discover, then query — the endpoint comes from the manifest:

curl -s -X POST https://demo.fondouk.dev/fondouk/ucp/catalog/search \
  -H 'Content-Type: application/json' \
  -d '{"query":"t-shirt","pagination":{"limit":1}}'
Enter fullscreen mode Exit fullscreen mode
{
  "ucp": { "version": "2026-04-08",
    "capabilities": { "dev.ucp.shopping.catalog.search": [{ "version": "2026-04-08" }] } },
  "products": [{
    "id": "gid://demo.fondouk.dev/1/product/1",
    "title": "Hummingbird printed t-shirt",
    "price_range": { "min": { "amount": 2294, "currency": "EUR" },
                     "max": { "amount": 2294, "currency": "EUR" } },
    "variants": [{
      "id": "gid://demo.fondouk.dev/1/variant/1-1", "title": "S / White",
      "price": { "amount": 2294, "currency": "EUR" },
      "availability": { "available": true, "status": "in_stock" }
    }]
  }],
  "pagination": { "has_next_page": true, "cursor": "eyJvIjoxfQ", "total_count": 6 }
}
Enter fullscreen mode Exit fullscreen mode

amount is in minor units (2294 = €22.94), currency explicit — as the published UCP schema mandates. The same graph feeds the MCP server, so tools/call search_catalog returns the same catalog data in its structuredContent.

A manifest is a promise

The detail that separates a usable server from a merely conformant one: only declare what actually responds. While an endpoint is a stub, its capability does not appear — services: {}, capabilities: {}. An empty manifest beats a lying one: on the day of a real agent test, a 501 on an advertised capability breaks trust.

Hardening, because "survive 50 agents" is tested, not declared

Token-bucket rate limiting (60/min, burst 120) with Retry-After, cache invalidated by product hooks, request-body and batch size caps (413/400). Proven under crawl on both PS8 and PS9: the store stays responsive.

In short

Read-only, zero configuration, public = public. Visibility is free forever; agent analytics and per-agent control are Fondouk Pro's paid layer; checkout and other platforms are backlog. The code is open (MIT): github.com/fondouk-dev/fondouk.


The module described in this guide is free and MIT-licensed: https://github.com/fondouk-dev/fondouk — you can query the live demo right now:

 curl https://demo.fondouk.dev/.well-known/ucp
Enter fullscreen mode Exit fullscreen mode

A Pro version with an AI-agent analytics dashboard (which agents visit your store, which products they view, structured-vs-storefront adoption ratio, per-agent control, GDPR tooling) is available on the official PrestaShop Addons marketplace: https://addons.prestashop.com/en/analytics-stats-prestashop-modules/98377-fondouk-pro-ai-agent-analytics-for-your-store.html

Top comments (0)