story-theme-mcp 1.2.2 → 1.3.0

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.
package/README.md CHANGED
@@ -52,6 +52,7 @@ Le MCP ne contient pas le thème : au démarrage, il demande à l'espace membre
52
52
  | Étape | Outils |
53
53
  |---|---|
54
54
  | Connaître le thème | `get_started`, `theme_source`, `search_theme`, `list_sections`, `get_section`, `list_blocks`, `get_block`, `get_theme_settings`, `get_theme_template`, `read_guide` |
55
+ | S'inspirer | `inspiration` : les 11 boutiques démo composées par le designer — l'ossature de leurs pages, à regarder avant de construire |
55
56
  | Concevoir | `recipes` (15 compositions de pages), `generate_color_schemes`, `check_contrast`, `find_fonts` |
56
57
  | Construire vite | `build_store` : la V1 complète d'une boutique en un appel (direction artistique, pages de marque, moments signature, textes, traduction, zip) |
57
58
  | Composer librement | `compose_page` : n'importe quelle page avec toutes les sections du thème, en donnant seulement le contenu (titre, texte, boutons, étapes, cartes, questions, chiffres…) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "story-theme-mcp",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Serveur MCP du Story Thème : votre assistant IA connaît le thème Shopify Story par cœur et construit des boutiques propres et professionnelles.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -23,6 +23,7 @@
23
23
  "dependencies": {
24
24
  "@modelcontextprotocol/sdk": "^1.30.0",
25
25
  "fflate": "^0.8.3",
26
+ "story-theme-mcp": "^1.2.1",
26
27
  "zod": "^4.6.5"
27
28
  },
