@wootsup/yt-builder-mcp 1.1.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/README.md +14 -10
  2. package/bin/yt-builder-mcp.js +55 -40
  3. package/dist/auth.d.ts +18 -0
  4. package/dist/auth.d.ts.map +1 -1
  5. package/dist/auth.js +18 -0
  6. package/dist/auth.js.map +1 -1
  7. package/dist/catalog/build-catalog.d.ts +63 -0
  8. package/dist/catalog/build-catalog.d.ts.map +1 -0
  9. package/dist/catalog/build-catalog.js +166 -0
  10. package/dist/catalog/build-catalog.js.map +1 -0
  11. package/dist/cli/invocation-decision.d.ts +57 -0
  12. package/dist/cli/invocation-decision.d.ts.map +1 -0
  13. package/dist/cli/invocation-decision.js +86 -0
  14. package/dist/cli/invocation-decision.js.map +1 -0
  15. package/dist/client.d.ts +28 -0
  16. package/dist/client.d.ts.map +1 -1
  17. package/dist/client.js +40 -1
  18. package/dist/client.js.map +1 -1
  19. package/dist/clients/claude-code.d.ts.map +1 -1
  20. package/dist/clients/claude-code.js +3 -1
  21. package/dist/clients/claude-code.js.map +1 -1
  22. package/dist/clients/claude-desktop.d.ts.map +1 -1
  23. package/dist/clients/claude-desktop.js +3 -1
  24. package/dist/clients/claude-desktop.js.map +1 -1
  25. package/dist/clients/cline.d.ts.map +1 -1
  26. package/dist/clients/cline.js +3 -1
  27. package/dist/clients/cline.js.map +1 -1
  28. package/dist/clients/codex-cli.d.ts +1 -1
  29. package/dist/clients/codex-cli.d.ts.map +1 -1
  30. package/dist/clients/codex-cli.js +98 -0
  31. package/dist/clients/codex-cli.js.map +1 -1
  32. package/dist/clients/continue.d.ts.map +1 -1
  33. package/dist/clients/continue.js +70 -1
  34. package/dist/clients/continue.js.map +1 -1
  35. package/dist/clients/cursor.d.ts.map +1 -1
  36. package/dist/clients/cursor.js +3 -1
  37. package/dist/clients/cursor.js.map +1 -1
  38. package/dist/clients/gemini-cli.d.ts.map +1 -1
  39. package/dist/clients/gemini-cli.js +3 -1
  40. package/dist/clients/gemini-cli.js.map +1 -1
  41. package/dist/clients/index.d.ts +104 -0
  42. package/dist/clients/index.d.ts.map +1 -1
  43. package/dist/clients/index.js +141 -0
  44. package/dist/clients/index.js.map +1 -1
  45. package/dist/clients/roo-code.d.ts.map +1 -1
  46. package/dist/clients/roo-code.js +3 -1
  47. package/dist/clients/roo-code.js.map +1 -1
  48. package/dist/clients/zed.d.ts.map +1 -1
  49. package/dist/clients/zed.js +3 -1
  50. package/dist/clients/zed.js.map +1 -1
  51. package/dist/gateway/advanced-read-tool.d.ts +37 -0
  52. package/dist/gateway/advanced-read-tool.d.ts.map +1 -0
  53. package/dist/gateway/advanced-read-tool.js +156 -0
  54. package/dist/gateway/advanced-read-tool.js.map +1 -0
  55. package/dist/gateway/advanced-tool/domains.d.ts +18 -3
  56. package/dist/gateway/advanced-tool/domains.d.ts.map +1 -1
  57. package/dist/gateway/advanced-tool/domains.js +28 -9
  58. package/dist/gateway/advanced-tool/domains.js.map +1 -1
  59. package/dist/gateway/advanced-tool/register.d.ts.map +1 -1
  60. package/dist/gateway/advanced-tool/register.js +43 -11
  61. package/dist/gateway/advanced-tool/register.js.map +1 -1
  62. package/dist/gateway/essentials.d.ts +14 -4
  63. package/dist/gateway/essentials.d.ts.map +1 -1
  64. package/dist/gateway/essentials.js +55 -5
  65. package/dist/gateway/essentials.js.map +1 -1
  66. package/dist/gateway/test-support.d.ts +3 -3
  67. package/dist/gateway/test-support.js +3 -3
  68. package/dist/index.d.ts +2 -1
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +19 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/install-skill.js +1 -1
  73. package/dist/proxy/bridge.d.ts +36 -0
  74. package/dist/proxy/bridge.d.ts.map +1 -0
  75. package/dist/proxy/bridge.js +129 -0
  76. package/dist/proxy/bridge.js.map +1 -0
  77. package/dist/proxy/mode.d.ts +10 -0
  78. package/dist/proxy/mode.d.ts.map +1 -0
  79. package/dist/proxy/mode.js +20 -0
  80. package/dist/proxy/mode.js.map +1 -0
  81. package/dist/server.d.ts +6 -4
  82. package/dist/server.d.ts.map +1 -1
  83. package/dist/server.js +14 -4
  84. package/dist/server.js.map +1 -1
  85. package/dist/setup-cli.d.ts +41 -0
  86. package/dist/setup-cli.d.ts.map +1 -1
  87. package/dist/setup-cli.js +163 -1
  88. package/dist/setup-cli.js.map +1 -1
  89. package/dist/setup-prompts.d.ts +23 -0
  90. package/dist/setup-prompts.d.ts.map +1 -1
  91. package/dist/setup-prompts.js +76 -1
  92. package/dist/setup-prompts.js.map +1 -1
  93. package/dist/setup-wizard.d.ts.map +1 -1
  94. package/dist/setup-wizard.js +7 -0
  95. package/dist/setup-wizard.js.map +1 -1
  96. package/dist/sites/bootstrap.d.ts +41 -0
  97. package/dist/sites/bootstrap.d.ts.map +1 -0
  98. package/dist/sites/bootstrap.js +58 -0
  99. package/dist/sites/bootstrap.js.map +1 -0
  100. package/dist/sites/client-pool.d.ts +33 -0
  101. package/dist/sites/client-pool.d.ts.map +1 -1
  102. package/dist/sites/client-pool.js +69 -1
  103. package/dist/sites/client-pool.js.map +1 -1
  104. package/dist/sites/registry.d.ts +30 -0
  105. package/dist/sites/registry.d.ts.map +1 -1
  106. package/dist/sites/registry.js.map +1 -1
  107. package/dist/sites/store.d.ts +8 -1
  108. package/dist/sites/store.d.ts.map +1 -1
  109. package/dist/sites/store.js +17 -2
  110. package/dist/sites/store.js.map +1 -1
  111. package/dist/sites/tools/index.d.ts.map +1 -1
  112. package/dist/sites/tools/index.js +9 -1
  113. package/dist/sites/tools/index.js.map +1 -1
  114. package/dist/sites/tools/use-site.d.ts +47 -0
  115. package/dist/sites/tools/use-site.d.ts.map +1 -0
  116. package/dist/sites/tools/use-site.js +203 -0
  117. package/dist/sites/tools/use-site.js.map +1 -0
  118. package/dist/tools/bind-guidance.d.ts +80 -0
  119. package/dist/tools/bind-guidance.d.ts.map +1 -0
  120. package/dist/tools/bind-guidance.js +95 -0
  121. package/dist/tools/bind-guidance.js.map +1 -0
  122. package/dist/tools/elements/builders.d.ts.map +1 -1
  123. package/dist/tools/elements/builders.js +75 -18
  124. package/dist/tools/elements/builders.js.map +1 -1
  125. package/dist/tools/elements/handlers-write.d.ts +20 -3
  126. package/dist/tools/elements/handlers-write.d.ts.map +1 -1
  127. package/dist/tools/elements/handlers-write.js +372 -10
  128. package/dist/tools/elements/handlers-write.js.map +1 -1
  129. package/dist/tools/elements/handlers.d.ts +6 -1
  130. package/dist/tools/elements/handlers.d.ts.map +1 -1
  131. package/dist/tools/elements/handlers.js +77 -10
  132. package/dist/tools/elements/handlers.js.map +1 -1
  133. package/dist/tools/elements/layout-hierarchy-rules.d.ts +62 -0
  134. package/dist/tools/elements/layout-hierarchy-rules.d.ts.map +1 -0
  135. package/dist/tools/elements/layout-hierarchy-rules.js +154 -0
  136. package/dist/tools/elements/layout-hierarchy-rules.js.map +1 -0
  137. package/dist/tools/format/elements-format.d.ts +35 -0
  138. package/dist/tools/format/elements-format.d.ts.map +1 -1
  139. package/dist/tools/format/elements-format.js +134 -12
  140. package/dist/tools/format/elements-format.js.map +1 -1
  141. package/dist/tools/format/inspection-format.d.ts +29 -0
  142. package/dist/tools/format/inspection-format.d.ts.map +1 -1
  143. package/dist/tools/format/inspection-format.js +99 -22
  144. package/dist/tools/format/inspection-format.js.map +1 -1
  145. package/dist/tools/format/pages-format.d.ts.map +1 -1
  146. package/dist/tools/format/pages-format.js +7 -0
  147. package/dist/tools/format/pages-format.js.map +1 -1
  148. package/dist/tools/format/sources-format.d.ts +8 -0
  149. package/dist/tools/format/sources-format.d.ts.map +1 -1
  150. package/dist/tools/format/sources-format.js +10 -0
  151. package/dist/tools/format/sources-format.js.map +1 -1
  152. package/dist/tools/health.d.ts.map +1 -1
  153. package/dist/tools/health.js +24 -0
  154. package/dist/tools/health.js.map +1 -1
  155. package/dist/tools/index.d.ts.map +1 -1
  156. package/dist/tools/index.js +4 -0
  157. package/dist/tools/index.js.map +1 -1
  158. package/dist/tools/inspection.d.ts +24 -0
  159. package/dist/tools/inspection.d.ts.map +1 -1
  160. package/dist/tools/inspection.js +297 -5
  161. package/dist/tools/inspection.js.map +1 -1
  162. package/dist/tools/layout-flatten.d.ts +11 -8
  163. package/dist/tools/layout-flatten.d.ts.map +1 -1
  164. package/dist/tools/layout-flatten.js +17 -6
  165. package/dist/tools/layout-flatten.js.map +1 -1
  166. package/dist/tools/multi-items/builders.d.ts.map +1 -1
  167. package/dist/tools/multi-items/builders.js +18 -3
  168. package/dist/tools/multi-items/builders.js.map +1 -1
  169. package/dist/tools/multi-items/clean-implode-handler.d.ts.map +1 -1
  170. package/dist/tools/multi-items/clean-implode-handler.js +2 -2
  171. package/dist/tools/multi-items/clean-implode-handler.js.map +1 -1
  172. package/dist/tools/multi-items/format/inspection-format.d.ts +41 -0
  173. package/dist/tools/multi-items/format/inspection-format.d.ts.map +1 -0
  174. package/dist/tools/multi-items/format/inspection-format.js +64 -0
  175. package/dist/tools/multi-items/format/inspection-format.js.map +1 -0
  176. package/dist/tools/multi-items/inspect-handler.d.ts +50 -2
  177. package/dist/tools/multi-items/inspect-handler.d.ts.map +1 -1
  178. package/dist/tools/multi-items/inspect-handler.js +237 -10
  179. package/dist/tools/multi-items/inspect-handler.js.map +1 -1
  180. package/dist/tools/multi-items/inspection-cache.d.ts +85 -0
  181. package/dist/tools/multi-items/inspection-cache.d.ts.map +1 -0
  182. package/dist/tools/multi-items/inspection-cache.js +126 -0
  183. package/dist/tools/multi-items/inspection-cache.js.map +1 -0
  184. package/dist/tools/pages/builders.d.ts +31 -2
  185. package/dist/tools/pages/builders.d.ts.map +1 -1
  186. package/dist/tools/pages/builders.js +173 -17
  187. package/dist/tools/pages/builders.js.map +1 -1
  188. package/dist/tools/pages/handlers-read.d.ts +6 -2
  189. package/dist/tools/pages/handlers-read.d.ts.map +1 -1
  190. package/dist/tools/pages/handlers-read.js +228 -26
  191. package/dist/tools/pages/handlers-read.js.map +1 -1
  192. package/dist/tools/pages/handlers-write.d.ts +143 -7
  193. package/dist/tools/pages/handlers-write.d.ts.map +1 -1
  194. package/dist/tools/pages/handlers-write.js +215 -11
  195. package/dist/tools/pages/handlers-write.js.map +1 -1
  196. package/dist/tools/pages/index.d.ts +1 -1
  197. package/dist/tools/pages/index.d.ts.map +1 -1
  198. package/dist/tools/pages/index.js +1 -1
  199. package/dist/tools/pages/index.js.map +1 -1
  200. package/dist/tools/pages/schemas.d.ts +166 -0
  201. package/dist/tools/pages/schemas.d.ts.map +1 -1
  202. package/dist/tools/pages/schemas.js +347 -0
  203. package/dist/tools/pages/schemas.js.map +1 -1
  204. package/dist/tools/shared-schemas.d.ts.map +1 -1
  205. package/dist/tools/shared-schemas.js +26 -11
  206. package/dist/tools/shared-schemas.js.map +1 -1
  207. package/dist/tools/sources/builders.d.ts.map +1 -1
  208. package/dist/tools/sources/builders.js +64 -6
  209. package/dist/tools/sources/builders.js.map +1 -1
  210. package/dist/tools/sources/handlers-bind.d.ts +13 -1
  211. package/dist/tools/sources/handlers-bind.d.ts.map +1 -1
  212. package/dist/tools/sources/handlers-bind.js +219 -5
  213. package/dist/tools/sources/handlers-bind.js.map +1 -1
  214. package/dist/tools/sources/handlers.d.ts +3 -0
  215. package/dist/tools/sources/handlers.d.ts.map +1 -1
  216. package/dist/tools/sources/handlers.js +45 -8
  217. package/dist/tools/sources/handlers.js.map +1 -1
  218. package/dist/tools/sparse-fields.d.ts +23 -0
  219. package/dist/tools/sparse-fields.d.ts.map +1 -1
  220. package/dist/tools/sparse-fields.js +29 -0
  221. package/dist/tools/sparse-fields.js.map +1 -1
  222. package/dist/tools/sublayout/builders.d.ts +23 -0
  223. package/dist/tools/sublayout/builders.d.ts.map +1 -0
  224. package/dist/tools/sublayout/builders.js +54 -0
  225. package/dist/tools/sublayout/builders.js.map +1 -0
  226. package/dist/tools/sublayout/index.d.ts +13 -0
  227. package/dist/tools/sublayout/index.d.ts.map +1 -0
  228. package/dist/tools/sublayout/index.js +13 -0
  229. package/dist/tools/sublayout/index.js.map +1 -0
  230. package/dist/tools/sublayout/inspect-sublayout-handler.d.ts +95 -0
  231. package/dist/tools/sublayout/inspect-sublayout-handler.d.ts.map +1 -0
  232. package/dist/tools/sublayout/inspect-sublayout-handler.js +181 -0
  233. package/dist/tools/sublayout/inspect-sublayout-handler.js.map +1 -0
  234. package/dist/tools/tool-builder/results.d.ts +31 -0
  235. package/dist/tools/tool-builder/results.d.ts.map +1 -1
  236. package/dist/tools/tool-builder/results.js +95 -4
  237. package/dist/tools/tool-builder/results.js.map +1 -1
  238. package/dist/uninstall-skill.d.ts +52 -0
  239. package/dist/uninstall-skill.d.ts.map +1 -0
  240. package/dist/uninstall-skill.js +146 -0
  241. package/dist/uninstall-skill.js.map +1 -0
  242. package/dist/uninstall.d.ts +73 -0
  243. package/dist/uninstall.d.ts.map +1 -0
  244. package/dist/uninstall.js +82 -0
  245. package/dist/uninstall.js.map +1 -0
  246. package/icon.png +0 -0
  247. package/manifest.json +10 -10
  248. package/package.json +7 -3
  249. package/skills/yt-builder-mcp/SKILL.md +157 -88
