@diviops/mcp-server 1.5.21 → 1.5.24

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
@@ -41,11 +41,13 @@ claude mcp add diviops-mcp \
41
41
  --env WP_URL=http://your-site.local \
42
42
  --env WP_USER=your-wp-username \
43
43
  --env WP_APP_PASSWORD=xxxxXXXXxxxxXXXXxxxxXXXX \
44
- -- npx @diviops/mcp-server
44
+ -- npx -y --package @diviops/mcp-server diviops-mcp
45
45
  ```
46
46
 
47
47
  Then ask Claude: **"List the pages on my site."** Claude calls `diviops_page_list` and renders the result. You're authoring with the suite.
48
48
 
49
+ For Claude Desktop JSON, use `"command": "npx"` with args `["-y", "--package", "@diviops/mcp-server", "diviops-mcp"]`. The package also ships `diviops-preset`, so the explicit package/bin form is required; `npx @diviops/mcp-server` cannot reliably infer which bin to run.
50
+
49
51
  For a deeper walkthrough (containerized environments, WP-CLI configuration, troubleshooting installation), see [setup-guide.md](../docs/setup-guide.md).
50
52
 
51
53
  ## Example workflow
@@ -64,7 +66,7 @@ The skill enforces the Divi block format, the design system, and the response co
64
66
 
65
67
  ## Tools at a glance
66
68
 
67
- The server exposes **73 always-on tools** across the categories below. Each category links to representative tools; the full table lives in [server-reference.md](../docs/server-reference.md).
69
+ The server exposes **74 always-on tools** across the categories below. Each category links to representative tools; the full table lives in [server-reference.md](../docs/server-reference.md).
68
70
 
69
71
  | Category | Use case | Tool prefixes |
70
72
  |----------|----------|---------------|
@@ -219,6 +221,7 @@ The server connects via standard WordPress REST API and works with any environme
219
221
  Common quick fixes — full reference in [troubleshooting.md](../docs/troubleshooting.md).
220
222
 
221
223
  - **"Missing required environment variable(s)"** — ensure `WP_URL`, `WP_USER`, `WP_APP_PASSWORD` are all set on `claude mcp add`.
224
+ - **`npx @diviops/mcp-server` fails with "could not determine executable to run"** — use `npx -y --package @diviops/mcp-server diviops-mcp`; this explicitly selects the MCP server bin.
222
225
  - **"Connection failed"** — verify the plugin is active by visiting `{WP_URL}/wp-json/diviops/v1/schema/settings`; test the credentials with `curl -u "user:pass" …`.
223
226
  - **"This tool requires plugin capability"** — the plugin doesn't advertise the capability this tool needs. Update the plugin to the latest release.
224
227
  - **Preset edits not visible on the frontend** — Divi serves frontend CSS from `wp-content/et-cache/{post_id}/`, which `wp cache flush` doesn't touch. Use `diviops_meta_flush_cache` after preset writes.
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import { WPClient } from "./wp-client.js";
15
15
  import { MissingCapabilityError } from "./compatibility.js";
16
16
  import { ErrorCodes, envelopeMap, recordIdempotent, serializeEnvelope, withCode, wrapResponse, } from "./envelope.js";
17
17
  import { optimizeSchema } from "./schema-optimizer.js";
18
+ import { schemaModuleRoute } from "./schema-route.js";
18
19
  import { createWpCli } from "./wp-cli.js";
19
20
  import { findForeignVarRefs, scanAttrsForForeignVarRefs, isolationErrorResult, } from "./validate-attrs.js";
20
21
  import { readFileSync, readdirSync } from "fs";
@@ -347,7 +348,7 @@ registerPluginTool("diviops_schema_get_module", {
347
348
  content: [{ type: "text", text: serializeEnvelope(failure, "diviops_schema_get_module") }],
348
349
  };
349
350
  }
350
- const result = await wp.requestEnveloped(`/schema/module/${encodeURIComponent(module_name)}`);
351
+ const result = await wp.requestEnveloped(schemaModuleRoute(module_name));
351
352
  const projected = envelopeMap(result, (data) => raw ? data : optimizeSchema(data));
352
353
  return {
353
354
  content: [
@@ -1773,6 +1774,50 @@ registerPluginTool("diviops_tb_layout_update", {
1773
1774
  ],
1774
1775
  };
1775
1776
  });
1777
+ registerPluginTool("diviops_tb_layout_block_insert", {
1778
+ description: "Insert one or more serialized Divi blocks into an existing Theme Builder layout without replacing the whole layout. Target a unique parent with `parent_selector` (for example `divi/group[adminLabel=\"Legal Col\"]`, or `divi/group` only when it is unique) or an explicit zero-based `parent_path` from the parsed block tree such as `0.1.2`. `position=append|prepend` inserts as children of the target block; `position=before|after` inserts beside the target within its parent. Ambiguous selectors return ok:false with code 'invalid_input'; missing targets return 'not_found'. The route parses and validates the inserted blocks, rejects malformed pseudo-escapes such as bare `u003c`, validates the final serialized layout before saving, and returns a no-op when the exact requested block sequence already exists at the insertion point." +
1779
+ DRY_RUN_DESC_SUFFIX,
1780
+ inputSchema: {
1781
+ layout_id: z.number().int().describe("Theme Builder layout post ID to mutate"),
1782
+ parent_selector: z
1783
+ .string()
1784
+ .optional()
1785
+ .describe('Unique selector such as `divi/group[adminLabel="Legal Col"]` or `divi/column`. Provide exactly one of parent_selector or parent_path.'),
1786
+ parent_path: z
1787
+ .string()
1788
+ .optional()
1789
+ .describe('Zero-based parsed-tree path such as `0`, `0.1`, or `0.1.2`. Provide exactly one of parent_selector or parent_path.'),
1790
+ position: z
1791
+ .enum(["append", "prepend", "before", "after"])
1792
+ .optional()
1793
+ .default("append")
1794
+ .describe("Where to insert relative to the target block."),
1795
+ content: z.string().describe("One or more serialized Divi blocks to insert"),
1796
+ dry_run: DRY_RUN_FIELD,
1797
+ },
1798
+ annotations: { idempotentHint: false },
1799
+ _meta: { idempotent: "conditional" },
1800
+ }, async ({ layout_id, parent_selector, parent_path, position, content, dry_run }) => {
1801
+ const body = {
1802
+ content,
1803
+ position: position ?? "append",
1804
+ };
1805
+ if (parent_selector !== undefined)
1806
+ body.parent_selector = parent_selector;
1807
+ if (parent_path !== undefined)
1808
+ body.parent_path = parent_path;
1809
+ if (dry_run)
1810
+ body.dry_run = true;
1811
+ const result = await wp.requestEnveloped(`/theme-builder/layout/block-insert/${layout_id}`, {
1812
+ method: "POST",
1813
+ body,
1814
+ });
1815
+ return {
1816
+ content: [
1817
+ { type: "text", text: serializeEnvelope(result, "diviops_tb_layout_block_insert") },
1818
+ ],
1819
+ };
1820
+ });
1776
1821
  registerPluginTool("diviops_tb_template_create", {
1777
1822
  description: "Create a Theme Builder template with custom header and/or footer. Automatically creates layout posts, sets conditions, and links to Theme Builder. Pass condition=\"default\" (case-insensitive) or an empty string to register the template as the catch-all Default Website Template — the route writes the `_et_default = '1'` flag with an empty `_et_use_on`, matching the meta shape Divi's TB router gates the default route on; any other condition string lands in `_et_use_on` unchanged. Default Website Template is a singleton scoped to the active Theme Builder master: if the active master's `_et_template` linked list already names an et_template carrying `_et_default = '1'` (regardless of `_et_enabled` status — the router resolves by linked-list position before checking the enable-gate, so a disabled existing default linked ahead of the new one would still shadow it), the route rejects with code `tb_template.default_already_exists` (HTTP 409) and `error.data.existing_default_id` + `error.data.master_post_id`. Templates outside the active master's linked list (orphan defaults, library-cloned-master defaults) cannot shadow the router's pick and DO NOT block creation. Caller resolves a real conflict by trashing the existing default (diviops_tb_template_trash) or pinning this template to a specific condition; the route never silently flips the existing default's flag or proceeds with non-deterministic router state. If the Theme Builder master post is missing (fresh substrate that never opened Divi → Theme Builder in WP Admin), the route auto-bootstraps one with the same shape Divi creates on first admin visit and returns `data.master_post_bootstrapped: true` so callers can audit the side-effect. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; failures during master-post bootstrap or template/layout insert surface the underlying WP_Error code (commonly `db_insert_error`, `db_update_error`, or other slugs from the WordPress vocabulary), not a generic `wp_error` — branch on `error.code` against the WP slug, not against a hard-coded string. The literal `wp_error` slug only surfaces when the upstream WP_Error has an empty code." +
1778
1823
  DRY_RUN_DESC_SUFFIX,
@@ -2046,7 +2091,7 @@ registerLocalTool("diviops_meta_wp_cli", {
2046
2091
  }, async ({ command }) => {
2047
2092
  const response = await wrapResponse(async () => {
2048
2093
  if (!wpCli) {
2049
- withCode("meta_wp_cli.not_configured", "WP-CLI not configured.", 'Set the WP_PATH environment variable to your WordPress installation path. Example: claude mcp add diviops-mcp -- env WP_URL=http://site.local WP_USER=admin WP_APP_PASSWORD="xxxx" WP_PATH="/Users/you/Local Sites/your-site/app/public" npx @diviops/mcp-server. Local site ID is auto-detected from WP_PATH; set LOCAL_SITE_ID explicitly if needed.');
2094
+ withCode("meta_wp_cli.not_configured", "WP-CLI not configured.", 'Set the WP_PATH environment variable to your WordPress installation path. Example: claude mcp add diviops-mcp --env WP_URL=http://site.local --env WP_USER=admin --env WP_APP_PASSWORD=xxxx --env "WP_PATH=/Users/you/Local Sites/your-site/app/public" -- npx -y --package @diviops/mcp-server diviops-mcp. Local site ID is auto-detected from WP_PATH; set LOCAL_SITE_ID explicitly if needed.');
2050
2095
  }
2051
2096
  const result = await wpCli.run(command);
2052
2097
  if (!result.success) {
@@ -0,0 +1,2 @@
1
+ export declare function normalizeSchemaModuleName(moduleName: string): string;
2
+ export declare function schemaModuleRoute(moduleName: string): string;
@@ -0,0 +1,7 @@
1
+ export function normalizeSchemaModuleName(moduleName) {
2
+ const trimmed = moduleName.trim();
3
+ return trimmed.startsWith("divi/") ? trimmed.slice("divi/".length) : trimmed;
4
+ }
5
+ export function schemaModuleRoute(moduleName) {
6
+ return `/schema/module/${encodeURIComponent(normalizeSchemaModuleName(moduleName))}`;
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diviops/mcp-server",
3
- "version": "1.5.21",
3
+ "version": "1.5.24",
4
4
  "description": "MCP server exposing Divi 5 Visual Builder as tools for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",