28
29
  "keywords": [
@@ -0,0 +1,81 @@
1
+ // =====================================================================
2
+ // Les boutiques démo, vues par l'assistant
3
+ // =====================================================================
4
+ // Onze boutiques composées par le designer du thème dorment dans l'espace membre. L'assistant
5
+ // ne les voyait pas : il repartait des recettes, et deux marques du même style se ressemblaient.
6
+ //
7
+ // Il lit maintenant leur OSSATURE — quelles sections, dans quel ordre, avec quels blocs — pour
8
+ // s'en inspirer comme un designer regarde le travail d'un confrère. Jamais leurs textes : dix
9
+ // boutiques clientes qui racontent la même histoire seraient pires que dix boutiques identiques.
10
+ //
11
+ // Deux chemins d'accès, selon l'endroit où tourne le MCP :
12
+ // connecteur `config.demos` : l'espace membre sert les démos sans aller-retour réseau ;
13
+ // local l'API `/api/v1/mcp/demos`, avec le jeton du client.
14
+
15
+ const CHEMIN = '/api/v1/mcp/demos';
16
+
17
+ /** Rend les démos accessibles, quelle que soit la façon dont le MCP est servi. */
18
+ export function creerDemos(config) {
19
+ // Connecteur : l'espace membre a déjà tout sous la main.
20
+ if (config.demos) return config.demos;
21
+
22
+ const appel = async (chemin) => {
23
+ if (!config.token) {
24
+ throw new Error(
25
+ 'Les boutiques démo demandent un jeton : ajoutez STORY_MCP_TOKEN (espace membre Story Thème > MCP & Appli).',
26
+ );
27
+ }
28
+ const reponse = await fetch(`${config.apiBase.replace(/\/+$/, '')}${chemin}`, {
29
+ headers: { Authorization: `Bearer ${config.token}`, Accept: 'application/json' },
30
+ signal: AbortSignal.timeout(15000),
31
+ });
32
+ if (!reponse.ok) throw new Error(`réponse ${reponse.status} de l’espace membre`);
33
+ return (await reponse.json()).data;
34
+ };
35
+
36
+ return {
37
+ lister: async () => (await appel(CHEMIN)).demos,
38
+ composition: async (slug) => appel(`${CHEMIN}/${encodeURIComponent(slug)}`),
39
+ };
40
+ }
41
+
42
+ /** Le catalogue, en une liste lisible. */
43
+ export function formatDemos(demos) {
44
+ if (!demos?.length) return 'Aucune boutique démo disponible.';
45
+ return [
46
+ `${demos.length} boutiques démo, composées par le designer du thème. Regardez celle dont l’activité ressemble le plus à celle du marchand, puis inspirez-vous de sa COMPOSITION — jamais de ses textes.`,
47
+ '',
48
+ ...demos.map((demo) => `- **${demo.slug}** — ${demo.nom} · ${demo.niche}${demo.type ? ` · ${demo.type}` : ''}\n ${demo.pitch}`),
49
+ '',
50
+ 'inspiration({ demo: "<slug>" }) donne la composition de ses pages.',
51
+ ].join('\n');
52
+ }
53
+
54
+ /** La composition d'une démo : une ligne par section, dans l'ordre de la page. */
55
+ export function formatComposition(composition, pageDemandee) {
56
+ const pages = composition.pages || {};
57
+ const noms = Object.keys(pages);
58
+ if (!noms.length) return `La démo « ${composition.slug} » n’expose aucune page.`;
59
+
60
+ const choisies = pageDemandee
61
+ ? noms.filter((nom) => nom === pageDemandee || nom === `page.${pageDemandee}`)
62
+ : ['index', 'product', 'collection'].filter((nom) => pages[nom]);
63
+
64
+ if (!choisies.length) return `Page « ${pageDemandee} » absente. Pages disponibles : ${noms.join(', ')}.`;
65
+
66
+ const lignes = [`# ${composition.nom} (${composition.slug}) — ${composition.niche}`, composition.pitch, ''];
67
+ for (const nom of choisies) {
68
+ lignes.push(`## ${nom} — ${pages[nom].length} sections`);
69
+ pages[nom].forEach((section, index) => {
70
+ const blocs = section.blocs?.length ? ` — blocs : ${section.blocs.join(', ')}` : '';
71
+ const palette = section.palette ? ` [${section.palette}]` : '';
72
+ lignes.push(`${index + 1}. ${section.type}${palette}${blocs}`);
73
+ });
74
+ lignes.push('');
75
+ }
76
+ lignes.push(`Pages disponibles : ${noms.join(', ')}.`);
77
+ lignes.push(
78
+ 'Inspirez-vous de l’enchaînement et du rythme des palettes ; écrivez vos propres textes, et n’imposez pas cette structure si la marque appelle autre chose.',
79
+ );
80
+ return lignes.join('\n');
81
+ }
@@ -17,7 +17,7 @@ Règles d'or :
17
17
  - Partez d'un preset (sections et blocs) ou d'une recette : ils sont réglés par le designer du thème.
18
18
  - Les images ne peuvent pas être envoyées par le MCP : laissez les champs image vides, la check-list les liste. Si le marchand a déjà déposé ses images dans Contenu > Fichiers, utilisez shopify://shop_images/<nom-exact>.
19
19
  - Les produits, collections, menus et pages se créent dans l'administration Shopify ; le thème ne fait que les afficher. Utilisez les handles donnés par le marchand (ou lus avec un connecteur Shopify).
20
- - Honnêteté commerciale : pas de faux avis, faux chiffres, faux stock, compte à rebours qui redémarre, logos presse inventés.`,
20
+ - Une V1 est une MAQUETTE : écrivez des avis, des chiffres et des conditions plausibles pour qu'elle ressemble à une boutique finie, puis listez tout ce que vous avez inventé sous « Ce que j'ai inventé, à valider ». Le marchand corrige en une passe et remplace les avis par de vrais avant la mise en ligne.\n- Deux limites qui ne se négocient pas, même en maquette : aucun compte à rebours qui redémarre à l'infini, aucun stock simulé (pratiques commerciales trompeuses).`,
21
21
  },
22
22
 