@@ -12,12 +12,17 @@ description: Drive the YOOtheme Pro Page Builder on WordPress or Joomla 5/6. Dis
12
12
  > WordPress and Joomla 5/6.
13
13
 
14
14
  This skill helps AI assistants drive the YOOtheme Pro Page Builder through the
15
- `@wootsup/yt-builder-mcp` server. The server exposes 27 typed, scoped, idempotent
16
- callable tools. 20 of them advertised as first-class entries in `tools/list`
17
- (17 essential L1 forwards + 2 direct L3 entries + 1 gateway), and 7 advanced
18
- tools reachable through the single `yootheme_builder_advanced` gateway. This
19
- 3-lane split keeps `tools/list` well below the ~40-tool Cursor cap while every
20
- catalogued tool stays fully reachable.
15
+ `@wootsup/yt-builder-mcp` server. The server catalogues 32 typed, scoped,
16
+ idempotent tools (34 callable end-to-end with the two gateways). 29 of them
17
+ advertised as first-class entries in `tools/list`
18
+ (25 essential L1 forwards + 2 direct L3 entries + 2 gateways), and 5 advanced
19
+ tools reachable through the gateways. The write-capable
20
+ `yootheme_builder_advanced` gateway routes all 5; the read-only
21
+ `yootheme_builder_advanced_read` gateway routes only the read-only subset
22
+ (page_get_schema, element_get_binding, inspect_sublayout) and is itself
23
+ read-only-annotated, so MCP hosts can auto-allow pure inspection without a
24
+ per-call write-approval prompt. This 3-lane split keeps `tools/list` well below
25
+ the ~40-tool Cursor cap while every catalogued tool stays fully reachable.
21
26
 
