@diviops/mcp-server 1.2.0 → 1.4.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 CHANGED
@@ -96,7 +96,7 @@ The server connects via standard WordPress REST API and works with any environme
96
96
 
97
97
  > **WP-CLI note:** `WP_PATH` keeps the existing Local by Flywheel behavior by running `wp` directly on the host filesystem. For Docker-based environments (DDEV, wp-env, DevKinsta, WordPress Studio), set `WP_CLI_CMD` to the wrapper command instead. When `WP_CLI_CMD` is set, the server executes the wrapper from `WP_PATH` if provided, otherwise from its current working directory. The MCP server still validates the requested WP-CLI subcommand against its allowlist before executing either path.
98
98
 
99
- ## Available Tools (65)
99
+ ## Available Tools (66)
100
100
 
101
101
  ### Read (30)
102
102
  | Tool | Description |
@@ -108,7 +108,7 @@ The server connects via standard WordPress REST API and works with any environme
108
108
  | `diviops_page_get_layout` | Get parsed block tree (layout structure) |
109
109
  | `diviops_section_get` | Get a single section's markup by admin label |
110
110
  | `diviops_schema_list_modules` | List all available Divi modules |
111
- | `diviops_schema_get_module` | Get attribute schema for a module (optimized by default, `raw: true` for full) |
111
+ | `diviops_schema_get_module` | Get attribute schema for a module. Default `mode: "single"` returns one module's schema (optimized; `raw: true` for full). `mode: "dump_all"` snapshots every Divi module in one call along with a `schema_version` hash over `*PresetAttrsMap.php` and a `divi_version` field — build-time entry point for the skill regen pipeline. |
112
112
  | `diviops_schema_get_settings` | Get Divi site settings and theme options |
113
113
  | `diviops_global_color_list` | Get global color palette |
114
114
  | `diviops_global_font_list` | Get global font definitions |
@@ -132,7 +132,7 @@ The server connects via standard WordPress REST API and works with any environme
132
132
  | `diviops_scf_field_group_list` | List all SCF/ACF field groups (post_name = ACF key, post_title, post_status, post_modified). Queries the `acf-field-group` post type via `wp post list` (works on SCF 6.8.4+ and older ACF) |
133
133
  | `diviops_scf_field_group_get` | Fetch a single SCF/ACF field-group post by ACF key (`group_abc123` → post_name) or numeric WP post ID. For the parsed/structured field tree, use `diviops_scf_export --field-groups=<key> --stdout` |
134
134
 
135
- ### Write (33)
135
+ ### Write (34)
136
136
  | Tool | Description |
137
137
  |------|-------------|
138
138
  | `diviops_page_create` | Create a new page with optional Divi content |
@@ -163,7 +163,8 @@ The server connects via standard WordPress REST API and works with any environme
163
163
  | `diviops_variable_create_fluid_system` | Batch-emit a fluid typography + spacing + radius variable set in one call. Mirrors Divi 5.4.0's Variable Generator Modal at the algorithm level (clamp() math is identical to `diviops_variable_create`'s fluid mode) but layers profile-selectable anchors over it: `divi-default` (360→1350) matches ET's defaults, `wide` (320→1920) matches the diviops convention, `custom` takes explicit anchors. Each category is independent and optional. Typography uses modular-scale chains (named ratios `major-third`/`perfect-fifth`/`golden`/etc., or raw numbers) — h1 = largest, hN = base. Spacing/radius support `linear` or `geometric` step distributions. `dry_run: true` returns the full plan without persisting; `overwrite: false` (default) skips existing IDs. Single atomic write to the registry — mid-batch failures roll back cleanly |
164
164
  | `diviops_variable_delete` | Delete a variable by ID. Returns HTTP 409 when live references exist unless `force=true` (use `diviops_variable_scan_orphans` to find reference locations). Returns HTTP 403 for Divi's customizer-bound defaults (`gcid-primary-color`, `gcid-secondary-color`, `gcid-heading-color`, `gcid-body-color`, `gcid-link-color` — managed via WP Customizer) |
165
165
  | `diviops_canvas_create` | Create a canvas page |
166
- | `diviops_canvas_update` | Update canvas content |
166
+ | `diviops_canvas_duplicate` | Deep-copy a canvas (content + canvas-specific meta). Default copy title `<source> (Copy)` auto-suffixes on collision (Copy 2, …); explicit `title` collisions return 409. Supports `dry_run` |
167
+ | `diviops_canvas_update` | Update canvas content and/or metadata. Pass any subset of fields — `{canvas_post_id, title}` renames without touching content |
167
168
  | `diviops_canvas_delete` | Delete a canvas page |
168
169
  | `diviops_scf_export` | Export SCF schema (field groups, post types, taxonomies, options pages) as JSON to a directory under the safe-root, or to stdout. Wraps `wp scf json export` |
169
170
  | `diviops_scf_import` | Import SCF schema from a JSON file (mutates DB; idempotent — existing items are updated). Wraps `wp scf json import <file>` |
package/dist/index.js CHANGED
@@ -198,18 +198,53 @@ registerPluginTool("diviops_schema_list_modules", {
198
198
  };
199
199
  });
