@noleemits/vision-builder-control-mcp 4.31.0 → 4.32.1

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.
Files changed (2) hide show
  1. package/index.js +86 -12
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -104,7 +104,7 @@ process.on('SIGINT', () => {
104
104
  // CONFIG
105
105
  // ================================================================
106
106
 
107
- const VERSION = '4.31.0';
107
+ const VERSION = '4.32.0';
108
108
  const MIN_PLUGIN_VERSION = '4.13.0'; // Minimum WP plugin version required by this MCP server
109
109
 
110
110
  // ================================================================
@@ -2317,7 +2317,7 @@ function getToolDefinitions() {
2317
2317
  },
2318
2318
  {
2319
2319
  name: 'create_post',
2320
- description: 'Create a WordPress post, page, or CPT. Supports content, excerpt, taxonomies, featured image, Elementor setup, and initial RankMath SEO data.',
2320
+ description: 'Create a WordPress post, page, or CPT. Supports content, excerpt, taxonomies, featured image, parent linking (by slug or ID), Elementor setup, and initial RankMath SEO data.',
2321
2321
  inputSchema: {
2322
2322
  type: 'object',
2323
2323
  properties: {
@@ -2327,8 +2327,11 @@ function getToolDefinitions() {
2327
2327
  excerpt: { type: 'string', description: 'Post excerpt' },
2328
2328
  status: { type: 'string', enum: ['draft', 'publish', 'private', 'pending'], description: 'Post status (default: draft)' },
2329
2329
  slug: { type: 'string', description: 'URL slug (auto-generated if omitted)' },
2330
+ parent: { type: 'number', description: 'Parent post ID (for hierarchical types). Use this OR parent_slug.' },
2331
+ parent_slug: { type: 'string', description: 'Parent slug — looked up against the same post_type. Saves a follow-up update_post call.' },
2330
2332
  taxonomies: { type: 'object', description: 'Object: {"category": [1,2], "post_tag": ["seo","health"]}' },
2331
2333
  featured_image_id: { type: 'number', description: 'Media library attachment ID' },
2334
+ featured_image_basename: { type: 'string', description: 'Filename of an attachment (e.g. "boating-accidents-v2.jpg") — resolves to the matching attachment ID. Use instead of featured_image_id when you have the source filename but not the ID.' },
2332
2335
  elementor: { type: 'boolean', description: 'Set up for Elementor editing' },
2333
2336
  seo: { type: 'object', description: 'RankMath SEO: {title, description, focus_keyword, robots, ...}' }
2334
2337
  },
@@ -2337,7 +2340,7 @@ function getToolDefinitions() {
2337
2340
  },
2338
2341
  {
2339
2342
  name: 'update_post',
2340
- description: 'Update any field on a WordPress post/page. Only provided fields are changed. Supports title, content, status, slug, excerpt, taxonomies, featured image, and RankMath SEO. Pass preserve_modified_date=true to keep the original last-modified date.',
2343
+ description: 'Update any field on a WordPress post/page. Only provided fields are changed. Supports title, content, status, slug, excerpt, parent (ID or slug), taxonomies, featured image (ID or basename), and RankMath SEO. Pass preserve_modified_date=true to keep the original last-modified date.',
2341
2344
  inputSchema: {
2342
2345
  type: 'object',
2343
2346
  properties: {
@@ -2348,8 +2351,10 @@ function getToolDefinitions() {
2348
2351
  status: { type: 'string', enum: ['draft', 'publish', 'private', 'pending', 'trash'] },
2349
2352
  slug: { type: 'string' },
2350
2353
  parent: { type: 'number', description: 'Parent post/page ID (for hierarchical types like pages). Changes URL structure.' },
2354
+ parent_slug: { type: 'string', description: 'Parent slug — looked up against this post\'s post_type. Use instead of parent when you don\'t know the parent ID.' },
2351
2355
  taxonomies: { type: 'object', description: '{"category": [1,2]}' },
2352
2356
  featured_image_id: { type: 'number', description: 'Attachment ID (omit to keep, pass null to remove)' },
2357
+ featured_image_basename: { type: 'string', description: 'Filename of an attachment to use as featured image. Resolved to the matching ID server-side.' },
2353
2358
  seo: { type: 'object', description: 'RankMath SEO fields to update' },
2354
2359
  preserve_modified_date: { type: 'boolean', description: 'If true, keep the original last-modified date instead of updating it to now. Useful for content fixes that should not change the published "updated" date.' }
2355
2360
  },
@@ -2612,6 +2617,56 @@ function getToolDefinitions() {
2612
2617
  }
2613
2618
  },
2614
2619
 
2620
+ // ── Patch + Migration (v4.32.0) ──
2621
+ {
2622
+ name: 'patch_template',
2623
+ description: 'Apply surgical operations to an Elementor library template without re-uploading the full JSON. Op types: set_settings (merge keys into element.settings), replace_element (swap full element node), insert_element (add child at position 0/end/<int>), remove_element (delete by id), append_custom_css / set_custom_css (add or replace settings.custom_css). Returns per-op success/error so partial failures don\'t roll back the batch.',
2624
+ inputSchema: {
2625
+ type: 'object',
2626
+ properties: {
2627
+ template_id: { type: 'number', description: 'Elementor library template ID' },
2628
+ ops: {
2629
+ type: 'array',
2630
+ description: 'Ordered list of operations to apply.',
2631
+ items: {
2632
+ type: 'object',
2633
+ properties: {
2634
+ type: { type: 'string', enum: ['set_settings', 'replace_element', 'insert_element', 'remove_element', 'append_custom_css', 'set_custom_css'] },
2635
+ id: { type: 'string', description: 'Target element ID (required for set_settings, replace_element, remove_element, append_custom_css, set_custom_css)' },
2636
+ parent_id: { type: 'string', description: 'Parent element ID (required for insert_element)' },
2637
+ position: { description: 'Insert position: 0, "end", or numeric index. Default: end.' },
2638
+ element: { type: 'object', description: 'Full element JSON (required for replace_element, insert_element)' },
2639
+ settings: { type: 'object', description: 'Settings to merge (required for set_settings)' },
2640
+ css: { type: 'string', description: 'CSS text (required for append_custom_css, set_custom_css)' }
2641
+ },
2642
+ required: ['type']
2643
+ }
2644
+ }
2645
+ },
2646
+ required: ['template_id', 'ops']
2647
+ }
2648
+ },
2649
+ {
2650
+ name: 'migrate_extracted_md',
2651
+ description: 'One-shot migration: take an extract-format markdown chunk and create or update a CPT post with cleaned HTML body, featured image, parent linking, and RankMath SEO. Idempotent: re-running with the same slug+post_type updates instead of duplicating. Pass md_content (preferred) or md_path. Use hero_basename to look up the featured image by filename instead of attachment ID.',
2652
+ inputSchema: {
2653
+ type: 'object',
2654
+ properties: {
2655
+ md_content: { type: 'string', description: 'Raw markdown body (preferred — no server-side path needed)' },
2656
+ md_path: { type: 'string', description: 'Server-side path to MD file. Only useful if WP server can read the path.' },
2657
+ post_type: { type: 'string', description: 'CPT slug (e.g. "practice-area")' },
2658
+ slug: { type: 'string', description: 'Post slug — used as idempotency key' },
2659
+ parent_slug: { type: 'string', description: 'Optional. Looked up against same post_type for hierarchical CPTs.' },
2660
+ hero_basename: { type: 'string', description: 'Optional. Filename like "boating-accidents-v2.jpg" — resolves to the matching attachment.' },
2661
+ title_override: { type: 'string', description: 'Optional. Overrides extracted H1.' },
2662
+ seo_focus_keyword: { type: 'string', description: 'Optional. RankMath focus keyword.' },
2663
+ status: { type: 'string', enum: ['draft', 'publish', 'private', 'pending'], description: 'Default: publish' },
2664
+ strip_first_paragraph: { type: 'boolean', description: 'Drop the first <p> if it duplicates H1+lede (default: true)' }
2665
+ },
2666
+ required: ['post_type', 'slug']
2667
+ }
2668
+ },
2669
+
2615
2670
  // ── Custom post types & taxonomies (v4.31.0) ──
2616
2671
  {
2617
2672
  name: 'register_cpt',
@@ -4156,15 +4211,10 @@ async function handleToolCall(name, args) {
4156
4211
 
4157
4212
  case 'create_post': {
4158
4213
  const body = { title: args.title };
4159
- if (args.post_type) body.post_type = args.post_type;
4160
- if (args.content) body.content = args.content;
4161
- if (args.excerpt) body.excerpt = args.excerpt;
4162
- if (args.status) body.status = args.status;
4163
- if (args.slug) body.slug = args.slug;
4164
- if (args.taxonomies) body.taxonomies = args.taxonomies;
4165
- if (args.featured_image_id) body.featured_image_id = args.featured_image_id;
4166
- if (args.elementor) body.elementor = args.elementor;
4167
- if (args.seo) body.seo = args.seo;
4214
+ const passthrough = ['post_type', 'content', 'excerpt', 'status', 'slug',
4215
+ 'parent', 'parent_slug', 'taxonomies', 'featured_image_id',
4216
+ 'featured_image_basename', 'elementor', 'seo'];
4217
+ for (const k of passthrough) if (args[k] !== undefined) body[k] = args[k];
4168
4218
  const r = await apiCall('/posts', 'POST', body);
4169
4219
  if (!r.success) return ok(`Failed: ${r.message || 'Unknown error'}`);
4170
4220
  return ok(`Post created!\nID: ${r.id} | Type: ${r.type} | Status: ${r.status}\nTitle: ${r.title}\nSlug: ${r.slug}\nURL: ${r.url}\nEdit: ${r.edit_url}`);
@@ -4472,6 +4522,30 @@ async function handleToolCall(name, args) {
4472
4522
  return ok(`Assigned menu #${r.menu_id} to location "${r.location}".`);
4473
4523
  }
4474
4524
 
4525
+ case 'patch_template': {
4526
+ const r = await apiCall(`/templates/${args.template_id}/patch`, 'POST', { ops: args.ops });
4527
+ const okLines = r.applied.map(a => ` ✓ #${a.index} ${a.type}`);
4528
+ const errLines = (r.errors || []).map(e => ` ✗ #${e.index} ${e.type}: ${e.error}`);
4529
+ return ok(
4530
+ `Patched template ${r.template_id}: ${r.applied.length} op(s) applied${r.errors?.length ? ', ' + r.errors.length + ' failed' : ''}\n` +
4531
+ okLines.concat(errLines).join('\n')
4532
+ );
4533
+ }
4534
+
4535
+ case 'migrate_extracted_md': {
4536
+ const body = {};
4537
+ ['md_content','md_path','post_type','slug','parent_slug','hero_basename','title_override','seo_focus_keyword','status','strip_first_paragraph']
4538
+ .forEach(k => { if (args[k] !== undefined) body[k] = args[k]; });
4539
+ const r = await apiCall('/migrate/extracted-md', 'POST', body);
4540
+ return ok(
4541
+ `${r.was_existing ? 'Updated' : 'Created'} ${args.post_type} #${r.post_id}\n` +
4542
+ ` H1: ${r.parsed.h1}\n` +
4543
+ ` Title: ${args.title_override || r.parsed.h1}\n` +
4544
+ ` Body: ${r.parsed.body_length} chars\n` +
4545
+ ` URL: ${r.url}`
4546
+ );
4547
+ }
4548
+
4475
4549
  case 'register_cpt': {
4476
4550
  const r = await apiCall('/post-types', 'POST', args);
4477
4551
  return ok(`${r.created ? 'Registered' : 'Updated'} CPT "${r.slug}"\n hierarchical: ${r.def.hierarchical}\n rewrite_slug: ${r.def.rewrite_slug || '(default)'}\n has_archive: ${r.def.has_archive}\n supports: ${(r.def.supports || []).join(', ')}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noleemits/vision-builder-control-mcp",
3
- "version": "4.31.0",
3
+ "version": "4.32.1",
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",