@diviops/mcp-server 0.2.11 → 0.2.13
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/dist/compatibility.d.ts +1 -1
- package/dist/compatibility.js +1 -1
- package/dist/index.js +34 -5
- package/dist/validate-attrs.d.ts +27 -0
- package/dist/validate-attrs.js +63 -0
- package/package.json +1 -1
package/dist/compatibility.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Version compatibility between MCP server and WP plugin.
|
|
3
3
|
*/
|
|
4
4
|
/** Minimum WP plugin version this server requires. */
|
|
5
|
-
export declare const MIN_PLUGIN_VERSION = "1.0.0-beta.
|
|
5
|
+
export declare const MIN_PLUGIN_VERSION = "1.0.0-beta.31";
|
|
6
6
|
/**
|
|
7
7
|
* Compare two semver-like version strings (supports pre-release tags).
|
|
8
8
|
*
|
package/dist/compatibility.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Version compatibility between MCP server and WP plugin.
|
|
3
3
|
*/
|
|
4
4
|
/** Minimum WP plugin version this server requires. */
|
|
5
|
-
export const MIN_PLUGIN_VERSION = '1.0.0-beta.
|
|
5
|
+
export const MIN_PLUGIN_VERSION = '1.0.0-beta.31';
|
|
6
6
|
/**
|
|
7
7
|
* Compare two semver-like version strings (supports pre-release tags).
|
|
8
8
|
*
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { z } from "zod";
|
|
|
14
14
|
import { WPClient } from "./wp-client.js";
|
|
15
15
|
import { optimizeSchema } from "./schema-optimizer.js";
|
|
16
16
|
import { createWpCli } from "./wp-cli.js";
|
|
17
|
+
import { findForeignVarRefs, scanAttrsForForeignVarRefs, isolationErrorResult, } from "./validate-attrs.js";
|
|
17
18
|
import { readFileSync, readdirSync } from "fs";
|
|
18
19
|
import { join, dirname } from "path";
|
|
19
20
|
import { fileURLToPath } from "url";
|
|
@@ -240,6 +241,9 @@ server.registerTool("diviops_update_page_content", {
|
|
|
240
241
|
.describe("Full page content in WordPress block markup format (<!-- wp:divi/section -->...<!-- /wp:divi/section -->)"),
|
|
241
242
|
},
|
|
242
243
|
}, async ({ page_id, content }) => {
|
|
244
|
+
const hits = findForeignVarRefs(content, "content");
|
|
245
|
+
if (hits.length > 0)
|
|
246
|
+
return isolationErrorResult("diviops_update_page_content", hits);
|
|
243
247
|
const result = await wp.request(`/page/${page_id}/content`, {
|
|
244
248
|
method: "POST",
|
|
245
249
|
body: { content },
|
|
@@ -296,6 +300,9 @@ server.registerTool("diviops_append_section", {
|
|
|
296
300
|
.describe('Where to insert: "start" or "end" (default)'),
|
|
297
301
|
},
|
|
298
302
|
}, async ({ page_id, content, position }) => {
|
|
303
|
+
const hits = findForeignVarRefs(content, "content");
|
|
304
|
+
if (hits.length > 0)
|
|
305
|
+
return isolationErrorResult("diviops_append_section", hits);
|
|
299
306
|
const result = await wp.request(`/page/${page_id}/append`, {
|
|
300
307
|
method: "POST",
|
|
301
308
|
body: { content, position: position ?? "end" },
|
|
@@ -330,6 +337,9 @@ server.registerTool("diviops_replace_section", {
|
|
|
330
337
|
.describe("Which match to target (1-based, default: 1)"),
|
|
331
338
|
},
|
|
332
339
|
}, async ({ page_id, label, match_text, content, occurrence }) => {
|
|
340
|
+
const hits = findForeignVarRefs(content, "content");
|
|
341
|
+
if (hits.length > 0)
|
|
342
|
+
return isolationErrorResult("diviops_replace_section", hits);
|
|
333
343
|
const body = { content, occurrence };
|
|
334
344
|
if (label)
|
|
335
345
|
body.label = label;
|
|
@@ -443,6 +453,9 @@ server.registerTool("diviops_update_module", {
|
|
|
443
453
|
.describe("Attribute paths (dot notation) and their new values"),
|
|
444
454
|
},
|
|
445
455
|
}, async ({ page_id, label, match_text, auto_index, occurrence, attrs }) => {
|
|
456
|
+
const hits = scanAttrsForForeignVarRefs(attrs);
|
|
457
|
+
if (hits.length > 0)
|
|
458
|
+
return isolationErrorResult("diviops_update_module", hits);
|
|
446
459
|
const body = { attrs };
|
|
447
460
|
if (auto_index)
|
|
448
461
|
body.auto_index = auto_index;
|
|
@@ -552,6 +565,11 @@ server.registerTool("diviops_create_page", {
|
|
|
552
565
|
.describe("Post status"),
|
|
553
566
|
},
|
|
554
567
|
}, async ({ title, content, status }) => {
|
|
568
|
+
if (content) {
|
|
569
|
+
const hits = findForeignVarRefs(content, "content");
|
|
570
|
+
if (hits.length > 0)
|
|
571
|
+
return isolationErrorResult("diviops_create_page", hits);
|
|
572
|
+
}
|
|
555
573
|
const result = await wp.request("/page/create", {
|
|
556
574
|
method: "POST",
|
|
557
575
|
body: { title, content: content ?? "", status: status ?? "draft" },
|
|
@@ -708,7 +726,7 @@ server.registerTool("diviops_preset_create", {
|
|
|
708
726
|
};
|
|
709
727
|
});
|
|
710
728
|
server.registerTool("diviops_preset_reassign", {
|
|
711
|
-
description: 'Reassign a preset UUID across page content.
|
|
729
|
+
description: 'Reassign a preset UUID across page content. Covers both module-level refs (`attrs.modulePreset[...]`) and attribute-level group-preset refs (`attrs.groupPreset.<slot>.presetId`), plus — for group presets — registry chain refs in other presets\' `attrs.groupPresets.<slot>.presetId`. The `scope` param controls which ref types are walked (default "both", auto-selects based on new_uuid\'s bucket). Cross-bucket swaps (module ↔ group) are rejected. For module-scope swaps, optionally strips inline attrs that duplicate the new preset\'s attrs (otherwise inline wins over preset); slot-scoped inline strip for group scope is not yet implemented and is skipped with an advisory. Defaults to dry-run — set mode="apply" to actually rewrite. Use this to consolidate repeated inline styling into a reusable preset after creating one with diviops_preset_create.',
|
|
712
730
|
inputSchema: {
|
|
713
731
|
old_uuid: z
|
|
714
732
|
.string()
|
|
@@ -724,15 +742,26 @@ server.registerTool("diviops_preset_reassign", {
|
|
|
724
742
|
.enum(["dry-run", "apply"])
|
|
725
743
|
.optional()
|
|
726
744
|
.default("dry-run")
|
|
727
|
-
.describe('"dry-run" (default) returns the diff without writing. "apply" rewrites page content.'),
|
|
745
|
+
.describe('"dry-run" (default) returns the diff without writing. "apply" rewrites page content (and registry chains for group-scope swaps).'),
|
|
728
746
|
strip_inline: z
|
|
729
747
|
.boolean()
|
|
730
748
|
.optional()
|
|
731
749
|
.default(true)
|
|
732
|
-
.describe("If true (default), strip inline attrs that deep-equal the new preset's attrs so the preset actually takes effect. Set false to swap UUIDs only."),
|
|
750
|
+
.describe("If true (default), strip inline attrs that deep-equal the new preset's attrs so the preset actually takes effect. Applies to module-scope swaps only; group-scope swaps currently skip strip with an advisory in the summary. Set false to swap UUIDs only."),
|
|
751
|
+
scope: z
|
|
752
|
+
.enum(["module", "group", "both"])
|
|
753
|
+
.optional()
|
|
754
|
+
.default("both")
|
|
755
|
+
.describe('"module" walks `attrs.modulePreset[...]` only. "group" walks `attrs.groupPreset.<slot>.presetId` plus registry chain refs (`attrs.groupPresets.<slot>.presetId` in other presets). "both" (default) auto-selects based on new_uuid\'s bucket — module/group identity is disjoint, so there is one valid walk per swap. An explicit "module" or "group" rejects if new_uuid is in the wrong bucket.'),
|
|
733
756
|
},
|
|
734
|
-
}, async ({ old_uuid, new_uuid, page_ids, mode, strip_inline }) => {
|
|
735
|
-
const body = {
|
|
757
|
+
}, async ({ old_uuid, new_uuid, page_ids, mode, strip_inline, scope }) => {
|
|
758
|
+
const body = {
|
|
759
|
+
old_uuid,
|
|
760
|
+
new_uuid,
|
|
761
|
+
mode,
|
|
762
|
+
strip_inline,
|
|
763
|
+
scope,
|
|
764
|
+
};
|
|
736
765
|
if (page_ids)
|
|
737
766
|
body.page_ids = page_ids;
|
|
738
767
|
const result = await wp.request("/preset-reassign", {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolation-rule validator: bans cross-system var(--alias) refs in Divi
|
|
3
|
+
* module attrs. See SKILL.md rule 8 and references/module-formats.md
|
|
4
|
+
* §"Design Token References in Attrs".
|
|
5
|
+
*
|
|
6
|
+
* CSS spec: var(--undeclared-name) with no fallback falls through to the
|
|
7
|
+
* property's initial value (0 for padding, browser default for color).
|
|
8
|
+
* Tool reports success, renderer emits ref as-is, page silently breaks.
|
|
9
|
+
*
|
|
10
|
+
* Only var() refs to gcid-* / gvid-* pass — Divi-owned namespaces that
|
|
11
|
+
* auto-resolve via :root. Any other alias is rejected.
|
|
12
|
+
*/
|
|
13
|
+
export interface ForeignVarRef {
|
|
14
|
+
alias: string;
|
|
15
|
+
snippet: string;
|
|
16
|
+
location?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function findForeignVarRefs(value: unknown, location?: string): ForeignVarRef[];
|
|
19
|
+
export declare function scanAttrsForForeignVarRefs(attrs: Record<string, unknown>): ForeignVarRef[];
|
|
20
|
+
export declare function formatIsolationError(tool: string, hits: ForeignVarRef[]): string;
|
|
21
|
+
export declare function isolationErrorResult(tool: string, hits: ForeignVarRef[]): {
|
|
22
|
+
content: {
|
|
23
|
+
type: "text";
|
|
24
|
+
text: string;
|
|
25
|
+
}[];
|
|
26
|
+
isError: boolean;
|
|
27
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolation-rule validator: bans cross-system var(--alias) refs in Divi
|
|
3
|
+
* module attrs. See SKILL.md rule 8 and references/module-formats.md
|
|
4
|
+
* §"Design Token References in Attrs".
|
|
5
|
+
*
|
|
6
|
+
* CSS spec: var(--undeclared-name) with no fallback falls through to the
|
|
7
|
+
* property's initial value (0 for padding, browser default for color).
|
|
8
|
+
* Tool reports success, renderer emits ref as-is, page silently breaks.
|
|
9
|
+
*
|
|
10
|
+
* Only var() refs to gcid-* / gvid-* pass — Divi-owned namespaces that
|
|
11
|
+
* auto-resolve via :root. Any other alias is rejected.
|
|
12
|
+
*/
|
|
13
|
+
const VAR_REF_RE = /var\(\s*--([A-Za-z_][A-Za-z0-9_-]*)/g;
|
|
14
|
+
const ALLOWED_PREFIXES = ["gcid-", "gvid-"];
|
|
15
|
+
export function findForeignVarRefs(value, location) {
|
|
16
|
+
if (typeof value !== "string" || value.length === 0)
|
|
17
|
+
return [];
|
|
18
|
+
const hits = [];
|
|
19
|
+
VAR_REF_RE.lastIndex = 0;
|
|
20
|
+
let m;
|
|
21
|
+
while ((m = VAR_REF_RE.exec(value)) !== null) {
|
|
22
|
+
const alias = m[1];
|
|
23
|
+
if (ALLOWED_PREFIXES.some((p) => alias.startsWith(p)))
|
|
24
|
+
continue;
|
|
25
|
+
hits.push({ alias, snippet: `var(--${alias})`, location });
|
|
26
|
+
}
|
|
27
|
+
return hits;
|
|
28
|
+
}
|
|
29
|
+
export function scanAttrsForForeignVarRefs(attrs) {
|
|
30
|
+
const hits = [];
|
|
31
|
+
for (const [path, value] of Object.entries(attrs)) {
|
|
32
|
+
hits.push(...findForeignVarRefs(value, path));
|
|
33
|
+
}
|
|
34
|
+
return hits;
|
|
35
|
+
}
|
|
36
|
+
export function formatIsolationError(tool, hits) {
|
|
37
|
+
const uniq = new Map();
|
|
38
|
+
for (const h of hits) {
|
|
39
|
+
const k = `${h.snippet}::${h.location ?? ""}`;
|
|
40
|
+
if (!uniq.has(k))
|
|
41
|
+
uniq.set(k, h);
|
|
42
|
+
}
|
|
43
|
+
const lines = [
|
|
44
|
+
`Isolation-rule violation in ${tool}: module attrs cannot reference non-Divi CSS aliases.`,
|
|
45
|
+
"",
|
|
46
|
+
"Offending refs:",
|
|
47
|
+
...[...uniq.values()].map((h) => ` - ${h.snippet}${h.location ? ` (at ${h.location})` : ""}`),
|
|
48
|
+
"",
|
|
49
|
+
"Allowed: var(--gcid-*) and var(--gvid-*) (Divi-owned, auto-emitted to :root).",
|
|
50
|
+
'Canonical form: $variable({"type":"content","value":{"name":"gvid-your-token","settings":{}}})$',
|
|
51
|
+
"",
|
|
52
|
+
"Fix: register the token inside Divi Variable Manager (readable ID is fine, e.g. gvid-oa-space-3) and reference via $variable({...})$. See SKILL.md rule 8 / references/module-formats.md#design-token-references-in-attrs-canonical-variable-only.",
|
|
53
|
+
];
|
|
54
|
+
return lines.join("\n");
|
|
55
|
+
}
|
|
56
|
+
export function isolationErrorResult(tool, hits) {
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{ type: "text", text: formatIsolationError(tool, hits) },
|
|
60
|
+
],
|
|
61
|
+
isError: true,
|
|
62
|
+
};
|
|
63
|
+
}
|