23
23
  architecture: {
@@ -46,7 +46,7 @@ Règles d'or :
46
46
  - **Photos.** Une photo de qualité vaut mieux que trois moyennes. Les visuels d'exemple du thème ne doivent jamais partir en ligne.
47
47
  - **Animations.** Celles des presets suffisent. Un seul effet fort par page (zoom au défilement, cartes empilées, scrollytelling).
48
48
  - **Mobile d'abord.** Tailles mobiles des titres (size_mobile), stack_on_mobile, bannière avec image mobile dédiée si le visuel est large.
49
- - **Sobriété commerciale.** Pas de pop-up à l'arrivée, une seule pop-up au total, pas d'urgence inventée, pas de superlatifs sans preuve.`,
49
+ - **Sobriété commerciale.** Pas de pop-up à l'arrivée, une seule pop-up au total, pas de superlatifs sans preuve. Une offre de lancement ou un lot sont légitimes ; un compte à rebours qui redémarre et un stock simulé ne le sont pas.`,
50
50
  },
51
51
 
52
52
  couleurs: {
@@ -246,7 +246,7 @@ GUIDES.signatures = {
246
246
  - Comparatif / pourquoi nous : comparison, calculator (coût par usage), stats réels.
247
247
  - Landing de campagne : banner, countdown (vraie date), featured-product, comparison, FAQ.
248
248
 
249
- **Règles** : 7 à 10 sections par page, alterner visuel et texte, une section colorée sur trois, un seul effet fort par écran, jamais de faux chiffres ni de faux avis.`,
249
+ **Règles** : 7 à 10 sections par page, alterner visuel et texte, une section colorée sur trois, un seul effet fort par écran. Les avis et chiffres d'une maquette sont des exemples à valider : écrivez-les, puis déclarez-les.`,
250
250
  };
251
251
 
252
252
  export const GUIDE_TOPICS = Object.keys(GUIDES);
package/src/server.js CHANGED
@@ -20,6 +20,7 @@ import { composeSections } from './build/compose.js';
20
20
  import { DIRECTIONS } from './knowledge/directions.js';
21
21
  import { ProjectStore, checkRelativePath } from './project/store.js';
22
22
  import { localSource } from './theme/source.js';
23
+ import { creerDemos, formatDemos, formatComposition } from './knowledge/demos.js';
23
24
  import { reviewProject, formatReview, findSchemeReferences } from './project/review.js';
24
25
  import { exportTheme, importTheme, migrateProject } from './project/export.js';
25
26
  import { GUIDES, GUIDE_TOPICS } from './knowledge/guides.js';
@@ -42,7 +43,9 @@ Avant la première construction, posez UNE seule salve de questions — jamais d
42
43
  3. Une couleur de marque, un logo ? Des photos déjà déposées dans Shopify ?
43
44
  4. Vos vraies conditions : livraison, retours, garantie ?
44
45
 
45
- Terminez par : « ou dites-moi simplement "vas-y" et je construis avec ce que j'ai ». S'ils disent d'y aller, ou si le premier message contient déjà tout, construisez immédiatement sans rien demander.
46
+ Terminez par : « ou dites-moi simplement "vas-y" et je construis avec ce que j'ai ». S'ils disent d'y aller, construisez immédiatement.
47
+
48
+ Même quand le premier message semble complet, DEUX questions restent à poser avant de construire, parce que leurs réponses changent la page elle-même et qu'elles manquent presque toujours : les photos déjà déposées dans Shopify (sans elles, héro typographique et sections remplacées) et la collection principale (bouton du héro, meilleures ventes). Posez-les en une ligne, et construisez dès la réponse.
46
49
 
47
50
  ## 2. La V1 doit être une boutique, pas un squelette
48
51
 
@@ -50,7 +53,9 @@ Appelez build_store AVEC un paramètre copy le plus rempli possible. Une V1 qui
50
53
 
51
54
  ÉCRIVEZ LE CONTENU. Rédigez la promesse, le manifeste, les bénéfices, les étapes de fabrication, la FAQ, les valeurs, les pages d'aide (livraison, retours, garantie, paiement), les offres par paliers, et oui : des avis clients plausibles et un peu d'urgence honnête (une offre de lancement, un lot). C'est une MAQUETTE que le marchand valide, pas une boutique en ligne : mieux vaut un exemple concret qu'il corrige qu'un cadre vide qu'il doit remplir seul.
52
55
 
53
- La fiche produit part AVEC des offres par paliers (1 / 2 / 3 pièces, ou lot + cadeau) : c'est le défaut, écrivez-en les libellés et les remises, et rappelez au marchand de créer les réductions correspondantes dans Shopify.\n\nVariez la structure : donnez une direction artistique (build_store direction) et, pour deux boutiques du même style, un paramètre variation différent. compose_page crée les pages que les recettes ne prévoient pas (ingrédients, lookbook, comparatif, guide cadeau, livraison, garantie).
56
+ La fiche produit part AVEC des offres par paliers (1 / 2 / 3 pièces, ou lot + cadeau) : c'est le défaut, écrivez-en les libellés et les remises, et rappelez au marchand de créer les réductions correspondantes dans Shopify.\n\nAvant de construire, appelez inspiration : les onze boutiques démo sont composées par le designer du thème. Regardez celle dont l'activité ressemble le plus à celle du marchand et reprenez-en l'enchaînement et le rythme des palettes — jamais les textes, et sans vous y enfermer si la marque appelle autre chose.
57
+
58
+ Variez la structure : donnez une direction artistique (build_store direction) et, pour deux boutiques du même style, un paramètre variation différent. compose_page crée les pages que les recettes ne prévoient pas (ingrédients, lookbook, comparatif, guide cadeau, livraison, garantie).
54
59
 
55
60
  ## 3. Déclarez TOUT ce que vous avez inventé
56
61
 
@@ -296,7 +301,7 @@ export function createServer(config) {
296
301
  '',
297
302
  '## Outils',
298
303
  '- Connaître le thème : search_theme, list_sections, get_section, list_blocks, get_block, get_theme_settings, get_theme_template, read_guide',
299
- '- Concevoir : recipes, generate_color_schemes, check_contrast, find_fonts',
304
+ '- Concevoir : inspiration (les 11 boutiques démo, à regarder avant de composer), recipes, generate_color_schemes, check_contrast, find_fonts',
300
305
  '- Construire vite : build_store (V1 complète en un appel), compose_page (pages originales avec toutes les sections), read_guide signatures',
301
306
  '- Construire : create_project, update_brief, apply_recipe, localize_template, list_texts, edit_template, write_file, update_theme_settings, read_file, exclude_template',
302
307
  '- Contrôler : validate_json, review_project, undo, revert_file',
@@ -374,6 +379,30 @@ export function createServer(config) {
374
379
  }),
375
380
  );