22
27
  ## How to use this MCP server
23
28
 
@@ -48,8 +53,10 @@ Some clients (notably Claude Desktop with the `.dxt` bundle) expose **two**
48
53
  entries when the user types "YT Builder MCP" into the picker:
49
54
 
50
55
  - **`YT Builder MCP for YOOtheme Pro (unofficial)`**: the MCP **server**.
51
- Provides the 20 first-class tools (17 essential, 2 direct, 1 gateway).
52
- The `yootheme_builder_advanced` gateway routes 7 additional tools.
56
+ Provides the 28 first-class tools (24 essential, 2 direct, 2 gateways).
57
+ The `yootheme_builder_advanced` gateway routes all 5 advanced tools; the
58
+ read-only `yootheme_builder_advanced_read` gateway routes the read-only
59
+ subset without a write-approval prompt.
53
60
  - **`Von YT Builder MCP for YOOtheme Pro`**: the bundled **skill** (this
54
61
  document). Gives the agent the workflow knowledge needed to drive those
55
62
  tools correctly on first try.
@@ -74,24 +81,37 @@ The server exposes:
74
81
  - **2 direct top-level tools**, always callable, always in `tools/list`:
75
82
  `yootheme_builder_health` and `yootheme_builder_diagnose`. These are
76
83
  the "the gateway itself might be broken" escape hatch.
77
- - **17 essential forwarded tools**: common reads + the most-used writes
78
- (pages_list, get_etag, element_list / add / update_settings / get / move /
79
- clone / delete, page_get_layout, sources_list, element_types_list,
80
- element_type_get_schema, template_summary, inspect_multi_items_binding,
81
- sites_list, sites_test). Always advertised in `tools/list` so AI clients
82
- see them first-class.
83
- - **7 advanced captured tools**: everything else (page_save, page_publish,
84
- page_get_schema, element_get_binding, element_bind_source,
85
- element_unbind_source, clean_implode_directives). Reachable through one
86
- gateway tool: `yootheme_builder_advanced({ tool: "<name>", input: { ... } })`.
87
- - **1 gateway tool**: `yootheme_builder_advanced`.
88
-
89
- `tools/list` therefore advertises 20 names (17 + 2 + 1). That's 17 L1
90
- essentials + 2 L3 direct + 1 gateway. The total callable surface is 27
91
- (20 advertised + 7 advanced reachable through the gateway). If the AI
84
+ - **25 essential forwarded tools**: common reads + the most-used writes
85
+ (pages_list, pages_create, pages_delete, page_dynamize, scaffold_section,
86
+ get_etag, element_list / add / update_settings / get / move / clone / delete,
87
+ page_get_layout, sources_list, element_types_list, element_type_get_schema,
88
+ template_summary, inspect_multi_items_binding, sites_list, sites_test,
89
+ use_site, and the bind→save→publish chain element_bind_source /
90
+ page_save / page_publish). Always advertised in `tools/list` so AI
91
+ clients see them first-class. `scaffold_section` creates a valid
92
+ section row column hull in one call — the easiest correct way to
93
+ start a layout block (content placed anywhere else is rejected by the
94
+ structural-hierarchy guard).
95
+ - **5 advanced captured tools**: everything else (page_get_schema,
96
+ element_get_binding, element_unbind_source, clean_implode_directives,
97
+ and `inspect_sublayout`). Reachable through one gateway tool:
98
+ `yootheme_builder_advanced({ tool: "<name>", arguments: { ... } })`. For
99
+ example, to inspect a panel/grid_item `< SUBLAYOUT`:
100
+ `yootheme_builder_advanced({ tool: "yootheme_builder_inspect_sublayout", arguments: { template_id, element_path } })`.
101
+ - **2 gateway tools**: `yootheme_builder_advanced` (write-capable; can route
102
+ into delete/unbind, so hosts gate it) and `yootheme_builder_advanced_read`
103
+ (read-only; routes ONLY the read-only advanced subset — page_get_schema,
104
+ element_get_binding, inspect_sublayout — and is read-only-annotated so hosts
105
+ auto-allow it). Prefer the read gateway for pure inspection:
106
+ `yootheme_builder_advanced_read({ tool: "yootheme_builder_page_get_schema", arguments: { template_id } })`.
107
+
108
+ `tools/list` therefore advertises 29 names (25 + 2 + 2). That's 25 L1
109
+ essentials + 2 L3 direct + 2 gateways. The total callable surface is 34
110
+ (29 advertised + 5 advanced reachable through the gateways). If the AI
92
111
  client reports "tool not found", you are almost certainly calling an
93
112
  advanced tool by its raw name. Wrap it in
94
- `yootheme_builder_advanced({ tool, input })` instead.
113
+ `yootheme_builder_advanced({ tool, arguments })` (or the read gateway for
114
+ read-only tools) instead.
95
115
 
96
116
  ## Site and frontend URLs (for deep-linking and verification)
97
117
 
@@ -137,7 +157,7 @@ Do not loop on auth errors.
137
157
  > REST endpoints for per-article custom layouts, but they are NOT exposed
138
158
  > via MCP tools in v1.x. To use that surface, call the REST endpoints
