@wootsup/yt-builder-mcp 0.2.0-alpha.2
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/LICENSE +21 -0
- package/README.md +221 -0
- package/bin/yt-builder-mcp.js +59 -0
- package/dist/auth.d.ts +39 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +93 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +84 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +151 -0
- package/dist/client.js.map +1 -0
- package/dist/clients/claude-code.d.ts +18 -0
- package/dist/clients/claude-code.d.ts.map +1 -0
- package/dist/clients/claude-code.js +53 -0
- package/dist/clients/claude-code.js.map +1 -0
- package/dist/clients/claude-desktop.d.ts +19 -0
- package/dist/clients/claude-desktop.d.ts.map +1 -0
- package/dist/clients/claude-desktop.js +56 -0
- package/dist/clients/claude-desktop.js.map +1 -0
- package/dist/clients/cline.d.ts +26 -0
- package/dist/clients/cline.d.ts.map +1 -0
- package/dist/clients/cline.js +80 -0
- package/dist/clients/cline.js.map +1 -0
- package/dist/clients/codex-cli.d.ts +42 -0
- package/dist/clients/codex-cli.d.ts.map +1 -0
- package/dist/clients/codex-cli.js +194 -0
- package/dist/clients/codex-cli.js.map +1 -0
- package/dist/clients/continue.d.ts +13 -0
- package/dist/clients/continue.d.ts.map +1 -0
- package/dist/clients/continue.js +52 -0
- package/dist/clients/continue.js.map +1 -0
- package/dist/clients/cursor.d.ts +12 -0
- package/dist/clients/cursor.d.ts.map +1 -0
- package/dist/clients/cursor.js +38 -0
- package/dist/clients/cursor.js.map +1 -0
- package/dist/clients/gemini-cli.d.ts +18 -0
- package/dist/clients/gemini-cli.d.ts.map +1 -0
- package/dist/clients/gemini-cli.js +44 -0
- package/dist/clients/gemini-cli.js.map +1 -0
- package/dist/clients/home.d.ts +14 -0
- package/dist/clients/home.d.ts.map +1 -0
- package/dist/clients/home.js +20 -0
- package/dist/clients/home.js.map +1 -0
- package/dist/clients/index.d.ts +52 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +72 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/roo-code.d.ts +23 -0
- package/dist/clients/roo-code.d.ts.map +1 -0
- package/dist/clients/roo-code.js +69 -0
- package/dist/clients/roo-code.js.map +1 -0
- package/dist/clients/zed.d.ts +12 -0
- package/dist/clients/zed.d.ts.map +1 -0
- package/dist/clients/zed.js +41 -0
- package/dist/clients/zed.js.map +1 -0
- package/dist/errors/hints.d.ts +51 -0
- package/dist/errors/hints.d.ts.map +1 -0
- package/dist/errors/hints.js +95 -0
- package/dist/errors/hints.js.map +1 -0
- package/dist/errors/mask.d.ts +35 -0
- package/dist/errors/mask.d.ts.map +1 -0
- package/dist/errors/mask.js +49 -0
- package/dist/errors/mask.js.map +1 -0
- package/dist/errors/sanitize.d.ts +31 -0
- package/dist/errors/sanitize.d.ts.map +1 -0
- package/dist/errors/sanitize.js +90 -0
- package/dist/errors/sanitize.js.map +1 -0
- package/dist/errors.d.ts +42 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -0
- package/dist/gateway/advanced-tool/discovery.d.ts +19 -0
- package/dist/gateway/advanced-tool/discovery.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/discovery.js +53 -0
- package/dist/gateway/advanced-tool/discovery.js.map +1 -0
- package/dist/gateway/advanced-tool/domains.d.ts +42 -0
- package/dist/gateway/advanced-tool/domains.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/domains.js +88 -0
- package/dist/gateway/advanced-tool/domains.js.map +1 -0
- package/dist/gateway/advanced-tool/execute.d.ts +29 -0
- package/dist/gateway/advanced-tool/execute.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/execute.js +54 -0
- package/dist/gateway/advanced-tool/execute.js.map +1 -0
- package/dist/gateway/advanced-tool/index.d.ts +36 -0
- package/dist/gateway/advanced-tool/index.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/index.js +39 -0
- package/dist/gateway/advanced-tool/index.js.map +1 -0
- package/dist/gateway/advanced-tool/register.d.ts +18 -0
- package/dist/gateway/advanced-tool/register.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/register.js +62 -0
- package/dist/gateway/advanced-tool/register.js.map +1 -0
- package/dist/gateway/advanced-tool.d.ts +13 -0
- package/dist/gateway/advanced-tool.d.ts.map +1 -0
- package/dist/gateway/advanced-tool.js +13 -0
- package/dist/gateway/advanced-tool.js.map +1 -0
- package/dist/gateway/capturing-server.d.ts +117 -0
- package/dist/gateway/capturing-server.d.ts.map +1 -0
- package/dist/gateway/capturing-server.js +103 -0
- package/dist/gateway/capturing-server.js.map +1 -0
- package/dist/gateway/essentials.d.ts +49 -0
- package/dist/gateway/essentials.d.ts.map +1 -0
- package/dist/gateway/essentials.js +62 -0
- package/dist/gateway/essentials.js.map +1 -0
- package/dist/gateway/test-support.d.ts +41 -0
- package/dist/gateway/test-support.d.ts.map +1 -0
- package/dist/gateway/test-support.js +60 -0
- package/dist/gateway/test-support.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/install-skill.d.ts +35 -0
- package/dist/install-skill.d.ts.map +1 -0
- package/dist/install-skill.js +107 -0
- package/dist/install-skill.js.map +1 -0
- package/dist/platform/index.d.ts +49 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +38 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/server.d.ts +50 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +117 -0
- package/dist/server.js.map +1 -0
- package/dist/setup-cli.d.ts +100 -0
- package/dist/setup-cli.d.ts.map +1 -0
- package/dist/setup-cli.js +355 -0
- package/dist/setup-cli.js.map +1 -0
- package/dist/setup-prompts.d.ts +41 -0
- package/dist/setup-prompts.d.ts.map +1 -0
- package/dist/setup-prompts.js +142 -0
- package/dist/setup-prompts.js.map +1 -0
- package/dist/setup-token.d.ts +38 -0
- package/dist/setup-token.d.ts.map +1 -0
- package/dist/setup-token.js +106 -0
- package/dist/setup-token.js.map +1 -0
- package/dist/setup-wizard-defaults.d.ts +43 -0
- package/dist/setup-wizard-defaults.d.ts.map +1 -0
- package/dist/setup-wizard-defaults.js +160 -0
- package/dist/setup-wizard-defaults.js.map +1 -0
- package/dist/setup-wizard-handshake.d.ts +25 -0
- package/dist/setup-wizard-handshake.d.ts.map +1 -0
- package/dist/setup-wizard-handshake.js +103 -0
- package/dist/setup-wizard-handshake.js.map +1 -0
- package/dist/setup-wizard-types.d.ts +148 -0
- package/dist/setup-wizard-types.d.ts.map +1 -0
- package/dist/setup-wizard-types.js +11 -0
- package/dist/setup-wizard-types.js.map +1 -0
- package/dist/setup-wizard.d.ts +33 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +166 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/setup.d.ts +17 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +33 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools/elements/builders.d.ts +24 -0
- package/dist/tools/elements/builders.d.ts.map +1 -0
- package/dist/tools/elements/builders.js +150 -0
- package/dist/tools/elements/builders.js.map +1 -0
- package/dist/tools/elements/handlers-write.d.ts +48 -0
- package/dist/tools/elements/handlers-write.d.ts.map +1 -0
- package/dist/tools/elements/handlers-write.js +141 -0
- package/dist/tools/elements/handlers-write.js.map +1 -0
- package/dist/tools/elements/handlers.d.ts +56 -0
- package/dist/tools/elements/handlers.d.ts.map +1 -0
- package/dist/tools/elements/handlers.js +113 -0
- package/dist/tools/elements/handlers.js.map +1 -0
- package/dist/tools/elements/index.d.ts +28 -0
- package/dist/tools/elements/index.d.ts.map +1 -0
- package/dist/tools/elements/index.js +27 -0
- package/dist/tools/elements/index.js.map +1 -0
- package/dist/tools/elements.d.ts +13 -0
- package/dist/tools/elements.d.ts.map +1 -0
- package/dist/tools/elements.js +13 -0
- package/dist/tools/elements.js.map +1 -0
- package/dist/tools/elicitation.d.ts +87 -0
- package/dist/tools/elicitation.d.ts.map +1 -0
- package/dist/tools/elicitation.js +100 -0
- package/dist/tools/elicitation.js.map +1 -0
- package/dist/tools/format/elements-format.d.ts +34 -0
- package/dist/tools/format/elements-format.d.ts.map +1 -0
- package/dist/tools/format/elements-format.js +112 -0
- package/dist/tools/format/elements-format.js.map +1 -0
- package/dist/tools/format/health-format.d.ts +73 -0
- package/dist/tools/format/health-format.d.ts.map +1 -0
- package/dist/tools/format/health-format.js +178 -0
- package/dist/tools/format/health-format.js.map +1 -0
- package/dist/tools/format/inspection-format.d.ts +45 -0
- package/dist/tools/format/inspection-format.d.ts.map +1 -0
- package/dist/tools/format/inspection-format.js +125 -0
- package/dist/tools/format/inspection-format.js.map +1 -0
- package/dist/tools/format/pages-format.d.ts +39 -0
- package/dist/tools/format/pages-format.d.ts.map +1 -0
- package/dist/tools/format/pages-format.js +110 -0
- package/dist/tools/format/pages-format.js.map +1 -0
- package/dist/tools/format/sources-format.d.ts +25 -0
- package/dist/tools/format/sources-format.d.ts.map +1 -0
- package/dist/tools/format/sources-format.js +113 -0
- package/dist/tools/format/sources-format.js.map +1 -0
- package/dist/tools/health.d.ts +22 -0
- package/dist/tools/health.d.ts.map +1 -0
- package/dist/tools/health.js +147 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/inspection.d.ts +14 -0
- package/dist/tools/inspection.d.ts.map +1 -0
- package/dist/tools/inspection.js +115 -0
- package/dist/tools/inspection.js.map +1 -0
- package/dist/tools/layout-flatten.d.ts +63 -0
- package/dist/tools/layout-flatten.d.ts.map +1 -0
- package/dist/tools/layout-flatten.js +95 -0
- package/dist/tools/layout-flatten.js.map +1 -0
- package/dist/tools/pages/builders.d.ts +14 -0
- package/dist/tools/pages/builders.d.ts.map +1 -0
- package/dist/tools/pages/builders.js +97 -0
- package/dist/tools/pages/builders.js.map +1 -0
- package/dist/tools/pages/handlers-read.d.ts +24 -0
- package/dist/tools/pages/handlers-read.d.ts.map +1 -0
- package/dist/tools/pages/handlers-read.js +141 -0
- package/dist/tools/pages/handlers-read.js.map +1 -0
- package/dist/tools/pages/handlers-write.d.ts +21 -0
- package/dist/tools/pages/handlers-write.d.ts.map +1 -0
- package/dist/tools/pages/handlers-write.js +52 -0
- package/dist/tools/pages/handlers-write.js.map +1 -0
- package/dist/tools/pages/index.d.ts +17 -0
- package/dist/tools/pages/index.d.ts.map +1 -0
- package/dist/tools/pages/index.js +17 -0
- package/dist/tools/pages/index.js.map +1 -0
- package/dist/tools/pages/schemas.d.ts +30 -0
- package/dist/tools/pages/schemas.d.ts.map +1 -0
- package/dist/tools/pages/schemas.js +30 -0
- package/dist/tools/pages/schemas.js.map +1 -0
- package/dist/tools/pages.d.ts +13 -0
- package/dist/tools/pages.d.ts.map +1 -0
- package/dist/tools/pages.js +13 -0
- package/dist/tools/pages.js.map +1 -0
- package/dist/tools/progress-phases.d.ts +46 -0
- package/dist/tools/progress-phases.d.ts.map +1 -0
- package/dist/tools/progress-phases.js +48 -0
- package/dist/tools/progress-phases.js.map +1 -0
- package/dist/tools/shared-schemas.d.ts +15 -0
- package/dist/tools/shared-schemas.d.ts.map +1 -0
- package/dist/tools/shared-schemas.js +30 -0
- package/dist/tools/shared-schemas.js.map +1 -0
- package/dist/tools/sources/builders.d.ts +13 -0
- package/dist/tools/sources/builders.d.ts.map +1 -0
- package/dist/tools/sources/builders.js +88 -0
- package/dist/tools/sources/builders.js.map +1 -0
- package/dist/tools/sources/handlers-bind.d.ts +26 -0
- package/dist/tools/sources/handlers-bind.d.ts.map +1 -0
- package/dist/tools/sources/handlers-bind.js +123 -0
- package/dist/tools/sources/handlers-bind.js.map +1 -0
- package/dist/tools/sources/handlers.d.ts +45 -0
- package/dist/tools/sources/handlers.d.ts.map +1 -0
- package/dist/tools/sources/handlers.js +84 -0
- package/dist/tools/sources/handlers.js.map +1 -0
- package/dist/tools/sources/index.d.ts +19 -0
- package/dist/tools/sources/index.d.ts.map +1 -0
- package/dist/tools/sources/index.js +18 -0
- package/dist/tools/sources/index.js.map +1 -0
- package/dist/tools/sources.d.ts +14 -0
- package/dist/tools/sources.d.ts.map +1 -0
- package/dist/tools/sources.js +14 -0
- package/dist/tools/sources.js.map +1 -0
- package/dist/tools/sparse-fields.d.ts +80 -0
- package/dist/tools/sparse-fields.d.ts.map +1 -0
- package/dist/tools/sparse-fields.js +144 -0
- package/dist/tools/sparse-fields.js.map +1 -0
- package/dist/tools/tool-builder/annotations.d.ts +22 -0
- package/dist/tools/tool-builder/annotations.d.ts.map +1 -0
- package/dist/tools/tool-builder/annotations.js +35 -0
- package/dist/tools/tool-builder/annotations.js.map +1 -0
- package/dist/tools/tool-builder/define.d.ts +31 -0
- package/dist/tools/tool-builder/define.d.ts.map +1 -0
- package/dist/tools/tool-builder/define.js +31 -0
- package/dist/tools/tool-builder/define.js.map +1 -0
- package/dist/tools/tool-builder/index.d.ts +28 -0
- package/dist/tools/tool-builder/index.d.ts.map +1 -0
- package/dist/tools/tool-builder/index.js +27 -0
- package/dist/tools/tool-builder/index.js.map +1 -0
- package/dist/tools/tool-builder/results.d.ts +59 -0
- package/dist/tools/tool-builder/results.d.ts.map +1 -0
- package/dist/tools/tool-builder/results.js +125 -0
- package/dist/tools/tool-builder/results.js.map +1 -0
- package/dist/tools/tool-builder/types.d.ts +82 -0
- package/dist/tools/tool-builder/types.d.ts.map +1 -0
- package/dist/tools/tool-builder/types.js +9 -0
- package/dist/tools/tool-builder/types.js.map +1 -0
- package/dist/tools/tool-builder.d.ts +16 -0
- package/dist/tools/tool-builder.d.ts.map +1 -0
- package/dist/tools/tool-builder.js +16 -0
- package/dist/tools/tool-builder.js.map +1 -0
- package/icon.png +0 -0
- package/manifest.json +63 -0
- package/package.json +81 -0
- package/skills/yootheme-builder/SKILL.md +582 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yootheme-builder
|
|
3
|
+
description: Drive the YOOtheme Pro page builder programmatically — discover pages, inspect layouts, add/move/clone/delete elements, bind dynamic sources, diagnose 401/403 auth failures. Use when the user wants to build, modify, audit, or troubleshoot a YOOtheme-powered WordPress site through the YT Builder MCP server.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# YT Builder MCP for YOOtheme Pro (unofficial) — Skill
|
|
7
|
+
|
|
8
|
+
> Independent third-party project. YOOtheme® is a registered trademark of YOOtheme GmbH
|
|
9
|
+
> ([yootheme.com](https://yootheme.com)). YT Builder MCP is built by WootsUp (getimo
|
|
10
|
+
> productions) and is not affiliated with, endorsed by, or sponsored by YOOtheme.
|
|
11
|
+
> The integration uses YOOtheme Pro's public extension points.
|
|
12
|
+
|
|
13
|
+
This skill helps AI assistants drive the YOOtheme Pro page builder through the
|
|
14
|
+
`@wootsup/yt-builder-mcp` server. The server exposes 22 typed,
|
|
15
|
+
scoped, idempotent tools behind a 10-entry Gateway-Hub (so it stays
|
|
16
|
+
inside the 80-tool Cursor cap even when the catalogue grows).
|
|
17
|
+
|
|
18
|
+
## How to use this MCP server
|
|
19
|
+
|
|
20
|
+
The user invokes you through Claude Desktop, Cursor, Zed, Continue, or
|
|
21
|
+
any other MCP-aware AI client. Setup looks like this:
|
|
22
|
+
|
|
23
|
+
1. The user installs the WordPress plugin
|
|
24
|
+
(`https://wootsup.com/products/yt-builder-mcp`) and generates
|
|
25
|
+
a Bearer key in **wp-admin → Tools → "YT Builder MCP" → Bearer Keys**.
|
|
26
|
+
2. The user runs `npx -y @wootsup/yt-builder-mcp setup` once;
|
|
27
|
+
the wizard probes the plugin, validates the key, and writes the
|
|
28
|
+
MCP server entry into every selected AI client's config file.
|
|
29
|
+
3. The user restarts their AI client. The server is now visible.
|
|
30
|
+
4. The user asks for a YOOtheme task (build, audit, change, diagnose).
|
|
31
|
+
|
|
32
|
+
When the user asks a YOOtheme-related question, **always start with
|
|
33
|
+
`yootheme_builder_health`** — it confirms the plugin is reachable and
|
|
34
|
+
returns the plugin/YOOtheme/WordPress/PHP versions you need to know
|
|
35
|
+
about before reading or writing layout state.
|
|
36
|
+
|
|
37
|
+
If a tool returns `401 Unauthorized` or `403 Forbidden`, jump straight
|
|
38
|
+
to **Workflow 4: Diagnose 401/auth failure**. Do not retry blindly.
|
|
39
|
+
|
|
40
|
+
## Gateway routing (so you know what you can call)
|
|
41
|
+
|
|
42
|
+
The server exposes:
|
|
43
|
+
|
|
44
|
+
- **2 direct top-level tools** — always callable, always in `tools/list`:
|
|
45
|
+
`yootheme_builder_health` and `yootheme_builder_diagnose`. These are
|
|
46
|
+
the "the gateway itself might be broken" escape hatch.
|
|
47
|
+
- **7 essential forwarded tools** — common reads + the most-used writes
|
|
48
|
+
(page list / get / save / publish, element list / add / update). Also
|
|
49
|
+
always in `tools/list` so AI clients see them first-class.
|
|
50
|
+
- **12 advanced captured tools** — everything else (move, clone, delete,
|
|
51
|
+
schema introspection, source binding). Reachable through one gateway
|
|
52
|
+
tool: `yootheme_builder_advanced({ tool: "<name>", input: { ... } })`.
|
|
53
|
+
- **1 gateway tool** — `yootheme_builder_advanced`.
|
|
54
|
+
|
|
55
|
+
If the AI client reports "tool not found", you are almost certainly
|
|
56
|
+
calling an advanced tool by its raw name. Wrap it in
|
|
57
|
+
`yootheme_builder_advanced({ tool, input })` instead.
|
|
58
|
+
|
|
59
|
+
## Scopes (Bearer key permissions)
|
|
60
|
+
|
|
61
|
+
Every Bearer key has a scope, set at key creation time:
|
|
62
|
+
|
|
63
|
+
| Scope | Reads | Writes | Destructive |
|
|
64
|
+
|----------|-------|--------|-------------|
|
|
65
|
+
| `read` | ✓ | ✗ | ✗ |
|
|
66
|
+
| `write` | ✓ | ✓ | ✗ |
|
|
67
|
+
| `admin` | ✓ | ✓ | ✓ |
|
|
68
|
+
|
|
69
|
+
When a tool returns `{ error: 'insufficient_scope', context: { required: 'write', actual: 'read' } }`,
|
|
70
|
+
ask the user to regenerate the key with a higher scope **before**
|
|
71
|
+
retrying. Do not loop on auth errors.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Workflow 1: Build a hero section
|
|
76
|
+
|
|
77
|
+
**Goal:** Add a fresh hero section (heading + sub-heading + CTA button)
|
|
78
|
+
to an existing page.
|
|
79
|
+
|
|
80
|
+
**Canonical tool-call sequence (real parameter names — snake_case):**
|
|
81
|
+
|
|
82
|
+
1. `yootheme_builder_health` — confirm plugin reachable; note plugin
|
|
83
|
+
version (some element types are version-gated).
|
|
84
|
+
2. `yootheme_builder_pages_list({ fields: ["id", "label"] })` — find
|
|
85
|
+
the target template. Returns `[{ id, label, ... }]`. If the user
|
|
86
|
+
named a specific page, match on `label` (exact then fuzzy).
|
|
87
|
+
3. `yootheme_builder_get_etag()` — fetch the current top-level
|
|
88
|
+
optimistic-lock ETag. Every write tool requires it via `etag`.
|
|
89
|
+
4. `yootheme_builder_element_add({ template_id: "<id>", parent_path: "", element_type: "section", props: { background: "primary" }, etag: "<etag>" })`
|
|
90
|
+
— append a new section at the template root (`parent_path: ""`).
|
|
91
|
+
Returns `{ path: "/0/children/N", etag: "<fresh>" }`.
|
|
92
|
+
5. `yootheme_builder_element_add({ template_id, parent_path: "<section-path>", element_type: "row", etag: "<fresh-etag>" })`
|
|
93
|
+
— add a row inside the section. Use the etag returned by the
|
|
94
|
+
previous write — etags rotate every mutation.
|
|
95
|
+
6. `yootheme_builder_element_add({ template_id, parent_path: "<row-path>", element_type: "headline", props: { content: "<h1 text>" }, etag })`
|
|
96
|
+
— add a headline.
|
|
97
|
+
7. `yootheme_builder_element_add({ template_id, parent_path: "<row-path>", element_type: "text", props: { content: "<sub text>" }, etag })`
|
|
98
|
+
— add a text element.
|
|
99
|
+
8. `yootheme_builder_element_add({ template_id, parent_path: "<row-path>", element_type: "button", props: { content: "<cta>", link: "<url>" }, etag })`
|
|
100
|
+
— add the CTA button.
|
|
101
|
+
9. `yootheme_builder_page_save({ template_id, etag })` — persist the
|
|
102
|
+
working copy (visible in YOOtheme Customizer preview).
|
|
103
|
+
10. `yootheme_builder_page_publish({ template_id, etag })` — make the
|
|
104
|
+
changes live on the front-end.
|
|
105
|
+
|
|
106
|
+
**Common pitfalls:**
|
|
107
|
+
|
|
108
|
+
- **Wrong parameter names.** Every tool uses snake_case. Use
|
|
109
|
+
`template_id` (not `pageId`), `parent_path` (not `parentPath`),
|
|
110
|
+
`element_type` (not `type`), `props` (not `settings`), `etag`
|
|
111
|
+
(not `ifMatch`). The MCP server rejects unknown keys with a
|
|
112
|
+
Zod-validation error.
|
|
113
|
+
- **Forgetting `etag`.** Every write tool needs the latest etag. The
|
|
114
|
+
shared schema marks it required (min length 1). On `412 Precondition
|
|
115
|
+
Failed` re-fetch via `yootheme_builder_get_etag` and retry.
|
|
116
|
+
- **Adding non-row elements directly to a section.** Sections expect a
|
|
117
|
+
row in between. The server returns a structured error with a
|
|
118
|
+
human-readable hint when you skip the row.
|
|
119
|
+
- **Saving without publishing.** `page_save` is the equivalent of the
|
|
120
|
+
YOOtheme Customizer "Save" button — content lives in the staging
|
|
121
|
+
copy. Visitors see nothing until `page_publish`.
|
|
122
|
+
- **Reusing a stale etag across many writes.** Every write returns a
|
|
123
|
+
fresh etag in the response. Pass THAT etag into the next write —
|
|
124
|
+
don't hold the one from the original `get_etag` call.
|
|
125
|
+
|
|
126
|
+
**Worked example (tool-call snippet):**
|
|
127
|
+
|
|
128
|
+
```jsonc
|
|
129
|
+
// Step 4 — add the section. parent_path: "" means template root.
|
|
130
|
+
yootheme_builder_element_add({
|
|
131
|
+
template_id: "home",
|
|
132
|
+
parent_path: "",
|
|
133
|
+
element_type: "section",
|
|
134
|
+
props: { background: "primary" },
|
|
135
|
+
etag: "abc123" // from yootheme_builder_get_etag
|
|
136
|
+
})
|
|
137
|
+
// Response: { path: "/0/children/3", etag: "def456" }
|
|
138
|
+
// → next call uses etag "def456"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Edge case:** YOOtheme allows nested sections (rare) — if the user
|
|
142
|
+
asks for a "card grid inside a hero", you still need the
|
|
143
|
+
`section → row → column → grid` hierarchy, even when the parent
|
|
144
|
+
section sits inside another section.
|
|
145
|
+
|
|
146
|
+
**Success criterion:** After `page_publish`, navigating to the page
|
|
147
|
+
URL on the front-end shows the new hero section above the previous
|
|
148
|
+
content. Re-reading the layout via
|
|
149
|
+
`yootheme_builder_page_get_layout({ template_id })` shows the new
|
|
150
|
+
section as the last child of the template root.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Workflow 2: Bind a dynamic source to a grid
|
|
155
|
+
|
|
156
|
+
**Goal:** Wire an existing Grid (or other multi-item element) to a
|
|
157
|
+
Source from API Mapper or the built-in YOOtheme Sources system so it
|
|
158
|
+
renders dynamic items.
|
|
159
|
+
|
|
160
|
+
**Canonical tool-call sequence (real parameter names — snake_case):**
|
|
161
|
+
|
|
162
|
+
1. `yootheme_builder_health` — confirm plugin reachable.
|
|
163
|
+
2. `yootheme_builder_pages_list({ fields: ["id", "label"] })` and
|
|
164
|
+
`yootheme_builder_page_get_layout({ template_id: "<id>", flat: false })`
|
|
165
|
+
— locate the target Grid. Note its JSON-Pointer `path` (e.g.
|
|
166
|
+
`/0/children/2/children/0`).
|
|
167
|
+
3. `yootheme_builder_element_get({ template_id, element_path })` —
|
|
168
|
+
fetch the Grid's current props so you can preserve them; binding
|
|
169
|
+
sets `props.source` and leaves the rest alone.
|
|
170
|
+
4. `yootheme_builder_sources_list()` — enumerate available Sources.
|
|
171
|
+
Each returns `{ name, label, origin, kind }`. Pick the one the
|
|
172
|
+
user asked for.
|
|
173
|
+
5. `yootheme_builder_element_get_binding({ template_id, element_path })`
|
|
174
|
+
— check whether the Grid is already bound (idempotency: skip step
|
|
175
|
+
7 if `source_name` already matches the user's intent).
|
|
176
|
+
6. `yootheme_builder_get_etag()` — fetch the optimistic-lock etag for
|
|
177
|
+
the upcoming mutation.
|
|
178
|
+
7. `yootheme_builder_element_bind_source({ template_id, element_path, source_name: "<name>", etag: "<etag>" })`
|
|
179
|
+
— apply the binding. Returns `{ path, etag, has_binding: true }`.
|
|
180
|
+
Pass `source_id: "<origin>:<name>"` as well **only** when two
|
|
181
|
+
plugins register a source with the same `source_name` (the server
|
|
182
|
+
surfaces the ambiguity as an elicitation prompt; if the host
|
|
183
|
+
doesn't support elicitation you'll see a structured error listing
|
|
184
|
+
the candidates).
|
|
185
|
+
8. `yootheme_builder_page_save({ template_id, etag: "<fresh>" })`
|
|
186
|
+
then `yootheme_builder_page_publish({ template_id, etag: "<fresh>" })`.
|
|
187
|
+
|
|
188
|
+
**Common pitfalls:**
|
|
189
|
+
|
|
190
|
+
- **Inventing `fieldMap`.** The bind tool's schema is just
|
|
191
|
+
`template_id`, `element_path`, `source_name`, optional `source_id`,
|
|
192
|
+
`etag`. Field mapping happens inside YOOtheme at render time based
|
|
193
|
+
on the element's own field bindings — not via an MCP parameter.
|
|
194
|
+
- **Wrong parameter names.** Use `template_id` (not `pageId`),
|
|
195
|
+
`element_path` (not `path`), `source_name` (not `sourceName`),
|
|
196
|
+
`etag` (not `ifMatch`).
|
|
197
|
+
- **Source not in the list.** API Mapper sources only appear once
|
|
198
|
+
they're PUBLISHED in API Mapper (not just saved). If
|
|
199
|
+
`yootheme_builder_sources_list` returns no match for the name the
|
|
200
|
+
user typed, send the user to API Mapper to publish it.
|
|
201
|
+
- **Binding non-list elements.** Only multi-item element types (Grid,
|
|
202
|
+
List, Switcher, Slider, Slideshow, Carousel, Map) accept a source
|
|
203
|
+
binding. Binding a single-item element like Headline returns a
|
|
204
|
+
structured `validation` error.
|
|
205
|
+
- **Forgetting `etag`.** Every write requires the optimistic-lock
|
|
206
|
+
etag. On `412 Precondition Failed` re-fetch via
|
|
207
|
+
`yootheme_builder_get_etag` and retry.
|
|
208
|
+
|
|
209
|
+
**Worked example (tool-call snippet):**
|
|
210
|
+
|
|
211
|
+
```jsonc
|
|
212
|
+
// Step 7 — bind a Posts source onto a Grid element.
|
|
213
|
+
yootheme_builder_element_bind_source({
|
|
214
|
+
template_id: "home",
|
|
215
|
+
element_path: "/0/children/2/children/0",
|
|
216
|
+
source_name: "wp_posts",
|
|
217
|
+
etag: "abc123"
|
|
218
|
+
// source_id: "wordpress:wp_posts" // pass ONLY when name collides
|
|
219
|
+
})
|
|
220
|
+
// Response: { path: "/0/children/2/children/0", etag: "def456", has_binding: true }
|
|
221
|
+
// Verify:
|
|
222
|
+
yootheme_builder_element_get_binding({
|
|
223
|
+
template_id: "home",
|
|
224
|
+
element_path: "/0/children/2/children/0"
|
|
225
|
+
})
|
|
226
|
+
// → { source_name: "wp_posts", source_config: { ... }, ... }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Edge case:** A Source can render zero items at runtime (e.g. empty
|
|
230
|
+
search filter). The bind call still succeeds; the front-end Grid just
|
|
231
|
+
shows the YOOtheme "no items" placeholder. Don't treat empty render
|
|
232
|
+
as a binding failure — verify by re-reading
|
|
233
|
+
`yootheme_builder_element_get_binding`.
|
|
234
|
+
|
|
235
|
+
**Success criterion:** After publish, the Grid on the front-end shows
|
|
236
|
+
items from the Source (verify by item count and at least one
|
|
237
|
+
field-value spot-check). `yootheme_builder_element_get_binding`
|
|
238
|
+
returns the new `source_name`.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Workflow 3: Clone & modify a section within a template
|
|
243
|
+
|
|
244
|
+
**Goal:** Duplicate a section inside the SAME template and tweak the
|
|
245
|
+
copy. Common variants: A/B-style hero, repeated CTA blocks, mirroring
|
|
246
|
+
a row layout. (Cross-template duplication is **not** supported by
|
|
247
|
+
`element_clone` — see "Important scope note" below.)
|
|
248
|
+
|
|
249
|
+
**Important scope note:** `yootheme_builder_element_clone` is
|
|
250
|
+
**sibling-only and intra-template**. Its real schema is
|
|
251
|
+
`{ template_id, element_path, etag }` — there is **no** `destPageId`
|
|
252
|
+
or `destParentPath`. The cloned element lands at the same parent,
|
|
253
|
+
right after the source. To move the clone elsewhere in the SAME
|
|
254
|
+
template, call `yootheme_builder_element_move` afterwards. To
|
|
255
|
+
duplicate into a DIFFERENT template, flag to the user that
|
|
256
|
+
cross-template clone is not currently supported and suggest a
|
|
257
|
+
WordPress-level template duplication.
|
|
258
|
+
|
|
259
|
+
**Canonical tool-call sequence (real parameter names — snake_case):**
|
|
260
|
+
|
|
261
|
+
1. `yootheme_builder_health` — confirm plugin reachable.
|
|
262
|
+
2. `yootheme_builder_pages_list({ fields: ["id", "label"] })` —
|
|
263
|
+
locate the template by `label`. Note its `id`.
|
|
264
|
+
3. `yootheme_builder_page_get_schema({ template_id })` — flat schema
|
|
265
|
+
view (lighter than `page_get_layout`) showing every element path
|
|
266
|
+
+ type. Pick the JSON-Pointer path of the section to clone.
|
|
267
|
+
4. `yootheme_builder_get_etag()` — fetch the optimistic-lock etag.
|
|
268
|
+
5. `yootheme_builder_element_clone({ template_id, element_path: "<src-path>", etag: "<etag>" })`
|
|
269
|
+
— clone as sibling. Returns `{ path: "<new-path>", etag: "<fresh>" }`.
|
|
270
|
+
The new path is at the same parent, immediately after the source.
|
|
271
|
+
6. (Optional) `yootheme_builder_element_move({ template_id, element_path: "<new-path>", to_parent_path: "<other-parent>", to_index: 0, etag: "<fresh>" })`
|
|
272
|
+
— re-parent the clone within the same template if needed.
|
|
273
|
+
7. `yootheme_builder_element_update_settings({ template_id, element_path: "<final-path>", props: { ... }, etag: "<fresh>" })`
|
|
274
|
+
— replace the `props` on the clone. **Existing props NOT in the
|
|
275
|
+
request are removed** (update_settings is a full replace, not a
|
|
276
|
+
merge). Read the current props first via
|
|
277
|
+
`yootheme_builder_element_get` if you only want to tweak a subset.
|
|
278
|
+
8. `yootheme_builder_page_save({ template_id, etag })` then
|
|
279
|
+
`yootheme_builder_page_publish({ template_id, etag })`.
|
|
280
|
+
|
|
281
|
+
**Common pitfalls:**
|
|
282
|
+
|
|
283
|
+
- **Inventing destination parameters.** `element_clone` does NOT
|
|
284
|
+
accept `destPageId`, `destParentPath`, or any cross-template
|
|
285
|
+
argument. It's sibling-only within ONE template.
|
|
286
|
+
- **Treating `element_update_settings` as a merge.** The handler
|
|
287
|
+
REPLACES the entire `props` object on the element; any key you
|
|
288
|
+
don't include is removed. Use `element_get` first if you need to
|
|
289
|
+
preserve siblings of the field you're changing.
|
|
290
|
+
- **Clone-then-update path drift.** The clone returns a path that's
|
|
291
|
+
correct at the moment of the call. If you fire off many ops in
|
|
292
|
+
parallel, a concurrent edit may shift indices — refresh via
|
|
293
|
+
`get_etag` + `page_get_schema` between independent batches.
|
|
294
|
+
- **Cloning a bound element keeps the binding.** `element_clone`
|
|
295
|
+
copies the entire element including `props.source`. If the user
|
|
296
|
+
wanted a "data-free" copy, call
|
|
297
|
+
`yootheme_builder_element_unbind_source` on the new path
|
|
298
|
+
afterwards.
|
|
299
|
+
- **Wrong parameter names.** Use `template_id`, `element_path`,
|
|
300
|
+
`etag` (NOT `pageId`, `srcPath`, `ifMatch`).
|
|
301
|
+
|
|
302
|
+
**Worked example (tool-call snippet):**
|
|
303
|
+
|
|
304
|
+
```jsonc
|
|
305
|
+
// Step 5 — clone the section element as a sibling.
|
|
306
|
+
yootheme_builder_element_clone({
|
|
307
|
+
template_id: "home",
|
|
308
|
+
element_path: "/0/children/2", // the hero section to duplicate
|
|
309
|
+
etag: "abc123"
|
|
310
|
+
})
|
|
311
|
+
// Response: { path: "/0/children/3", etag: "def456" }
|
|
312
|
+
|
|
313
|
+
// Step 7 — tweak the clone (replace props entirely).
|
|
314
|
+
// First read the current shape so you can preserve siblings:
|
|
315
|
+
const current = yootheme_builder_element_get({
|
|
316
|
+
template_id: "home",
|
|
317
|
+
element_path: "/0/children/3",
|
|
318
|
+
});
|
|
319
|
+
yootheme_builder_element_update_settings({
|
|
320
|
+
template_id: "home",
|
|
321
|
+
element_path: "/0/children/3",
|
|
322
|
+
props: { ...current.props, background: "secondary" },
|
|
323
|
+
etag: "def456"
|
|
324
|
+
})
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Edge case:** When cloning a Grid with a source binding, the binding
|
|
328
|
+
is preserved (same `source_name`). If the user wants a "data-free"
|
|
329
|
+
copy, follow up with `yootheme_builder_element_unbind_source` on the
|
|
330
|
+
new path. Verify with `yootheme_builder_element_get_binding`.
|
|
331
|
+
|
|
332
|
+
**Success criterion:** After publish,
|
|
333
|
+
`yootheme_builder_page_get_schema({ template_id })` shows the new
|
|
334
|
+
section at the cloned path with the user's tweaks reflected in
|
|
335
|
+
`element_get` on that path.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Workflow 4: Diagnose a 401 / 403 / auth failure
|
|
340
|
+
|
|
341
|
+
**Goal:** Recover from `401 Unauthorized` or `403 Forbidden` without
|
|
342
|
+
guessing — and without rotating the user's key unnecessarily.
|
|
343
|
+
|
|
344
|
+
**Canonical tool-call sequence:**
|
|
345
|
+
|
|
346
|
+
1. `yootheme_builder_diagnose` — single probe that hits `/health` (no
|
|
347
|
+
auth) and then `/etag` (Bearer auth). Returns
|
|
348
|
+
`{ plugin_reachable, plugin_version, yootheme_loaded, yootheme_version,
|
|
349
|
+
endpoint_count, bearer_valid, bearer_error?, summary? }`. Call this
|
|
350
|
+
**before** any other tool when you see auth errors. (Takes no
|
|
351
|
+
arguments — the schema is `{}`.)
|
|
352
|
+
2. **Interpret the result:**
|
|
353
|
+
- `plugin_reachable: false` → the WordPress install is down OR the
|
|
354
|
+
plugin is deactivated. Send the user to wp-admin → Plugins →
|
|
355
|
+
activate "YT Builder MCP". Do not retry until they confirm.
|
|
356
|
+
- `plugin_reachable: true, bearer_valid: false` → the Bearer key is
|
|
357
|
+
wrong (typo, revoked, or wrong key for this install). The
|
|
358
|
+
`bearer_error` field carries the upstream HTTP status. Send the
|
|
359
|
+
user to wp-admin → Tools → "YT Builder MCP" → Bearer Keys → either
|
|
360
|
+
copy the existing key into their MCP client config, or generate
|
|
361
|
+
a new one. Then they must restart the AI client.
|
|
362
|
+
- `plugin_reachable: true, bearer_valid: true` but the original
|
|
363
|
+
tool returned a 403 → the key works but the scope is too low for
|
|
364
|
+
the tool's required scope (`write` for mutations, `admin` for
|
|
365
|
+
destructive operations). Ask the user to regenerate the key with
|
|
366
|
+
a higher scope and restart the AI client.
|
|
367
|
+
3. **Walk the user through key rotation if needed:**
|
|
368
|
+
- "Go to wp-admin → Tools → YT Builder MCP → Bearer Keys."
|
|
369
|
+
- "Click 'Generate New Key', pick the scope (admin for full access)."
|
|
370
|
+
- "Copy the key — it's shown ONCE; you cannot recover it later."
|
|
371
|
+
- "Update your AI client config: replace `YTB_MCP_BEARER_TOKEN`
|
|
372
|
+
with the new key. The fastest way is to re-run
|
|
373
|
+
`npx -y @wootsup/yt-builder-mcp setup`."
|
|
374
|
+
- "Restart Claude / Cursor / Zed / Continue / Cline / Roo Code."
|
|
375
|
+
- "Confirm with `yootheme_builder_diagnose` that
|
|
376
|
+
`bearer_valid: true` before retrying the original task."
|
|
377
|
+
|
|
378
|
+
**Common pitfalls:**
|
|
379
|
+
|
|
380
|
+
- **Treating 401 as a network error.** A network error has no HTTP
|
|
381
|
+
status — it's a TCP/TLS / DNS failure. 401 means the server
|
|
382
|
+
responded "I do not accept this key", which is fundamentally a
|
|
383
|
+
config problem.
|
|
384
|
+
- **Stripping the `Bearer ` prefix.** The MCP server adds it
|
|
385
|
+
automatically when reading `YTB_MCP_BEARER_TOKEN`. If the user
|
|
386
|
+
pasted the prefix into the env var, the value sent will be
|
|
387
|
+
`Bearer Bearer ytb_live_…` and every request 401s.
|
|
388
|
+
- **Trying every tool to "see which ones work".** Don't. One
|
|
389
|
+
`yootheme_builder_diagnose` call tells you whether the failure is
|
|
390
|
+
reachability, auth, or scope.
|
|
391
|
+
- **Confusing 401 with 403.** 401 = "I don't recognise this key"
|
|
392
|
+
(rotate). 403 = "I recognise the key but it lacks the required
|
|
393
|
+
scope" (regenerate with higher scope). Different error codes,
|
|
394
|
+
different recovery — never collapse them into one branch.
|
|
395
|
+
|
|
396
|
+
**Worked example (tool-call snippet):**
|
|
397
|
+
|
|
398
|
+
```jsonc
|
|
399
|
+
// First — never retry blindly. Call diagnose (no args).
|
|
400
|
+
yootheme_builder_diagnose({})
|
|
401
|
+
// Response shape:
|
|
402
|
+
// {
|
|
403
|
+
// plugin_reachable: true,
|
|
404
|
+
// plugin_version: "0.1.0-alpha.1",
|
|
405
|
+
// yootheme_loaded: true,
|
|
406
|
+
// yootheme_version: "5.0.22",
|
|
407
|
+
// endpoint_count: 16,
|
|
408
|
+
// bearer_valid: false, // ← key is bad
|
|
409
|
+
// bearer_error: "HTTP 401: invalid_token"
|
|
410
|
+
// }
|
|
411
|
+
// → diagnosis: rotate the key. Send user to wp-admin → Settings.
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**Edge case:** `plugin_reachable: true` but `yootheme_loaded: false`
|
|
415
|
+
— the user installed the MCP plugin but YOOtheme itself isn't
|
|
416
|
+
active. The MCP server still answers, but every tool that touches
|
|
417
|
+
the YOOtheme layout returns an empty/error response. Surface the
|
|
418
|
+
mismatch ("YOOtheme is not active on this install") instead of
|
|
419
|
+
retrying.
|
|
420
|
+
|
|
421
|
+
**Success criterion:** A subsequent `yootheme_builder_diagnose`
|
|
422
|
+
returns `plugin_reachable: true` AND `bearer_valid: true`. The
|
|
423
|
+
original tool now returns a non-auth response.
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Workflow 5: Add a custom element type to a page
|
|
428
|
+
|
|
429
|
+
**Goal:** Inspect what element types are installed on the user's
|
|
430
|
+
YOOtheme install (built-ins + YOOtheme Pro + YOOessentials + child
|
|
431
|
+
theme + plugin-contributed elements), pick the right one, and place
|
|
432
|
+
an instance with a sensible default props payload.
|
|
433
|
+
|
|
434
|
+
**Canonical tool-call sequence (real parameter names — snake_case):**
|
|
435
|
+
|
|
436
|
+
1. `yootheme_builder_health` — note the YOOtheme version; custom
|
|
437
|
+
elements often require a minimum YOOtheme major.
|
|
438
|
+
2. `yootheme_builder_element_types_list({ fields: ["name", "label", "origin"] })`
|
|
439
|
+
— narrow the catalogue with sparse-fields. Returns rows like
|
|
440
|
+
`{ name: "headline", label: "Headline", origin: "core", ... }`.
|
|
441
|
+
3. `yootheme_builder_element_type_get_schema({ type_name: "<picked>" })`
|
|
442
|
+
— fetch the prop schema for the chosen type. Returns the field
|
|
443
|
+
definitions you can pass via `props`. Note: the parameter is
|
|
444
|
+
`type_name`, not `name`.
|
|
445
|
+
4. `yootheme_builder_pages_list({ fields: ["id", "label"] })` and
|
|
446
|
+
`yootheme_builder_page_get_layout({ template_id, flat: false })`
|
|
447
|
+
— locate the `parent_path` (JSON-Pointer) where the new element
|
|
448
|
+
should land.
|
|
449
|
+
5. `yootheme_builder_get_etag()` — fetch the optimistic-lock etag.
|
|
450
|
+
6. `yootheme_builder_element_add({ template_id, parent_path: "<path>", element_type: "<picked-name>", props: { ... }, etag })`
|
|
451
|
+
— the server validates `props` against the type schema and
|
|
452
|
+
returns a structured `validation` error with a per-field issue
|
|
453
|
+
list if anything is missing or malformed.
|
|
454
|
+
7. (Optional) `yootheme_builder_element_update_settings({ template_id, element_path: "<new-path>", props: { ... }, etag })`
|
|
455
|
+
— iterate on the props. **Note: this REPLACES `props` entirely**
|
|
456
|
+
— include every key you want to keep.
|
|
457
|
+
8. `yootheme_builder_page_save({ template_id, etag })` then
|
|
458
|
+
`yootheme_builder_page_publish({ template_id, etag })`.
|
|
459
|
+
|
|
460
|
+
**Common pitfalls:**
|
|
461
|
+
|
|
462
|
+
- **Wrong parameter name on the type-schema tool.** It's
|
|
463
|
+
`type_name`, not `name`. The server's Zod schema rejects unknown
|
|
464
|
+
keys.
|
|
465
|
+
- **Wrong parameter names on `element_add`.** Use `template_id`
|
|
466
|
+
(not `pageId`), `parent_path` (not `parentPath`), `element_type`
|
|
467
|
+
(not `type` / `name`), `props` (not `settings`), `etag` (not
|
|
468
|
+
`ifMatch`).
|
|
469
|
+
- **`element_update_settings` is a full replace, not a merge.** Any
|
|
470
|
+
key NOT in the request is REMOVED from `props`. Read the existing
|
|
471
|
+
shape via `yootheme_builder_element_get` first if you only want
|
|
472
|
+
to tweak a subset.
|
|
473
|
+
- **Custom elements without a schema.** A poorly-built third-party
|
|
474
|
+
element may not register a prop schema. In that case
|
|
475
|
+
`yootheme_builder_element_type_get_schema` returns an empty/sparse
|
|
476
|
+
schema and the server accepts arbitrary `props`. Don't assume "no
|
|
477
|
+
schema = no required fields" — read the third-party element's
|
|
478
|
+
docs.
|
|
479
|
+
- **Type name vs. label confusion.** The `name` field on the
|
|
480
|
+
catalogue row is the machine identifier (e.g. `pro_slider`); the
|
|
481
|
+
`label` is the human display string ("Pro Slider"). Always pass
|
|
482
|
+
the `name` (as `element_type` / `type_name`).
|
|
483
|
+
- **Pro-only types on a Free install.** YOOtheme Free does not
|
|
484
|
+
register `pro_*` element types. Surface that to the user instead
|
|
485
|
+
of retrying with a different filter.
|
|
486
|
+
|
|
487
|
+
**Worked example (tool-call snippet):**
|
|
488
|
+
|
|
489
|
+
```jsonc
|
|
490
|
+
// Step 2 — narrow the catalogue with sparse-fields to save tokens.
|
|
491
|
+
yootheme_builder_element_types_list({
|
|
492
|
+
fields: ["name", "label", "origin"]
|
|
493
|
+
})
|
|
494
|
+
// Rows: [{ name: "headline", label: "Headline", origin: "core" }, ...]
|
|
495
|
+
|
|
496
|
+
// Step 3 — fetch the schema (note: type_name, not name).
|
|
497
|
+
yootheme_builder_element_type_get_schema({ type_name: "headline" })
|
|
498
|
+
// Returns the field definitions for the headline's `props`.
|
|
499
|
+
|
|
500
|
+
// Step 6 — place the element.
|
|
501
|
+
yootheme_builder_element_add({
|
|
502
|
+
template_id: "home",
|
|
503
|
+
parent_path: "/0/children/2", // row inside section
|
|
504
|
+
element_type: "headline",
|
|
505
|
+
props: { content: "Welcome", tag: "h1" },
|
|
506
|
+
etag: "abc123"
|
|
507
|
+
})
|
|
508
|
+
// Response: { path: "/0/children/2/children/0", etag: "def456" }
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Edge case:** A child theme can override a built-in element's
|
|
512
|
+
schema in PHP. The `origin` field will read `child_theme` instead
|
|
513
|
+
of `core`. If you see surprising required keys, that's the override
|
|
514
|
+
talking — surface this to the user so they know their theme is
|
|
515
|
+
customising element defaults.
|
|
516
|
+
|
|
517
|
+
**Success criterion:** After publish, the front-end shows the new
|
|
518
|
+
element rendered with its default props. A re-read of the layout
|
|
519
|
+
shows the element under `parent_path` with the chosen `element_type`
|
|
520
|
+
and the props payload you passed.
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## When something doesn't fit one of these 5 workflows
|
|
525
|
+
|
|
526
|
+
- **Move an element** (intra-template reorder/reparent): use
|
|
527
|
+
`yootheme_builder_element_move({ template_id, element_path,
|
|
528
|
+
to_parent_path, to_index, etag })`. Reorders or reparents without
|
|
529
|
+
re-creating.
|
|
530
|
+
- **Delete an element**: use `yootheme_builder_element_delete({
|
|
531
|
+
template_id, element_path, etag, confirm: true })`.
|
|
532
|
+
Elicitation-aware — confirms via the AI client prompt before
|
|
533
|
+
destroying state when `confirm` is omitted. On hosts without
|
|
534
|
+
elicitation, it returns a preview-with-confirm-required response;
|
|
535
|
+
call again with `confirm: true`.
|
|
536
|
+
- **Unbind a source**: use `yootheme_builder_element_unbind_source({
|
|
537
|
+
template_id, element_path, etag, confirm: true })`. Same
|
|
538
|
+
elicitation flow as delete.
|
|
539
|
+
- **Flat schema inspection** (e.g. enumerate every element path +
|
|
540
|
+
type without fetching the whole nested tree): use
|
|
541
|
+
`yootheme_builder_page_get_schema({ template_id })`.
|
|
542
|
+
- **Etag-only fetch** (e.g. polling for concurrent edits): use
|
|
543
|
+
`yootheme_builder_get_etag()` (takes no arguments) — cheaper than
|
|
544
|
+
fetching the full layout.
|
|
545
|
+
|
|
546
|
+
If the user asks for something none of the above covers (e.g. global
|
|
547
|
+
theme settings, menu management, media library), tell them clearly:
|
|
548
|
+
"This MCP server only covers the YOOtheme Page Builder surface. For
|
|
549
|
+
<X> you'll need <YOOtheme MCP / WP REST / direct wp-admin>." Don't
|
|
550
|
+
fabricate tool calls.
|
|
551
|
+
|
|
552
|
+
## Appendix: Tool Catalog (auto-generated)
|
|
553
|
+
|
|
554
|
+
<!-- TOOL-CATALOG:BEGIN -->
|
|
555
|
+
|
|
556
|
+
**21 tools** — generated by `scripts/extract-tools.mjs` from the compiled `buildAllTools()` registry. Do not hand-edit this section; re-run `npm run build && node scripts/extract-tools.mjs` after changing tool definitions.
|
|
557
|
+
|
|
558
|
+
| Tool | Kind | Input keys | Description |
|
|
559
|
+
| --- | --- | --- | --- |
|
|
560
|
+
| `yootheme_builder_diagnose` | read+openWorld | _(none)_ | Run a full diagnostic: hit /health (no auth), then attempt an authenticated call (/etag) to confirm the Bearer key is valid. Use when health passes but tools return 401/403. |
|
|
561
|
+
| `yootheme_builder_element_add` | openWorld | `children`, `element_type`, `etag`, `parent_path`, `props`, `template_id` | Add a new element to a template. Provide `parent_path` (or "" for root), `element_type` (e.g. "headline", "text", "grid"), and optional `props` / `children`. Returns the new element's JSON-Pointer path. Requires ETag. |
|
|
562
|
+
| `yootheme_builder_element_bind_source` | idempotent+openWorld | `element_path`, `etag`, `source_id`, `source_name`, `template_id` | Bind a Builder source to an element (sets `props.source`). Pass source_name from sources_list; pass source_id to disambiguate cross-plugin name collisions. Requires ETag. |
|
|
563
|
+
| `yootheme_builder_element_clone` | openWorld | `element_path`, `etag`, `template_id` | Clone an element as a sibling (same parent, immediately after the source). Returns the new element's path. Requires ETag. |
|
|
564
|
+
| `yootheme_builder_element_delete` | destructive+openWorld | `confirm`, `element_path`, `etag`, `template_id` | PERMANENTLY delete an element and all its children. Cannot be undone. Always ask the user to confirm first, then call again with `confirm: true`. Requires ETag. |
|
|
565
|
+
| `yootheme_builder_element_get` | read+openWorld | `element_path`, `template_id` | Get the full element object at a specific JSON-Pointer path, including props and children. Use yootheme_builder_element_list to discover paths. |
|
|
566
|
+
| `yootheme_builder_element_get_binding` | read+openWorld | `element_path`, `template_id` | Read the source binding (and source_config/source_args/etc.) attached to an element. Returns the empty object if the element is not bound. |
|
|
567
|
+
| `yootheme_builder_element_list` | read+openWorld | `fields`, `template_id` | List all elements in a template as a flat array with JSON-Pointer paths + element types. Best starting-point for "find the element I want to edit". Pass `fields:["path","element_type"]` to narrow each row. |
|
|
568
|
+
| `yootheme_builder_element_move` | idempotent+openWorld | `element_path`, `etag`, `template_id`, `to_index`, `to_parent_path` | Move an element to a new parent + index in the tree. Useful for reordering or reparenting (e.g. moving a card from one grid column to another). Requires ETag. |
|
|
569
|
+
| `yootheme_builder_element_type_get_schema` | read+openWorld | `type_name` | Get the prop/field schema for a single element type. Use the result to discover valid keys for `props` when calling yootheme_builder_element_add or _update_settings. |
|
|
570
|
+
| `yootheme_builder_element_types_list` | read+openWorld | `fields` | List element types registered on this site (built-ins + YOOessentials/uEssentials extras). Names feed `element_type` of element_add. Pass `fields[]` to narrow each row. |
|
|
571
|
+
| `yootheme_builder_element_unbind_source` | destructive+openWorld | `confirm`, `element_path`, `etag`, `template_id` | Remove the source binding from an element. Clears `props.source`. Destructive in the sense that it may break dynamic-content rendering — always ask the user to confirm. Requires ETag. |
|
|
572
|
+
| `yootheme_builder_element_update_settings` | idempotent+openWorld | `element_path`, `etag`, `props`, `template_id` | Replace the `props` on an element. Use this for any setting change — title, margins, classes, sources, etc. Requires ETag. Existing props NOT in the request are removed. |
|
|
573
|
+
| `yootheme_builder_get_etag` | read+openWorld | _(none)_ | Get the current top-level state ETag. Pass this back via the `etag` parameter on any write tool to prevent overwriting concurrent edits. |
|
|
574
|
+
| `yootheme_builder_health` | read+openWorld | _(none)_ | Check that the YT Builder MCP plugin is installed and reachable. Returns plugin version, YOOtheme Pro version (if loaded), and the list of available REST endpoints. Unauthenticated probe — call this first when troubleshooting connectivity. |
|
|
575
|
+
| `yootheme_builder_page_get_layout` | read+openWorld | `fields`, `flat`, `template_id` | Get full layout tree for one template. Default nested `{layout, etag}`. Set `flat:true` for depth-first array `{elements:[...], etag}`; combine with `fields[]` to project per-element. |
|
|
576
|
+
| `yootheme_builder_page_get_schema` | read+openWorld | `template_id` | Get the flat schema for a template — a list of nodes with their JSON-Pointer paths and element types. Best entry-point for navigation: lighter than page_get_layout, sufficient to locate elements before editing. |
|
|
577
|
+
| `yootheme_builder_page_publish` | openWorld | `etag`, `template_id` | Publish a template. Currently behaves as save + sets `published: true` in the response. Requires ETag. |
|
|
578
|
+
| `yootheme_builder_page_save` | idempotent+openWorld | `etag`, `template_id` | Re-run save-transforms on a template and persist. Useful after a series of low-level writes to trigger the Builder normalization pass. Requires ETag. |
|
|
579
|
+
| `yootheme_builder_pages_list` | read+openWorld | `fields` | List all YOOtheme templates ("pages") on the site. Returns id, label and usage metadata for each. Use this first to discover template IDs. Pass `fields:["id","label"]` to project per-item to a smaller shape. |
|
|
580
|
+
| `yootheme_builder_sources_list` | read+openWorld | `fields` | List Builder sources grouped by origin (apimapper/wordpress/essentials). Returns name+label per source — pick one for `element_bind_source`. Pass `fields[]` to narrow each row. |
|
|
581
|
+
|
|
582
|
+
<!-- TOOL-CATALOG:END -->
|