@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.
Files changed (299) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +221 -0
  3. package/bin/yt-builder-mcp.js +59 -0
  4. package/dist/auth.d.ts +39 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +93 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/client.d.ts +84 -0
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +151 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/clients/claude-code.d.ts +18 -0
  13. package/dist/clients/claude-code.d.ts.map +1 -0
  14. package/dist/clients/claude-code.js +53 -0
  15. package/dist/clients/claude-code.js.map +1 -0
  16. package/dist/clients/claude-desktop.d.ts +19 -0
  17. package/dist/clients/claude-desktop.d.ts.map +1 -0
  18. package/dist/clients/claude-desktop.js +56 -0
  19. package/dist/clients/claude-desktop.js.map +1 -0
  20. package/dist/clients/cline.d.ts +26 -0
  21. package/dist/clients/cline.d.ts.map +1 -0
  22. package/dist/clients/cline.js +80 -0
  23. package/dist/clients/cline.js.map +1 -0
  24. package/dist/clients/codex-cli.d.ts +42 -0
  25. package/dist/clients/codex-cli.d.ts.map +1 -0
  26. package/dist/clients/codex-cli.js +194 -0
  27. package/dist/clients/codex-cli.js.map +1 -0
  28. package/dist/clients/continue.d.ts +13 -0
  29. package/dist/clients/continue.d.ts.map +1 -0
  30. package/dist/clients/continue.js +52 -0
  31. package/dist/clients/continue.js.map +1 -0
  32. package/dist/clients/cursor.d.ts +12 -0
  33. package/dist/clients/cursor.d.ts.map +1 -0
  34. package/dist/clients/cursor.js +38 -0
  35. package/dist/clients/cursor.js.map +1 -0
  36. package/dist/clients/gemini-cli.d.ts +18 -0
  37. package/dist/clients/gemini-cli.d.ts.map +1 -0
  38. package/dist/clients/gemini-cli.js +44 -0
  39. package/dist/clients/gemini-cli.js.map +1 -0
  40. package/dist/clients/home.d.ts +14 -0
  41. package/dist/clients/home.d.ts.map +1 -0
  42. package/dist/clients/home.js +20 -0
  43. package/dist/clients/home.js.map +1 -0
  44. package/dist/clients/index.d.ts +52 -0
  45. package/dist/clients/index.d.ts.map +1 -0
  46. package/dist/clients/index.js +72 -0
  47. package/dist/clients/index.js.map +1 -0
  48. package/dist/clients/roo-code.d.ts +23 -0
  49. package/dist/clients/roo-code.d.ts.map +1 -0
  50. package/dist/clients/roo-code.js +69 -0
  51. package/dist/clients/roo-code.js.map +1 -0
  52. package/dist/clients/zed.d.ts +12 -0
  53. package/dist/clients/zed.d.ts.map +1 -0
  54. package/dist/clients/zed.js +41 -0
  55. package/dist/clients/zed.js.map +1 -0
  56. package/dist/errors/hints.d.ts +51 -0
  57. package/dist/errors/hints.d.ts.map +1 -0
  58. package/dist/errors/hints.js +95 -0
  59. package/dist/errors/hints.js.map +1 -0
  60. package/dist/errors/mask.d.ts +35 -0
  61. package/dist/errors/mask.d.ts.map +1 -0
  62. package/dist/errors/mask.js +49 -0
  63. package/dist/errors/mask.js.map +1 -0
  64. package/dist/errors/sanitize.d.ts +31 -0
  65. package/dist/errors/sanitize.d.ts.map +1 -0
  66. package/dist/errors/sanitize.js +90 -0
  67. package/dist/errors/sanitize.js.map +1 -0
  68. package/dist/errors.d.ts +42 -0
  69. package/dist/errors.d.ts.map +1 -0
  70. package/dist/errors.js +61 -0
  71. package/dist/errors.js.map +1 -0
  72. package/dist/gateway/advanced-tool/discovery.d.ts +19 -0
  73. package/dist/gateway/advanced-tool/discovery.d.ts.map +1 -0
  74. package/dist/gateway/advanced-tool/discovery.js +53 -0
  75. package/dist/gateway/advanced-tool/discovery.js.map +1 -0
  76. package/dist/gateway/advanced-tool/domains.d.ts +42 -0
  77. package/dist/gateway/advanced-tool/domains.d.ts.map +1 -0
  78. package/dist/gateway/advanced-tool/domains.js +88 -0
  79. package/dist/gateway/advanced-tool/domains.js.map +1 -0
  80. package/dist/gateway/advanced-tool/execute.d.ts +29 -0
  81. package/dist/gateway/advanced-tool/execute.d.ts.map +1 -0
  82. package/dist/gateway/advanced-tool/execute.js +54 -0
  83. package/dist/gateway/advanced-tool/execute.js.map +1 -0
  84. package/dist/gateway/advanced-tool/index.d.ts +36 -0
  85. package/dist/gateway/advanced-tool/index.d.ts.map +1 -0
  86. package/dist/gateway/advanced-tool/index.js +39 -0
  87. package/dist/gateway/advanced-tool/index.js.map +1 -0
  88. package/dist/gateway/advanced-tool/register.d.ts +18 -0
  89. package/dist/gateway/advanced-tool/register.d.ts.map +1 -0
  90. package/dist/gateway/advanced-tool/register.js +62 -0
  91. package/dist/gateway/advanced-tool/register.js.map +1 -0
  92. package/dist/gateway/advanced-tool.d.ts +13 -0
  93. package/dist/gateway/advanced-tool.d.ts.map +1 -0
  94. package/dist/gateway/advanced-tool.js +13 -0
  95. package/dist/gateway/advanced-tool.js.map +1 -0
  96. package/dist/gateway/capturing-server.d.ts +117 -0
  97. package/dist/gateway/capturing-server.d.ts.map +1 -0
  98. package/dist/gateway/capturing-server.js +103 -0
  99. package/dist/gateway/capturing-server.js.map +1 -0
  100. package/dist/gateway/essentials.d.ts +49 -0
  101. package/dist/gateway/essentials.d.ts.map +1 -0
  102. package/dist/gateway/essentials.js +62 -0
  103. package/dist/gateway/essentials.js.map +1 -0
  104. package/dist/gateway/test-support.d.ts +41 -0
  105. package/dist/gateway/test-support.d.ts.map +1 -0
  106. package/dist/gateway/test-support.js +60 -0
  107. package/dist/gateway/test-support.js.map +1 -0
  108. package/dist/index.d.ts +25 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +77 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/install-skill.d.ts +35 -0
  113. package/dist/install-skill.d.ts.map +1 -0
  114. package/dist/install-skill.js +107 -0
  115. package/dist/install-skill.js.map +1 -0
  116. package/dist/platform/index.d.ts +49 -0
  117. package/dist/platform/index.d.ts.map +1 -0
  118. package/dist/platform/index.js +38 -0
  119. package/dist/platform/index.js.map +1 -0
  120. package/dist/server.d.ts +50 -0
  121. package/dist/server.d.ts.map +1 -0
  122. package/dist/server.js +117 -0
  123. package/dist/server.js.map +1 -0
  124. package/dist/setup-cli.d.ts +100 -0
  125. package/dist/setup-cli.d.ts.map +1 -0
  126. package/dist/setup-cli.js +355 -0
  127. package/dist/setup-cli.js.map +1 -0
  128. package/dist/setup-prompts.d.ts +41 -0
  129. package/dist/setup-prompts.d.ts.map +1 -0
  130. package/dist/setup-prompts.js +142 -0
  131. package/dist/setup-prompts.js.map +1 -0
  132. package/dist/setup-token.d.ts +38 -0
  133. package/dist/setup-token.d.ts.map +1 -0
  134. package/dist/setup-token.js +106 -0
  135. package/dist/setup-token.js.map +1 -0
  136. package/dist/setup-wizard-defaults.d.ts +43 -0
  137. package/dist/setup-wizard-defaults.d.ts.map +1 -0
  138. package/dist/setup-wizard-defaults.js +160 -0
  139. package/dist/setup-wizard-defaults.js.map +1 -0
  140. package/dist/setup-wizard-handshake.d.ts +25 -0
  141. package/dist/setup-wizard-handshake.d.ts.map +1 -0
  142. package/dist/setup-wizard-handshake.js +103 -0
  143. package/dist/setup-wizard-handshake.js.map +1 -0
  144. package/dist/setup-wizard-types.d.ts +148 -0
  145. package/dist/setup-wizard-types.d.ts.map +1 -0
  146. package/dist/setup-wizard-types.js +11 -0
  147. package/dist/setup-wizard-types.js.map +1 -0
  148. package/dist/setup-wizard.d.ts +33 -0
  149. package/dist/setup-wizard.d.ts.map +1 -0
  150. package/dist/setup-wizard.js +166 -0
  151. package/dist/setup-wizard.js.map +1 -0
  152. package/dist/setup.d.ts +17 -0
  153. package/dist/setup.d.ts.map +1 -0
  154. package/dist/setup.js +33 -0
  155. package/dist/setup.js.map +1 -0
  156. package/dist/tools/elements/builders.d.ts +24 -0
  157. package/dist/tools/elements/builders.d.ts.map +1 -0
  158. package/dist/tools/elements/builders.js +150 -0
  159. package/dist/tools/elements/builders.js.map +1 -0
  160. package/dist/tools/elements/handlers-write.d.ts +48 -0
  161. package/dist/tools/elements/handlers-write.d.ts.map +1 -0
  162. package/dist/tools/elements/handlers-write.js +141 -0
  163. package/dist/tools/elements/handlers-write.js.map +1 -0
  164. package/dist/tools/elements/handlers.d.ts +56 -0
  165. package/dist/tools/elements/handlers.d.ts.map +1 -0
  166. package/dist/tools/elements/handlers.js +113 -0
  167. package/dist/tools/elements/handlers.js.map +1 -0
  168. package/dist/tools/elements/index.d.ts +28 -0
  169. package/dist/tools/elements/index.d.ts.map +1 -0
  170. package/dist/tools/elements/index.js +27 -0
  171. package/dist/tools/elements/index.js.map +1 -0
  172. package/dist/tools/elements.d.ts +13 -0
  173. package/dist/tools/elements.d.ts.map +1 -0
  174. package/dist/tools/elements.js +13 -0
  175. package/dist/tools/elements.js.map +1 -0
  176. package/dist/tools/elicitation.d.ts +87 -0
  177. package/dist/tools/elicitation.d.ts.map +1 -0
  178. package/dist/tools/elicitation.js +100 -0
  179. package/dist/tools/elicitation.js.map +1 -0
  180. package/dist/tools/format/elements-format.d.ts +34 -0
  181. package/dist/tools/format/elements-format.d.ts.map +1 -0
  182. package/dist/tools/format/elements-format.js +112 -0
  183. package/dist/tools/format/elements-format.js.map +1 -0
  184. package/dist/tools/format/health-format.d.ts +73 -0
  185. package/dist/tools/format/health-format.d.ts.map +1 -0
  186. package/dist/tools/format/health-format.js +178 -0
  187. package/dist/tools/format/health-format.js.map +1 -0
  188. package/dist/tools/format/inspection-format.d.ts +45 -0
  189. package/dist/tools/format/inspection-format.d.ts.map +1 -0
  190. package/dist/tools/format/inspection-format.js +125 -0
  191. package/dist/tools/format/inspection-format.js.map +1 -0
  192. package/dist/tools/format/pages-format.d.ts +39 -0
  193. package/dist/tools/format/pages-format.d.ts.map +1 -0
  194. package/dist/tools/format/pages-format.js +110 -0
  195. package/dist/tools/format/pages-format.js.map +1 -0
  196. package/dist/tools/format/sources-format.d.ts +25 -0
  197. package/dist/tools/format/sources-format.d.ts.map +1 -0
  198. package/dist/tools/format/sources-format.js +113 -0
  199. package/dist/tools/format/sources-format.js.map +1 -0
  200. package/dist/tools/health.d.ts +22 -0
  201. package/dist/tools/health.d.ts.map +1 -0
  202. package/dist/tools/health.js +147 -0
  203. package/dist/tools/health.js.map +1 -0
  204. package/dist/tools/index.d.ts +23 -0
  205. package/dist/tools/index.d.ts.map +1 -0
  206. package/dist/tools/index.js +23 -0
  207. package/dist/tools/index.js.map +1 -0
  208. package/dist/tools/inspection.d.ts +14 -0
  209. package/dist/tools/inspection.d.ts.map +1 -0
  210. package/dist/tools/inspection.js +115 -0
  211. package/dist/tools/inspection.js.map +1 -0
  212. package/dist/tools/layout-flatten.d.ts +63 -0
  213. package/dist/tools/layout-flatten.d.ts.map +1 -0
  214. package/dist/tools/layout-flatten.js +95 -0
  215. package/dist/tools/layout-flatten.js.map +1 -0
  216. package/dist/tools/pages/builders.d.ts +14 -0
  217. package/dist/tools/pages/builders.d.ts.map +1 -0
  218. package/dist/tools/pages/builders.js +97 -0
  219. package/dist/tools/pages/builders.js.map +1 -0
  220. package/dist/tools/pages/handlers-read.d.ts +24 -0
  221. package/dist/tools/pages/handlers-read.d.ts.map +1 -0
  222. package/dist/tools/pages/handlers-read.js +141 -0
  223. package/dist/tools/pages/handlers-read.js.map +1 -0
  224. package/dist/tools/pages/handlers-write.d.ts +21 -0
  225. package/dist/tools/pages/handlers-write.d.ts.map +1 -0
  226. package/dist/tools/pages/handlers-write.js +52 -0
  227. package/dist/tools/pages/handlers-write.js.map +1 -0
  228. package/dist/tools/pages/index.d.ts +17 -0
  229. package/dist/tools/pages/index.d.ts.map +1 -0
  230. package/dist/tools/pages/index.js +17 -0
  231. package/dist/tools/pages/index.js.map +1 -0
  232. package/dist/tools/pages/schemas.d.ts +30 -0
  233. package/dist/tools/pages/schemas.d.ts.map +1 -0
  234. package/dist/tools/pages/schemas.js +30 -0
  235. package/dist/tools/pages/schemas.js.map +1 -0
  236. package/dist/tools/pages.d.ts +13 -0
  237. package/dist/tools/pages.d.ts.map +1 -0
  238. package/dist/tools/pages.js +13 -0
  239. package/dist/tools/pages.js.map +1 -0
  240. package/dist/tools/progress-phases.d.ts +46 -0
  241. package/dist/tools/progress-phases.d.ts.map +1 -0
  242. package/dist/tools/progress-phases.js +48 -0
  243. package/dist/tools/progress-phases.js.map +1 -0
  244. package/dist/tools/shared-schemas.d.ts +15 -0
  245. package/dist/tools/shared-schemas.d.ts.map +1 -0
  246. package/dist/tools/shared-schemas.js +30 -0
  247. package/dist/tools/shared-schemas.js.map +1 -0
  248. package/dist/tools/sources/builders.d.ts +13 -0
  249. package/dist/tools/sources/builders.d.ts.map +1 -0
  250. package/dist/tools/sources/builders.js +88 -0
  251. package/dist/tools/sources/builders.js.map +1 -0
  252. package/dist/tools/sources/handlers-bind.d.ts +26 -0
  253. package/dist/tools/sources/handlers-bind.d.ts.map +1 -0
  254. package/dist/tools/sources/handlers-bind.js +123 -0
  255. package/dist/tools/sources/handlers-bind.js.map +1 -0
  256. package/dist/tools/sources/handlers.d.ts +45 -0
  257. package/dist/tools/sources/handlers.d.ts.map +1 -0
  258. package/dist/tools/sources/handlers.js +84 -0
  259. package/dist/tools/sources/handlers.js.map +1 -0
  260. package/dist/tools/sources/index.d.ts +19 -0
  261. package/dist/tools/sources/index.d.ts.map +1 -0
  262. package/dist/tools/sources/index.js +18 -0
  263. package/dist/tools/sources/index.js.map +1 -0
  264. package/dist/tools/sources.d.ts +14 -0
  265. package/dist/tools/sources.d.ts.map +1 -0
  266. package/dist/tools/sources.js +14 -0
  267. package/dist/tools/sources.js.map +1 -0
  268. package/dist/tools/sparse-fields.d.ts +80 -0
  269. package/dist/tools/sparse-fields.d.ts.map +1 -0
  270. package/dist/tools/sparse-fields.js +144 -0
  271. package/dist/tools/sparse-fields.js.map +1 -0
  272. package/dist/tools/tool-builder/annotations.d.ts +22 -0
  273. package/dist/tools/tool-builder/annotations.d.ts.map +1 -0
  274. package/dist/tools/tool-builder/annotations.js +35 -0
  275. package/dist/tools/tool-builder/annotations.js.map +1 -0
  276. package/dist/tools/tool-builder/define.d.ts +31 -0
  277. package/dist/tools/tool-builder/define.d.ts.map +1 -0
  278. package/dist/tools/tool-builder/define.js +31 -0
  279. package/dist/tools/tool-builder/define.js.map +1 -0
  280. package/dist/tools/tool-builder/index.d.ts +28 -0
  281. package/dist/tools/tool-builder/index.d.ts.map +1 -0
  282. package/dist/tools/tool-builder/index.js +27 -0
  283. package/dist/tools/tool-builder/index.js.map +1 -0
  284. package/dist/tools/tool-builder/results.d.ts +59 -0
  285. package/dist/tools/tool-builder/results.d.ts.map +1 -0
  286. package/dist/tools/tool-builder/results.js +125 -0
  287. package/dist/tools/tool-builder/results.js.map +1 -0
  288. package/dist/tools/tool-builder/types.d.ts +82 -0
  289. package/dist/tools/tool-builder/types.d.ts.map +1 -0
  290. package/dist/tools/tool-builder/types.js +9 -0
  291. package/dist/tools/tool-builder/types.js.map +1 -0
  292. package/dist/tools/tool-builder.d.ts +16 -0
  293. package/dist/tools/tool-builder.d.ts.map +1 -0
  294. package/dist/tools/tool-builder.js +16 -0
  295. package/dist/tools/tool-builder.js.map +1 -0
  296. package/icon.png +0 -0
  297. package/manifest.json +63 -0
  298. package/package.json +81 -0
  299. 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 -->