Fulfiller — print catalogue & quotes
Print catalogue and live quotes from Fulfiller, a French online printer shipping across the EU.
Should I use this
Quality & Safety
Findings (1)
- LOWin quote_by_quantity
Based on automated analysis of tool definitions and protocol compliance.
Context Cost
This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.
Install
One-Click Install
Add this to your `claude_desktop_config.json` file:
{
"mcpServers": {
"agentic-commerce-api": {
"url": "https://www.fulfiller.com/agenticopenapi/mcp"
}
}
}Remote endpoints
https://www.fulfiller.com/agenticopenapi/mcpstreamable-httpWhat it can do
Tool inventory
Tools (11)
🟢search_products(q, limit)
Recherche plein-texte dans le catalogue Fulfiller et renvoie des produits COTABLES (productId, sku, url, et le prix d’appel du site quand il en affiche un). Point d’entrée obligatoire : les autres outils travaillent sur le productId renvoyé ici. `startingPrice` est le prix d’appel AFFICHÉ par le site : indicatif, parfois périmé ou propre à une configuration, ni plancher garanti ni prix ferme — seul quote_product l’est. Sa base (TOTAL, UNIT, PER_SQUARE_METER) change d’un produit à l’autre : ne comparez jamais deux bases différentes. Pour le citer, utilisez `startingPrice.displayLabel`. Un résultat peut lister dans `alsoListedAs` d’autres fiches du site qui vendent le MÊME produit : ne les présentez pas comme des produits distincts.
Input Schema
{
"type": "object",
"properties": {
"q": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Un ou deux mots produit en français (ex. « carte de visite », « bâche publicitaire »), pas une phrase : le moteur exige TOUS les mots. Une phrase qui ne trouve rien est réessayée une fois sans ses mots de contexte (`relaxedQuery` le signale). Les dimensions (« 300x100 »), les mesures (« 350g », « 1 000 ex ») et une quantité en tête de requête (« 500 cartes ») sont ignorées : elles se précisent à la cotation. Il faut au moins un mot de 3 lettres ou plus : une requête sans mot exploitable (« ab », « 50 ») est refusée."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 24,
"description": "Nombre de résultats souhaité (1 à 24)."
}
},
"required": [
"q"
]
}🟢resolve_product(productId, sku, url)
Convertit une identité EXACTE en identité complète (productId, sku, nom, url) : `sku` (lisible dans le JSON-LD d’une page produit), `url` de la page, ou `productId`. À PRÉFÉRER à search_products dès que vous tenez l’un des trois — la recherche est approximative, celle-ci ne l’est pas. Fournissez exactement un argument.
Input Schema
{
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant rendu par search_products (alias : `zeloomProductId`)."
},
"sku": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"description": "SKU exact (ex. « POSTER_PHOTO_SUR_MESURE »)."
},
"url": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"description": "URL d’une fiche produit de www.fulfiller.com (http, sans www ou relative acceptées). Une page catégorie est refusée : cherchez alors avec search_products."
}
},
"required": []
}🟢get_product_details(productId, sku, url)
Fiche PIM d’un produit : descriptions, points clés, questions fréquentes, contraintes de fichier (fond perdu, résolution, marges, mode colorimétrique) et ressources (visuels, gabarits, consignes techniques). À utiliser pour DÉCRIRE un produit ou aider à préparer un fichier. Ne renvoie aucun prix — pour coter, passez par quote_product ; un prix écrit dans les textes de la fiche (« Dès 14.5 € ») est souvent périmé, ne l’annoncez pas. Les vidéos sont dans `videos`, pas dans `images`. Fournissez exactement un argument (`productId`, `sku` ou `url`). Les champs absents ne sont pas renseignés au PIM ; la FAQ est rare.
Input Schema
{
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant rendu par search_products (alias : `zeloomProductId`)."
},
"sku": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"description": "SKU exact, tel que rendu par search_products ou resolve_product."
},
"url": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"description": "URL d’une fiche produit de www.fulfiller.com (http, sans www ou relative acceptées). Une page catégorie est refusée : cherchez alors avec search_products."
}
},
"required": []
}🟢get_product_configuration(productId)
Renvoie les options configurables d’un produit (quantités, délais, valeurs, tailles) et leurs valeurs disponibles. À appeler AVANT validate_selection ou quote_product : chez Fulfiller, un produit n’est pas un SKU à prix figé mais un espace de configuration. `presets` : configurations prêtes à l’emploi, cotables telles quelles via `selection.preset`. Les dépendances entre options (une finition qui exige le recto verso…) n’y figurent pas : validate_selection les révèle (`enabledOptions`).
Input Schema
{
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant rendu par search_products."
}
},
"required": [
"productId"
]
}🟢validate_selection(productId, selection)
Résout une sélection tolérante en codes canoniques et vérifie sa COMMANDABILITÉ, sans renvoyer de prix. Sert à corriger une sélection avant de la coter. `summary` décrit en clair la sélection retenue, et son `status` dit pour chaque option si la valeur est celle demandée, un défaut, ou un changement. `requiresReview` à `true` : la sélection retenue diffère de celle demandée, `alerts` dit pourquoi. `enabledOptions` liste, pour chaque option, les valeurs activables AVEC cette sélection : c’est là que se lisent les dépendances entre options.
Input Schema
{
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant rendu par search_products."
},
"selection": {
"type": "object",
"properties": {
"options": {
"type": "array",
"maxItems": 50,
"items": {
"type": "object",
"properties": {
"option": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"QUANTITY",
"DELIVERY_TIME",
"LIST_OF_VALUES",
"SIZE"
]
},
"value": {
"type": "string"
},
"quantity": {
"type": "integer",
"minimum": 1
},
"size": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"width": {
"type": "number",
"exclusiveMinimum": 0
},
"height": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"code"
]
}
},
"required": [
"option",
"type"
]
}
},
"preset": {
"type": "string",
"maxLength": 128
}
},
"required": [
"options"
],
"description": "Sélection d’options : même forme et mêmes règles que `selection` de quote_product (codes ou libellés, défauts du produit, `preset`, tailles)."
}
},
"required": [
"productId",
"selection"
]
}🟢quote_product(productId, selection)
Devis RÉEL pour une sélection : prix HT calculé par le moteur de tarification (jamais lu dans une table de SKU), devise, délai, promotion. C’est le seul prix ferme. Le prix à annoncer est `devis.finalPriceHt` (promotion déduite) ; `devis.priceHt` est le prix catalogue, BARRÉ quand `devis.promo` est présent. Pour dire à l’utilisateur ce qui est coté, citez `summary` (libellés lisibles). Si `requiresReview` vaut `true`, le prix porte sur la sélection décrite par `summary`, pas sur la demande : les lignes dont le `status` n’est ni AS_REQUESTED ni DEFAULT disent ce qui a changé.
Input Schema
{
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant rendu par search_products."
},
"selection": {
"type": "object",
"description": "Sélection d’options. Une option non précisée prend la valeur de la préselection par défaut du produit (`preselections` de get_product_configuration), comme sur le site ; `{\"options\": []}` cote donc la configuration par défaut. La résolution est TOLÉRANTE : code ou libellé, sans souci de casse ni d’accents ; une valeur partielle est retenue si une SEULE valeur la contient (« vinyle » → « Impression sur vinyle adhésif contrecollé », « A5 » → « 14,8 x 21 cm (A5) »), ou par ses mesures (« 148x210 mm ») ; parmi plusieurs, la plus proche est retenue et signalée, ou la SEULE compatible avec le reste de la sélection (« soft touch » sur une carte recto verso). Ce qui n’est pas retenu tel quel est signalé dans `alerts` et dans `summary`.",
"properties": {
"options": {
"type": "array",
"maxItems": 50,
"items": {
"type": "object",
"properties": {
"option": {
"type": "string",
"description": "Code de l’option, tel que rendu par get_product_configuration. Un libellé (« Délai de livraison ») est rattaché à la seule option qui lui correspond, et c’est signalé."
},
"type": {
"type": "string",
"enum": [
"QUANTITY",
"DELIVERY_TIME",
"LIST_OF_VALUES",
"SIZE"
],
"description": "Seuls ces 4 types sont SÉLECTIONNABLES. Un autre type déclaré par la configuration (LIST_OF_RANGES, QUANTITY_BY_VALUE) est décrit mais non sélectionnable : le défaut du produit s’applique."
},
"value": {
"type": "string",
"description": "Code ou libellé de la valeur retenue (types LIST_OF_VALUES et DELIVERY_TIME)."
},
"quantity": {
"type": "integer",
"minimum": 1,
"description": "Quantité demandée (type QUANTITY). Hors des paliers du produit, l’alerte donne les quantités possibles et la plus proche."
},
"size": {
"type": "object",
"description": "Gabarit de taille (option de type SIZE). Gabarit FIXE (bornes min = max) : le code suffit, ses dimensions sont complétées, et des dimensions données dans un sens ou dans l’autre (60×80 ou 80×60) désignent le même gabarit. Gabarit à SAISIE LIBRE (`allowsCustomInput`) : width et height sont requises, dans l’unité de l’option (`unit`, souvent cm : une bâche de 3 m × 1 m s’écrit width 300, height 100) et DANS les bornes du gabarit : hors bornes, la cotation est refusée (SIZE_OUT_OF_RANGE, bornes dans le message).",
"properties": {
"code": {
"type": "string",
"description": "Code ou libellé du gabarit, ou ses mesures (« 60x80 », « 148x210 mm ») : elles désignent le gabarit fixe de ces dimensions, sinon le gabarit sur mesure."
},
"width": {
"type": "number",
"exclusiveMinimum": 0
},
"height": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"code"
]
}
},
"required": [
"option",
"type"
]
}
},
"preset": {
"type": "string",
"maxLength": 128,
"description": "Code ou nom d’un preset de get_product_configuration (`presets` : « Le moins cher », « Top vente »…) : ses valeurs servent de base, `options` les précise ou les remplace."
}
},
"required": [
"options"
]
}
},
"required": [
"productId",
"selection"
]
}🟢quote_by_quantity(productId, selection, quantities)
Grille de devis pour plusieurs paliers de quantité en UN appel — la bonne façon de répondre à « combien pour 100, 250, 500 ? ». Prix de chaque palier : `devis.finalPriceHt` (promotion déduite). Même règle que quote_product, palier par palier : hors plage → erreur localisée sans invalider les autres ; hors du pas → coté au palier commandable le plus proche, que `quotedQuantity` indique (le prix porte sur cette quantité, pas sur le palier demandé).
Input Schema
{
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant rendu par search_products."
},
"selection": {
"type": "object",
"properties": {
"options": {
"type": "array",
"maxItems": 50,
"items": {
"type": "object",
"properties": {
"option": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"QUANTITY",
"DELIVERY_TIME",
"LIST_OF_VALUES",
"SIZE"
]
},
"value": {
"type": "string"
},
"quantity": {
"type": "integer",
"minimum": 1
},
"size": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"width": {
"type": "number",
"exclusiveMinimum": 0
},
"height": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"code"
]
}
},
"required": [
"option",
"type"
]
}
},
"preset": {
"type": "string",
"maxLength": 128
}
},
"required": [
"options"
],
"description": "Sélection d’options : même forme et mêmes règles que `selection` de quote_product (codes ou libellés, défauts du produit, `preset`, tailles)."
},
"quantities": {
"type": "array",
"minItems": 1,
"maxItems": 20,
"items": {
"type": "integer",
"minimum": 1
},
"description": "Paliers de quantité à coter (1 à 20)."
}
},
"required": [
"productId",
"selection",
"quantities"
]
}🟡create_cart(lines, idempotencyKey)
Crée un panier anonyme, éventuellement avec ses premières lignes (cotées à l’ajout, prix FIGÉ). Un panier n’est PAS une commande : rien n’est engagé, et le total est HT — hors TVA et hors port. Chaque ligne désigne le produit par son productId (rendu par search_products) ; le sku est facultatif. `unitPriceHt` et `lineTotalHt` sont promotion déduite. Renvoie le panier complet : n’appelez pas get_cart juste après.
Input Schema
{
"type": "object",
"properties": {
"lines": {
"type": "array",
"maxItems": 5,
"items": {
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant produit rendu par search_products (alias : `zeloomProductId`)."
},
"sku": {
"type": "string",
"maxLength": 128,
"description": "SKU exact rendu par search_products. FACULTATIF : absent, il est déduit du productId (PRODUCT_AMBIGUOUS si plusieurs fiches vendent ce produit : le message cite leurs SKU)."
},
"selection": {
"type": "object",
"properties": {
"options": {
"type": "array",
"maxItems": 50,
"items": {
"type": "object",
"properties": {
"option": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"QUANTITY",
"DELIVERY_TIME",
"LIST_OF_VALUES",
"SIZE"
]
},
"value": {
"type": "string"
},
"quantity": {
"type": "integer",
"minimum": 1
},
"size": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"width": {
"type": "number",
"exclusiveMinimum": 0
},
"height": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"code"
]
}
},
"required": [
"option",
"type"
]
}
},
"preset": {
"type": "string",
"maxLength": 128
}
},
"required": [
"options"
],
"description": "Sélection d’options : même forme et mêmes règles que `selection` de quote_product (codes ou libellés, défauts du produit, `preset`, tailles)."
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"description": "Nombre de LOTS identiques au prix unitaire figé — PAS un palier de volume : 2×50 exemplaires n’a pas le même prix qu’1×100. Pour changer de volume, re-cotez avec une autre QUANTITY dans `selection`, puis remplacez la ligne. Défaut 1."
}
},
"required": [
"productId",
"selection"
]
},
"description": "Lignes initiales (0 à 5). Un panier vide est légitime."
},
"idempotencyKey": {
"type": "string",
"format": "uuid",
"description": "UUID optionnel. Rejouer le MÊME appel avec la même clé renvoie le panier déjà créé au lieu d’en créer un second : à utiliser si vous devez réessayer après une erreur."
}
},
"required": []
}🟢get_cart(cartId, display)
Renvoie le panier tel que FIGÉ à l’ajout : aucune re-cotation à la lecture. `pricedAt` par ligne et `expiresAt` donnent l’âge des prix — au-delà, re-cotez avec quote_product. Sert à RELIRE un panier existant (plus tard dans la conversation, ou quand l’utilisateur demande à le revoir) : inutile juste après create_cart ou update_cart, qui renvoient déjà le panier complet.
Input Schema
{
"type": "object",
"properties": {
"cartId": {
"type": "string",
"description": "Identifiant du panier (UUID) rendu par create_cart. C’est une CAPACITÉ : qui le détient accède au panier — ne le publiez pas hors de la conversation qui l’a créé."
},
"display": {
"type": "boolean",
"description": "Afficher la carte du panier à l’utilisateur, dans les clients qui affichent les interfaces MCP Apps. `false` pour un appel intermédiaire qu’il n’a pas besoin de voir (vérifier avant de répondre) ; omis ou `true` dès qu’il doit voir le résultat. Sans effet sur le résultat lui-même."
}
},
"required": [
"cartId"
]
}🔴update_cart(cartId, remove, update, add)
Applique, DANS CET ORDRE : retraits (`remove`), changements de quantité (`update`, sans re-cotation), puis ajouts (`add`, cotés). Renvoie le panier complet après application : n’appelez pas get_cart juste après.
Input Schema
{
"type": "object",
"properties": {
"cartId": {
"type": "string",
"description": "Identifiant du panier (UUID) rendu par create_cart. C’est une CAPACITÉ : qui le détient accède au panier — ne le publiez pas hors de la conversation qui l’a créé."
},
"remove": {
"type": "array",
"maxItems": 20,
"items": {
"type": "string"
},
"description": "`lineId` des lignes à retirer."
},
"update": {
"type": "array",
"maxItems": 20,
"items": {
"type": "object",
"properties": {
"lineId": {
"type": "string"
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 1000
}
},
"required": [
"lineId",
"quantity"
]
},
"description": "Nouveau nombre de LOTS pour des lignes existantes (prix unitaire figé inchangé)."
},
"add": {
"type": "array",
"maxItems": 5,
"items": {
"type": "object",
"properties": {
"productId": {
"type": "integer",
"minimum": 1,
"description": "Identifiant produit rendu par search_products (alias : `zeloomProductId`)."
},
"sku": {
"type": "string",
"maxLength": 128,
"description": "SKU exact rendu par search_products. FACULTATIF : absent, il est déduit du productId (PRODUCT_AMBIGUOUS si plusieurs fiches vendent ce produit : le message cite leurs SKU)."
},
"selection": {
"type": "object",
"properties": {
"options": {
"type": "array",
"maxItems": 50,
"items": {
"type": "object",
"properties": {
"option": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"QUANTITY",
"DELIVERY_TIME",
"LIST_OF_VALUES",
"SIZE"
]
},
"value": {
"type": "string"
},
"quantity": {
"type": "integer",
"minimum": 1
},
"size": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"width": {
"type": "number",
"exclusiveMinimum": 0
},
"height": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"code"
]
}
},
"required": [
"option",
"type"
]
}
},
"preset": {
"type": "string",
"maxLength": 128
}
},
"required": [
"options"
],
"description": "Sélection d’options : même forme et mêmes règles que `selection` de quote_product (codes ou libellés, défauts du produit, `preset`, tailles)."
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"description": "Nombre de LOTS identiques au prix unitaire figé — PAS un palier de volume : 2×50 exemplaires n’a pas le même prix qu’1×100. Pour changer de volume, re-cotez avec une autre QUANTITY dans `selection`, puis remplacez la ligne. Défaut 1."
}
},
"required": [
"productId",
"selection"
]
},
"description": "Nouvelles lignes, cotées à l’ajout."
}
},
"required": [
"cartId"
]
}🔴delete_cart(cartId)
Supprime DÉFINITIVEMENT un panier et toutes ses lignes. Irréversible. Pour retirer une seule ligne, utilisez update_cart avec `remove`.
Input Schema
{
"type": "object",
"properties": {
"cartId": {
"type": "string",
"description": "Identifiant du panier (UUID) rendu par create_cart. C’est une CAPACITÉ : qui le détient accède au panier — ne le publiez pas hors de la conversation qui l’a créé."
}
},
"required": [
"cartId"
]
}Community
Evidence