bricks-builder-mcp 3.11.5 → 3.12.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 +8 -2
- package/package.json +3 -3
- package/server.js +467 -4
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ C'est la méthode **complète et la plus simple**. Tu obtiens : les outils MCP *
|
|
|
34
34
|
|
|
35
35
|
C'est fini. Aucun terminal, aucun JSON à éditer.
|
|
36
36
|
|
|
37
|
-
### (Optionnel mais recommandé) Activer
|
|
37
|
+
### (Optionnel mais recommandé) Activer les screenshots MCP
|
|
38
38
|
|
|
39
39
|
Dans un terminal, une seule fois :
|
|
40
40
|
|
|
@@ -42,7 +42,7 @@ Dans un terminal, une seule fois :
|
|
|
42
42
|
npx playwright install chromium
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Ça télécharge Chromium (~130 Mo) pour que Claude puisse prendre des screenshots de tes pages
|
|
45
|
+
Ça télécharge Chromium (~130 Mo) pour que Claude/Codex puisse prendre des screenshots de tes pages avec `verify_element`, `audit_page` et `audit_design_page`.
|
|
46
46
|
|
|
47
47
|
### Test final
|
|
48
48
|
|
|
@@ -125,6 +125,12 @@ Claude Code détectera automatiquement le skill au prochain démarrage.
|
|
|
125
125
|
- Idéal pour : état initial avant refonte, démo client, audit après une grosse vague de modifs
|
|
126
126
|
- Checks intégrés : containers vides, images cassées + alt manquants, text-align mixé, débordement horizontal global de la page
|
|
127
127
|
|
|
128
|
+
**⭐ Audit design IA (v3.12+)**
|
|
129
|
+
- `audit_design_page` — dossier de revue visuelle pour l'IA : fullpage screenshots, crops de sections, contexte DOM/design et brief de critique.
|
|
130
|
+
- Objectif : détecter les défauts d'harmonie que les checks techniques ne voient pas
|
|
131
|
+
- Analyse attendue : hiérarchie, rythme, alignements, densité, largeur, typo, CTA, cohérence entre sections, sensation premium, responsive
|
|
132
|
+
- Ne remplace pas `audit_page` : workflow recommandé = `audit_page` pour les bugs techniques, puis `audit_design_page` pour la critique webdesign générale
|
|
133
|
+
|
|
128
134
|
**⭐ Upload optimisé (v3.8+)**
|
|
129
135
|
- `upload_local_file`, `upload_local_files_batch` — l'IA donne juste le path local, le MCP server lit le fichier, conversion WebP automatique (-80 à -95% de poids), renommage SEO via le `title`
|
|
130
136
|
- `upload_media`, `upload_media_batch` (accepte URL HTTP/HTTPS **ou** data URI)
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "bricks-builder-mcp",
|
|
4
|
-
"version": "3.
|
|
5
|
-
"description": "Serveur MCP pour piloter Bricks Builder (WordPress) depuis Claude — édition de pages, gestion d'éléments,
|
|
4
|
+
"version": "3.12.0",
|
|
5
|
+
"description": "Serveur MCP pour piloter Bricks Builder (WordPress) depuis Claude/Codex — édition de pages, gestion d'éléments, audit technique, audit design visuel, upload optimisé WebP. Communauté Discord : https://discord.gg/rX22zHRzH",
|
|
6
6
|
"homepage": "https://discord.gg/rX22zHRzH",
|
|
7
7
|
"main": "server.js",
|
|
8
8
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"start": "node server.js",
|
|
17
|
-
"postinstall": "node -e \"console.log('\\n[bricks-builder-mcp] Pour activer verify_element, lance UNE FOIS :\\n npx playwright install chromium\\n')\"",
|
|
17
|
+
"postinstall": "node -e \"console.log('\\n[bricks-builder-mcp] Pour activer verify_element/audit_page/audit_design_page, lance UNE FOIS :\\n npx playwright install chromium\\n')\"",
|
|
18
18
|
"test": "echo \"No tests yet\" && exit 0"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
package/server.js
CHANGED
|
@@ -356,7 +356,7 @@ console.error = (...args) => {
|
|
|
356
356
|
};
|
|
357
357
|
|
|
358
358
|
logToFile('========================================');
|
|
359
|
-
logToFile('DÉMARRAGE BRICKS MCP SERVER v3.0');
|
|
359
|
+
logToFile('DÉMARRAGE BRICKS MCP SERVER v3.12.0');
|
|
360
360
|
logToFile('========================================');
|
|
361
361
|
|
|
362
362
|
// Configuration WordPress
|
|
@@ -384,7 +384,7 @@ if (!process.env.WORDPRESS_URL || !process.env.API_KEY) {
|
|
|
384
384
|
const mcpServer = new Server(
|
|
385
385
|
{
|
|
386
386
|
name: "bricks-mcp-v3",
|
|
387
|
-
version: "3.
|
|
387
|
+
version: "3.12.0",
|
|
388
388
|
},
|
|
389
389
|
{
|
|
390
390
|
capabilities: {
|
|
@@ -828,6 +828,31 @@ mcpServer.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
828
828
|
description: "Récupère les settings globaux de Bricks Builder pour ce site (typographie globale, breakpoints, palette, classes globales, theme styles). Utile au début d'un projet pour comprendre la base visuelle existante.",
|
|
829
829
|
inputSchema: { type: "object", properties: {} },
|
|
830
830
|
},
|
|
831
|
+
{
|
|
832
|
+
name: "get_element_schema",
|
|
833
|
+
description: "Découvre les éléments Bricks disponibles et leurs contrôles natifs depuis le registre runtime Bricks. Sans `element`, retourne le catalogue compact. Avec `element` (ex: button, image, form), retourne les contrôles/settings de cet élément + les settings hérités communs si includeInherited=true.",
|
|
834
|
+
inputSchema: {
|
|
835
|
+
type: "object",
|
|
836
|
+
properties: {
|
|
837
|
+
element: {
|
|
838
|
+
type: "string",
|
|
839
|
+
description: "Nom technique Bricks de l'élément (ex: section, container, heading, text-basic, button, image). Si omis, retourne le catalogue compact.",
|
|
840
|
+
},
|
|
841
|
+
catalogOnly: {
|
|
842
|
+
type: "boolean",
|
|
843
|
+
description: "Forcer le mode catalogue compact même si element est absent. Défaut: true quand element est omis.",
|
|
844
|
+
},
|
|
845
|
+
includeInherited: {
|
|
846
|
+
type: "boolean",
|
|
847
|
+
description: "Inclure les contrôles de style communs (_padding, _typography, _background, etc.) avec un schema d'élément ciblé. Défaut: true.",
|
|
848
|
+
},
|
|
849
|
+
raw: {
|
|
850
|
+
type: "boolean",
|
|
851
|
+
description: "Inclure une version assainie des contrôles Bricks bruts. Plus lourd en tokens, à utiliser seulement pour debug.",
|
|
852
|
+
},
|
|
853
|
+
},
|
|
854
|
+
},
|
|
855
|
+
},
|
|
831
856
|
{
|
|
832
857
|
name: "update_global_styles",
|
|
833
858
|
description: "Met à jour les settings globaux Bricks via fusion récursive. Utile pour appliquer une typo de site ou une convention CSS partout d'un coup. Seuls les champs fournis sont modifiés.",
|
|
@@ -1176,6 +1201,46 @@ mcpServer.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1176
1201
|
required: ["pageId"],
|
|
1177
1202
|
},
|
|
1178
1203
|
},
|
|
1204
|
+
{
|
|
1205
|
+
name: "audit_design_page",
|
|
1206
|
+
description: "⭐ AUDIT DESIGN GÉNÉRAL d'une page Bricks. Capture la page en multi-viewport, découpe les sections visibles, extrait le contexte DOM/design, puis retourne un dossier de revue visuelle pour l'IA. À utiliser pour détecter harmonie, hiérarchie, rythme, cohérence, sensation premium, responsive et zones qui font 'pas fini'. Ne remplace pas audit_page : il complète l'audit technique par une vraie passe webdesign.",
|
|
1207
|
+
inputSchema: {
|
|
1208
|
+
type: "object",
|
|
1209
|
+
properties: {
|
|
1210
|
+
pageId: { type: "number", description: "L'ID de la page WP à auditer" },
|
|
1211
|
+
viewports: {
|
|
1212
|
+
type: "array",
|
|
1213
|
+
items: { type: "string", enum: ["desktop", "tablet", "mobile_landscape", "mobile_portrait"] },
|
|
1214
|
+
description: "Viewports à capturer (défaut: ['desktop', 'mobile_portrait']).",
|
|
1215
|
+
},
|
|
1216
|
+
brief: {
|
|
1217
|
+
type: "string",
|
|
1218
|
+
description: "Contexte métier/design optionnel pour calibrer l'audit (ex: artisan carreleur premium local, objectif devis, cible particuliers).",
|
|
1219
|
+
},
|
|
1220
|
+
maxSections: {
|
|
1221
|
+
type: "number",
|
|
1222
|
+
description: "Nombre max de sections cropées par viewport (défaut: 8). Le fullpage reste inclus.",
|
|
1223
|
+
},
|
|
1224
|
+
maxCropHeight: {
|
|
1225
|
+
type: "number",
|
|
1226
|
+
description: "Hauteur max d'un crop de section en px (défaut: 1200) pour éviter des images trop lourdes.",
|
|
1227
|
+
},
|
|
1228
|
+
includeFullPage: {
|
|
1229
|
+
type: "boolean",
|
|
1230
|
+
description: "Inclure le screenshot fullpage propre par viewport (défaut: true).",
|
|
1231
|
+
},
|
|
1232
|
+
includeSectionCrops: {
|
|
1233
|
+
type: "boolean",
|
|
1234
|
+
description: "Inclure les crops de sections principales (défaut: true).",
|
|
1235
|
+
},
|
|
1236
|
+
sectionSelector: {
|
|
1237
|
+
type: "string",
|
|
1238
|
+
description: "Sélecteur CSS optionnel pour découper les sections (défaut: sections Bricks sous #brx-content, fallback main/body).",
|
|
1239
|
+
},
|
|
1240
|
+
},
|
|
1241
|
+
required: ["pageId"],
|
|
1242
|
+
},
|
|
1243
|
+
},
|
|
1179
1244
|
|
|
1180
1245
|
// ===== v3.6.0 — FEEDBACK SYSTEM (missing MCP features) =====
|
|
1181
1246
|
{
|
|
@@ -1638,6 +1703,16 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1638
1703
|
result = await callWordPressAPI("/get-global-styles", "GET");
|
|
1639
1704
|
break;
|
|
1640
1705
|
|
|
1706
|
+
case "get_element_schema":
|
|
1707
|
+
console.error(`[LOG] Exécution: get_element_schema`);
|
|
1708
|
+
result = await callWordPressAPI("/get-element-schema", "POST", {
|
|
1709
|
+
element: args.element || "",
|
|
1710
|
+
catalogOnly: args.catalogOnly,
|
|
1711
|
+
includeInherited: args.includeInherited,
|
|
1712
|
+
raw: args.raw || false,
|
|
1713
|
+
});
|
|
1714
|
+
break;
|
|
1715
|
+
|
|
1641
1716
|
case "update_global_styles":
|
|
1642
1717
|
console.error(`[LOG] Exécution: update_global_styles`);
|
|
1643
1718
|
result = await callWordPressAPI("/update-global-styles", "POST", {
|
|
@@ -2531,6 +2606,394 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2531
2606
|
return { content: responseContent };
|
|
2532
2607
|
}
|
|
2533
2608
|
|
|
2609
|
+
// ===== v3.12 — AUDIT_DESIGN_PAGE (dossier de revue visuelle pour IA) =====
|
|
2610
|
+
case "audit_design_page": {
|
|
2611
|
+
const pageId = args.pageId;
|
|
2612
|
+
const viewportList = (Array.isArray(args.viewports) && args.viewports.length > 0)
|
|
2613
|
+
? args.viewports
|
|
2614
|
+
: ["desktop", "mobile_portrait"];
|
|
2615
|
+
const maxSections = Number.isFinite(args.maxSections)
|
|
2616
|
+
? Math.max(1, Math.min(24, Math.round(args.maxSections)))
|
|
2617
|
+
: 8;
|
|
2618
|
+
const maxCropHeight = Number.isFinite(args.maxCropHeight)
|
|
2619
|
+
? Math.max(400, Math.min(2400, Math.round(args.maxCropHeight)))
|
|
2620
|
+
: 1200;
|
|
2621
|
+
const includeFullPage = args.includeFullPage !== false;
|
|
2622
|
+
const includeSectionCrops = args.includeSectionCrops !== false;
|
|
2623
|
+
const sectionSelector = args.sectionSelector || "#brx-content > .brxe-section, main > .brxe-section, .brxe-section";
|
|
2624
|
+
const brief = args.brief || "";
|
|
2625
|
+
|
|
2626
|
+
// Récupérer l'URL de la page via /list-pages pour rester compatible avec les plugins déjà installés.
|
|
2627
|
+
const allPages = await callWordPressAPI("/list-pages", "GET");
|
|
2628
|
+
const pageMeta = (Array.isArray(allPages) ? allPages : []).find(p => p.id === pageId);
|
|
2629
|
+
if (!pageMeta) {
|
|
2630
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: false, error: `Page ${pageId} introuvable dans list_bricks_pages`, hint: "Vérifie l'ID via list_bricks_pages." }, null, 2) }] };
|
|
2631
|
+
}
|
|
2632
|
+
const pageUrl = pageMeta.url;
|
|
2633
|
+
|
|
2634
|
+
const perViewport = [];
|
|
2635
|
+
const imageContent = [];
|
|
2636
|
+
|
|
2637
|
+
for (const viewport of viewportList) {
|
|
2638
|
+
let page;
|
|
2639
|
+
try {
|
|
2640
|
+
page = await getNewPage(viewport);
|
|
2641
|
+
} catch (browserErr) {
|
|
2642
|
+
perViewport.push({
|
|
2643
|
+
viewport,
|
|
2644
|
+
success: false,
|
|
2645
|
+
error: browserErr.message,
|
|
2646
|
+
hint: "Installation Chromium requise pour audit_design_page. Lance : npx playwright install chromium",
|
|
2647
|
+
});
|
|
2648
|
+
continue;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
try {
|
|
2652
|
+
await page.goto(pageUrl, { waitUntil: 'load', timeout: 30000 });
|
|
2653
|
+
await page.waitForTimeout(1000);
|
|
2654
|
+
|
|
2655
|
+
// Même stratégie que audit_page : déclencher les lazy-load avant de capturer.
|
|
2656
|
+
await page.evaluate(() => {
|
|
2657
|
+
document.querySelectorAll('img[loading="lazy"]').forEach(img => {
|
|
2658
|
+
img.loading = 'eager';
|
|
2659
|
+
});
|
|
2660
|
+
});
|
|
2661
|
+
await page.evaluate(() => {
|
|
2662
|
+
return new Promise(resolve => {
|
|
2663
|
+
const totalHeight = document.documentElement.scrollHeight;
|
|
2664
|
+
let scrolled = 0;
|
|
2665
|
+
const step = 450;
|
|
2666
|
+
const timer = setInterval(() => {
|
|
2667
|
+
window.scrollBy(0, step);
|
|
2668
|
+
scrolled += step;
|
|
2669
|
+
if (scrolled >= totalHeight) {
|
|
2670
|
+
clearInterval(timer);
|
|
2671
|
+
window.scrollTo(0, 0);
|
|
2672
|
+
setTimeout(resolve, 500);
|
|
2673
|
+
}
|
|
2674
|
+
}, 70);
|
|
2675
|
+
});
|
|
2676
|
+
});
|
|
2677
|
+
try {
|
|
2678
|
+
await page.waitForLoadState('networkidle', { timeout: 12000 });
|
|
2679
|
+
} catch {}
|
|
2680
|
+
await page.waitForTimeout(500);
|
|
2681
|
+
|
|
2682
|
+
const designContext = await page.evaluate(({ sectionSelector, maxSections }) => {
|
|
2683
|
+
const normalizeText = (value, max = 260) => {
|
|
2684
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
2685
|
+
return text.length > max ? text.slice(0, max - 1) + '…' : text;
|
|
2686
|
+
};
|
|
2687
|
+
const num = (value) => Number.parseFloat(value) || 0;
|
|
2688
|
+
const bbox = (el) => {
|
|
2689
|
+
const rect = el.getBoundingClientRect();
|
|
2690
|
+
return {
|
|
2691
|
+
x: Math.round(rect.left + window.scrollX),
|
|
2692
|
+
y: Math.round(rect.top + window.scrollY),
|
|
2693
|
+
w: Math.round(rect.width),
|
|
2694
|
+
h: Math.round(rect.height),
|
|
2695
|
+
};
|
|
2696
|
+
};
|
|
2697
|
+
const isVisible = (el) => {
|
|
2698
|
+
if (!el || !el.getBoundingClientRect) return false;
|
|
2699
|
+
const rect = el.getBoundingClientRect();
|
|
2700
|
+
if (rect.width < 2 || rect.height < 2) return false;
|
|
2701
|
+
const cs = getComputedStyle(el);
|
|
2702
|
+
return cs.display !== 'none' && cs.visibility !== 'hidden' && num(cs.opacity) > 0.02;
|
|
2703
|
+
};
|
|
2704
|
+
const labelFor = (el) => {
|
|
2705
|
+
const tag = el.tagName.toLowerCase();
|
|
2706
|
+
const id = el.id ? `#${el.id}` : '';
|
|
2707
|
+
const classes = Array.from(el.classList || []).slice(0, 5).map(c => `.${c}`).join('');
|
|
2708
|
+
return `${tag}${id}${classes}`;
|
|
2709
|
+
};
|
|
2710
|
+
const pickComputed = (el) => {
|
|
2711
|
+
const cs = getComputedStyle(el);
|
|
2712
|
+
return {
|
|
2713
|
+
display: cs.display,
|
|
2714
|
+
position: cs.position,
|
|
2715
|
+
backgroundColor: cs.backgroundColor,
|
|
2716
|
+
color: cs.color,
|
|
2717
|
+
fontFamily: cs.fontFamily,
|
|
2718
|
+
fontSize: cs.fontSize,
|
|
2719
|
+
fontWeight: cs.fontWeight,
|
|
2720
|
+
lineHeight: cs.lineHeight,
|
|
2721
|
+
textAlign: cs.textAlign,
|
|
2722
|
+
padding: {
|
|
2723
|
+
top: cs.paddingTop,
|
|
2724
|
+
right: cs.paddingRight,
|
|
2725
|
+
bottom: cs.paddingBottom,
|
|
2726
|
+
left: cs.paddingLeft,
|
|
2727
|
+
},
|
|
2728
|
+
margin: {
|
|
2729
|
+
top: cs.marginTop,
|
|
2730
|
+
right: cs.marginRight,
|
|
2731
|
+
bottom: cs.marginBottom,
|
|
2732
|
+
left: cs.marginLeft,
|
|
2733
|
+
},
|
|
2734
|
+
gap: cs.gap,
|
|
2735
|
+
maxWidth: cs.maxWidth,
|
|
2736
|
+
width: cs.width,
|
|
2737
|
+
overflow: `${cs.overflowX}/${cs.overflowY}`,
|
|
2738
|
+
};
|
|
2739
|
+
};
|
|
2740
|
+
|
|
2741
|
+
let sectionNodes = [];
|
|
2742
|
+
try {
|
|
2743
|
+
sectionNodes = Array.from(document.querySelectorAll(sectionSelector)).filter(isVisible);
|
|
2744
|
+
} catch {}
|
|
2745
|
+
if (sectionNodes.length === 0) {
|
|
2746
|
+
const root = document.querySelector('#brx-content') || document.querySelector('main') || document.body;
|
|
2747
|
+
sectionNodes = Array.from(root.children || []).filter(isVisible);
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
const seen = new Set();
|
|
2751
|
+
sectionNodes = sectionNodes.filter(el => {
|
|
2752
|
+
if (seen.has(el)) return false;
|
|
2753
|
+
seen.add(el);
|
|
2754
|
+
const rect = el.getBoundingClientRect();
|
|
2755
|
+
return rect.width >= 120 && rect.height >= 80;
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2758
|
+
const pageHeadings = Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6,.brxe-heading'))
|
|
2759
|
+
.filter(isVisible)
|
|
2760
|
+
.slice(0, 40)
|
|
2761
|
+
.map(el => {
|
|
2762
|
+
const cs = getComputedStyle(el);
|
|
2763
|
+
return {
|
|
2764
|
+
text: normalizeText(el.innerText || el.textContent, 120),
|
|
2765
|
+
tag: el.tagName.toLowerCase(),
|
|
2766
|
+
label: labelFor(el),
|
|
2767
|
+
fontSizePx: Math.round(num(cs.fontSize)),
|
|
2768
|
+
fontWeight: cs.fontWeight,
|
|
2769
|
+
lineHeight: cs.lineHeight,
|
|
2770
|
+
color: cs.color,
|
|
2771
|
+
bbox: bbox(el),
|
|
2772
|
+
};
|
|
2773
|
+
});
|
|
2774
|
+
|
|
2775
|
+
const pageButtons = Array.from(document.querySelectorAll('a,button,.brxe-button'))
|
|
2776
|
+
.filter(el => isVisible(el) && normalizeText(el.innerText || el.textContent, 80).length > 0)
|
|
2777
|
+
.slice(0, 40)
|
|
2778
|
+
.map(el => {
|
|
2779
|
+
const cs = getComputedStyle(el);
|
|
2780
|
+
return {
|
|
2781
|
+
text: normalizeText(el.innerText || el.textContent, 80),
|
|
2782
|
+
label: labelFor(el),
|
|
2783
|
+
fontSizePx: Math.round(num(cs.fontSize)),
|
|
2784
|
+
lineHeight: cs.lineHeight,
|
|
2785
|
+
whiteSpace: cs.whiteSpace,
|
|
2786
|
+
bbox: bbox(el),
|
|
2787
|
+
};
|
|
2788
|
+
});
|
|
2789
|
+
|
|
2790
|
+
const sections = sectionNodes.map((section, index) => {
|
|
2791
|
+
const headings = Array.from(section.querySelectorAll('h1,h2,h3,h4,h5,h6,.brxe-heading'))
|
|
2792
|
+
.filter(isVisible)
|
|
2793
|
+
.slice(0, 8)
|
|
2794
|
+
.map(el => {
|
|
2795
|
+
const cs = getComputedStyle(el);
|
|
2796
|
+
return {
|
|
2797
|
+
text: normalizeText(el.innerText || el.textContent, 140),
|
|
2798
|
+
tag: el.tagName.toLowerCase(),
|
|
2799
|
+
label: labelFor(el),
|
|
2800
|
+
fontSizePx: Math.round(num(cs.fontSize)),
|
|
2801
|
+
fontWeight: cs.fontWeight,
|
|
2802
|
+
lineHeight: cs.lineHeight,
|
|
2803
|
+
textAlign: cs.textAlign,
|
|
2804
|
+
bbox: bbox(el),
|
|
2805
|
+
};
|
|
2806
|
+
});
|
|
2807
|
+
|
|
2808
|
+
const ctas = Array.from(section.querySelectorAll('a,button,.brxe-button'))
|
|
2809
|
+
.filter(el => isVisible(el) && normalizeText(el.innerText || el.textContent, 80).length > 0)
|
|
2810
|
+
.slice(0, 8)
|
|
2811
|
+
.map(el => {
|
|
2812
|
+
const cs = getComputedStyle(el);
|
|
2813
|
+
return {
|
|
2814
|
+
text: normalizeText(el.innerText || el.textContent, 80),
|
|
2815
|
+
label: labelFor(el),
|
|
2816
|
+
fontSizePx: Math.round(num(cs.fontSize)),
|
|
2817
|
+
lineHeight: cs.lineHeight,
|
|
2818
|
+
whiteSpace: cs.whiteSpace,
|
|
2819
|
+
bbox: bbox(el),
|
|
2820
|
+
};
|
|
2821
|
+
});
|
|
2822
|
+
|
|
2823
|
+
const images = Array.from(section.querySelectorAll('img'))
|
|
2824
|
+
.filter(isVisible)
|
|
2825
|
+
.slice(0, 8)
|
|
2826
|
+
.map(img => ({
|
|
2827
|
+
src: img.currentSrc || img.src || '',
|
|
2828
|
+
alt: img.alt || '',
|
|
2829
|
+
natural: { width: img.naturalWidth || 0, height: img.naturalHeight || 0 },
|
|
2830
|
+
bbox: bbox(img),
|
|
2831
|
+
}));
|
|
2832
|
+
|
|
2833
|
+
const directChildren = Array.from(section.children || [])
|
|
2834
|
+
.filter(isVisible)
|
|
2835
|
+
.slice(0, 12)
|
|
2836
|
+
.map(el => ({
|
|
2837
|
+
label: labelFor(el),
|
|
2838
|
+
text: normalizeText(el.innerText || el.textContent, 90),
|
|
2839
|
+
bbox: bbox(el),
|
|
2840
|
+
display: getComputedStyle(el).display,
|
|
2841
|
+
}));
|
|
2842
|
+
|
|
2843
|
+
return {
|
|
2844
|
+
sectionNumber: index + 1,
|
|
2845
|
+
label: labelFor(section),
|
|
2846
|
+
id: section.id || null,
|
|
2847
|
+
classes: Array.from(section.classList || []),
|
|
2848
|
+
bbox: bbox(section),
|
|
2849
|
+
style: pickComputed(section),
|
|
2850
|
+
textPreview: normalizeText(section.innerText || section.textContent, 360),
|
|
2851
|
+
textLength: normalizeText(section.innerText || section.textContent, 100000).length,
|
|
2852
|
+
headings,
|
|
2853
|
+
ctas,
|
|
2854
|
+
images: {
|
|
2855
|
+
count: images.length,
|
|
2856
|
+
missingAlt: images.filter(i => !i.alt.trim()).length,
|
|
2857
|
+
samples: images,
|
|
2858
|
+
},
|
|
2859
|
+
directChildren,
|
|
2860
|
+
};
|
|
2861
|
+
});
|
|
2862
|
+
|
|
2863
|
+
return {
|
|
2864
|
+
title: document.title,
|
|
2865
|
+
url: location.href,
|
|
2866
|
+
pageDimensions: {
|
|
2867
|
+
width: document.documentElement.clientWidth,
|
|
2868
|
+
height: document.documentElement.scrollHeight,
|
|
2869
|
+
scrollWidth: document.documentElement.scrollWidth,
|
|
2870
|
+
},
|
|
2871
|
+
body: pickComputed(document.body),
|
|
2872
|
+
contentRoot: document.querySelector('#brx-content') ? '#brx-content' : (document.querySelector('main') ? 'main' : 'body'),
|
|
2873
|
+
sectionsTotal: sections.length,
|
|
2874
|
+
sections: sections.slice(0, maxSections),
|
|
2875
|
+
headingInventory: pageHeadings,
|
|
2876
|
+
ctaInventory: pageButtons,
|
|
2877
|
+
};
|
|
2878
|
+
}, { sectionSelector, maxSections });
|
|
2879
|
+
|
|
2880
|
+
const viewportImages = [];
|
|
2881
|
+
|
|
2882
|
+
if (includeFullPage) {
|
|
2883
|
+
const fullRef = `${viewport}:fullpage`;
|
|
2884
|
+
const buf = await page.screenshot({ type: 'jpeg', quality: 78, fullPage: true });
|
|
2885
|
+
imageContent.push({
|
|
2886
|
+
ref: fullRef,
|
|
2887
|
+
label: `${viewport} — page complète`,
|
|
2888
|
+
mimeType: "image/jpeg",
|
|
2889
|
+
data: buf.toString('base64'),
|
|
2890
|
+
});
|
|
2891
|
+
viewportImages.push({ ref: fullRef, kind: "fullpage" });
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
if (includeSectionCrops) {
|
|
2895
|
+
const pageDimensions = designContext.pageDimensions || {};
|
|
2896
|
+
const pageWidth = Math.max(pageDimensions.scrollWidth || 0, pageDimensions.width || 0, 1);
|
|
2897
|
+
const pageHeight = Math.max(pageDimensions.height || 0, 1);
|
|
2898
|
+
const margin = 12;
|
|
2899
|
+
|
|
2900
|
+
for (const section of designContext.sections) {
|
|
2901
|
+
const box = section.bbox;
|
|
2902
|
+
if (!box || box.w < 20 || box.h < 20) continue;
|
|
2903
|
+
const clip = {
|
|
2904
|
+
x: Math.max(0, box.x - margin),
|
|
2905
|
+
y: Math.max(0, box.y - margin),
|
|
2906
|
+
width: Math.min(pageWidth - Math.max(0, box.x - margin), box.w + margin * 2),
|
|
2907
|
+
height: Math.min(pageHeight - Math.max(0, box.y - margin), Math.min(box.h + margin * 2, maxCropHeight)),
|
|
2908
|
+
};
|
|
2909
|
+
if (clip.width < 20 || clip.height < 20) continue;
|
|
2910
|
+
const ref = `${viewport}:section-${section.sectionNumber}`;
|
|
2911
|
+
try {
|
|
2912
|
+
const buf = await page.screenshot({ type: 'jpeg', quality: 82, clip });
|
|
2913
|
+
imageContent.push({
|
|
2914
|
+
ref,
|
|
2915
|
+
label: `${viewport} — section ${section.sectionNumber} — ${section.label}`,
|
|
2916
|
+
mimeType: "image/jpeg",
|
|
2917
|
+
data: buf.toString('base64'),
|
|
2918
|
+
});
|
|
2919
|
+
section.cropImageRef = ref;
|
|
2920
|
+
section.cropNote = box.h > maxCropHeight ? `Crop tronqué à ${maxCropHeight}px de haut.` : "Crop complet.";
|
|
2921
|
+
viewportImages.push({ ref, kind: "section", sectionNumber: section.sectionNumber });
|
|
2922
|
+
} catch (cropErr) {
|
|
2923
|
+
section.cropError = cropErr.message;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
perViewport.push({
|
|
2929
|
+
viewport,
|
|
2930
|
+
success: true,
|
|
2931
|
+
pageDimensions: designContext.pageDimensions,
|
|
2932
|
+
body: designContext.body,
|
|
2933
|
+
contentRoot: designContext.contentRoot,
|
|
2934
|
+
sectionsTotal: designContext.sectionsTotal,
|
|
2935
|
+
sectionsCaptured: designContext.sections.length,
|
|
2936
|
+
headingInventory: designContext.headingInventory,
|
|
2937
|
+
ctaInventory: designContext.ctaInventory,
|
|
2938
|
+
sections: designContext.sections,
|
|
2939
|
+
images: viewportImages,
|
|
2940
|
+
});
|
|
2941
|
+
} finally {
|
|
2942
|
+
if (page && !page.isClosed()) {
|
|
2943
|
+
await page.close().catch(() => {});
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
const summary = {
|
|
2949
|
+
success: true,
|
|
2950
|
+
tool: "audit_design_page",
|
|
2951
|
+
pageId,
|
|
2952
|
+
pageUrl,
|
|
2953
|
+
pageTitle: pageMeta.title,
|
|
2954
|
+
brief,
|
|
2955
|
+
method: "Ce tool ne fait pas une liste de règles hardcodées. Il fournit screenshots + crops + contexte DOM pour une critique IA générale de webdesign.",
|
|
2956
|
+
reviewInstructionForAI: [
|
|
2957
|
+
"Inspecte les images avant de conclure. Ne te limite pas aux métriques DOM.",
|
|
2958
|
+
"Cherche les problèmes d'harmonie globale : hiérarchie, rythme vertical, densité, largeur de contenu, alignements, équilibre image/texte, cohérence entre sections, typographie, CTA, sensation premium, confiance, responsive.",
|
|
2959
|
+
"Signale aussi les zones qui semblent correctes techniquement mais faibles visuellement : trop template, vide mal placé, élément qui flotte, rupture de style, section trop lourde/légère, manque d'intention.",
|
|
2960
|
+
"Ne transforme pas chaque différence en bug. Priorise les points qu'un humain exigeant verrait en pleine page.",
|
|
2961
|
+
"Pour chaque finding : zone/section, viewport, sévérité, confiance, pourquoi ça nuit au design, correction proposée.",
|
|
2962
|
+
],
|
|
2963
|
+
suggestedOutputShape: {
|
|
2964
|
+
findings: [
|
|
2965
|
+
{
|
|
2966
|
+
severity: "critical|major|minor",
|
|
2967
|
+
confidence: "high|medium|low",
|
|
2968
|
+
viewport: "desktop|tablet|mobile_portrait",
|
|
2969
|
+
section: "section number or page area",
|
|
2970
|
+
issue: "description courte",
|
|
2971
|
+
whyItMatters: "impact design/UX",
|
|
2972
|
+
suggestedFix: "action concrète",
|
|
2973
|
+
},
|
|
2974
|
+
],
|
|
2975
|
+
globalRead: "lecture générale de la page",
|
|
2976
|
+
whatToValidateWithMathieu: "points subjectifs à faire trancher",
|
|
2977
|
+
},
|
|
2978
|
+
imageOrder: imageContent.map((img, index) => ({
|
|
2979
|
+
index: index + 1,
|
|
2980
|
+
ref: img.ref,
|
|
2981
|
+
label: img.label,
|
|
2982
|
+
})),
|
|
2983
|
+
viewports: perViewport,
|
|
2984
|
+
};
|
|
2985
|
+
|
|
2986
|
+
const responseContent = [
|
|
2987
|
+
{ type: "text", text: JSON.stringify(summary, null, 2) },
|
|
2988
|
+
];
|
|
2989
|
+
for (const img of imageContent) {
|
|
2990
|
+
responseContent.push({ type: "text", text: `Image ${imageContent.indexOf(img) + 1} — ${img.label} — ref: ${img.ref}` });
|
|
2991
|
+
responseContent.push({ type: "image", data: img.data, mimeType: img.mimeType });
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
return { content: responseContent };
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2534
2997
|
// ===== v3.6.0 — FEEDBACK SYSTEM =====
|
|
2535
2998
|
case "report_missing_feature":
|
|
2536
2999
|
result = await callWordPressAPI("/report-missing-feature", "POST", {
|
|
@@ -2731,7 +3194,7 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2731
3194
|
async function main() {
|
|
2732
3195
|
const transport = new StdioServerTransport();
|
|
2733
3196
|
await mcpServer.connect(transport);
|
|
2734
|
-
console.error("[READY] MCP Bricks Builder v3.0 démarré et connecté");
|
|
3197
|
+
console.error("[READY] MCP Bricks Builder v3.12.0 démarré et connecté");
|
|
2735
3198
|
}
|
|
2736
3199
|
|
|
2737
|
-
main().catch(console.error);
|
|
3200
|
+
main().catch(console.error);
|