@noleemits/vision-builder-control-mcp 4.43.1 → 4.45.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 +308 -11
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Noleemits Vision Builder Control MCP Server
|
|
4
4
|
*
|
|
5
5
|
* Provides 69 tools for building and managing WordPress/Elementor sites.
|
|
6
|
+
* v4.45.0: patch_kit_global_css — surgical CSS operations (remove_rule, remove_selector_from_rules, find_replace) without full-payload round-trip; solves 30k-token MCP param-size barrier for large kits.
|
|
6
7
|
* v4.42.0: audit_text + replace_text — unified search/replace across Elementor + post columns with literal/regex/fuzzy modes. Fuzzy mode tolerates NBSP, curly quotes/dashes, ideographic punctuation — fixes the "copy-pasted needle won't match" bug.
|
|
7
8
|
* v4.35.0: PixelVault image integration — pixelvault_status, find_images, generate_image, get_batch_status, insert_image tools; proxy REST endpoints at /nvbc/v1/pixelvault/*.
|
|
8
9
|
* v4.34.0: Design tokens auto-inject as CSS custom properties (--nvbc-color-*, --nvbc-fs-*, --nvbc-space-*) site-wide via wp_head; kit global CSS tools (get_kit_global_css, set_kit_global_css); get_kit_settings formatter handles gap-style objects.
|
|
@@ -108,7 +109,7 @@ process.on('SIGINT', () => {
|
|
|
108
109
|
// CONFIG
|
|
109
110
|
// ================================================================
|
|
110
111
|
|
|
111
|
-
const VERSION = '4.
|
|
112
|
+
const VERSION = '4.45.0';
|
|
112
113
|
const MIN_PLUGIN_VERSION = '4.13.0'; // Minimum WP plugin version required by this MCP server
|
|
113
114
|
|
|
114
115
|
// ================================================================
|
|
@@ -155,6 +156,25 @@ function deepStripCDATA(obj) {
|
|
|
155
156
|
return obj;
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Render custom_css anti-pattern warnings (from the v4.44.1 analyzer) as a
|
|
161
|
+
* trailing block on tool output. Returns an empty string when there are none.
|
|
162
|
+
* Each warning includes selector, rule, suggestion, and (when surfaced from
|
|
163
|
+
* update_element_recursive) the element_id the CSS was written to.
|
|
164
|
+
*/
|
|
165
|
+
function formatCssWarnings(warnings) {
|
|
166
|
+
if (!Array.isArray(warnings) || warnings.length === 0) return '';
|
|
167
|
+
let out = `\n\nWARNING — ${warnings.length} custom_css pattern(s) have a native Elementor equivalent:\n`;
|
|
168
|
+
warnings.forEach((w, i) => {
|
|
169
|
+
const where = w.element_id ? ` [element ${w.element_id}]` : '';
|
|
170
|
+
out += ` ${i + 1}. ${w.rule}${where} on "${w.selector || '?'}"\n`;
|
|
171
|
+
out += ` Native: ${w.native || '(see suggestion)'}\n`;
|
|
172
|
+
if (w.suggestion) out += ` Tip: ${w.suggestion}\n`;
|
|
173
|
+
});
|
|
174
|
+
out += ` The write was applied; consider switching approach so the layout stays editable in the WYSIWYG.`;
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
|
|
158
178
|
const CONFIG = {
|
|
159
179
|
wordpressUrl: process.env.WP_URL || 'http://localhost',
|
|
160
180
|
username: process.env.WP_USER || '',
|
|
@@ -2150,9 +2170,41 @@ function getToolDefinitions() {
|
|
|
2150
2170
|
required: ['page_id', 'element_id', 'target_parent_id']
|
|
2151
2171
|
}
|
|
2152
2172
|
},
|
|
2173
|
+
{
|
|
2174
|
+
name: 'set_container_layout',
|
|
2175
|
+
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+.',
|
|
2176
|
+
inputSchema: {
|
|
2177
|
+
type: 'object',
|
|
2178
|
+
properties: {
|
|
2179
|
+
page_id: { type: 'number', description: 'WordPress page ID' },
|
|
2180
|
+
container_id: { type: 'string', description: 'Elementor ID of the container to configure' },
|
|
2181
|
+
mode: { type: 'string', enum: ['grid', 'flex-row', 'flex-column'], description: 'Layout mode' },
|
|
2182
|
+
columns: { type: 'number', description: 'Grid mode: number of columns (default 3). Ignored for flex modes.' },
|
|
2183
|
+
rows: { type: 'number', description: 'Grid mode: number of explicit rows (default 1).' },
|
|
2184
|
+
gap: { description: 'Gap in px (number) or object {row, column, unit?}. Default 24.' },
|
|
2185
|
+
auto_flow: { type: 'string', enum: ['row', 'column', 'dense'], description: 'Grid auto-flow direction.' },
|
|
2186
|
+
align_items: {
|
|
2187
|
+
type: 'string',
|
|
2188
|
+
description: 'Cross-axis alignment. Grid: start|center|end|stretch. Flex: flex-start|center|flex-end|stretch|baseline.',
|
|
2189
|
+
},
|
|
2190
|
+
justify_items: { type: 'string', enum: ['start', 'center', 'end', 'stretch'], description: 'Grid-only: per-item main-axis alignment.' },
|
|
2191
|
+
justify_content: {
|
|
2192
|
+
type: 'string',
|
|
2193
|
+
enum: ['flex-start', 'center', 'flex-end', 'space-between', 'space-around', 'space-evenly'],
|
|
2194
|
+
description: 'Flex-only: main-axis distribution.',
|
|
2195
|
+
},
|
|
2196
|
+
wrap: { type: 'string', enum: ['wrap', 'nowrap'], description: 'Flex-only: wrap behavior.' },
|
|
2197
|
+
responsive: {
|
|
2198
|
+
description: 'Responsive variants. Default = true (auto: tablet halved, mobile 1 col). Pass false to skip variants. Or pass { tablet: {columns, gap}, mobile: {columns, gap} } for explicit values.',
|
|
2199
|
+
},
|
|
2200
|
+
force: { type: 'boolean', description: 'Override edit locks (default: false)' }
|
|
2201
|
+
},
|
|
2202
|
+
required: ['page_id', 'container_id', 'mode']
|
|
2203
|
+
}
|
|
2204
|
+
},
|
|
2153
2205
|
{
|
|
2154
2206
|
name: 'update_element',
|
|
2155
|
-
description: 'Update any element\'s settings by its Elementor ID or path. Patch widget properties like hover_color, background_color, __globals__ overrides, text, link URLs, etc. Supports dot-notation for nested keys (e.g. "__globals__.hover_color"). Supports element_path as alternative to element_id (e.g. "sectionId/1/0" = section → child 1 → child 0). Use list_elements or export_page first to find the element ID and current settings. IMPORTANT: Prefer settings_json (JSON string) over settings (object) for reliable MCP transport. AUTO-INJECTED KEYS: background_background:"classic" auto-set when you set background_color; typography_typography:"custom" auto-set when you set font properties; flex_grow expands to all 3 required keys; grid column strings auto-wrapped in object format. ICON WIDGET: For view="default" use "icon_size" for dimensions. For view="stacked" or "framed" (circle/square background) use "size" instead — "icon_size" is silently ignored in stacked/framed mode. LOOP-GRID WIDGET: query settings use the prefix "post_query_" (post_query_post_type, post_query_orderby, post_query_order, post_query_posts_per_page) — NOT "posts_post_type". Setting "posts_post_type" is silently accepted but ignored. RACE CONDITION: when patching multiple elements on the SAME page in parallel, use batch_update_elements instead — parallel update_element calls can clobber each other. ATOMIC SCHEMA VALIDATION (v4.42.5+): writes that violate the Elementor V4 atomic schema are rejected up front (HTTP 400 invalid_atomic_setting) instead of silently corrupting the page. Currently enforced: e-heading.tag ∈ {h1..h6}, e-paragraph.tag ∈ {p, span}. If you need a non-heading visual element styled like an eyebrow, use e-paragraph (tag=p or span) — not e-heading with tag=div.',
|
|
2207
|
+
description: 'Update any element\'s settings by its Elementor ID or path. Patch widget properties like hover_color, background_color, __globals__ overrides, text, link URLs, etc. Supports dot-notation for nested keys (e.g. "__globals__.hover_color"). Supports element_path as alternative to element_id (e.g. "sectionId/1/0" = section → child 1 → child 0). Use list_elements or export_page first to find the element ID and current settings. IMPORTANT: Prefer settings_json (JSON string) over settings (object) for reliable MCP transport. AUTO-INJECTED KEYS: background_background:"classic" auto-set when you set background_color; typography_typography:"custom" auto-set when you set font properties; flex_grow expands to all 3 required keys; grid column strings auto-wrapped in object format. ICON WIDGET: For view="default" use "icon_size" for dimensions. For view="stacked" or "framed" (circle/square background) use "size" instead — "icon_size" is silently ignored in stacked/framed mode. LOOP-GRID WIDGET: query settings use the prefix "post_query_" (post_query_post_type, post_query_orderby, post_query_order, post_query_posts_per_page) — NOT "posts_post_type". Setting "posts_post_type" is silently accepted but ignored. RACE CONDITION: when patching multiple elements on the SAME page in parallel, use batch_update_elements instead — parallel update_element calls can clobber each other. ATOMIC SCHEMA VALIDATION (v4.42.5+): writes that violate the Elementor V4 atomic schema are rejected up front (HTTP 400 invalid_atomic_setting) instead of silently corrupting the page. Currently enforced: e-heading.tag ∈ {h1..h6}, e-paragraph.tag ∈ {p, span}. If you need a non-heading visual element styled like an eyebrow, use e-paragraph (tag=p or span) — not e-heading with tag=div. NATIVE GRID/FLEX TIP: for container layout (grid columns, flex direction, gap), prefer set_container_layout — it produces WYSIWYG-editable native settings instead of forcing you to assemble the raw Elementor schema.',
|
|
2156
2208
|
inputSchema: {
|
|
2157
2209
|
type: 'object',
|
|
2158
2210
|
properties: {
|
|
@@ -3191,8 +3243,29 @@ function getToolDefinitions() {
|
|
|
3191
3243
|
},
|
|
3192
3244
|
{
|
|
3193
3245
|
name: 'get_kit_global_css',
|
|
3194
|
-
description: 'Read the active kit\'s custom_css (Elementor "Custom CSS" field). This CSS is output site-wide via Elementor\'s own stylesheet pipeline. Use to inspect site-wide CSS variables, base styles, etc.',
|
|
3195
|
-
inputSchema: {
|
|
3246
|
+
description: 'Read the active kit\'s custom_css (Elementor "Custom CSS" field). This CSS is output site-wide via Elementor\'s own stylesheet pipeline. Use to inspect site-wide CSS variables, base styles, etc. v4.44.4+: pass start_line + end_line to read a slice (kit CSS often exceeds 60k chars and overflows context — slice + search_kit_global_css solve that).',
|
|
3247
|
+
inputSchema: {
|
|
3248
|
+
type: 'object',
|
|
3249
|
+
properties: {
|
|
3250
|
+
start_line: { type: 'number', description: 'First line (1-indexed) of the slice to read. Omit with end_line to read the whole file.' },
|
|
3251
|
+
end_line: { type: 'number', description: 'Last line (inclusive) of the slice. Pair with start_line.' }
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
},
|
|
3255
|
+
{
|
|
3256
|
+
name: 'search_kit_global_css',
|
|
3257
|
+
description: 'Pattern-match against the kit\'s custom_css. Returns each hit with line number + surrounding context. Cheap "is .walcc-grid--3 already defined?" lookup that avoids shipping the full 60k+ kit CSS through the context window. Supports regex via regex=true. v4.44.4+.',
|
|
3258
|
+
inputSchema: {
|
|
3259
|
+
type: 'object',
|
|
3260
|
+
properties: {
|
|
3261
|
+
pattern: { type: 'string', description: 'String to find (literal by default; pass regex=true to treat as regex).' },
|
|
3262
|
+
regex: { type: 'boolean', description: 'Treat pattern as a regex. Default: false.' },
|
|
3263
|
+
case_sensitive: { type: 'boolean', description: 'Default: false (case-insensitive).' },
|
|
3264
|
+
context: { type: 'number', description: 'Lines of context above + below each hit. Default: 1.' },
|
|
3265
|
+
max_matches: { type: 'number', description: 'Cap on returned hits. Default: 50.' }
|
|
3266
|
+
},
|
|
3267
|
+
required: ['pattern']
|
|
3268
|
+
}
|
|
3196
3269
|
},
|
|
3197
3270
|
{
|
|
3198
3271
|
name: 'set_kit_global_css',
|
|
@@ -3207,6 +3280,57 @@ function getToolDefinitions() {
|
|
|
3207
3280
|
required: ['css']
|
|
3208
3281
|
}
|
|
3209
3282
|
},
|
|
3283
|
+
{
|
|
3284
|
+
name: 'patch_kit_global_css',
|
|
3285
|
+
description: 'Surgically patch the active kit\'s custom_css with operations (remove_rule, remove_selector_from_rules, find_replace). Avoids the full-payload round-trip of set_kit_global_css. Always dry_run=true first. v4.45.0+.',
|
|
3286
|
+
inputSchema: {
|
|
3287
|
+
type: 'object',
|
|
3288
|
+
required: ['operations'],
|
|
3289
|
+
properties: {
|
|
3290
|
+
operations: {
|
|
3291
|
+
type: 'array',
|
|
3292
|
+
items: { type: 'object' },
|
|
3293
|
+
description: 'Array of operation objects. Each must have a `type` field. remove_rule: {type, selector} — drop every rule block whose every selector matches the root class (word-boundary). remove_selector_from_rules: {type, selector} — remove matching selector from compound selector lists; drop rule if empty. find_replace: {type, find, replace, regex} — literal or regex find/replace on the full CSS string.'
|
|
3294
|
+
},
|
|
3295
|
+
dry_run: {
|
|
3296
|
+
type: 'boolean',
|
|
3297
|
+
description: 'Preview without saving (default: true). Set false to apply.'
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
},
|
|
3302
|
+
{
|
|
3303
|
+
name: 'audit_unused_kit_css',
|
|
3304
|
+
description: 'Find class selectors in the kit\'s custom_css that aren\'t referenced via css_classes/_css_classes on any element across every Elementor page and template. Pure read-only — no writes. Returns: defined/referenced/unused class counts, estimated bytes wasted, and prune_candidates (each with the rules that define them — selector, line, snippet, byte length). Use to clean up dead utility CSS before adding new rules. v4.44.2+.',
|
|
3305
|
+
inputSchema: {
|
|
3306
|
+
type: 'object',
|
|
3307
|
+
properties: {
|
|
3308
|
+
ignore_prefixes: {
|
|
3309
|
+
type: 'string',
|
|
3310
|
+
description: 'Comma-separated class-name prefixes to skip (e.g. "elementor-,e-,wp-"). Useful for framework classes the kit CSS overrides but doesn\'t define.'
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
},
|
|
3315
|
+
{
|
|
3316
|
+
name: 'list_widget_types',
|
|
3317
|
+
description: 'Enumerate every registered Elementor widget and element type on this site. Returns name, title, and an is_atomic flag (atomic V4 widgets like e-heading vs classic widgets like heading). Use before get_widget_schema to find the right widget name. v4.44.3+.',
|
|
3318
|
+
inputSchema: { type: 'object', properties: {} }
|
|
3319
|
+
},
|
|
3320
|
+
{
|
|
3321
|
+
name: 'get_widget_schema',
|
|
3322
|
+
description: 'Read the full settings schema for one widget or element type — every accepted control with type, label, default, options, responsive flag, and section grouping. For atomic V4 widgets, returns the serialized props_schema instead (different system). Use to avoid having to read Elementor source. Pass section to scope to one control section, search to filter by control-name substring, or keys_only=true for just the names. v4.44.3+.',
|
|
3323
|
+
inputSchema: {
|
|
3324
|
+
type: 'object',
|
|
3325
|
+
properties: {
|
|
3326
|
+
widget_type: { type: 'string', description: 'Widget or element name (e.g. "heading", "e-heading", "container", "image", "button"). Use list_widget_types to enumerate.' },
|
|
3327
|
+
section: { type: 'string', description: 'Restrict to controls inside this control section (e.g. "section_title_style").' },
|
|
3328
|
+
search: { type: 'string', description: 'Substring filter on control name.' },
|
|
3329
|
+
keys_only: { type: 'boolean', description: 'Return only control name + type per entry (slim payload). Default: false.' }
|
|
3330
|
+
},
|
|
3331
|
+
required: ['widget_type']
|
|
3332
|
+
}
|
|
3333
|
+
},
|
|
3210
3334
|
// ── PixelVault image generation (v4.35.0) ──
|
|
3211
3335
|
{
|
|
3212
3336
|
name: 'pixelvault_status',
|
|
@@ -4096,6 +4220,33 @@ async function handleToolCall(name, args) {
|
|
|
4096
4220
|
return ok(`Moved ${r.element_type} (${r.element_id}) to parent ${r.target_parent_id}${r.position !== null ? ` at position ${r.position}` : ''}`);
|
|
4097
4221
|
}
|
|
4098
4222
|
|
|
4223
|
+
case 'set_container_layout': {
|
|
4224
|
+
try {
|
|
4225
|
+
const body = {
|
|
4226
|
+
container_id: args.container_id,
|
|
4227
|
+
mode: args.mode,
|
|
4228
|
+
};
|
|
4229
|
+
if (args.columns !== undefined) body.columns = args.columns;
|
|
4230
|
+
if (args.rows !== undefined) body.rows = args.rows;
|
|
4231
|
+
if (args.gap !== undefined) body.gap = args.gap;
|
|
4232
|
+
if (args.auto_flow) body.auto_flow = args.auto_flow;
|
|
4233
|
+
if (args.align_items) body.align_items = args.align_items;
|
|
4234
|
+
if (args.justify_items) body.justify_items = args.justify_items;
|
|
4235
|
+
if (args.justify_content) body.justify_content = args.justify_content;
|
|
4236
|
+
if (args.wrap) body.wrap = args.wrap;
|
|
4237
|
+
if (args.responsive !== undefined) body.responsive = args.responsive;
|
|
4238
|
+
if (args.force) body.force = true;
|
|
4239
|
+
const r = await apiCall(`/pages/${args.page_id}/set-container-layout`, 'POST', body);
|
|
4240
|
+
if (r.code || r.error) return ok(`Failed: ${r.message || r.error || 'Unknown error'}`);
|
|
4241
|
+
return ok(
|
|
4242
|
+
`Container ${r.container_id} set to ${r.mode} on page ${r.page_id}.\n` +
|
|
4243
|
+
`Applied keys: ${(r.applied_keys || []).join(', ')}`
|
|
4244
|
+
);
|
|
4245
|
+
} catch (err) {
|
|
4246
|
+
return ok(`Failed: ${err.message}`);
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4099
4250
|
case 'update_element': {
|
|
4100
4251
|
// Resolve settings: prefer settings_json (string) for reliable MCP transport, fall back to settings (object)
|
|
4101
4252
|
let settings = null;
|
|
@@ -4124,7 +4275,9 @@ async function handleToolCall(name, args) {
|
|
|
4124
4275
|
}
|
|
4125
4276
|
const r = await apiCall(`/pages/${args.page_id}/update-element`, 'POST', body);
|
|
4126
4277
|
if (r.code || r.error) return ok(`Failed: ${r.message || r.error || 'Unknown error'}`);
|
|
4127
|
-
|
|
4278
|
+
let out = `Element ${r.element_id} updated on page ${r.page_id}.\nUpdated keys: ${r.updated_keys.join(', ')}`;
|
|
4279
|
+
out += formatCssWarnings(r.css_warnings);
|
|
4280
|
+
return ok(out);
|
|
4128
4281
|
} catch (err) {
|
|
4129
4282
|
return ok(`Failed: ${err.message}`);
|
|
4130
4283
|
}
|
|
@@ -4854,10 +5007,11 @@ async function handleToolCall(name, args) {
|
|
|
4854
5007
|
const r = await apiCall(`/templates/${args.template_id}/patch`, 'POST', { ops: args.ops });
|
|
4855
5008
|
const okLines = r.applied.map(a => ` ✓ #${a.index} ${a.type}`);
|
|
4856
5009
|
const errLines = (r.errors || []).map(e => ` ✗ #${e.index} ${e.type}: ${e.error}`);
|
|
4857
|
-
|
|
5010
|
+
let out =
|
|
4858
5011
|
`Patched template ${r.template_id}: ${r.applied.length} op(s) applied${r.errors?.length ? ', ' + r.errors.length + ' failed' : ''}\n` +
|
|
4859
|
-
okLines.concat(errLines).join('\n')
|
|
4860
|
-
);
|
|
5012
|
+
okLines.concat(errLines).join('\n');
|
|
5013
|
+
out += formatCssWarnings(r.css_warnings);
|
|
5014
|
+
return ok(out);
|
|
4861
5015
|
}
|
|
4862
5016
|
|
|
4863
5017
|
case 'migrate_extracted_md': {
|
|
@@ -5094,6 +5248,9 @@ async function handleToolCall(name, args) {
|
|
|
5094
5248
|
out += ` [${x.index}] ${x.error}\n`;
|
|
5095
5249
|
});
|
|
5096
5250
|
}
|
|
5251
|
+
// Aggregate custom_css anti-pattern warnings across all results
|
|
5252
|
+
const allWarnings = r.results.reduce((acc, x) => x.css_warnings ? acc.concat(x.css_warnings) : acc, []);
|
|
5253
|
+
out += formatCssWarnings(allWarnings);
|
|
5097
5254
|
return ok(out);
|
|
5098
5255
|
}
|
|
5099
5256
|
|
|
@@ -5648,10 +5805,48 @@ async function handleToolCall(name, args) {
|
|
|
5648
5805
|
}
|
|
5649
5806
|
|
|
5650
5807
|
case 'get_kit_global_css': {
|
|
5651
|
-
const
|
|
5808
|
+
const params = new URLSearchParams();
|
|
5809
|
+
if (args.start_line) params.set('start_line', String(args.start_line));
|
|
5810
|
+
if (args.end_line) params.set('end_line', String(args.end_line));
|
|
5811
|
+
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
5812
|
+
const r = await apiCall(`/kit-global-css${qs}`);
|
|
5813
|
+
if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
|
|
5814
|
+
let header = `=== KIT GLOBAL CSS ===\nKit ID: ${r.kit_id}\n`;
|
|
5815
|
+
if (r.start_line) {
|
|
5816
|
+
header += `Lines ${r.start_line}-${r.end_line} of ${r.total_lines} (slice = ${r.length} bytes; total = ${r.total_length} bytes)\n\n`;
|
|
5817
|
+
} else {
|
|
5818
|
+
header += `Length: ${r.length} bytes (${r.total_lines} lines)\n`;
|
|
5819
|
+
if (r.length > 30000) {
|
|
5820
|
+
header += `Tip: payload is large — use start_line+end_line for slices or search_kit_global_css for lookups.\n`;
|
|
5821
|
+
}
|
|
5822
|
+
header += `\n`;
|
|
5823
|
+
}
|
|
5824
|
+
return ok(header + (r.css || '(empty)'));
|
|
5825
|
+
}
|
|
5826
|
+
|
|
5827
|
+
case 'search_kit_global_css': {
|
|
5828
|
+
const params = new URLSearchParams({ pattern: args.pattern || '' });
|
|
5829
|
+
if (args.regex) params.set('regex', '1');
|
|
5830
|
+
if (args.case_sensitive) params.set('case_sensitive', '1');
|
|
5831
|
+
if (args.context !== undefined) params.set('context', String(args.context));
|
|
5832
|
+
if (args.max_matches !== undefined) params.set('max_matches', String(args.max_matches));
|
|
5833
|
+
const r = await apiCall(`/search-kit-global-css?${params.toString()}`);
|
|
5652
5834
|
if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
|
|
5653
|
-
let out =
|
|
5654
|
-
|
|
5835
|
+
let out =
|
|
5836
|
+
`=== KIT CSS SEARCH ===\n` +
|
|
5837
|
+
`Kit ID: ${r.kit_id} | Lines: ${r.total_lines}\n` +
|
|
5838
|
+
`Pattern: ${r.is_regex ? '/' : '"'}${r.pattern}${r.is_regex ? '/' : '"'}\n` +
|
|
5839
|
+
`Matches: ${r.match_count}${r.truncated ? ' (truncated — pass higher max_matches)' : ''}\n`;
|
|
5840
|
+
if (r.match_count === 0) return ok(out + `\nNo matches.`);
|
|
5841
|
+
out += `\n`;
|
|
5842
|
+
r.matches.forEach((m, i) => {
|
|
5843
|
+
out += `--- Match ${i + 1} @ L${m.line} ---\n`;
|
|
5844
|
+
m.context.forEach((line, idx) => {
|
|
5845
|
+
const ln = m.context_start_line + idx;
|
|
5846
|
+
const marker = ln === m.line ? '>' : ' ';
|
|
5847
|
+
out += ` ${marker} ${String(ln).padStart(5)} | ${line}\n`;
|
|
5848
|
+
});
|
|
5849
|
+
});
|
|
5655
5850
|
return ok(out);
|
|
5656
5851
|
}
|
|
5657
5852
|
|
|
@@ -5668,6 +5863,108 @@ async function handleToolCall(name, args) {
|
|
|
5668
5863
|
return ok(out);
|
|
5669
5864
|
}
|
|
5670
5865
|
|
|
5866
|
+
case 'patch_kit_global_css': {
|
|
5867
|
+
const body = { operations: args.operations ?? [] };
|
|
5868
|
+
if (args.dry_run !== undefined) body.dry_run = args.dry_run;
|
|
5869
|
+
const r = await apiCall('/kit-global-css/patch', 'POST', body);
|
|
5870
|
+
if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
|
|
5871
|
+
let out = r.dry_run
|
|
5872
|
+
? `=== PATCH KIT GLOBAL CSS (DRY RUN) ===\n`
|
|
5873
|
+
: `=== PATCH KIT GLOBAL CSS ===\n`;
|
|
5874
|
+
out += `Kit ID: ${r.kit_id}\n`;
|
|
5875
|
+
out += `${r.summary}\n`;
|
|
5876
|
+
if (r.operations_applied && r.operations_applied.length) {
|
|
5877
|
+
out += `\nOperations applied (${r.operations_applied.length}):\n`;
|
|
5878
|
+
r.operations_applied.forEach((op, i) => {
|
|
5879
|
+
const sel = op.selector ? ` "${op.selector}"` : op.find ? ` find="${op.find}"` : '';
|
|
5880
|
+
out += ` ${i + 1}. [${op.type}]${sel} matches=${op.matches} bytes_removed=${op.bytes_removed}\n`;
|
|
5881
|
+
});
|
|
5882
|
+
}
|
|
5883
|
+
out += `\n${r.note}`;
|
|
5884
|
+
return ok(out);
|
|
5885
|
+
}
|
|
5886
|
+
|
|
5887
|
+
case 'audit_unused_kit_css': {
|
|
5888
|
+
const qs = args.ignore_prefixes ? `?ignore_prefixes=${encodeURIComponent(args.ignore_prefixes)}` : '';
|
|
5889
|
+
const r = await apiCall(`/audit-unused-kit-css${qs}`);
|
|
5890
|
+
if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
|
|
5891
|
+
let out =
|
|
5892
|
+
`=== UNUSED KIT CSS AUDIT ===\n` +
|
|
5893
|
+
`Kit ID: ${r.kit_id}\n` +
|
|
5894
|
+
`Kit CSS: ${r.kit_css_bytes} bytes\n` +
|
|
5895
|
+
`Classes defined: ${r.defined_class_count}\n` +
|
|
5896
|
+
`Classes referenced: ${r.referenced_class_count}\n` +
|
|
5897
|
+
`Classes UNUSED: ${r.unused_class_count}\n` +
|
|
5898
|
+
`Estimated waste: ~${r.estimated_bytes_unused} bytes\n`;
|
|
5899
|
+
if (r.ignored_prefixes && r.ignored_prefixes.length) {
|
|
5900
|
+
out += `Ignored prefixes: ${r.ignored_prefixes.join(', ')}\n`;
|
|
5901
|
+
}
|
|
5902
|
+
if (r.unused_class_count === 0) {
|
|
5903
|
+
out += `\nNo unused classes — kit CSS is clean.`;
|
|
5904
|
+
return ok(out);
|
|
5905
|
+
}
|
|
5906
|
+
out += `\n--- Prune candidates ---\n`;
|
|
5907
|
+
r.prune_candidates.forEach((c, i) => {
|
|
5908
|
+
out += `\n${i + 1}. .${c.class}\n`;
|
|
5909
|
+
c.rules.forEach(rule => {
|
|
5910
|
+
out += ` L${rule.line} (${rule.bytes}b) ${rule.snippet}\n`;
|
|
5911
|
+
});
|
|
5912
|
+
});
|
|
5913
|
+
return ok(out);
|
|
5914
|
+
}
|
|
5915
|
+
|
|
5916
|
+
case 'list_widget_types': {
|
|
5917
|
+
const r = await apiCall('/widget-types');
|
|
5918
|
+
if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
|
|
5919
|
+
let out = `=== WIDGET / ELEMENT TYPES ===\nWidgets: ${r.widget_count} | Elements: ${r.element_count}\n\n`;
|
|
5920
|
+
out += `--- Widgets ---\n`;
|
|
5921
|
+
r.widgets.forEach(w => {
|
|
5922
|
+
const atomic = w.is_atomic ? ' [atomic]' : '';
|
|
5923
|
+
const cat = w.category ? ` (${w.category})` : '';
|
|
5924
|
+
out += ` ${w.name.padEnd(28)} ${w.title}${atomic}${cat}\n`;
|
|
5925
|
+
});
|
|
5926
|
+
out += `\n--- Elements ---\n`;
|
|
5927
|
+
r.elements.forEach(e => {
|
|
5928
|
+
const atomic = e.is_atomic ? ' [atomic]' : '';
|
|
5929
|
+
out += ` ${e.name.padEnd(28)} ${e.title}${atomic}\n`;
|
|
5930
|
+
});
|
|
5931
|
+
return ok(out);
|
|
5932
|
+
}
|
|
5933
|
+
|
|
5934
|
+
case 'get_widget_schema': {
|
|
5935
|
+
const params = new URLSearchParams({ widget_type: args.widget_type || '' });
|
|
5936
|
+
if (args.section) params.set('section', args.section);
|
|
5937
|
+
if (args.search) params.set('search', args.search);
|
|
5938
|
+
if (args.keys_only) params.set('keys_only', '1');
|
|
5939
|
+
const r = await apiCall(`/widget-schema?${params.toString()}`);
|
|
5940
|
+
if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
|
|
5941
|
+
let out =
|
|
5942
|
+
`=== ${r.widget_type} (${r.kind}${r.is_atomic ? ', atomic V4' : ''}) ===\n` +
|
|
5943
|
+
`Title: ${r.title}\n`;
|
|
5944
|
+
if (r.is_atomic) {
|
|
5945
|
+
out += `Props: ${r.props_count}\n\n${r.note}\n\n--- Props Schema ---\n`;
|
|
5946
|
+
out += JSON.stringify(r.props_schema, null, 2);
|
|
5947
|
+
return ok(out);
|
|
5948
|
+
}
|
|
5949
|
+
out += `Controls: ${r.control_count}`;
|
|
5950
|
+
if (r.filter.section || r.filter.search || r.filter.keys_only) {
|
|
5951
|
+
out += ` (filtered)`;
|
|
5952
|
+
}
|
|
5953
|
+
out += `\n\n--- Controls ---\n`;
|
|
5954
|
+
for (const [name, c] of Object.entries(r.controls)) {
|
|
5955
|
+
const meta = [`type=${c.type}`];
|
|
5956
|
+
if (c.section) meta.push(`section=${c.section}`);
|
|
5957
|
+
if (c.responsive) meta.push('responsive');
|
|
5958
|
+
if (c.options) meta.push(`options=[${c.options.slice(0, 6).join(',')}${c.options.length > 6 ? '…' : ''}]`);
|
|
5959
|
+
if (c.range_units) meta.push(`units=[${c.range_units.join(',')}]`);
|
|
5960
|
+
if (c.size_units) meta.push(`size_units=[${c.size_units.join(',')}]`);
|
|
5961
|
+
const dflt = c.default !== undefined ? ` default=${typeof c.default === 'object' ? JSON.stringify(c.default).slice(0, 40) : c.default}` : '';
|
|
5962
|
+
out += ` ${name.padEnd(36)} ${meta.join(' | ')}${dflt}\n`;
|
|
5963
|
+
if (c.label && !args.keys_only) out += `${' '.repeat(38)}label: ${c.label}\n`;
|
|
5964
|
+
}
|
|
5965
|
+
return ok(out);
|
|
5966
|
+
}
|
|
5967
|
+
|
|
5671
5968
|
// ── PixelVault image generation ──
|
|
5672
5969
|
|
|
5673
5970
|
case 'pixelvault_status': {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noleemits/vision-builder-control-mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.45.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",
|