200
200
  registerPluginTool("diviops_schema_get_module", {
201
- description: "Get the attribute schema for a Divi module. Returns optimized schema by default (~70% smaller) with content-relevant fields only. Use raw: true for the full schema including CSS selectors and VB metadata.",
201
+ description: "Get the attribute schema for a Divi module. Default mode 'single' returns one module's schema (optimized, ~70% smaller; pass raw: true for full). Mode 'dump_all' snapshots every Divi module in one call and includes a `schema_version` hash over the canonical *PresetAttrsMap.php files build-time entry point for the skill regen pipeline; ignores `module_name` and `raw`.",
202
202
  inputSchema: {
203
+ mode: z
204
+ .enum(["single", "dump_all"])
205
+ .optional()
206
+ .default("single")
207
+ .describe("'single' (default): return one module's schema. 'dump_all': return every module keyed by name plus schema_version + divi_version."),
203
208
  module_name: z
204
209
  .string()
205
- .describe('Module name, e.g. "text", "image", "accordion", or full "divi/text"'),
210
+ .optional()
211
+ .describe('Module name, e.g. "text", "image", "accordion", or full "divi/text". Required when mode="single"; ignored when mode="dump_all".'),
206
212
  raw: z
207
213
  .boolean()
208
214
  .optional()
209
215
  .default(false)
210
- .describe("Return full schema including CSS selectors and VB metadata"),
216
+ .describe("Return full schema including CSS selectors and VB metadata. Applies to mode='single' only."),
211
217
  },
212
- }, async ({ module_name, raw }) => {
218
+ }, async ({ mode, module_name, raw }) => {
219
+ if (mode === "dump_all") {
220
+ // Capability gate for the dump-all surface: handled here (rather
221
+ // than the wrapper's auto-derived `schema_get_module` key) so older
222
+ // plugins without /schema/module/dump-all surface a clean upgrade
223
+ // hint instead of a 404 from wp.request.
224
+ if (handshakeState.kind === "ok" &&
225
+ !handshakeState.capabilities["schema_get_module_dump_all"]) {
226
+ const err = new MissingCapabilityError("schema_get_module_dump_all", handshakeState.pluginVersion);
227
+ return {
228
+ content: [{ type: "text", text: err.message }],
229
+ isError: true,
230
+ };
231
+ }
232
+ const result = await wp.request("/schema/module/dump-all");
233
+ return {
234
+ content: [{ type: "text", text: JSON.stringify(result) }],
235
+ };
236
+ }
237
+ if (!module_name) {
238
+ return {
239
+ content: [
240
+ {
241
+ type: "text",
242
+ text: "module_name is required when mode='single'",
243
+ },
244
+ ],
245
+ isError: true,
246
+ };
247
+ }
213
248
  const result = await wp.request(`/schema/module/${encodeURIComponent(module_name)}`);
214
249
  const output = raw ? result : optimizeSchema(result);
215
250
  return {
@@ -1376,7 +1411,7 @@ registerPluginTool("diviops_canvas_get", {
1376
1411
  };
1377
1412
  });
1378
1413
  registerPluginTool("diviops_canvas_update", {
1379
- description: "Update a canvas's content and/or metadata. Content replaces the entire canvas.",
1414
+ description: "Update a canvas's content and/or metadata. Pass any subset of fields — e.g. `{canvas_post_id, title}` to rename without touching content. `content` replaces the entire canvas when present. At least one of content/title/append_to_main/z_index is required.",
1380
1415
  inputSchema: {
1381
1416
  canvas_post_id: z.number().describe("Canvas post ID"),
1382
1417
  content: z
@@ -1410,6 +1445,34 @@ registerPluginTool("diviops_canvas_update", {
1410
1445
  ],
1411
1446
  };
1412
1447
  });
1448
+ registerPluginTool("diviops_canvas_duplicate", {
1449
+ description: "Deep-copy a canvas (post_content + canvas-specific meta: parent page, append_to_main, z_index). Source canvas untouched. Default copy title is `<source title> (Copy)` with auto-suffix on collision (Copy 2, Copy 3, …) — use this for repeat-clone workflows. Pass an explicit `title` for a deliberate name; collisions return 409 instead of silently auto-suffixing. Pass `dry_run: true` to preview without mutating.",
1450
+ inputSchema: {
1451
+ canvas_post_id: z.number().describe("Source canvas post ID"),
1452
+ title: z
1453
+ .string()
1454
+ .optional()
1455
+ .describe("Optional explicit title for the duplicate. Omit to auto-derive `<source> (Copy [N])`. Explicit collisions return 409."),
1456
+ dry_run: z
1457
+ .boolean()
1458
+ .optional()
1459
+ .default(false)
1460
+ .describe("When true, return the change plan without creating the canvas."),
1461
+ },
1462
+ }, async ({ canvas_post_id, title, dry_run }) => {
1463
+ const body = { dry_run: dry_run ?? false };
1464
+ if (title !== undefined)
1465
+ body.title = title;
1466
+ const result = await wp.request(`/canvas/duplicate/${canvas_post_id}`, {
1467
+ method: "POST",
1468
+ body,
1469
+ });
1470
+ return {
1471
+ content: [
1472
+ { type: "text", text: JSON.stringify(result) },
1473
+ ],
1474
+ };
1475
+ });
1413
1476
  registerPluginTool("diviops_canvas_delete", {
1414
1477
  description: "Delete a canvas. This permanently removes the canvas post.",
1415
1478
  inputSchema: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diviops/mcp-server",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "MCP server exposing Divi 5 Visual Builder as tools for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,8 @@
16
16
  "build": "tsc",
17
17
  "start": "node dist/index.js",
18
18
  "dev": "tsc --watch",
19
- "prepublishOnly": "npm run build"
19
+ "prepublishOnly": "npm run build",
20
+ "regen:skill": "node scripts/regen-module-formats.mjs"
20
21
  },
21
22
  "keywords": [
22
23
  "mcp",