139
159
  > directly with the Bearer key. MCP tool coverage for L2 articles is
140
- > planned for v1.2.0.
160
+ > planned for a future release.
141
161
 
142
162
  ---
143
163
 
@@ -281,8 +301,8 @@ one conversation.
281
301
  - `yootheme_builder_pages_list({ site_id: "wp-acme", fields: ["id", "label"] })`
282
302
  - `yootheme_builder_get_etag({ site_id: "wp-acme" })`
283
303
  - `yootheme_builder_element_update_settings({ site_id: "wp-acme", template_id: "home", element_path: "/0/children/0/children/0/children/0", props: { content: "New headline" }, merge: true, etag: "<etag>" })`
284
- - `yootheme_builder_advanced({ tool: "yootheme_builder_page_save", input: { site_id: "wp-acme", template_id: "home", etag: "<fresh>" } })`
285
- - `yootheme_builder_advanced({ tool: "yootheme_builder_page_publish", input: { site_id: "wp-acme", template_id: "home", etag: "<fresh>" } })`
304
+ - `yootheme_builder_page_save({ site_id: "wp-acme", template_id: "home", etag: "<fresh>" })`
305
+ - `yootheme_builder_page_publish({ site_id: "wp-acme", template_id: "home", etag: "<fresh>" })`
286
306
  3. The customer sees a stream of replies, each prefixed with the matching
287
307
  `[label @ host]`, so it is obvious which site is at which step.
288
308
 
@@ -320,11 +340,11 @@ to an existing page.
320
340
  add a text element.
321
341
  8. `yootheme_builder_element_add({ template_id, parent_path: "<row-path>", element_type: "button", props: { content: "<cta>", link: "<url>" }, etag })`:
322
342
  add the CTA button.
323
- 9. `yootheme_builder_advanced({ tool: "yootheme_builder_page_save", input: { template_id, etag } })`:
343
+ 9. `yootheme_builder_page_save({ template_id, etag })`:
324
344
  persist the working copy (visible in YOOtheme Customizer preview).
325
- `page_save` is an advanced tool; call it through the gateway.
326
- 10. `yootheme_builder_advanced({ tool: "yootheme_builder_page_publish", input: { template_id, etag } })`:
327
- make the changes live on the front-end. Also an advanced tool.
345
+ `page_save` is a first-class L1 tool; call it directly.
346
+ 10. `yootheme_builder_page_publish({ template_id, etag })`:
347
+ make the changes live on the front-end. Also a first-class L1 tool.
328
348
 
329
349
  **Common pitfalls:**
330
350
 
@@ -345,9 +365,11 @@ to an existing page.
345
365
  - **Reusing a stale etag across many writes.** Every write returns a
346
366
  fresh etag in the response. Pass THAT etag into the next write.
347
367
  Don't hold the one from the original `get_etag` call.
348
- - **Calling page_save / page_publish by name.** Both are advanced
349
- (L2) tools. Call them through `yootheme_builder_advanced({ tool, input })`,
350
- not directly. The first sign you forgot is "tool not found".
368
+ - **Forgetting to publish.** `page_save` and `page_publish` are both
369
+ first-class L1 tools (call them directly by name they are advertised
370
+ in `tools/list`). Saving stages the change; only `page_publish` makes
371
+ it live. A saved-but-unpublished edit looks "done" in the Customizer
372
+ but visitors still see the old page.
351
373
 
352
374
  **Worked example (tool-call snippet):**
353
375
 
