story-theme-mcp 1.2.3 → 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.3",
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",
@@ -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
+ }
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';
@@ -52,7 +53,9 @@ Appelez build_store AVEC un paramètre copy le plus rempli possible. Une V1 qui
52
53
 
53
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.
54
55
 
55
- 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).
56
59
 
57
60
  ## 3. Déclarez TOUT ce que vous avez inventé
58
61
 
@@ -298,7 +301,7 @@ export function createServer(config) {
298
301
  '',
299
302
  '## Outils',
300
303
  '- Connaître le thème : search_theme, list_sections, get_section, list_blocks, get_block, get_theme_settings, get_theme_template, read_guide',
301
- '- 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',
302
305
  '- Construire vite : build_store (V1 complète en un appel), compose_page (pages originales avec toutes les sections), read_guide signatures',
303
306
  '- Construire : create_project, update_brief, apply_recipe, localize_template, list_texts, edit_template, write_file, update_theme_settings, read_file, exclude_template',
304
307
  '- Contrôler : validate_json, review_project, undo, revert_file',
@@ -376,6 +379,30 @@ export function createServer(config) {
376
379
  }),
377
380
  );
378
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
+
379
406
  server.registerTool(
380
407
  'read_guide',
381
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.3';
3
+ export const VERSION = '1.3.0';