@noleemits/vision-builder-control-mcp 4.86.0 → 4.96.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/index.js +180 -10
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -111,7 +111,7 @@ process.on('SIGINT', () => {
|
|
|
111
111
|
// CONFIG
|
|
112
112
|
// ================================================================
|
|
113
113
|
|
|
114
|
-
const VERSION = '4.
|
|
114
|
+
const VERSION = '4.96.0';
|
|
115
115
|
const MIN_PLUGIN_VERSION = '4.13.0'; // Minimum WP plugin version required by this MCP server
|
|
116
116
|
|
|
117
117
|
// ================================================================
|
|
@@ -2213,7 +2213,7 @@ function getToolDefinitions() {
|
|
|
2213
2213
|
},
|
|
2214
2214
|
{
|
|
2215
2215
|
name: 'set_container_layout',
|
|
2216
|
-
description: 'Set a container\'s layout (flex/grid) from a clean intent — prefer this over raw update_element when you want WYSIWYG-editable native Elementor layout instead of custom_css workarounds. Translates {mode, columns, gap, responsive} into the full Elementor schema (container_type, grid_columns_grid object, gap object shape, _tablet/_mobile variants, etc.). Modes: "grid" (uses Elementor native CSS Grid — best for card layouts), "flex-row" (horizontal flex), "flex-column" (vertical stack). Defaults: gap=24px, responsive=auto (tablet halves columns, mobile = 1 col). Pass responsive: false to skip responsive variants. Only operates on containers (rejected with invalid_target if you target a widget). The resulting layout shows up in Elementor\'s Layout panel as if a human set it, so non-technical editors can modify it through the UI. v4.44.0+. v4.86.0: containers
|
|
2216
|
+
description: 'Set a container\'s layout (flex/grid) from a clean intent — prefer this over raw update_element when you want WYSIWYG-editable native Elementor layout instead of custom_css workarounds. Translates {mode, columns, gap, responsive} into the full Elementor schema (container_type, grid_columns_grid object, gap object shape, _tablet/_mobile variants, etc.). Modes: "grid" (uses Elementor native CSS Grid — best for card layouts), "flex-row" (horizontal flex), "flex-column" (vertical stack). Defaults: gap=24px, responsive=auto (tablet halves columns, mobile = 1 col). Pass responsive: false to skip responsive variants. Only operates on containers (rejected with invalid_target if you target a widget). The resulting layout shows up in Elementor\'s Layout panel as if a human set it, so non-technical editors can modify it through the UI. v4.44.0+. WIDTH SEMANTICS (v4.86.0+): containers default to content_width:"full" — no boxed `.e-con-inner` wrapper, flex-row children lay out side-by-side, and `.your-class > .e-con` CSS hits direct children. Pass content_width:"boxed" for a centered max-width container; combine with boxed_width to set the cap (Elementor\'s default cap is 1140px when unset). Common gotcha: a boxed container with a child carrying _element_custom_width in px greater than the boxed cap will overflow at every viewport — pre-flight with validate_section, which now flags this (child_width_exceeds_parent, flex_row_fixed_widths_overflow). For atomic v4 e-flexbox, width is set via a different settings shape — use add_element preset:"cols-N" for ready-made full-width column rows.',
|
|
2217
2217
|
inputSchema: {
|
|
2218
2218
|
type: 'object',
|
|
2219
2219
|
properties: {
|
|
@@ -3663,7 +3663,7 @@ function getToolDefinitions() {
|
|
|
3663
3663
|
},
|
|
3664
3664
|
{
|
|
3665
3665
|
name: 'append_section',
|
|
3666
|
-
description: 'Append a section to an existing page or template. Pass full section JSON OR use the preset param for instant gap-safe column layouts. PRESET SHORTHAND: pass preset="cols-3" (or cols-2, cols-4, sidebar-left, sidebar-right, split, stack) and the scaffold JSON is generated for you — no manual flex-grow JSON needed. The section param becomes optional when using preset; any settings in section are merged into the preset root (use it to set background_color, css_classes, _title, etc.). engine param selects v3 (classic Container) or v4 (atomic e-flexbox); auto-detected if omitted.',
|
|
3666
|
+
description: 'Append a section to an existing page or template. Pass full section JSON OR use the preset param for instant gap-safe column layouts. PRESET SHORTHAND: pass preset="cols-3" (or cols-2, cols-4, sidebar-left, sidebar-right, split, stack) and the scaffold JSON is generated for you — no manual flex-grow JSON needed. The section param becomes optional when using preset; any settings in section are merged into the preset root (use it to set background_color, css_classes, _title, etc.). engine param selects v3 (classic Container) or v4 (atomic e-flexbox); auto-detected if omitted. WIDTH SEMANTICS: section root containers default to content_width="full" (no boxed `.e-con-inner` wrapper) — flex-row children lay out side-by-side; pass content_width="boxed" + numeric boxed_width for a centered max-width section. When a section has content_width="boxed", any child with _element_custom_width in px greater than boxed_width will overflow horizontally — run validate_section before submitting to catch this (rules child_width_exceeds_parent + flex_row_fixed_widths_overflow, v4.95.0+).',
|
|
3667
3667
|
inputSchema: {
|
|
3668
3668
|
type: 'object',
|
|
3669
3669
|
properties: {
|
|
@@ -3681,7 +3681,7 @@ function getToolDefinitions() {
|
|
|
3681
3681
|
},
|
|
3682
3682
|
{
|
|
3683
3683
|
name: 'validate_section',
|
|
3684
|
-
description: 'Lint a section JSON for known bug patterns BEFORE submitting it via append_section / build_page / import_template. Detects: (1) class_based_selectors_only — [data-id^="prefix"] selectors that match nested elements; (2) grid_target_e_con_inner — grid CSS on outer container instead of `.your-grid > .e-con-inner`; (3) button_styling_needs_scoped_css — button widgets without scoped CSS override; (4) numeric_dimensional_fields — clamp()/var() in padding/margin/font_size; (5) no_id_prefix_collision — child element ids that start with the parent\'s id. Returns warnings
|
|
3684
|
+
description: 'Lint a section JSON for known bug patterns BEFORE submitting it via append_section / build_page / import_template. Detects: (1) class_based_selectors_only — [data-id^="prefix"] selectors that match nested elements; (2) grid_target_e_con_inner — grid CSS on outer container instead of `.your-grid > .e-con-inner`; (3) button_styling_needs_scoped_css — button widgets without scoped CSS override; (4) numeric_dimensional_fields — clamp()/var() in padding/margin/font_size; (5) no_id_prefix_collision — child element ids that start with the parent\'s id; (6) v4.95.0+ child_width_exceeds_parent — a single child\'s px _element_custom_width is wider than the parent\'s boxed content_width (would overflow at every viewport); (7) v4.95.0+ flex_row_fixed_widths_overflow — on flex_direction=row + flex_wrap=nowrap, the sum of children\'s fixed px widths exceeds the parent\'s content_width (would overflow horizontally) — emitted as an ERROR. Returns warnings + errors arrays with rule name, message, and offending element/selector. Run this whenever you generate complex section JSON, especially with custom CSS or boxed containers carrying fixed-width children. v4.95.0 width rules are v3-only (atomic e-flexbox uses a different settings shape).',
|
|
3685
3685
|
inputSchema: {
|
|
3686
3686
|
type: 'object',
|
|
3687
3687
|
properties: {
|
|
@@ -3960,12 +3960,14 @@ function getToolDefinitions() {
|
|
|
3960
3960
|
// ── Class Registry ──
|
|
3961
3961
|
{
|
|
3962
3962
|
name: 'list_classes',
|
|
3963
|
-
description: 'List
|
|
3963
|
+
description: 'List registered CSS classes in the NVBC class registry. Returns classes with name, description, category, applies_to, scope, and status. Use to discover available classes before applying them via update_element. By default DRAFT site classes are hidden — pass include_drafts:true to see them. Pass page_id to filter site classes to those that are global OR scoped to that page (built-ins always pass). v4.92.0+ adds scope/status filtering; pre-v4.92 site classes are treated as scope=global + status=published.',
|
|
3964
3964
|
inputSchema: {
|
|
3965
3965
|
type: 'object',
|
|
3966
3966
|
properties: {
|
|
3967
|
-
category:
|
|
3968
|
-
applies_to:
|
|
3967
|
+
category: { type: 'string', enum: ['hero', 'cards', 'layout', 'grid', 'spacing', 'typography', 'decorative', 'utility'], description: 'Filter to a specific category.' },
|
|
3968
|
+
applies_to: { type: 'string', enum: ['container', 'widget', 'heading', 'button', 'image', 'any'], description: 'Filter to classes that apply to this element type.' },
|
|
3969
|
+
page_id: { type: 'number', description: 'WordPress page ID. When set, site classes with scope=page are filtered to those including this page. Omit to see all (global + every page-scoped class).' },
|
|
3970
|
+
include_drafts: { type: 'boolean', description: 'Include site classes with status=draft. Default false.' }
|
|
3969
3971
|
}
|
|
3970
3972
|
}
|
|
3971
3973
|
},
|
|
@@ -4009,7 +4011,7 @@ function getToolDefinitions() {
|
|
|
4009
4011
|
},
|
|
4010
4012
|
{
|
|
4011
4013
|
name: 'upsert_class',
|
|
4012
|
-
description: 'Define or update a site CSS class — stores its metadata AND CSS body in the NVBC class registry. NVBC renders the CSS page-scoped (only on pages that use the class); it is NOT written to the Elementor Kit. The class appears in list_classes with source=[site] and in the editor Browse picker. Optional apply_to: attach the class to element IDs on specific pages in the same call. Call again with the same name to update (css is replaced). v4.76.0+.',
|
|
4014
|
+
description: 'Define or update a site CSS class — stores its metadata AND CSS body in the NVBC class registry. NVBC renders the CSS page-scoped (only on pages that use the class); it is NOT written to the Elementor Kit. The class appears in list_classes with source=[site] and in the editor Browse picker. Optional apply_to: attach the class to element IDs on specific pages in the same call. Call again with the same name to update (css is replaced). v4.76.0+. v4.92.0 adds scope (global vs page) and status (published vs draft): a page-scoped class only appears in the editor picker on its target pages; a draft class is hidden from the picker entirely until you flip status to published.',
|
|
4013
4015
|
inputSchema: {
|
|
4014
4016
|
type: 'object',
|
|
4015
4017
|
properties: {
|
|
@@ -4018,9 +4020,12 @@ function getToolDefinitions() {
|
|
|
4018
4020
|
description: { type: 'string', description: 'Human-readable description shown in list_classes output.' },
|
|
4019
4021
|
category: { type: 'string', enum: ['hero', 'cards', 'layout', 'grid', 'spacing', 'typography', 'decorative', 'utility'], description: 'Category for list_classes grouping. Default: "utility".' },
|
|
4020
4022
|
applies_to: { type: 'array', items: { type: 'string' }, description: 'Element types this class applies to. Default: ["any"].' },
|
|
4023
|
+
scope: { type: 'string', enum: ['global', 'page'], description: 'v4.92.0+. "global" (default) = appears in the editor picker on every page. "page" = only appears in the picker on the pages listed in scope_page_ids (or derived from apply_to). Render is unaffected: if the class is on an element, its CSS still emits — scope only filters editor-picker visibility.' },
|
|
4024
|
+
scope_page_ids: { type: 'array', items: { type: 'number' }, description: 'v4.92.0+. Required when scope=page (or supply apply_to with page_id entries and the server will derive these). WordPress page IDs the class belongs to.' },
|
|
4025
|
+
status: { type: 'string', enum: ['published', 'draft'], description: 'v4.92.0+. "published" (default) = appears in list_classes + editor picker. "draft" = hidden from both until promoted. Useful while iterating on a class.' },
|
|
4021
4026
|
apply_to: {
|
|
4022
4027
|
type: 'array',
|
|
4023
|
-
description: 'Optional: attach this class to these elements in the same call.',
|
|
4028
|
+
description: 'Optional: attach this class to these elements in the same call. When scope=page and scope_page_ids is omitted, the page_ids here are used as scope_page_ids.',
|
|
4024
4029
|
items: {
|
|
4025
4030
|
type: 'object',
|
|
4026
4031
|
properties: {
|
|
@@ -4088,6 +4093,55 @@ function getToolDefinitions() {
|
|
|
4088
4093
|
required: ['id']
|
|
4089
4094
|
}
|
|
4090
4095
|
},
|
|
4096
|
+
{
|
|
4097
|
+
name: 'install_pill_presets',
|
|
4098
|
+
description: 'Install the shipped eyebrow/badge "pill" presets (pill-on-light, pill-on-dark, pill-outline) into the active site. On Elementor 4 each pill becomes a REAL Elementor Global Class in the kit — editable in the native Class Manager, edits propagate to every instance, and it survives plugin removal. On Elementor 3 each becomes an NVBC registry site-class whose CSS renders page-scoped for pages that use it. Color props bind to an existing kit variable when one matches the pill\'s semantic name (accent / accent-ink / accent-soft / surface-scrim) or your var_map; otherwise a literal default is baked in — no variables are ever created. pill-outline uses currentColor and needs zero brand setup. Geometry is always baked. Defaults to a DRY RUN — pass dry_run:false to write. Returns label→g-id (v4) so you can place a pill via add_element with settings.classes.value:["g-id"]. v4.90.0+.',
|
|
4099
|
+
inputSchema: {
|
|
4100
|
+
type: 'object',
|
|
4101
|
+
properties: {
|
|
4102
|
+
engine: { type: 'string', enum: ['auto', 'v4', 'v3', 'both'], description: 'Which store to install into. auto (default) = v4 if Elementor 4 is present, else v3. both = install to both stores.' },
|
|
4103
|
+
pills: { type: 'array', items: { type: 'string' }, description: 'Pill names to install. Omit for all shipped pills (pill-on-light, pill-on-dark, pill-outline).' },
|
|
4104
|
+
var_map: { type: 'object', description: 'Explicit v4 variable bindings, e.g. {"accent":"e-gv-1a2b3c4"}. Overrides the by-label match. Keys are semantic names: accent, accent-ink, accent-soft, surface-scrim.' },
|
|
4105
|
+
dry_run: { type: 'boolean', description: 'Report only, write nothing. Default TRUE — pass false to apply.' }
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
},
|
|
4109
|
+
{
|
|
4110
|
+
name: 'list_icons',
|
|
4111
|
+
description: 'List bundled Phosphor icons. NVBC ships a curated ~130-icon set of Phosphor regular weight as static SVG assets. Output groups icons by category (arrows, status, navigation, business, medical, legal, communication, files, media, people, misc) and includes a flat sorted name array. Filter with q (substring search, case-insensitive) or category. Pair with sideload_icon to convert a name into a WP attachment id for e-svg / icon-box. Icons sideloaded once survive plugin deactivation (they become normal media-library attachments). v4.96.0+.',
|
|
4112
|
+
inputSchema: {
|
|
4113
|
+
type: 'object',
|
|
4114
|
+
properties: {
|
|
4115
|
+
library: { type: 'string', enum: ['phosphor'], description: 'Icon library. Only "phosphor" is bundled. Default phosphor.' },
|
|
4116
|
+
weight: { type: 'string', enum: ['regular'], description: 'Icon weight. Only "regular" is bundled. Default regular.' },
|
|
4117
|
+
q: { type: 'string', description: 'Substring search (case-insensitive) — matches icon names containing the query.' },
|
|
4118
|
+
category: { type: 'string', enum: ['arrows', 'status', 'navigation', 'business', 'medical', 'legal', 'communication', 'files', 'media', 'people', 'misc'], description: 'Filter to a single category.' }
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
},
|
|
4122
|
+
{
|
|
4123
|
+
name: 'sideload_icon',
|
|
4124
|
+
description: 'Sideload a bundled Phosphor SVG into the WP media library and return its attachment id. On first reference the SVG is uploaded; subsequent calls hit the plugin-level cache and return instantly. Use the returned attachment_id with e-svg widgets, icon-box selected_icon (custom svg path), or anywhere an attachment id is accepted. Sideloaded icons become normal WP attachments — they persist if NVBC is deactivated. Pass shorthand:"phosphor:caduceus" for brevity, or library/weight/name explicitly. v4.96.0+.',
|
|
4125
|
+
inputSchema: {
|
|
4126
|
+
type: 'object',
|
|
4127
|
+
properties: {
|
|
4128
|
+
shorthand: { type: 'string', description: 'Convenience: "name", "library:name", or "library:weight:name". Defaults library=phosphor, weight=regular. e.g. "caduceus", "phosphor:check", "phosphor:regular:gavel".' },
|
|
4129
|
+
library: { type: 'string', enum: ['phosphor'], description: 'Library when not using shorthand. Default phosphor.' },
|
|
4130
|
+
weight: { type: 'string', enum: ['regular'], description: 'Weight when not using shorthand. Default regular.' },
|
|
4131
|
+
name: { type: 'string', description: 'Icon name (no extension) when not using shorthand. e.g. "arrow-right".' }
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
},
|
|
4135
|
+
{
|
|
4136
|
+
name: 'install_font_size_presets',
|
|
4137
|
+
description: 'Register the 7 built-in fluid font-size classes (nvbc-font-xxsm…xxl) as REAL Elementor 4 Global Classes so they appear in the Atomic Editor\'s class picker and apply on selection. Each class\'s font-size is var(--nvbc-font-size-X) (the design-token scale), so editing the token re-sizes every instance — no re-install. Idempotent (upsert by label). On Elementor 3 these classes already ship as built-in registry classes (page-scoped) and need no install. Defaults to a DRY RUN — pass dry_run:false to write. Returns label→g-id so you can place a size via add_element with settings.classes.value:["g-id"]. v4.91.0+.',
|
|
4138
|
+
inputSchema: {
|
|
4139
|
+
type: 'object',
|
|
4140
|
+
properties: {
|
|
4141
|
+
dry_run: { type: 'boolean', description: 'Report only, write nothing. Default TRUE — pass false to apply.' }
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
},
|
|
4091
4145
|
];
|
|
4092
4146
|
}
|
|
4093
4147
|
|
|
@@ -7624,10 +7678,19 @@ async function handleToolCall(name, args) {
|
|
|
7624
7678
|
const params = new URLSearchParams();
|
|
7625
7679
|
if (args.category) params.set('category', args.category);
|
|
7626
7680
|
if (args.applies_to) params.set('applies_to', args.applies_to);
|
|
7681
|
+
if (args.page_id) params.set('page_id', String(args.page_id));
|
|
7682
|
+
if (args.include_drafts) params.set('include_status', 'published,draft');
|
|
7627
7683
|
|
|
7628
7684
|
const r = await apiCall(`/class-registry?${params}`);
|
|
7629
7685
|
|
|
7630
7686
|
let msg = `=== CSS CLASS REGISTRY (${r.total} classes) ===\n`;
|
|
7687
|
+
if (r.filters) {
|
|
7688
|
+
const f = r.filters;
|
|
7689
|
+
const bits = [];
|
|
7690
|
+
if (f.page_id) bits.push(`page_id=${f.page_id}`);
|
|
7691
|
+
if (Array.isArray(f.include_status)) bits.push(`status=${f.include_status.join('+')}`);
|
|
7692
|
+
if (bits.length) msg += `Filters: ${bits.join(' · ')}\n`;
|
|
7693
|
+
}
|
|
7631
7694
|
msg += `Categories: ${r.categories.join(', ')}\n\n`;
|
|
7632
7695
|
|
|
7633
7696
|
const byCategory = {};
|
|
@@ -7641,7 +7704,17 @@ async function handleToolCall(name, args) {
|
|
|
7641
7704
|
for (const c of items) {
|
|
7642
7705
|
const tags = c.applies_to.join(', ');
|
|
7643
7706
|
const src = c.source === 'plugin' ? '[plugin]' : '[site]';
|
|
7644
|
-
|
|
7707
|
+
// v4.92.0 — surface scope/status when set on site classes.
|
|
7708
|
+
const flags = [];
|
|
7709
|
+
if (c.source !== 'plugin') {
|
|
7710
|
+
if (c.scope === 'page') {
|
|
7711
|
+
const ids = Array.isArray(c.scope_page_ids) ? c.scope_page_ids.join(',') : '';
|
|
7712
|
+
flags.push(`scope=page${ids ? `[${ids}]` : ''}`);
|
|
7713
|
+
}
|
|
7714
|
+
if (c.status === 'draft') flags.push('DRAFT');
|
|
7715
|
+
}
|
|
7716
|
+
const flagStr = flags.length ? ` (${flags.join(' · ')})` : '';
|
|
7717
|
+
msg += ` .${c.name} ${src}${flagStr}\n`;
|
|
7645
7718
|
msg += ` ${c.description}\n`;
|
|
7646
7719
|
msg += ` Applies to: ${tags}\n`;
|
|
7647
7720
|
if (c.source === 'site' && c.css) {
|
|
@@ -7664,11 +7737,28 @@ async function handleToolCall(name, args) {
|
|
|
7664
7737
|
applies_to: args.applies_to || ['any'],
|
|
7665
7738
|
};
|
|
7666
7739
|
if (args.css) regBody.css = args.css;
|
|
7740
|
+
// v4.92.0 — scope + status + scope_page_ids.
|
|
7741
|
+
if (args.scope) regBody.scope = args.scope;
|
|
7742
|
+
if (args.status) regBody.status = args.status;
|
|
7743
|
+
if (Array.isArray(args.scope_page_ids) && args.scope_page_ids.length) {
|
|
7744
|
+
regBody.scope_page_ids = args.scope_page_ids;
|
|
7745
|
+
}
|
|
7746
|
+
// When scope=page and no explicit ids, forward apply_to so the server derives them.
|
|
7747
|
+
if (args.scope === 'page' && !regBody.scope_page_ids && Array.isArray(args.apply_to)) {
|
|
7748
|
+
regBody.apply_to = args.apply_to;
|
|
7749
|
+
}
|
|
7667
7750
|
|
|
7668
7751
|
const r = await apiCall('/class-registry/custom', 'POST', regBody);
|
|
7669
7752
|
if (r.error || r.code) return ok(`Failed to save class definition: ${r.message || r.error}`);
|
|
7670
7753
|
|
|
7671
7754
|
let msg = `Class .${args.name} saved to registry.\n`;
|
|
7755
|
+
if (r.class?.scope === 'page') {
|
|
7756
|
+
const ids = (r.class.scope_page_ids || []).join(', ');
|
|
7757
|
+
msg += `Scope: page-only (${ids}). The class is hidden from the editor picker on other pages.\n`;
|
|
7758
|
+
}
|
|
7759
|
+
if (r.class?.status === 'draft') {
|
|
7760
|
+
msg += `Status: DRAFT. The class is hidden from list_classes + the editor picker until you flip it to published.\n`;
|
|
7761
|
+
}
|
|
7672
7762
|
if (args.css) {
|
|
7673
7763
|
msg += `CSS stored on the class — renders page-scoped (only on pages that use .${args.name}).\n`;
|
|
7674
7764
|
}
|
|
@@ -7756,6 +7846,86 @@ async function handleToolCall(name, args) {
|
|
|
7756
7846
|
return ok(msg);
|
|
7757
7847
|
}
|
|
7758
7848
|
|
|
7849
|
+
case 'list_icons': {
|
|
7850
|
+
const params = new URLSearchParams();
|
|
7851
|
+
if (args.library) params.set('library', args.library);
|
|
7852
|
+
if (args.weight) params.set('weight', args.weight);
|
|
7853
|
+
if (args.q) params.set('q', args.q);
|
|
7854
|
+
if (args.category) params.set('category', args.category);
|
|
7855
|
+
const r = await apiCall(`/icons?${params}`);
|
|
7856
|
+
if (r.error || r.code) return ok(`Failed: ${r.message || r.error}`);
|
|
7857
|
+
|
|
7858
|
+
let msg = `=== ICONS (${r.library} ${r.weight}, ${r.total} match${r.total === 1 ? '' : 'es'}) ===\n`;
|
|
7859
|
+
for (const [cat, names] of Object.entries(r.categories || {})) {
|
|
7860
|
+
msg += `\n── ${cat.toUpperCase()} (${names.length}) ──\n `;
|
|
7861
|
+
msg += names.join(', ') + '\n';
|
|
7862
|
+
}
|
|
7863
|
+
msg += `\nUse: sideload_icon({shorthand: "${r.library}:NAME"}) → attachment_id for e-svg / icon-box.\n`;
|
|
7864
|
+
return ok(msg);
|
|
7865
|
+
}
|
|
7866
|
+
|
|
7867
|
+
case 'sideload_icon': {
|
|
7868
|
+
const body = {};
|
|
7869
|
+
if (args.shorthand) body.shorthand = args.shorthand;
|
|
7870
|
+
if (args.library) body.library = args.library;
|
|
7871
|
+
if (args.weight) body.weight = args.weight;
|
|
7872
|
+
if (args.name) body.name = args.name;
|
|
7873
|
+
const r = await apiCall('/icons/sideload', 'POST', body);
|
|
7874
|
+
if (r.error || r.code) return ok(`Sideload failed: ${r.message || r.error || r.code}`);
|
|
7875
|
+
const tag = r.cached ? '(cache hit)' : '(uploaded)';
|
|
7876
|
+
let msg = `${r.library}:${r.weight}:${r.name} → attachment_id ${r.attachment_id} ${tag}\n`;
|
|
7877
|
+
msg += `URL: ${r.url}\n`;
|
|
7878
|
+
msg += `Use this attachment_id in:\n`;
|
|
7879
|
+
msg += ` • e-svg widget: settings.image.value.src.value.id\n`;
|
|
7880
|
+
msg += ` • icon widgets: selected_icon.library:"svg", selected_icon.value.id\n`;
|
|
7881
|
+
return ok(msg);
|
|
7882
|
+
}
|
|
7883
|
+
|
|
7884
|
+
case 'install_pill_presets': {
|
|
7885
|
+
const body = {};
|
|
7886
|
+
if (args.engine) body.engine = args.engine;
|
|
7887
|
+
if (Array.isArray(args.pills) && args.pills.length) body.pills = args.pills;
|
|
7888
|
+
if (args.var_map && typeof args.var_map === 'object') body.var_map = args.var_map;
|
|
7889
|
+
body.dry_run = (args.dry_run === false) ? false : true; // default dry run
|
|
7890
|
+
const r = await apiCall('/install-pill-presets', 'POST', body);
|
|
7891
|
+
if (r.error || r.code) return ok(`Install failed: ${r.message || r.error || r.code}`);
|
|
7892
|
+
|
|
7893
|
+
let msg = `${r.dry_run ? 'DRY RUN — nothing written.' : 'Installed.'} engine=${r.engine} Elementor4=${r.elementor4 ? 'yes' : 'no'}\n`;
|
|
7894
|
+
msg += `Pills: ${(r.pills || []).join(', ')}\n`;
|
|
7895
|
+
if (r.v4) {
|
|
7896
|
+
msg += `\n[v4 Global Classes]\n`;
|
|
7897
|
+
if (r.v4.note) msg += ` ${r.v4.note}\n`;
|
|
7898
|
+
for (const c of (r.v4.changes || [])) msg += ` ${c}\n`;
|
|
7899
|
+
if (r.v4.class_ids && Object.keys(r.v4.class_ids).length) {
|
|
7900
|
+
const ids = Object.entries(r.v4.class_ids).map(([l, id]) => `${l} → ${id || '(new)'}`);
|
|
7901
|
+
msg += ` ids: ${ids.join(', ')}\n`;
|
|
7902
|
+
}
|
|
7903
|
+
}
|
|
7904
|
+
if (r.v3) {
|
|
7905
|
+
msg += `\n[v3 Registry classes]\n`;
|
|
7906
|
+
for (const c of (r.v3.changes || [])) msg += ` ${c}\n`;
|
|
7907
|
+
if (r.v3.note) msg += ` ${r.v3.note}\n`;
|
|
7908
|
+
}
|
|
7909
|
+
if (r.dry_run) msg += `\nRun again with dry_run:false to apply.\n`;
|
|
7910
|
+
return ok(msg);
|
|
7911
|
+
}
|
|
7912
|
+
|
|
7913
|
+
case 'install_font_size_presets': {
|
|
7914
|
+
const body = { dry_run: (args.dry_run === false) ? false : true };
|
|
7915
|
+
const r = await apiCall('/install-font-size-presets', 'POST', body);
|
|
7916
|
+
if (r.error || r.code) return ok(`Install failed: ${r.message || r.error || r.code}`);
|
|
7917
|
+
let msg = `${r.dry_run ? 'DRY RUN — nothing written.' : 'Installed.'} Elementor4=${r.elementor4 ? 'yes' : 'no'}\n`;
|
|
7918
|
+
msg += `Sizes: ${(r.sizes || []).join(', ')}\n`;
|
|
7919
|
+
if (r.note) msg += `${r.note}\n`;
|
|
7920
|
+
for (const c of (r.changes || [])) msg += ` ${c}\n`;
|
|
7921
|
+
if (r.class_ids && Object.keys(r.class_ids).length) {
|
|
7922
|
+
const ids = Object.entries(r.class_ids).map(([l, id]) => `${l} → ${id || '(new)'}`);
|
|
7923
|
+
msg += ` ids: ${ids.join(', ')}\n`;
|
|
7924
|
+
}
|
|
7925
|
+
if (r.dry_run) msg += `\nRun again with dry_run:false to apply.\n`;
|
|
7926
|
+
return ok(msg);
|
|
7927
|
+
}
|
|
7928
|
+
|
|
7759
7929
|
case 'repair_page_classes': {
|
|
7760
7930
|
const r = await apiCall(`/pages/${args.page_id}/repair-element-classes`, 'POST', {
|
|
7761
7931
|
dry_run: args.dry_run ?? false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noleemits/vision-builder-control-mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.96.0",
|
|
4
4
|
"description": "Vision Builder Control MCP server - design token-driven page builder tools for WordPress/Elementor websites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|