376
381
 
382
+ server.registerTool(
383
+ 'inspiration',
384
+ {
385
+ title: 'Boutiques démo',
386
+ description:
387
+ 'The eleven Story Theme demo stores, composed by the theme designer. Without arguments: the catalogue (niche, positioning). With `demo`: how that store composes its pages — sections in order, palettes, block types. Look at the demo closest to the merchant’s business BEFORE building, and take inspiration from the sequence and rhythm — never from its texts.',
388
+ inputSchema: {
389
+ demo: z.string().optional().describe('Identifiant d’une démo (sylys, narvo, havn…)'),
390
+ page: z.string().optional().describe('Page précise : index, product, collection, ou une page de marque (page.a-propos…)'),
391
+ },
392
+ annotations: readOnly,
393
+ },
394
+ run(async ({ demo, page }) => {
395
+ const demos = creerDemos(config);
396
+ if (!demo) return ok(formatDemos(await demos.lister()));
397
+ const composition = await demos.composition(demo);
398
+ if (!composition) {
399
+ const connues = (await demos.lister()).map((entree) => entree.slug);
400
+ return fail(`Démo « ${demo} » inconnue. Disponibles : ${connues.join(', ')}.`);
401
+ }
402
+ return ok(formatComposition(composition, page));
403
+ }),
404
+ );
405
+
377
406
  server.registerTool(
378
407
  'read_guide',
379
408
  {
package/src/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Version du serveur MCP. Ici et dans package.json — les deux doivent être identiques
2
2
  // (npm test le vérifie), et rien d'autre ne la redéclare.
3
- export const VERSION = '1.2.2';
3
+ export const VERSION = '1.3.0';