@@ -363,11 +385,8 @@ yootheme_builder_element_add({
363
385
  // Response: { path: "/0/children/3", etag: "def456" }
364
386
  // → next call uses etag "def456"
365
387
 
366
- // Step 9. page_save is L2; call via the gateway.
367
- yootheme_builder_advanced({
368
- tool: "yootheme_builder_page_save",
369
- input: { template_id: "home", etag: "<latest>" }
370
- })
388
+ // Step 9. page_save is a first-class L1 tool; call it directly.
389
+ yootheme_builder_page_save({ template_id: "home", etag: "<latest>" })
371
390
  ```
372
391
 
373
392
  **Edge case:** YOOtheme allows nested sections (rare). If the user
@@ -402,21 +421,21 @@ renders dynamic items.
402
421
  4. `yootheme_builder_sources_list()`: enumerate available Sources.
403
422
  Each returns `{ name, label, origin, kind }`. Pick the one the
404
423
  user asked for.
405
- 5. `yootheme_builder_advanced({ tool: "yootheme_builder_element_get_binding", input: { template_id, element_path } })`
424
+ 5. `yootheme_builder_advanced({ tool: "yootheme_builder_element_get_binding", arguments: { template_id, element_path } })`
406
425
  check whether the Grid is already bound (idempotency: skip step
407
426
  7 if `source_name` already matches the user's intent).
408
427
  `element_get_binding` is an advanced (L2) tool. Call via the gateway.
409
428
  6. `yootheme_builder_get_etag()`: fetch the optimistic-lock etag for
410
429
  the upcoming mutation.
411
- 7. `yootheme_builder_advanced({ tool: "yootheme_builder_element_bind_source", input: { template_id, element_path, source_name: "<name>", etag: "<etag>" } })`
430
+ 7. `yootheme_builder_element_bind_source({ template_id, element_path, source_name: "<name>", etag: "<etag>" })`
412
431
  applies the binding. Returns `{ path, etag, has_binding: true }`.
413
432
  Pass `source_id: "<origin>:<name>"` as well **only** when two
414
433
  plugins register a source with the same `source_name` (the server
415
434
  surfaces the ambiguity as an elicitation prompt; if the host
416
435
  doesn't support elicitation you'll see a structured error listing
417
- the candidates). Also an advanced tool.
418
- 8. `yootheme_builder_advanced({ tool: "yootheme_builder_page_save", input: { template_id, etag: "<fresh>" } })`
419
- then `yootheme_builder_advanced({ tool: "yootheme_builder_page_publish", input: { template_id, etag: "<fresh>" } })`.
436
+ the candidates). This is a first-class L1 tool — call it directly.
437
+ 8. `yootheme_builder_page_save({ template_id, etag: "<fresh>" })`
438
+ then `yootheme_builder_page_publish({ template_id, etag: "<fresh>" })`.
420
439
 
421
440
  **Common pitfalls:**
422
441
 
@@ -439,29 +458,29 @@ renders dynamic items.
439
458
  - **Forgetting `etag`.** Every write requires the optimistic-lock
440
459
  etag. On `412 Precondition Failed` re-fetch via
441
460
  `yootheme_builder_get_etag` and retry.
442
- - **Calling bind/unbind directly.** Both `element_bind_source` and
443
- `element_get_binding` are L2 advanced. Wrap in
444
- `yootheme_builder_advanced({ tool, input })`.
461
+ - **Mis-routing bind vs the read helpers.** `element_bind_source` is
462
+ now a first-class L1 tool — call it directly by name. The binding
463
+ *read* helpers `element_get_binding` and `element_unbind_source` stay
464
+ L2 advanced; reach those through
465
+ `yootheme_builder_advanced({ tool, arguments })`.
445
466
 
446
467
  **Worked example (tool-call snippet):**
447
468
 
448
469
  ```jsonc
449
- // Step 7. Bind a Posts source onto a Grid element via the gateway.
450
- yootheme_builder_advanced({
451
- tool: "yootheme_builder_element_bind_source",
452
- input: {
453
- template_id: "home",
454
- element_path: "/0/children/2/children/0",
455
- source_name: "wp_posts",
456
- etag: "abc123"
457
- // source_id: "wordpress:wp_posts" // pass ONLY when name collides
458
- }
470
+ // Step 7. Bind a Posts source onto a Grid element. element_bind_source
471
+ // is a first-class L1 tool — call it directly.
472
+ yootheme_builder_element_bind_source({
473
+ template_id: "home",
474
+ element_path: "/0/children/2/children/0",
475
+ source_name: "wp_posts",
476
+ etag: "abc123"
477
+ // source_id: "wordpress:wp_posts" // pass ONLY when name collides
459
478
  })
460
479
  // Response: { path: "/0/children/2/children/0", etag: "def456", has_binding: true }
461
480
  // Verify (via gateway):
462
481
  yootheme_builder_advanced({
463
482
  tool: "yootheme_builder_element_get_binding",
464
- input: { template_id: "home", element_path: "/0/children/2/children/0" }
483
+ arguments: { template_id: "home", element_path: "/0/children/2/children/0" }
465
484
  })
466
485
  // → { source_name: "wp_posts", source_config: { ... }, ... }
467
486
  ```
@@ -477,6 +496,50 @@ items from the Source (verify by item count and at least one
477
496
  field-value spot-check). `yootheme_builder_element_get_binding`
478
497
  (via gateway) returns the new `source_name`.
479
498
 
499
+ ### Multi-Items renderer truth table (read before binding rich cards)
500
+
501
+ The YT5 renderer (`SourceTransform`) follows ONE rule:
502
+
503
+ > A `…List` source renders nested children **only** when the element
504
+ > carrying it is a **CONTAINER** whose template renders its children
505
+ > (`column`, `fragment`, `row`, `section`, `grid`). `grid_item` and every
506
+ > `*_item` atom are **LEAF renderers**: the list iterates them per row, but
507
+ > their template paints **only their own slots** and **never** renders nested
508
+ > children — so any elements nested inside an iterating `grid_item` are
509
+ > dropped and you see placeholders.
510
+
511
+ What the renderer consumes from `item.source.props`: it maps each row field
512
+ onto the element's **own** display slots (`grid_item`:
513
+ title/meta/content/image/video/hover_image/hover_video/link/icon; `panel`:
514
+ title/meta/content/image). It is **last-write-wins** — two fields mapped to
515
+ the same slot silently lose one.
516
+
517
+ Two canonical forms (both live-verified):
518
+
519
+ - **(a) Simple card, N ≤ slots — iterate ON the `grid_item`:**
520
+ `grid_item{ source:{ query:{name:"…List"}, props:{ title:{name:F1}, meta:{name:F2}, image:{name:F3} } } }`.
521
+ No nested children. Renders because the data lands on the item's own slots.
522
+
523
+ - **(b) Rich / arbitrary card, any N — iterate ON a CONTAINER:**
524
+ `column{ source:{query:{name:"…List"}} > fragment{#parent} > leaves{ source:{query:{name:"#parent"}, props:{<slot>:{name:Fk}}} }`.
525
+ One leaf per field, **no leaf limit**. `<slot>` is `content` for
526
+ headline/text, or title/meta/image for panel. The `fragment` wrapper is
527
+ **optional** (it may carry `source.query.name="#parent"` or no source —
528
+ both work because a fragment renders its children either way).
529
+
530
+ **Guards (the tool enforces these):**
531
+
532
+ - Binding a leaf that is a **DIRECT child of an iterating `grid_item`** (no
533
+ `fragment` between) auto-**consolidates** the field onto the item's own
534
+ slots (form (a)) and leaves the leaf source-free (`consolidated_to_parent`).
535
+ - A consolidation that targets an **occupied slot** OR a key that is **not a
536
+ slot** of the item type is **rejected** with `slot_collision` (HTTP 409) —
537
+ give each field its own leaf / a distinct slot.
538
+ - Binding a `…List` source ONTO a `grid_item` that already has nested
539
+ children surfaces a blocking `iterator_is_leaf_renderer` warning (the
540
+ children are render-dead): map the fields onto the item's slots, or move
541
+ the List onto a container.
542
+
480
543
  ---
481
544
 
482
545
  ## Workflow 3: Clone & modify a section within a template
@@ -501,7 +564,7 @@ CMS-level template duplication (in wp-admin or Joomla administrator).
501
564
  1. `yootheme_builder_health`: confirm host plugin reachable.
502
565
  2. `yootheme_builder_pages_list({ fields: ["id", "label"] })`:
503
566
  locate the template by `label`. Note its `id`.
504
- 3. `yootheme_builder_advanced({ tool: "yootheme_builder_page_get_schema", input: { template_id } })`
567
+ 3. `yootheme_builder_advanced({ tool: "yootheme_builder_page_get_schema", arguments: { template_id } })`
505
568
  returns a flat schema view (lighter than `page_get_layout`) showing every
506
569
  element path + type. Pick the JSON-Pointer path of the section
507
570
  to clone. `page_get_schema` is L2; call via the gateway.
@@ -517,8 +580,8 @@ CMS-level template duplication (in wp-admin or Joomla administrator).
517
580
  pass `merge: true` to apply a server-side deep-merge instead).
518
581
  Read the current props first via `yootheme_builder_element_get`
519
582
  if you only want to tweak a subset.
520
- 8. `yootheme_builder_advanced({ tool: "yootheme_builder_page_save", input: { template_id, etag } })`
521
- then `yootheme_builder_advanced({ tool: "yootheme_builder_page_publish", input: { template_id, etag } })`.
583
+ 8. `yootheme_builder_page_save({ template_id, etag })`
584
+ then `yootheme_builder_page_publish({ template_id, etag })`.
522
585
 
523
586
  **Common pitfalls:**
524
587
 
@@ -536,7 +599,7 @@ CMS-level template duplication (in wp-admin or Joomla administrator).
536
599
  - **Cloning a bound element keeps the binding.** `element_clone`
537
600
  copies the entire element including `props.source`. If the user
538
601
  wanted a "data-free" copy, call
539
- `yootheme_builder_advanced({ tool: "yootheme_builder_element_unbind_source", input: { ... } })`
602
+ `yootheme_builder_advanced({ tool: "yootheme_builder_element_unbind_source", arguments: { ... } })`
540
603
  on the new path afterwards.
541
604
  - **Wrong parameter names.** Use `template_id`, `element_path`,
542
605
  `etag` (NOT `pageId`, `srcPath`, `ifMatch`).
@@ -568,11 +631,11 @@ yootheme_builder_element_update_settings({
568
631
  **Edge case:** When cloning a Grid with a source binding, the binding
569
632
  is preserved (same `source_name`). If the user wants a "data-free"
570
633
  copy, follow up with
571
- `yootheme_builder_advanced({ tool: "yootheme_builder_element_unbind_source", input: { ... } })`
634
+ `yootheme_builder_advanced({ tool: "yootheme_builder_element_unbind_source", arguments: { ... } })`
572
635
  on the new path. Verify with the gateway `element_get_binding` call.
573
636
 
574
637
  **Success criterion:** After publish,
575
- `yootheme_builder_advanced({ tool: "yootheme_builder_page_get_schema", input: { template_id } })`
638
+ `yootheme_builder_advanced({ tool: "yootheme_builder_page_get_schema", arguments: { template_id } })`
576
639
  shows the new section at the cloned path with the user's tweaks reflected in
577
640
  `element_get` on that path.
578
641
 
@@ -650,7 +713,7 @@ yootheme_builder_diagnose({})
650
713
  // Response shape:
651
714
  // {
652
715
  // plugin_reachable: true,
653
- // plugin_version: "1.1.0",
716
+ // plugin_version: "1.2.0",
654
717
  // yootheme_loaded: true,
655
718
  // yootheme_version: "5.0.22",
656
719
  // endpoint_count: 16,
@@ -707,8 +770,8 @@ an instance with a sensible default props payload.
707
770
  iterates on the props. **Note: this REPLACES `props` entirely by
708
771
  default; pass `merge: true` for a server-side deep-merge.** When
709
772
  replacing, include every key you want to keep.
710
- 8. `yootheme_builder_advanced({ tool: "yootheme_builder_page_save", input: { template_id, etag } })`
711
- then `yootheme_builder_advanced({ tool: "yootheme_builder_page_publish", input: { template_id, etag } })`.
773
+ 8. `yootheme_builder_page_save({ template_id, etag })`
774
+ then `yootheme_builder_page_publish({ template_id, etag })`.
712
775
 
713
776
  **Common pitfalls:**
714
777
 
@@ -788,11 +851,11 @@ and the props payload you passed.
788
851
  elicitation, it returns a preview-with-confirm-required response;
789
852
  call again with `confirm: true`.
790
853
  - **Unbind a source**: call through the gateway:
791
- `yootheme_builder_advanced({ tool: "yootheme_builder_element_unbind_source", input: { template_id, element_path, etag, confirm: true } })`.
854
+ `yootheme_builder_advanced({ tool: "yootheme_builder_element_unbind_source", arguments: { template_id, element_path, etag, confirm: true } })`.
792
855
  Same elicitation flow as delete.
793
856
  - **Flat schema inspection** (e.g. enumerate every element path +
794
857
  type without fetching the whole nested tree): call through the
795
- gateway: `yootheme_builder_advanced({ tool: "yootheme_builder_page_get_schema", input: { template_id } })`.
858
+ gateway: `yootheme_builder_advanced({ tool: "yootheme_builder_page_get_schema", arguments: { template_id } })`.
796
859
  - **Etag-only fetch** (e.g. polling for concurrent edits): use
797
860
  `yootheme_builder_get_etag()` (takes no arguments) is cheaper than
798
861
  fetching the full layout.
@@ -807,7 +870,7 @@ and the props payload you passed.
807
870
  - **Strip legacy `implode` directives** from an element binding (audit-clean
808
871
  source props that pre-date the wrapper-source refactor): call through the
809
872
  gateway:
810
- `yootheme_builder_advanced({ tool: "yootheme_builder_clean_implode_directives", input: { template_id, element_path, etag } })`.
873
+ `yootheme_builder_advanced({ tool: "yootheme_builder_clean_implode_directives", arguments: { template_id, element_path, etag } })`.
811
874
  Returns the audit log + a fresh ETag; idempotent (`cleaned_count: 0` when
812
875
  there is nothing to remove).
813
876
 
@@ -821,35 +884,41 @@ Joomla administrator access." Don't fabricate tool calls.
821
884
 
822
885
  <!-- TOOL-CATALOG:BEGIN -->
823
886
 
824
- **26 catalogued tools** plus the `yootheme_builder_advanced` gateway = **27 reachable via `tools/list`** (17 L1 + 2 L3 + 1 gateway = 20 advertised; the gateway routes to 7 additional advanced tools, bringing the total to 27 callable). 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.
887
+ **32 catalogued tools** plus the `yootheme_builder_advanced` (write) + `yootheme_builder_advanced_read` (read-only) gateways = **34 reachable via `tools/list`** (25 L1 + 2 L3 + 2 gateways = 29 advertised; the gateways route to 5 additional advanced tools, bringing the total to 34 callable). 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.
825
888
 
826
889
  | Tool | Kind | Input keys | Description |
827
890
  | --- | --- | --- | --- |
828
- | `yootheme_builder_clean_implode_directives` | idempotent | `element_path`, `etag`, `site_id`, `template_id` | Strips `props.source.props.*.implode` directives from an element binding. Returns audit log + new ETag. Idempotent (cleaned_count: 0 when nothing to remove). Requires ETag. Operates on the default site unless site_id is provided. |
891
+ | `yootheme_builder_clean_implode_directives` | destructive | `confirm`, `element_path`, `etag`, `site_id`, `template_id` | Strips `props.source.props.*.implode` directives from an element binding. Returns audit log + new ETag. Idempotent at the server (cleaned_count: 0 when nothing to remove) but classed as destructive because the affected binding cannot be restored without re-mapping. Always ask the user to confirm first, then call again with `confirm: true`. Requires ETag. Operates on the default site unless site_id is provided. |
829
892
  | `yootheme_builder_diagnose` | read+idempotent | `site_id` | Full diagnostic: /health + authenticated /etag probe. Returns site_url, home_url, plugin reachability, Bearer validity in one call. First call when you need to know where the site lives. For per-template URLs see pages_list. Operates on the default site unless site_id is provided. |
830
- | `yootheme_builder_element_add` | mutating | `children`, `element_type`, `etag`, `parent_path`, `props`, `site_id`, `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. Operates on the default site unless site_id is provided. |
831
- | `yootheme_builder_element_bind_source` | idempotent | `bindingLevel`, `element_path`, `etag`, `field_mappings`, `site_id`, `source_id`, `source_name`, `template_id` | Binds a Builder source to an element (sets `props.source`). Use bindingLevel "item" on Multi-Items containers (grid/slideshow/switcher/…) to bind on the first *_item child instead of the container itself. Requires ETag. Operates on the default site unless site_id is provided. |
893
+ | `yootheme_builder_element_add` | mutating | `children`, `element_type`, `etag`, `index`, `parent_path`, `position`, `props`, `site_id`, `template_id`, `type_name` | Add a new element to a template. Provide `parent_path` (or "" for root), `element_type` (e.g. "headline", "text", "grid"), and optional `props` / `children`. Appends by default; pass `index` (0 = first) to insert before siblings. Returns the new element's JSON-Pointer path. Requires ETag. Operates on the default site unless site_id is provided. |
894
+ | `yootheme_builder_element_bind_source` | idempotent | `bindingLevel`, `element_path`, `etag`, `field_mappings`, `site_id`, `source_id`, `source_name`, `template_id` | Binds a Builder source to an element (canonical shape: `source` top-level). Pass `field_mappings` in the SAME call; bindingLevel "item" binds a container's first *_item child. Leaf under an iterating *_item → CONSOLIDATE onto item slots (409 slot_collision if taken/not a slot); leaf under a #parent fragment → INHERIT (`__node_item__`); standalone renders empty. List on a grid_item with children iterator_is_leaf_renderer. Map: bind `map_item`, map `location` to a "lat,lng" field or no markers. Requires ETag. Call directly. Operates on the default site unless site_id is provided. |
832
895
  | `yootheme_builder_element_clone` | mutating | `element_path`, `etag`, `site_id`, `template_id` | Clone an element as a sibling (same parent, immediately after the source). Returns the new element's path. Requires ETag. Operates on the default site unless site_id is provided. |
833
- | `yootheme_builder_element_delete` | destructive | `confirm`, `element_path`, `etag`, `site_id`, `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. Operates on the default site unless site_id is provided. |
896
+ | `yootheme_builder_element_delete` | destructive | `confirm`, `element_path`, `etag`, `site_id`, `template_id` | PERMANENTLY delete an element + children. Cannot be undone. Pass `confirm: true` (boolean) AND a fresh ETag. If `preview:true` returns despite confirm, check `details.received_confirm` — strings/numbers accepted, but boolean is canonical. Operates on the default site unless site_id is provided. |
834
897
  | `yootheme_builder_element_get` | read+idempotent | `element_path`, `site_id`, `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. Operates on the default site unless site_id is provided. |
835
- | `yootheme_builder_element_get_binding` | read+idempotent | `element_path`, `site_id`, `template_id` | Read the source binding attached to an element: the bound source name, the field-mappings (which source field feeds which element prop) and the query arguments/directives. Returns the empty object if the element is not bound. Operates on the default site unless site_id is provided. |
898
+ | `yootheme_builder_element_get_binding` | read+idempotent | `element_path`, `site_id`, `template_id` | Read the source binding attached to an element the bound source name, the field-mappings (which source field feeds which element prop) and the query arguments/directives. Returns the empty object if the element is not bound. Operates on the default site unless site_id is provided. |
836
899
  | `yootheme_builder_element_list` | read+idempotent | `cursor`, `depth`, `fields`, `limit`, `root_path`, `site_id`, `template_id` | List elements in a template as a flat array with JSON-Pointer paths + types. Scope with `root_path`/`depth` for a subtree, paginate with `limit`/`cursor` for large templates. `fields[]` narrows each row. Operates on the default site unless site_id is provided. |
837
900
  | `yootheme_builder_element_move` | idempotent | `element_path`, `etag`, `site_id`, `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. Operates on the default site unless site_id is provided. |
838
- | `yootheme_builder_element_type_get_schema` | read+idempotent | `element_type`, `site_id`, `type_name` | **Call before every `element_add` / `_update_settings`.** Unknown prop keys are silently dropped server-side, so guessing fails quietly. Returns `{name,type,label?}` field descriptors. Use `element_type`; `type_name` is DEPRECATED. Operates on the default site unless site_id is provided. |
839
- | `yootheme_builder_element_types_list` | read+idempotent | `fields`, `site_id` | 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. Operates on the default site unless site_id is provided. |
840
- | `yootheme_builder_element_unbind_source` | destructive | `confirm`, `element_path`, `etag`, `site_id`, `template_id` | Remove the source binding from an element. Clears `props.source`. Destructive in the sense that it may break dynamic-content rendering, so always ask the user to confirm. Requires ETag. Operates on the default site unless site_id is provided. |
841
- | `yootheme_builder_element_update_settings` | idempotent | `element_path`, `etag`, `merge`, `props`, `site_id`, `template_id` | Update `props` on an element. Default replaces all props; pass `merge:true` for server-side deep-merge (only request keys overwritten, others survive, which avoids read-modify-write races). Requires ETag. Operates on the default site unless site_id is provided. |
901
+ | `yootheme_builder_element_type_get_schema` | read+idempotent | `element_type`, `site_id`, `type_name` | **Call before every `element_add` / `_update_settings`** unknown prop keys are silently dropped server-side, so guessing fails quietly. Returns `{name,type,label?}` field descriptors. Use `element_type`; `type_name` is DEPRECATED. Operates on the default site unless site_id is provided. |
902
+ | `yootheme_builder_element_types_list` | read+idempotent | `fields`, `site_id` | 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. Keywords: "slider"/"carousel" → `slideshow`/`panel-slider`/`overlay-slider`. Prop keys differ per element (grid_item `title` vs headline `content`) — use element_type_get_schema before element_add/bind. Operates on the default site unless site_id is provided. |
903
+ | `yootheme_builder_element_unbind_source` | destructive | `confirm`, `element_path`, `etag`, `site_id`, `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. Operates on the default site unless site_id is provided. |
904
+ | `yootheme_builder_element_update_settings` | idempotent | `element_path`, `etag`, `merge`, `props`, `site_id`, `template_id` | Update `props` on an element. Default replaces all props; pass `merge:true` for server-side deep-merge (only request keys overwritten, others survive avoids read-modify-write races). Requires ETag. Operates on the default site unless site_id is provided. |
842
905
  | `yootheme_builder_get_etag` | read+idempotent | `site_id` | Get the current ETag (state revision) for the YOOtheme builder. Returns sha256+revision string used for optimistic locking on writes. Pass the returned value back as `etag` on any write tool (page_save, page_publish, element_add, element_update_settings, element_clone, element_move, element_delete). The server returns HTTP 412 if the ETag has changed since you read it. Keywords: get etag, current etag, state revision, optimistic lock, version stamp. Operates on the default site unless site_id is provided. |
843
906
  | `yootheme_builder_health` | read+idempotent | `site_id` | Check plugin installed/reachable. Returns plugin version, YT Pro version, REST endpoints. Authenticated payload adds site_url + home_url for deep-linking. See yootheme_builder_diagnose for Bearer-validity + connectivity summary. Operates on the default site unless site_id is provided. |
844
- | `yootheme_builder_inspect_multi_items_binding` | read+idempotent | `element_path`, `site_id`, `template_id` | Reports Multi-Items binding state: container/item pair (grid↔grid_item, slideshow↔slideshow_item, …), current binding level (none\|container\|item), and a recommended_fix when the binding sits on the container instead of the child. Operates on the default site unless site_id is provided. |
845
- | `yootheme_builder_page_get_layout` | read+idempotent | `fields`, `flat`, `site_id`, `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. Operates on the default site unless site_id is provided. |
846
- | `yootheme_builder_page_get_schema` | read+idempotent | `site_id`, `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. Operates on the default site unless site_id is provided. |
847
- | `yootheme_builder_page_publish` | idempotent | `etag`, `site_id`, `template_id` | Publish a template: persist state, flush YT + WP caches, snapshot the published-state ETag. ETag optional. When provided, 412 on conflict; when omitted, last-write-wins. Recommended for collaborative edits. Operates on the default site unless site_id is provided. |
848
- | `yootheme_builder_page_save` | idempotent | `etag`, `site_id`, `template_id` | Re-run save-transforms and persist. ETag optional. When provided, 412 on conflict; when omitted, last-write-wins. Recommended for collaborative edits. No-op when state is byte-identical (returns `no_changes:true`, ETag unchanged). Operates on the default site unless site_id is provided. |
849
- | `yootheme_builder_pages_list` | read+idempotent | `fields`, `site_id` | List all pages, templates, and layouts in the YOOtheme Pro builder. Returns template_id, label, type, element count, and frontend_url per row. CALL THIS FIRST to discover available template IDs before any tool that needs a template_id (page_get_layout, element_list, page_get_schema, etc.). Keywords: list pages, list templates, list layouts, discover template_id, index, available templates, what pages exist. Pass `fields:["id","label"]` to slim. Returns ALL pages in one call (no pagination, typically <50 templates per site). Operates on the default site unless site_id is provided. |
907
+ | `yootheme_builder_inspect_multi_items_binding` | read+idempotent | `element_path`, `site_id`, `template_id` | Reports Multi-Items binding state: container/item pair (grid↔grid_item, slideshow↔slideshow_item, map↔map_item, …), current binding level (none\|container\|item), and a recommended_fix when the binding sits on the container instead of the child. For map/map_item, a bound item without a `location` ("lat,lng") field is flagged as a blocking map_location_warning (renders 0 markers). A List on a grid_item with nested children → blocking iterator_is_leaf_renderer (children never render). Operates on the default site unless site_id is provided. |
908
+ | `yootheme_builder_inspect_sublayout` | read+idempotent | `element_path`, `site_id`, `template_id` | Inspects a panel/grid_item `< SUBLAYOUT`: lists the `fragment` subtree bindable leaves (with JSON-Pointer paths to bind on — NOT the container) and the render-time inner grid. Flags `ungated_grid_prop` (blocking): grid props set but the gate (title_align=left / image_align=left\|right) unsatisfied → silent no-op. For container↔item binding see inspect_multi_items_binding. Operates on the default site unless site_id is provided. |
909
+ | `yootheme_builder_page_dynamize` | mutating | `conditions`, `element_path`, `etag`, `leaf_map`, `list_source_name`, `page`, `remove_leaves`, `section_name`, `site_id`, `source_map`, `template_id` | One-call way to dynamize a designed section: copies the layout byte-identically and swaps only what you map. Use INSTEAD of hand-editing JSON. Give a REFERENCE (section_name from template_summary, or element_path) + list_source_name + a leaf_map (original leaf field-name flat flow field; #parent + tuned props kept). A section with several source blocks (Played/Next/Later) dynamizes in ONE call via source_map ({match, source_name, leaf_map?}[]). Sub-query panels: key by "<subquery>.<leaf>". Returns page + `summary`. Requires page.title. Operates on the default site unless site_id is provided. |
910
+ | `yootheme_builder_page_get_layout` | read+idempotent | `fields`, `flat`, `force`, `mode`, `site_id`, `template_id` | Get the layout tree for one template. For dynamize/bind work prefer `mode:"bindings_only"` just the binding skeleton (path, element_type, name, has_binding, source{query_name,props,has_condition}, children), no style props, typically <=15% of full size. Default `mode:"full"` returns nested `{layout, etag}`. `flat:true` gives a depth-first `{elements:[...], etag}` array (combine with `fields[]`). A repeated full read of an unchanged template auto-skips with `[unchanged]` — `force:true` re-fetches. Operates on the default site unless site_id is provided. |
911
+ | `yootheme_builder_page_get_schema` | read+idempotent | `site_id`, `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. Operates on the default site unless site_id is provided. |
912
+ | `yootheme_builder_page_publish` | idempotent | `etag`, `site_id`, `template_id` | Publish a template persist state, flush YT + WP caches, snapshot the published-state ETag. ETag optional when provided, 412 on conflict; when omitted, last-write-wins. Recommended for collaborative edits. Operates on the default site unless site_id is provided. |
913
+ | `yootheme_builder_page_save` | idempotent | `etag`, `layout`, `site_id`, `template_id` | Re-run save-transforms + flush caches. Optionally pass a whole `layout` to REPLACE the template tree ATOMICALLY (F85) — ideal for wholesale dynamization (read layout → swap query → remap fields → write back). A supplied layout is validated + ETag-locked; on rejection nothing is persisted. Omit `layout` to just re-run transforms (no-op if byte-identical) — for incremental edits use element_add/update/bind/delete first. New pages: pages_create. Publish: page_publish. ETag optional (412 on conflict; else last-write-wins). Operates on the default site unless site_id is provided. |
914
+ | `yootheme_builder_pages_create` | idempotent | `dry_run`, `layout`, `menu_item`, `published`, `site_id`, `slug`, `status`, `title` | Create a page (WordPress) or article (Joomla 5/6) with a YOOtheme per-page layout. Required: title. Optional: slug, status (default draft), published (default false → live on true: WP publish / Joomla state 1), layout (JSON), menu_item (Joomla SEF route), dry_run (preflight, no persist). Returns page_id (WP) / article_id (Joomla), slug, template_id (page:{id} or article:{id}), etag, warnings. Joomla: a published article has NO SEF route until a menu item points at it — pass menu_item, else frontend_url may 404. Operates on the default site unless site_id is provided. |
915
+ | `yootheme_builder_pages_delete` | idempotent | `confirm`, `force`, `post_id`, `site_id` | Delete a page (WordPress) or article (Joomla 5/6) with its YOOtheme per-page layout. Requires confirm:true (gate against accidental deletion). Default: trash (recoverable). force:true: permanent delete. Refuses non-page/article types with 403 (Layer 9 defense). Returns deleted:true, page_id (WP) / article_id (Joomla), force_deleted, bumped etag. Operates on the default site unless site_id is provided. |
916
+ | `yootheme_builder_pages_list` | read+idempotent | `fields`, `site_id` | List all pages, templates, and layouts in the YOOtheme Pro builder. Returns template_id, label, type, element count, frontend_url, and builder_url (deep-link to open it in the builder) per row. CALL THIS FIRST to discover available template IDs before any tool that needs a template_id (page_get_layout, element_list, page_get_schema, etc.). Keywords: list pages, list templates, discover template_id. Pass `fields:["id","label"]` to slim. Returns ALL pages in one call (no pagination, typically <50 templates per site). Operates on the default site unless site_id is provided. |
917
+ | `yootheme_builder_scaffold_section` | mutating | `columns`, `etag`, `site_id`, `template_id` | Create a VALID section → row → column hull in ONE atomic call and return the column JSON-Pointer path(s) to drop content into. This is the EASIEST correct way to start a new layout block: YOOtheme content must live in section → row → column, and adding content anywhere else (root, bare section, bare row) is rejected. Pass `columns` (1-12, default 1) for a multi-column row. Requires ETag. Operates on the default site unless site_id is provided. |
850
918
  | `yootheme_builder_sites_list` | read+idempotent | `site_id` | List all sites configured in this multi-site MCP installation. Returns site_id + URL + platform (wordpress\|joomla) + default flag per row. CALL THIS FIRST when working with a fresh MCP connection to discover available site_ids before targeting one with any other tool. Read-only, no REST calls. Keywords: list sites, list connections, list installations, discover site_id, available sites, configured sites, what sites exist, multi-site index. (site_id is accepted for schema-uniformity but ignored by this tool.) |
851
919
  | `yootheme_builder_sites_test` | read+idempotent | `site_id` | Verify connectivity to ONE site: probes /health (no auth) + /etag (auth) in parallel; returns plugin_reachable + bearer_valid. `site_id` is REQUIRED. Use sites_list to find IDs. |
852
- | `yootheme_builder_sources_list` | read+idempotent | `fields`, `site_id` | List all data sources, feeds, and dynamic content sources available in the YOOtheme Pro builder. Returns name + label + origin (apimapper / wordpress / joomla / essentials) per source. CALL THIS BEFORE binding any element to a data source. Pick a source name from the list and pass it to `element_bind_source`. Keywords: list sources, list data sources, list feeds, list bindings, dynamic content, available data, what sources exist. Pass `fields[]` to narrow each row. Operates on the default site unless site_id is provided. |
853
- | `yootheme_builder_template_summary` | read+idempotent | `site_id`, `template_id` | Token-efficient template overview: element counts by type, binding count, max nesting depth, and named landmark sections, computed server-side in one call. Use this to grasp a large template before pulling element_list or page_get_layout. Operates on the default site unless site_id is provided. |
920
+ | `yootheme_builder_sources_list` | read+idempotent | `fields`, `site_id` | List all data sources, feeds, and dynamic content sources available in the YOOtheme Pro builder. Returns name + label + origin (apimapper / wordpress / joomla / essentials) per source. CALL THIS BEFORE binding any element to a data source. Then bind directly with yootheme_builder_element_bind_source (a first-class tool in tools/list). Keywords: list sources, list data sources, list feeds, list bindings, dynamic content, available data, what sources exist. Pass `fields[]` to narrow each row. Operates on the default site unless site_id is provided. |
921
+ | `yootheme_builder_template_summary` | read+idempotent | `site_id`, `template_id` | Token-efficient template overview: element counts by type, binding count, max nesting depth, and named landmark sections computed server-side in one call. Use this to grasp a large template before pulling element_list or page_get_layout. Example: yootheme_builder_template_summary({ template_id: "default" }). Operates on the default site unless site_id is provided. |
922
+ | `yootheme_builder_use_site` | idempotent | `probe`, `site_id` | Switch the ACTIVE site for this multi-site session. After this call, every tool that omits `site_id` targets the site you picked here. NOTE: this default is PROCESS-WIDE — concurrent clients sharing this MCP server inherit it; pass `site_id` per call for isolation between parallel agents. Runs a /health + /etag probe by default; pass `probe: false` to switch offline. A degraded site is a soft warning (the switch still happens). `site_id` is REQUIRED. Keywords: switch/change/set active/target site, select installation, use profile. Use yootheme_builder_sites_list first to find IDs. |
854
923
 
855
924
  <!-- TOOL-CATALOG:END -->