@noleemits/vision-builder-control-mcp 4.16.0 → 4.18.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 +89 -7
  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.16.0';
107
+ const VERSION = '4.18.1';
108
108
  const MIN_PLUGIN_VERSION = '4.13.0'; // Minimum WP plugin version required by this MCP server
109
109
 
110
110
  // ================================================================
@@ -2003,7 +2003,7 @@ function getToolDefinitions() {
2003
2003
  },
2004
2004
  {
2005
2005
  name: 'fix_trailing_slashes',
2006
- description: 'Add missing trailing slashes to internal URLs across ALL post types (pages, posts, CPTs). Scans both Elementor widget data and Gutenberg/classic post_content. Skips anchors (#), query strings (?), external URLs, and file extensions (.pdf etc). ALWAYS run with dry_run=true first to preview changes.',
2006
+ description: 'Add missing trailing slashes to internal URLs across ALL post types (pages, posts, CPTs). Scans both Elementor widget data and Gutenberg/classic post_content. Skips anchors (#), query strings (?), external URLs, and file extensions (.pdf etc). Never changes post_modified date. ALWAYS run with dry_run=true first to preview changes.',
2007
2007
  inputSchema: {
2008
2008
  type: 'object',
2009
2009
  properties: {
@@ -2051,7 +2051,7 @@ function getToolDefinitions() {
2051
2051
  },
2052
2052
  {
2053
2053
  name: 'find_replace',
2054
- description: 'Global find & replace across all Elementor pages. Searches heading titles, text-editor HTML, button text, image-box titles/descriptions, icon-box titles/descriptions, icon-list items, toggle titles/content, and image alt text. Supports literal text or regex. Always preview with dry_run=true first.',
2054
+ description: 'Global find & replace across all Elementor pages. Searches heading titles, text-editor HTML, button text, image-box titles/descriptions, icon-box titles/descriptions, icon-list items, toggle titles/content, and image alt text. Supports literal text or regex. Never changes post_modified date (Elementor meta only). Always preview with dry_run=true first.',
2055
2055
  inputSchema: {
2056
2056
  type: 'object',
2057
2057
  properties: {
@@ -2155,7 +2155,7 @@ function getToolDefinitions() {
2155
2155
  },
2156
2156
  {
2157
2157
  name: 'update_post',
2158
- 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.',
2158
+ 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.',
2159
2159
  inputSchema: {
2160
2160
  type: 'object',
2161
2161
  properties: {
@@ -2168,7 +2168,8 @@ function getToolDefinitions() {
2168
2168
  parent: { type: 'number', description: 'Parent post/page ID (for hierarchical types like pages). Changes URL structure.' },
2169
2169
  taxonomies: { type: 'object', description: '{"category": [1,2]}' },
2170
2170
  featured_image_id: { type: 'number', description: 'Attachment ID (omit to keep, pass null to remove)' },
2171
- seo: { type: 'object', description: 'RankMath SEO fields to update' }
2171
+ seo: { type: 'object', description: 'RankMath SEO fields to update' },
2172
+ 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.' }
2172
2173
  },
2173
2174
  required: ['post_id']
2174
2175
  }
@@ -2230,6 +2231,16 @@ function getToolDefinitions() {
2230
2231
  }
2231
2232
  }
2232
2233
  },
2234
+ {
2235
+ name: 'audit_faq_schema',
2236
+ description: 'Audit all published posts/pages for FAQ schema issues: duplicate FAQPage markup (NVBC meta + RankMath block), orphaned NVBC schemas, unconverted plain FAQs, invalid JSON, empty Q&As, and encoding issues (bare u003c).',
2237
+ inputSchema: {
2238
+ type: 'object',
2239
+ properties: {
2240
+ post_type: { type: 'string', description: 'Optional: filter by post type (e.g., "page" or "post")' }
2241
+ }
2242
+ }
2243
+ },
2233
2244
  {
2234
2245
  name: 'update_term_seo',
2235
2246
  description: 'Update RankMath SEO fields on a taxonomy term (category, tag, custom taxonomy). Accepts any subset: title, description, focus_keyword, canonical_url, noindex (boolean convenience flag).',
@@ -2540,7 +2551,7 @@ function getToolDefinitions() {
2540
2551
  },
2541
2552
  {
2542
2553
  name: 'replace_post_content',
2543
- description: 'Bulk find-replace in WordPress post_content (Gutenberg/classic editor). Use for migrating URLs, fixing domains, replacing shortcodes. Always dry_run=true first to preview. Works on published posts and pages.',
2554
+ description: 'Bulk find-replace in WordPress post_content (Gutenberg/classic editor). Use for migrating URLs, fixing domains, replacing shortcodes. Always dry_run=true first to preview. Works on published posts and pages. Never changes post_modified date (uses direct DB write).',
2544
2555
  inputSchema: {
2545
2556
  type: 'object',
2546
2557
  properties: {
@@ -2579,6 +2590,18 @@ function getToolDefinitions() {
2579
2590
  required: ['posts']
2580
2591
  }
2581
2592
  },
2593
+ {
2594
+ name: 'convert_faq_to_rankmath',
2595
+ description: 'Scan post_content for plain Q/A FAQ sections and convert them to wp:rank-math/faq-block Gutenberg blocks. Detects H2 "FAQs" / "Frequently Asked Questions" headings followed by H3+paragraph Q/A pairs. Strips Q:/A: prefixes and   entities. Never changes post_modified date (direct DB write). ALWAYS dry_run=true first.',
2596
+ inputSchema: {
2597
+ type: 'object',
2598
+ properties: {
2599
+ post_id: { type: 'number', description: 'Convert only this specific post ID. Omit to scan all posts.' },
2600
+ post_type: { type: 'string', description: 'Filter by post type: "post", "page", etc. Omit for all types.' },
2601
+ dry_run: { type: 'boolean', description: 'Preview extracted Q&As without saving (default: true). Set false to apply conversion.' }
2602
+ }
2603
+ }
2604
+ },
2582
2605
  {
2583
2606
  name: 'get_global_styles',
2584
2607
  description: 'Read Elementor global colors and fonts from the active kit. Returns system colors (Primary, Secondary, Text, Accent), custom colors, system typography, and custom typography with all properties.',
@@ -3774,7 +3797,9 @@ async function handleToolCall(name, args) {
3774
3797
  const { post_id, ...fields } = args;
3775
3798
  const r = await apiCall(`/posts/${post_id}`, 'PUT', fields);
3776
3799
  if (!r.success) return ok(`Failed: ${r.message || 'Unknown error'}`);
3777
- return ok(`Post updated!\nID: ${r.id} | Status: ${r.status}\nTitle: ${r.title}\nUpdated: ${r.updated_fields.join(', ')}\nURL: ${r.url}`);
3800
+ let out = `Post updated!\nID: ${r.id} | Status: ${r.status}\nTitle: ${r.title}\nUpdated: ${r.updated_fields.join(', ')}\nURL: ${r.url}`;
3801
+ if (r.modified_date_preserved) out += `\nšŸ“Œ Modified date preserved: ${r.post_modified}`;
3802
+ return ok(out);
3778
3803
  }
3779
3804
 
3780
3805
  case 'delete_post': {
@@ -3839,6 +3864,36 @@ async function handleToolCall(name, args) {
3839
3864
  return ok(out);
3840
3865
  }
3841
3866
 
3867
+ case 'audit_faq_schema': {
3868
+ const params = new URLSearchParams();
3869
+ if (args.post_type) params.set('post_type', args.post_type);
3870
+ const qs = params.toString() ? `?${params.toString()}` : '';
3871
+ const r = await apiCall(`/audit-faq-schema${qs}`);
3872
+ if (!r.success) return ok(`Failed: ${r.message || 'Unknown error'}`);
3873
+ const s = r.summary;
3874
+ let out = `=== FAQ SCHEMA AUDIT ===\n`;
3875
+ out += `Total published: ${s.total_posts}\n`;
3876
+ out += `With RankMath FAQ block: ${s.has_rankmath_block}\n`;
3877
+ out += `With NVBC plugin schema: ${s.has_nvbc_schema}\n`;
3878
+ out += `Clean (no issues): ${s.clean}\n\n`;
3879
+ out += `--- Issues ---\n`;
3880
+ out += `Duplicate FAQ (both sources): ${s.duplicate_faq}\n`;
3881
+ out += `Plain FAQ unconverted: ${s.plain_faq_unconverted}\n`;
3882
+ out += `Invalid JSON: ${s.invalid_json}\n`;
3883
+ out += `Empty questions: ${s.empty_questions}\n`;
3884
+ out += `Encoding issues (bare u003c): ${s.encoding_issues}\n`;
3885
+ if (r.issues.length) {
3886
+ out += `\n--- POSTS WITH ISSUES (${r.issues.length}) ---\n`;
3887
+ r.issues.forEach(p => {
3888
+ out += ` [${p.id}] ${p.title} (${p.type}/${p.editor})`;
3889
+ out += `\n Issues: ${p.issues.join(', ')}\n`;
3890
+ });
3891
+ } else {
3892
+ out += '\nNo issues found — all FAQ schemas are clean!';
3893
+ }
3894
+ return ok(out);
3895
+ }
3896
+
3842
3897
  case 'update_term_seo': {
3843
3898
  const { term_id, taxonomy, ...seoFields } = args;
3844
3899
  const body = { ...seoFields };
@@ -4481,6 +4536,33 @@ async function handleToolCall(name, args) {
4481
4536
  return ok(out);
4482
4537
  }
4483
4538
 
4539
+ case 'convert_faq_to_rankmath': {
4540
+ const body = {};
4541
+ if (args.post_id) body.post_id = args.post_id;
4542
+ if (args.post_type) body.post_type = args.post_type;
4543
+ if (args.dry_run !== undefined) body.dry_run = args.dry_run;
4544
+ const r = await apiCall('/convert-faq-to-rankmath', 'POST', body);
4545
+ if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
4546
+ let out = r.dry_run ? `=== CONVERT FAQ TO RANKMATH (DRY RUN) ===\n` : `=== CONVERT FAQ TO RANKMATH ===\n`;
4547
+ out += `Posts scanned: ${r.posts_scanned} | Converted: ${r.posts_converted} | Total Q&As: ${r.total_questions}\n`;
4548
+ if (r.results?.length) {
4549
+ r.results.forEach(p => {
4550
+ out += `\nšŸ“„ [${p.post_id}] ${p.title} (${p.post_type}) — ${p.sections} section(s), ${p.questions} Q&A(s)${p.duplicates_removed ? ` [${p.duplicates_removed} duplicate(s) removed]` : ''}\n`;
4551
+ if (p.preview) {
4552
+ p.preview.forEach(sec => {
4553
+ out += ` Section: "${sec.heading}"\n`;
4554
+ sec.qa_pairs.forEach((qa, i) => {
4555
+ out += ` ${i + 1}. Q: ${qa.question}\n`;
4556
+ out += ` A: ${qa.answer}\n`;
4557
+ });
4558
+ });
4559
+ }
4560
+ });
4561
+ }
4562
+ out += `\n${r.note}`;
4563
+ return ok(out);
4564
+ }
4565
+
4484
4566
  case 'get_global_styles': {
4485
4567
  const r = await apiCall('/global-styles');
4486
4568
  if (r.code || r.error) return ok(`Failed: ${r.message || r.error}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noleemits/vision-builder-control-mcp",
3
- "version": "4.16.0",
3
+ "version": "4.18.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",