heurix-mcp-server 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ REFERENCEMENT-ANNUAIRES.md
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: heurix-mcp-server
3
+ Version: 0.1.0
4
+ Summary: Serveur MCP pour Heurix — recherche et classement de catalogues produits techniques en langage naturel
5
+ Project-URL: Homepage, https://heurix.fr
6
+ Project-URL: Documentation, https://heurix.fr/docs.html#ep-mcp
7
+ Project-URL: Repository, https://github.com/flahaut-alexis/heurix-mcp-server
8
+ Author-email: Alexis Flahaut <contact@heurix.fr>
9
+ License: MIT
10
+ Keywords: catalog,ecommerce,mcp,model-context-protocol,product-search,search
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: httpx>=0.27
18
+ Requires-Dist: mcp>=1.2.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Serveur MCP Heurix
22
+
23
+ Permet à un agent IA (Claude Desktop, Cursor, ou tout autre client MCP)
24
+ d'interroger directement un catalogue Heurix — recherche, parcours par
25
+ catégorie, statistiques — sans que l'agent ait à connaître l'API REST.
26
+ Chantier 6.1 de la roadmap, 24 juillet 2026.
27
+
28
+ Trois tools exposés :
29
+ - **`heurix_search`** — recherche par mot-clé, tolérante aux fautes de frappe
30
+ - **`heurix_browse`** — liste les produits d'une catégorie, avec tri (stock, prix, popularité...)
31
+ - **`heurix_catalog_stats`** — liste les catalogues et leurs catégories disponibles
32
+
33
+ Une fine couche au-dessus de l'API REST Heurix existante — aucune nouvelle
34
+ logique métier, juste une nouvelle façade que les agents IA savent parler
35
+ nativement.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install -r requirements.txt
41
+ ```
42
+
43
+ Testable en local avant de le brancher à un client :
44
+ ```bash
45
+ HEURIX_API_KEY=hx_votre_cle python3 server.py
46
+ ```
47
+ (Le serveur attend alors une connexion MCP sur stdin/stdout — `Ctrl+C` pour arrêter. C'est normal qu'il ne "fasse rien" visuellement : un client MCP doit s'y connecter pour que quoi que ce soit se passe.)
48
+
49
+ ## Configuration dans Claude Desktop
50
+
51
+ 1. Ouvrez Claude Desktop → **Réglages → Développeur → Modifier la configuration** (ça crée le fichier s'il n'existe pas encore)
52
+ 2. Le fichier se trouve à :
53
+ - macOS : `~/Library/Application Support/Claude/claude_desktop_config.json`
54
+ - Windows : `%APPDATA%\Claude\claude_desktop_config.json`
55
+ 3. Ajoutez (ou complétez s'il existe déjà d'autres serveurs) :
56
+
57
+ ```json
58
+ {
59
+ "mcpServers": {
60
+ "heurix": {
61
+ "command": "/usr/bin/python3",
62
+ "args": ["/chemin/complet/vers/heurix-mcp-server/server.py"],
63
+ "env": {
64
+ "HEURIX_API_KEY": "hx_votre_cle_api",
65
+ "HEURIX_API_BASE": "https://api.heurix.fr"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ **Important** : utilisez le **chemin complet** vers `python3` (`which python3` dans un terminal pour le trouver), pas juste `python3` — Claude Desktop lance la configuration avec un PATH minimal, une commande courte qui fonctionne dans votre terminal peut échouer silencieusement ici. Même chose pour le chemin vers `server.py` : complet, pas relatif.
73
+
74
+ 4. Redémarrez Claude Desktop entièrement (pas juste fermer la fenêtre)
75
+ 5. Un nouvel outil (icône 🔌 ou menu MCP selon la version) doit lister `heurix_search`, `heurix_browse`, `heurix_catalog_stats`
76
+
77
+ ## Configuration dans Cursor
78
+
79
+ Même structure de fichier, deux emplacements possibles :
80
+ - `.cursor/mcp.json` à la racine d'un projet (pour un serveur propre à ce projet)
81
+ - `~/.cursor/mcp.json` (global, disponible dans tous les projets)
82
+
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "heurix": {
87
+ "command": "/usr/bin/python3",
88
+ "args": ["/chemin/complet/vers/heurix-mcp-server/server.py"],
89
+ "env": {
90
+ "HEURIX_API_KEY": "hx_votre_cle_api",
91
+ "HEURIX_API_BASE": "https://api.heurix.fr"
92
+ }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ Ensuite : **Réglages Cursor → Tools & MCP**, vérifiez que "Enable MCP Servers" est coché, et que `heurix` apparaît avec un point vert (connecté). Si rien n'apparaît après un redémarrage, le panneau **Output → MCP** affiche les logs bruts du serveur — souvent plus parlant que l'interface elle-même pour diagnostiquer.
99
+
100
+ ## Exemple concret
101
+
102
+ **Ce qu'un utilisateur tape dans Claude Desktop**, sans rien connaître de l'API Heurix :
103
+
104
+ > J'ai un catalogue Heurix qui s'appelle quincaillerie-demo. Est-ce que j'ai des vis M8 en stock, et à quel prix ?
105
+
106
+ **Ce qui se passe côté agent** (invisible pour l'utilisateur, montré ici pour comprendre) :
107
+ 1. Claude appelle `heurix_search(catalog="quincaillerie-demo", query="vis M8")`
108
+ 2. Le serveur MCP relaie vers `POST https://api.heurix.fr/v1/index/quincaillerie-demo/search`
109
+ 3. La réponse JSON (hits, scores, stock, prix) revient à Claude
110
+
111
+ **Réponse type de Claude Desktop à l'utilisateur** :
112
+
113
+ > Oui, vous avez deux références de vis M8 en stock dans quincaillerie-demo :
114
+ >
115
+ > - **Vis M8 x 20 - Inox A2** — 120 en stock, 5,90 €
116
+ > - **Vis M8 x 30 - Inox A2** — 45 en stock, 7,90 €
117
+ >
118
+ > Voulez-vous que je regarde aussi si l'une d'elles a un stock faible, ou que je compare avec une autre référence ?
119
+
120
+ Aucune ligne de code, aucun appel curl — l'utilisateur pose une question en
121
+ langage naturel, l'agent fait le pont vers l'API.
122
+
123
+ ## Sécurité
124
+
125
+ `HEURIX_API_KEY` vit uniquement dans la configuration du client MCP (fichier
126
+ local sur la machine de l'utilisateur), jamais en argument de ligne de
127
+ commande, jamais transmise en clair dans un appel de tool — le serveur la
128
+ lit une fois au démarrage depuis l'environnement et l'utilise pour chaque
129
+ appel à l'API Heurix.
@@ -0,0 +1,109 @@
1
+ # Serveur MCP Heurix
2
+
3
+ Permet à un agent IA (Claude Desktop, Cursor, ou tout autre client MCP)
4
+ d'interroger directement un catalogue Heurix — recherche, parcours par
5
+ catégorie, statistiques — sans que l'agent ait à connaître l'API REST.
6
+ Chantier 6.1 de la roadmap, 24 juillet 2026.
7
+
8
+ Trois tools exposés :
9
+ - **`heurix_search`** — recherche par mot-clé, tolérante aux fautes de frappe
10
+ - **`heurix_browse`** — liste les produits d'une catégorie, avec tri (stock, prix, popularité...)
11
+ - **`heurix_catalog_stats`** — liste les catalogues et leurs catégories disponibles
12
+
13
+ Une fine couche au-dessus de l'API REST Heurix existante — aucune nouvelle
14
+ logique métier, juste une nouvelle façade que les agents IA savent parler
15
+ nativement.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install -r requirements.txt
21
+ ```
22
+
23
+ Testable en local avant de le brancher à un client :
24
+ ```bash
25
+ HEURIX_API_KEY=hx_votre_cle python3 server.py
26
+ ```
27
+ (Le serveur attend alors une connexion MCP sur stdin/stdout — `Ctrl+C` pour arrêter. C'est normal qu'il ne "fasse rien" visuellement : un client MCP doit s'y connecter pour que quoi que ce soit se passe.)
28
+
29
+ ## Configuration dans Claude Desktop
30
+
31
+ 1. Ouvrez Claude Desktop → **Réglages → Développeur → Modifier la configuration** (ça crée le fichier s'il n'existe pas encore)
32
+ 2. Le fichier se trouve à :
33
+ - macOS : `~/Library/Application Support/Claude/claude_desktop_config.json`
34
+ - Windows : `%APPDATA%\Claude\claude_desktop_config.json`
35
+ 3. Ajoutez (ou complétez s'il existe déjà d'autres serveurs) :
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "heurix": {
41
+ "command": "/usr/bin/python3",
42
+ "args": ["/chemin/complet/vers/heurix-mcp-server/server.py"],
43
+ "env": {
44
+ "HEURIX_API_KEY": "hx_votre_cle_api",
45
+ "HEURIX_API_BASE": "https://api.heurix.fr"
46
+ }
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ **Important** : utilisez le **chemin complet** vers `python3` (`which python3` dans un terminal pour le trouver), pas juste `python3` — Claude Desktop lance la configuration avec un PATH minimal, une commande courte qui fonctionne dans votre terminal peut échouer silencieusement ici. Même chose pour le chemin vers `server.py` : complet, pas relatif.
53
+
54
+ 4. Redémarrez Claude Desktop entièrement (pas juste fermer la fenêtre)
55
+ 5. Un nouvel outil (icône 🔌 ou menu MCP selon la version) doit lister `heurix_search`, `heurix_browse`, `heurix_catalog_stats`
56
+
57
+ ## Configuration dans Cursor
58
+
59
+ Même structure de fichier, deux emplacements possibles :
60
+ - `.cursor/mcp.json` à la racine d'un projet (pour un serveur propre à ce projet)
61
+ - `~/.cursor/mcp.json` (global, disponible dans tous les projets)
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "heurix": {
67
+ "command": "/usr/bin/python3",
68
+ "args": ["/chemin/complet/vers/heurix-mcp-server/server.py"],
69
+ "env": {
70
+ "HEURIX_API_KEY": "hx_votre_cle_api",
71
+ "HEURIX_API_BASE": "https://api.heurix.fr"
72
+ }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ Ensuite : **Réglages Cursor → Tools & MCP**, vérifiez que "Enable MCP Servers" est coché, et que `heurix` apparaît avec un point vert (connecté). Si rien n'apparaît après un redémarrage, le panneau **Output → MCP** affiche les logs bruts du serveur — souvent plus parlant que l'interface elle-même pour diagnostiquer.
79
+
80
+ ## Exemple concret
81
+
82
+ **Ce qu'un utilisateur tape dans Claude Desktop**, sans rien connaître de l'API Heurix :
83
+
84
+ > J'ai un catalogue Heurix qui s'appelle quincaillerie-demo. Est-ce que j'ai des vis M8 en stock, et à quel prix ?
85
+
86
+ **Ce qui se passe côté agent** (invisible pour l'utilisateur, montré ici pour comprendre) :
87
+ 1. Claude appelle `heurix_search(catalog="quincaillerie-demo", query="vis M8")`
88
+ 2. Le serveur MCP relaie vers `POST https://api.heurix.fr/v1/index/quincaillerie-demo/search`
89
+ 3. La réponse JSON (hits, scores, stock, prix) revient à Claude
90
+
91
+ **Réponse type de Claude Desktop à l'utilisateur** :
92
+
93
+ > Oui, vous avez deux références de vis M8 en stock dans quincaillerie-demo :
94
+ >
95
+ > - **Vis M8 x 20 - Inox A2** — 120 en stock, 5,90 €
96
+ > - **Vis M8 x 30 - Inox A2** — 45 en stock, 7,90 €
97
+ >
98
+ > Voulez-vous que je regarde aussi si l'une d'elles a un stock faible, ou que je compare avec une autre référence ?
99
+
100
+ Aucune ligne de code, aucun appel curl — l'utilisateur pose une question en
101
+ langage naturel, l'agent fait le pont vers l'API.
102
+
103
+ ## Sécurité
104
+
105
+ `HEURIX_API_KEY` vit uniquement dans la configuration du client MCP (fichier
106
+ local sur la machine de l'utilisateur), jamais en argument de ligne de
107
+ commande, jamais transmise en clair dans un appel de tool — le serveur la
108
+ lit une fois au démarrage depuis l'environnement et l'utilise pour chaque
109
+ appel à l'API Heurix.
@@ -0,0 +1,39 @@
1
+ # Paquet installable du serveur MCP Heurix.
2
+ #
3
+ # POURQUOI CE FICHIER : les annuaires MCP (mcp.so, Smithery, Glama) verifient
4
+ # un serveur en INTROSPECTANT un processus qu'ils lancent. Un zip telecharge
5
+ # depuis un site ne leur suffit pas -- il leur faut une commande executable.
6
+ # Publie sur PyPI, le serveur devient lancable par `uvx heurix-mcp-server`,
7
+ # ce que tout crawler d'annuaire sait faire.
8
+ [build-system]
9
+ requires = ["hatchling"]
10
+ build-backend = "hatchling.build"
11
+
12
+ [project]
13
+ name = "heurix-mcp-server"
14
+ version = "0.1.0"
15
+ description = "Serveur MCP pour Heurix — recherche et classement de catalogues produits techniques en langage naturel"
16
+ readme = "README.md"
17
+ requires-python = ">=3.10"
18
+ license = { text = "MIT" }
19
+ authors = [{ name = "Alexis Flahaut", email = "contact@heurix.fr" }]
20
+ keywords = ["mcp", "model-context-protocol", "search", "ecommerce", "catalog", "product-search"]
21
+ classifiers = [
22
+ "Development Status :: 4 - Beta",
23
+ "Intended Audience :: Developers",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
27
+ ]
28
+ dependencies = ["mcp>=1.2.0", "httpx>=0.27"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://heurix.fr"
32
+ Documentation = "https://heurix.fr/docs.html#ep-mcp"
33
+ Repository = "https://github.com/flahaut-alexis/heurix-mcp-server"
34
+
35
+ [project.scripts]
36
+ heurix-mcp-server = "server:main"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ include = ["server.py"]
@@ -0,0 +1,2 @@
1
+ mcp[cli]>=1.28.0
2
+ httpx>=0.27.0
@@ -0,0 +1,172 @@
1
+ """
2
+ Serveur MCP pour Heurix — chantier 6.1 de la roadmap (24 juillet 2026).
3
+
4
+ Enveloppe l'API REST Heurix existante en "tools" qu'un agent IA (Claude
5
+ Desktop, Cursor, un agent interne via le SDK MCP) peut découvrir et
6
+ appeler directement. Pas de nouvelle logique métier ici : une nouvelle
7
+ façade sur ce qui existe déjà (recherche, browse, stats catalogue).
8
+
9
+ Configuration — deux variables d'environnement, jamais codées en dur ni
10
+ passées en clair dans un appel de tool :
11
+ HEURIX_API_KEY votre clé API Heurix (hx_...)
12
+ HEURIX_API_BASE URL de base de l'API (défaut : https://api.heurix.fr)
13
+
14
+ Lancement local (test manuel) :
15
+ HEURIX_API_KEY=hx_... python3 server.py
16
+
17
+ Voir README.md pour la configuration dans Claude Desktop et Cursor.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import sys
23
+
24
+ import httpx
25
+ from mcp.server.fastmcp import FastMCP
26
+
27
+ HEURIX_API_KEY = os.environ.get("HEURIX_API_KEY", "").strip()
28
+ HEURIX_API_BASE = os.environ.get("HEURIX_API_BASE", "https://api.heurix.fr").rstrip("/")
29
+
30
+ if not HEURIX_API_KEY:
31
+ print(
32
+ "HEURIX_API_KEY manquante — définissez-la dans la configuration MCP de "
33
+ "votre client (Claude Desktop, Cursor...), jamais en argument de ligne "
34
+ "de commande ni codée en dur dans ce fichier.",
35
+ file=sys.stderr,
36
+ )
37
+ sys.exit(1)
38
+
39
+ mcp = FastMCP(
40
+ "heurix",
41
+ instructions=(
42
+ "Outils pour interroger un catalogue produit indexé sur Heurix — un "
43
+ "moteur de recherche et de classement pour catalogues techniques "
44
+ "(regex sur références produit, facettes, tri par catégorie). "
45
+ "Commencez par heurix_catalog_stats si vous ne connaissez pas encore "
46
+ "les catalogues, catégories ou attributs disponibles sur ce compte : "
47
+ "les noms de catalogue et de catégorie sont sensibles à la casse et "
48
+ "doivent être exacts, pas devinés."
49
+ ),
50
+ )
51
+
52
+
53
+ def _auth_headers() -> dict:
54
+ return {"Authorization": f"Bearer {HEURIX_API_KEY}"}
55
+
56
+
57
+ def _handle_response(r: httpx.Response) -> dict:
58
+ """Ne masque jamais une erreur API derrière un plantage générique — un
59
+ agent doit pouvoir comprendre, et au besoin expliquer à l'utilisateur,
60
+ ce qui s'est mal passé (catalogue introuvable, quota dépassé, accès
61
+ Browse non inclus dans le plan...) plutôt que recevoir une exception
62
+ opaque."""
63
+ if r.status_code >= 400:
64
+ try:
65
+ detail = r.json().get("detail", r.text)
66
+ except Exception: # noqa: BLE001 — reponse non-JSON, on renvoie le texte brut
67
+ detail = r.text
68
+ return {"error": True, "status_code": r.status_code, "detail": detail}
69
+ return r.json()
70
+
71
+
72
+ async def _get(path: str, params: dict | None = None) -> dict:
73
+ async with httpx.AsyncClient(timeout=15.0) as client:
74
+ r = await client.get(f"{HEURIX_API_BASE}{path}", headers=_auth_headers(), params=params or {})
75
+ return _handle_response(r)
76
+
77
+
78
+ async def _post(path: str, json_body: dict) -> dict:
79
+ async with httpx.AsyncClient(timeout=15.0) as client:
80
+ r = await client.post(f"{HEURIX_API_BASE}{path}", headers=_auth_headers(), json=json_body)
81
+ return _handle_response(r)
82
+
83
+
84
+ @mcp.tool()
85
+ async def heurix_search(catalog: str, query: str, filters: list[str] | None = None, limit: int = 10) -> dict:
86
+ """Recherche des produits dans un catalogue Heurix par mot-clé, avec
87
+ tolérance aux fautes de frappe et reconnaissance des références
88
+ techniques (diamètres, longueurs, matières, ISBN...). Utilisez
89
+ heurix_catalog_stats d'abord si le nom exact du catalogue n'est pas
90
+ déjà connu.
91
+
92
+ La réponse peut inclure `suggested_category` si un mot de la requête
93
+ recoupe une catégorie Browse connue — utile pour proposer "cherchiez-
94
+ vous plutôt dans telle catégorie ?", ne change jamais les résultats
95
+ eux-mêmes.
96
+
97
+ Args:
98
+ catalog: Nom exact du catalogue à interroger.
99
+ query: Texte de recherche, tel qu'un utilisateur le taperait —
100
+ les fautes de frappe et formats différents sont tolérés par
101
+ le moteur, pas la peine de les corriger avant d'appeler.
102
+ filters: Liste optionnelle de filtres exacts sur des annotations
103
+ connues (ex. ["DIAM_M8"]). Laisser vide si incertain plutôt
104
+ que de deviner une valeur.
105
+ limit: Nombre maximal de résultats, entre 1 et 100 (défaut 10).
106
+ """
107
+ return await _post(
108
+ f"/v1/index/{catalog}/search",
109
+ {"q": query, "filters": filters or [], "limit": max(1, min(limit, 100))},
110
+ )
111
+
112
+
113
+ @mcp.tool()
114
+ async def heurix_browse(catalog: str, category: str, sort: str = "stock", limit: int = 20) -> dict:
115
+ """Liste les produits d'une catégorie sans recherche textuelle — pour
116
+ une demande du type "montre-moi tous les produits de telle
117
+ catégorie", pas une recherche par mot-clé (utilisez heurix_search
118
+ pour ça). Utilisez heurix_catalog_stats pour découvrir les
119
+ catégories réellement disponibles si elles ne sont pas déjà connues
120
+ — inventer un nom de catégorie renverra une liste vide, pas une erreur.
121
+
122
+ Args:
123
+ catalog: Nom exact du catalogue.
124
+ category: Valeur de catégorie exacte, sensible à la casse.
125
+ sort: Stratégie de tri — "stock" (défaut, en stock d'abord),
126
+ "recent", "alphabetical", "price_asc", "price_desc",
127
+ "margin", ou "popular" (popularité réelle, clics et achats).
128
+ limit: Nombre maximal de résultats, entre 1 et 100 (défaut 20).
129
+ """
130
+ return await _get(
131
+ f"/v1/browse/{catalog}/{category}",
132
+ {"sort": sort, "limit": max(1, min(limit, 100))},
133
+ )
134
+
135
+
136
+ @mcp.tool()
137
+ async def heurix_catalog_stats(catalog: str | None = None) -> dict:
138
+ """Liste tous les catalogues accessibles avec cette clé API et leurs
139
+ statistiques de base (nombre de produits, pack de règles actif). Si
140
+ `catalog` est précisé, renvoie en plus les catégories Browse
141
+ disponibles pour ce catalogue. À appeler en premier pour découvrir
142
+ ce qui existe, avant une recherche ou un parcours par catégorie.
143
+
144
+ Args:
145
+ catalog: Nom d'un catalogue précis (optionnel). Sans ce
146
+ paramètre, liste tous les catalogues du compte.
147
+ """
148
+ if catalog:
149
+ stats = await _get(f"/v1/index/{catalog}/stats")
150
+ if stats.get("error"):
151
+ return stats
152
+ categories = await _get(f"/v1/index/{catalog}/browse-categories")
153
+ # Browse peut ne pas être inclus dans le plan de cette clé — pas une
154
+ # raison de faire échouer tout l'appel, juste une liste vide.
155
+ stats["browse_categories"] = categories.get("categories", []) if not categories.get("error") else []
156
+ return stats
157
+ return await _get("/v1/index/catalogs")
158
+
159
+
160
+ def main() -> None:
161
+ """Point d'entree de la commande `heurix-mcp-server`.
162
+
163
+ Declaree dans pyproject.toml sous [project.scripts] : sans cette
164
+ fonction, la commande installee depuis PyPI echouerait a l'import.
165
+ C'est aussi elle que les annuaires MCP lancent pour introspecter le
166
+ serveur et verifier les outils qu'il expose.
167
+ """
168
+ mcp.run(transport="stdio")
169
+
170
+
171
+ if __name__ == "__main__":
172
+ mcp.run(transport="stdio")