@cplace/mcp-server 1.9.1 → 1.10.5

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 (235) hide show
  1. package/README.md +12 -13
  2. package/dist/index.js +4 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/plugins/app-catalog.js +1 -1
  5. package/dist/plugins/app-catalog.js.map +1 -1
  6. package/dist/plugins/application-settings.d.ts.map +1 -1
  7. package/dist/plugins/application-settings.js +6 -3
  8. package/dist/plugins/application-settings.js.map +1 -1
  9. package/dist/plugins/core/widget-specifications/cf.cplace.platform.tableWidget/columns.md +6 -0
  10. package/dist/plugins/core.d.ts.map +1 -1
  11. package/dist/plugins/core.js +25 -13
  12. package/dist/plugins/core.js.map +1 -1
  13. package/dist/plugins/forms-wizard.js +2 -2
  14. package/dist/plugins/forms-wizard.js.map +1 -1
  15. package/dist/plugins/history.d.ts.map +1 -1
  16. package/dist/plugins/history.js +6 -3
  17. package/dist/plugins/history.js.map +1 -1
  18. package/dist/plugins/jobs.js +2 -2
  19. package/dist/plugins/jobs.js.map +1 -1
  20. package/dist/plugins/layouts.d.ts.map +1 -1
  21. package/dist/plugins/layouts.js +17 -8
  22. package/dist/plugins/layouts.js.map +1 -1
  23. package/dist/plugins/low-code.d.ts.map +1 -1
  24. package/dist/plugins/low-code.js +48 -17
  25. package/dist/plugins/low-code.js.map +1 -1
  26. package/dist/plugins/project-planning.d.ts.map +1 -1
  27. package/dist/plugins/project-planning.js +6 -3
  28. package/dist/plugins/project-planning.js.map +1 -1
  29. package/dist/plugins/resource-management.d.ts.map +1 -1
  30. package/dist/plugins/resource-management.js +6 -3
  31. package/dist/plugins/resource-management.js.map +1 -1
  32. package/dist/plugins/solution-management.d.ts.map +1 -1
  33. package/dist/plugins/solution-management.js +6 -3
  34. package/dist/plugins/solution-management.js.map +1 -1
  35. package/dist/plugins/types-write.d.ts.map +1 -1
  36. package/dist/plugins/types-write.js +6 -3
  37. package/dist/plugins/types-write.js.map +1 -1
  38. package/dist/plugins/widget-specifications.d.ts.map +1 -1
  39. package/dist/plugins/widget-specifications.js +2 -2
  40. package/dist/plugins/widget-specifications.js.map +1 -1
  41. package/dist/plugins/workspace-admin.d.ts.map +1 -1
  42. package/dist/plugins/workspace-admin.js +6 -3
  43. package/dist/plugins/workspace-admin.js.map +1 -1
  44. package/dist/str-replace-utils.js +1 -1
  45. package/dist/str-replace-utils.js.map +1 -1
  46. package/dist/tools/apps-catalog.d.ts +1 -1
  47. package/dist/tools/apps-catalog.d.ts.map +1 -1
  48. package/dist/tools/apps-catalog.js +16 -12
  49. package/dist/tools/apps-catalog.js.map +1 -1
  50. package/dist/tools/board-widget.d.ts +2 -2
  51. package/dist/tools/board-widget.d.ts.map +1 -1
  52. package/dist/tools/board-widget.js +354 -201
  53. package/dist/tools/board-widget.js.map +1 -1
  54. package/dist/tools/change-listeners.d.ts +2 -2
  55. package/dist/tools/change-listeners.d.ts.map +1 -1
  56. package/dist/tools/change-listeners.js +125 -57
  57. package/dist/tools/change-listeners.js.map +1 -1
  58. package/dist/tools/common-schemas.d.ts +1 -1
  59. package/dist/tools/common-schemas.d.ts.map +1 -1
  60. package/dist/tools/common-schemas.js +5 -3
  61. package/dist/tools/common-schemas.js.map +1 -1
  62. package/dist/tools/csv-export.d.ts +2 -2
  63. package/dist/tools/csv-export.d.ts.map +1 -1
  64. package/dist/tools/csv-export.js +29 -24
  65. package/dist/tools/csv-export.js.map +1 -1
  66. package/dist/tools/documents.d.ts +2 -2
  67. package/dist/tools/documents.d.ts.map +1 -1
  68. package/dist/tools/documents.js +34 -14
  69. package/dist/tools/documents.js.map +1 -1
  70. package/dist/tools/expert-settings.d.ts +3 -3
  71. package/dist/tools/expert-settings.d.ts.map +1 -1
  72. package/dist/tools/expert-settings.js +97 -38
  73. package/dist/tools/expert-settings.js.map +1 -1
  74. package/dist/tools/get-operations.d.ts +2 -2
  75. package/dist/tools/get-operations.d.ts.map +1 -1
  76. package/dist/tools/get-operations.js +29 -21
  77. package/dist/tools/get-operations.js.map +1 -1
  78. package/dist/tools/job-schedule-trigger.d.ts +2 -2
  79. package/dist/tools/job-schedule-trigger.d.ts.map +1 -1
  80. package/dist/tools/job-schedule-trigger.js +19 -10
  81. package/dist/tools/job-schedule-trigger.js.map +1 -1
  82. package/dist/tools/job-scripts.d.ts +2 -2
  83. package/dist/tools/job-scripts.d.ts.map +1 -1
  84. package/dist/tools/job-scripts.js +119 -55
  85. package/dist/tools/job-scripts.js.map +1 -1
  86. package/dist/tools/jobs.d.ts +2 -2
  87. package/dist/tools/jobs.d.ts.map +1 -1
  88. package/dist/tools/jobs.js +68 -38
  89. package/dist/tools/jobs.js.map +1 -1
  90. package/dist/tools/layout-richstring-shared.d.ts +24 -1
  91. package/dist/tools/layout-richstring-shared.d.ts.map +1 -1
  92. package/dist/tools/layout-richstring-shared.js +99 -1
  93. package/dist/tools/layout-richstring-shared.js.map +1 -1
  94. package/dist/tools/layout-richstring-widgets.d.ts +94 -2
  95. package/dist/tools/layout-richstring-widgets.d.ts.map +1 -1
  96. package/dist/tools/layout-richstring-widgets.js +247 -25
  97. package/dist/tools/layout-richstring-widgets.js.map +1 -1
  98. package/dist/tools/layout.d.ts +5 -4
  99. package/dist/tools/layout.d.ts.map +1 -1
  100. package/dist/tools/layout.js +235 -124
  101. package/dist/tools/layout.js.map +1 -1
  102. package/dist/tools/linting.d.ts +2 -2
  103. package/dist/tools/linting.d.ts.map +1 -1
  104. package/dist/tools/linting.js +5 -5
  105. package/dist/tools/linting.js.map +1 -1
  106. package/dist/tools/page-action-simulation.d.ts +18 -0
  107. package/dist/tools/page-action-simulation.d.ts.map +1 -0
  108. package/dist/tools/page-action-simulation.js +88 -0
  109. package/dist/tools/page-action-simulation.js.map +1 -0
  110. package/dist/tools/page-actions.d.ts +60 -0
  111. package/dist/tools/page-actions.d.ts.map +1 -0
  112. package/dist/tools/page-actions.js +284 -0
  113. package/dist/tools/page-actions.js.map +1 -0
  114. package/dist/tools/pages.d.ts +3 -3
  115. package/dist/tools/pages.d.ts.map +1 -1
  116. package/dist/tools/pages.js +237 -112
  117. package/dist/tools/pages.js.map +1 -1
  118. package/dist/tools/ppt-export-schemas.d.ts +1 -1
  119. package/dist/tools/ppt-export-schemas.d.ts.map +1 -1
  120. package/dist/tools/ppt-export-schemas.js +269 -122
  121. package/dist/tools/ppt-export-schemas.js.map +1 -1
  122. package/dist/tools/ppt-export.d.ts +2 -2
  123. package/dist/tools/ppt-export.d.ts.map +1 -1
  124. package/dist/tools/ppt-export.js +82 -43
  125. package/dist/tools/ppt-export.js.map +1 -1
  126. package/dist/tools/profile-management.d.ts +2 -2
  127. package/dist/tools/profile-management.d.ts.map +1 -1
  128. package/dist/tools/profile-management.js +109 -82
  129. package/dist/tools/profile-management.js.map +1 -1
  130. package/dist/tools/references.d.ts +2 -2
  131. package/dist/tools/references.d.ts.map +1 -1
  132. package/dist/tools/references.js +36 -24
  133. package/dist/tools/references.js.map +1 -1
  134. package/dist/tools/resource-planner-widget.d.ts +2 -2
  135. package/dist/tools/resource-planner-widget.d.ts.map +1 -1
  136. package/dist/tools/resource-planner-widget.js +334 -138
  137. package/dist/tools/resource-planner-widget.js.map +1 -1
  138. package/dist/tools/richstring-marker-resolver.d.ts +16 -0
  139. package/dist/tools/richstring-marker-resolver.d.ts.map +1 -0
  140. package/dist/tools/richstring-marker-resolver.js +47 -0
  141. package/dist/tools/richstring-marker-resolver.js.map +1 -0
  142. package/dist/tools/richstring-widgets.d.ts +6 -16
  143. package/dist/tools/richstring-widgets.d.ts.map +1 -1
  144. package/dist/tools/richstring-widgets.js +184 -129
  145. package/dist/tools/richstring-widgets.js.map +1 -1
  146. package/dist/tools/schedule.d.ts +2 -2
  147. package/dist/tools/schedule.d.ts.map +1 -1
  148. package/dist/tools/schedule.js +23 -14
  149. package/dist/tools/schedule.js.map +1 -1
  150. package/dist/tools/script-logs.d.ts +2 -2
  151. package/dist/tools/script-logs.d.ts.map +1 -1
  152. package/dist/tools/script-logs.js +71 -32
  153. package/dist/tools/script-logs.js.map +1 -1
  154. package/dist/tools/script.d.ts +2 -2
  155. package/dist/tools/script.d.ts.map +1 -1
  156. package/dist/tools/script.js +44 -26
  157. package/dist/tools/script.js.map +1 -1
  158. package/dist/tools/search.d.ts +2 -2
  159. package/dist/tools/search.d.ts.map +1 -1
  160. package/dist/tools/search.js +139 -102
  161. package/dist/tools/search.js.map +1 -1
  162. package/dist/tools/solution-packages.d.ts +2 -2
  163. package/dist/tools/solution-packages.d.ts.map +1 -1
  164. package/dist/tools/solution-packages.js +149 -61
  165. package/dist/tools/solution-packages.js.map +1 -1
  166. package/dist/tools/system.d.ts +2 -2
  167. package/dist/tools/system.d.ts.map +1 -1
  168. package/dist/tools/system.js +43 -21
  169. package/dist/tools/system.js.map +1 -1
  170. package/dist/tools/type-layouts.d.ts +2 -2
  171. package/dist/tools/type-layouts.d.ts.map +1 -1
  172. package/dist/tools/type-layouts.js +67 -31
  173. package/dist/tools/type-layouts.js.map +1 -1
  174. package/dist/tools/type-management.d.ts +86 -70
  175. package/dist/tools/type-management.d.ts.map +1 -1
  176. package/dist/tools/type-management.js +410 -138
  177. package/dist/tools/type-management.js.map +1 -1
  178. package/dist/tools/url-resolution.d.ts +2 -2
  179. package/dist/tools/url-resolution.d.ts.map +1 -1
  180. package/dist/tools/url-resolution.js +16 -12
  181. package/dist/tools/url-resolution.js.map +1 -1
  182. package/dist/tools/users.d.ts +2 -2
  183. package/dist/tools/users.d.ts.map +1 -1
  184. package/dist/tools/users.js +24 -14
  185. package/dist/tools/users.js.map +1 -1
  186. package/dist/tools/validators.d.ts +2 -2
  187. package/dist/tools/validators.d.ts.map +1 -1
  188. package/dist/tools/validators.js +68 -35
  189. package/dist/tools/validators.js.map +1 -1
  190. package/dist/tools/version-check.d.ts +2 -2
  191. package/dist/tools/version-check.d.ts.map +1 -1
  192. package/dist/tools/version-check.js +36 -30
  193. package/dist/tools/version-check.js.map +1 -1
  194. package/dist/tools/version-history.d.ts +2 -2
  195. package/dist/tools/version-history.d.ts.map +1 -1
  196. package/dist/tools/version-history.js +72 -41
  197. package/dist/tools/version-history.js.map +1 -1
  198. package/dist/tools/widget-simulation.d.ts +5 -6
  199. package/dist/tools/widget-simulation.d.ts.map +1 -1
  200. package/dist/tools/widget-simulation.js +60 -42
  201. package/dist/tools/widget-simulation.js.map +1 -1
  202. package/dist/tools/widget-tool-helpers.d.ts +17 -1
  203. package/dist/tools/widget-tool-helpers.d.ts.map +1 -1
  204. package/dist/tools/widget-tool-helpers.js +41 -6
  205. package/dist/tools/widget-tool-helpers.js.map +1 -1
  206. package/dist/tools/widgets.d.ts +2 -2
  207. package/dist/tools/widgets.d.ts.map +1 -1
  208. package/dist/tools/widgets.js +62 -43
  209. package/dist/tools/widgets.js.map +1 -1
  210. package/dist/tools/wizards.d.ts +2 -2
  211. package/dist/tools/wizards.d.ts.map +1 -1
  212. package/dist/tools/wizards.js +378 -182
  213. package/dist/tools/wizards.js.map +1 -1
  214. package/dist/tools/workflow-execution.d.ts +2 -2
  215. package/dist/tools/workflow-execution.d.ts.map +1 -1
  216. package/dist/tools/workflow-execution.js +27 -19
  217. package/dist/tools/workflow-execution.js.map +1 -1
  218. package/dist/tools/workflow-scripts.d.ts +2 -2
  219. package/dist/tools/workflow-scripts.d.ts.map +1 -1
  220. package/dist/tools/workflow-scripts.js +168 -81
  221. package/dist/tools/workflow-scripts.js.map +1 -1
  222. package/dist/tools/workflow.d.ts +2 -2
  223. package/dist/tools/workflow.d.ts.map +1 -1
  224. package/dist/tools/workflow.js +185 -74
  225. package/dist/tools/workflow.js.map +1 -1
  226. package/dist/tools/workspace-admin.d.ts +2 -2
  227. package/dist/tools/workspace-admin.d.ts.map +1 -1
  228. package/dist/tools/workspace-admin.js +82 -40
  229. package/dist/tools/workspace-admin.js.map +1 -1
  230. package/dist/tools/workspace-core.d.ts +2 -2
  231. package/dist/tools/workspace-core.d.ts.map +1 -1
  232. package/dist/tools/workspace-core.js +93 -53
  233. package/dist/tools/workspace-core.js.map +1 -1
  234. package/dist/utils/dateValidation.js.map +1 -1
  235. package/package.json +9 -4
@@ -1,21 +1,23 @@
1
- import { z } from "zod";
2
- import { debugLogWithTag } from "../logger.js";
3
- import { checkResponseSize } from "../utils.js";
1
+ import { z } from 'zod';
2
+ import { debugLogWithTag } from '../logger.js';
3
+ import { checkResponseSize } from '../utils.js';
4
4
  import { applyStrReplace } from '../str-replace-utils.js';
5
- import { determineApiEndpoint, buildRequestParams, findWidgetById } from './widget-tool-helpers.js';
5
+ import { determineApiEndpoint, buildRequestParams, findWidgetById, extractWidgetIdSummary, buildStaleIdErrorResponse, } from './widget-tool-helpers.js';
6
6
  import { jsonStringToObject } from './common-schemas.js';
7
- export const LayoutContextSchema = jsonStringToObject(z.union([
7
+ export const LayoutContextSchema = jsonStringToObject(z
8
+ .union([
8
9
  z.object({
9
- type: z.literal("page"),
10
- pageUID: z.string().describe("Page UID (e.g., 'page/abc123')")
10
+ type: z.literal('page'),
11
+ pageUID: z.string().describe("Page UID (e.g., 'page/abc123')"),
11
12
  }),
12
13
  z.object({
13
- type: z.literal("type"),
14
+ type: z.literal('type'),
14
15
  workspaceId: z.string().describe("Workspace ID (e.g., 'workspace123')"),
15
16
  typeInternalName: z.string().describe("Type internal name (e.g., 'cf.example.myType')"),
16
- alternativeLayoutName: z.string().optional().describe("Alternative layout name (optional)")
17
- })
18
- ]).describe("Layout target - specify whether operating on page or type layout"));
17
+ alternativeLayoutName: z.string().optional().describe('Alternative layout name (optional)'),
18
+ }),
19
+ ])
20
+ .describe('Layout target - specify whether operating on page or type layout'));
19
21
  const TOOL_EXECUTE_LAYOUT_SCRIPT = 'cplace_execute_layout_script';
20
22
  const TOOL_GET_LAYOUT_OVERVIEW = 'cplace_get_layout_overview';
21
23
  const TOOL_GET_LAYOUT_WIDGET_DETAILS = 'cplace_get_layout_widget_details';
@@ -100,11 +102,27 @@ Do NOT build the tag in a variable and pass it — the parser rejects tags outsi
100
102
  ✅ layout.updateWidget("id", { content: '<decoded-widget type="...">...</decoded-widget>' });
101
103
  ❌ var tag = '<decoded-widget ...>...</decoded-widget>'; layout.updateWidget("id", { content: tag });
102
104
 
105
+ EMBEDDED WIDGET SCRIPT ATTRIBUTES — PLACEHOLDER WORKFLOW (REQUIRED):
106
+ For script-typed attributes on embedded widgets (e.g. cf.cplace.lowCodeWidgets.lowCodeTextWidget.script),
107
+ initialize the value to a minimal placeholder body inside the inline <decoded-widget> JSON, then fill the
108
+ real script body afterward via cplace_richstring_set_widget_script (full replace) or
109
+ cplace_richstring_edit_widget_script (str-replace diff). Authoring a non-trivial script body inline
110
+ forces it through four nested encoding layers (HTML → JSON → JS string literal → MCP wire) and is a
111
+ documented source of escape-bug churn — the dedicated tools take the script as a plain string in a
112
+ structured argument and avoid all four layers.
113
+
114
+ Safe placeholder for cf.cplace.lowCodeWidgets.lowCodeText: return '';
115
+ Other script-bearing widget types: pick any runtime-valid placeholder for that widget.
116
+
117
+ Non-script attributes (strings, numbers, references, lists) do NOT have this problem and are authored
118
+ inline as normal JSON properties on the <decoded-widget> tag.
119
+
103
120
  For atomic single-embedded-widget edits without rewriting the full script,
104
- see cplace_layout_richstring_* tools.`,
121
+ see cplace_layout_richstring_* and cplace_richstring_*_widget_script tools.`,
105
122
  inputSchema: {
106
123
  context: LayoutContextSchema,
107
- script: z.string().describe(`JavaScript code that calls layout.define() exactly once OR one or more edit verbs.
124
+ script: z.string()
125
+ .describe(`JavaScript code that calls layout.define() exactly once OR one or more edit verbs.
108
126
 
109
127
  FULL REPLACE — layout.define() FORMAT:
110
128
  layout.define({
@@ -197,24 +215,31 @@ layout.updateWidget("myTable", { "height": 800 });
197
215
  EMBEDDED WIDGET CONSTRAINT:
198
216
  <decoded-widget> tags must appear directly inline in content: string literals — not in variables.
199
217
  ✅ layout.updateWidget("id", { content: '<decoded-widget ...>...</decoded-widget>' });
200
- ❌ var tag = '<decoded-widget ...>'; layout.updateWidget("id", { content: tag });`)
218
+ ❌ var tag = '<decoded-widget ...>'; layout.updateWidget("id", { content: tag });
219
+
220
+ EMBEDDED WIDGET SCRIPT ATTRIBUTES — placeholder workflow (REQUIRED):
221
+ For script-typed attributes on embedded widgets, initialize the value inline to a minimal
222
+ placeholder (e.g. return '';), then fill the real script body afterward via
223
+ cplace_richstring_set_widget_script or cplace_richstring_edit_widget_script. Authoring a
224
+ non-trivial script body inline forces it through four nested encoding layers and is a
225
+ documented source of escape-bug churn.`),
201
226
  },
202
- annotations: { title: "Execute Layout Script" }
227
+ annotations: { title: 'Execute Layout Script' },
203
228
  },
204
229
  [TOOL_GET_LAYOUT_OVERVIEW]: {
205
- description: "Get layout structure for page or type. Provides an overview of the layout grid including rows, columns, and widget summaries for both page and type layouts.",
230
+ description: 'Get layout structure for page or type. Provides an overview of the layout grid including rows, columns, and widget summaries for both page and type layouts.',
206
231
  inputSchema: {
207
- context: LayoutContextSchema
232
+ context: LayoutContextSchema,
208
233
  },
209
- annotations: { title: "Get Layout Overview" }
234
+ annotations: { title: 'Get Layout Overview' },
210
235
  },
211
236
  [TOOL_GET_LAYOUT_WIDGET_DETAILS]: {
212
- description: "Get detailed widget configuration from page or type layout. Extracts specific widget information from the layout structure.\n\nRichString widget content appears in decoded form: `<decoded-widget id=\"…\" type=\"…\">{\"properties\":{…}}</decoded-widget>` — no base64.",
237
+ description: 'Get detailed widget configuration from page or type layout. Extracts specific widget information from the layout structure.\n\nRichString widget content appears in decoded form: `<decoded-widget id="" type="">{"properties":{…}}</decoded-widget>` — no base64.',
213
238
  inputSchema: {
214
239
  context: LayoutContextSchema,
215
- widgetId: z.string().describe("The unique widget identifier to get details for")
240
+ widgetId: z.string().describe('The unique widget identifier to get details for'),
216
241
  },
217
- annotations: { title: "Get Layout Widget Details" }
242
+ annotations: { title: 'Get Layout Widget Details' },
218
243
  },
219
244
  [TOOL_GET_LAYOUT_SCRIPT]: {
220
245
  description: `Get the current layout as an executable script for a page or type layout.
@@ -236,20 +261,31 @@ RichString widget content is returned in decoded form:
236
261
  The output is directly executable — base64 tags are never emitted.`,
237
262
  inputSchema: {
238
263
  context: LayoutContextSchema,
239
- widgetIds: z.array(z.string()).optional().describe("Optional list of widget IDs to read. When provided, returns layout.updateWidget() fragments for just those widgets instead of the full layout.define() script. Widget IDs can be found via cplace_get_layout_overview.")
264
+ widgetIds: z
265
+ .array(z.string())
266
+ .optional()
267
+ .describe('Optional list of widget IDs to read. When provided, returns layout.updateWidget() fragments for just those widgets instead of the full layout.define() script. Widget IDs can be found via cplace_get_layout_overview.'),
240
268
  },
241
- annotations: { title: "Get Layout Script" }
269
+ annotations: { title: 'Get Layout Script' },
242
270
  },
243
271
  [TOOL_EDIT_LAYOUT]: {
244
- description: "Edit a layout using str_replace — find an exact text match and replace it. Reads the current layout.define() definition, applies the replacement, and writes back. Use this instead of cplace_execute_layout_script when making small changes to large layouts to avoid reproducing the entire definition.\n\nRichString widget content is in decoded form. `old_str` must match the decoded form exactly as returned by cplace_get_layout_script. Raw base64 <embeddedwidget> tags are rejected in both `old_str` and `new_str`.",
272
+ description: 'Edit a layout using str_replace — find an exact text match and replace it. Reads the current layout.define() definition, applies the replacement, and writes back. Use this instead of cplace_execute_layout_script when making small changes to large layouts to avoid reproducing the entire definition.\n\nRichString widget content is in decoded form. `old_str` must match the decoded form exactly as returned by cplace_get_layout_script. Raw base64 <embeddedwidget> tags are rejected in both `old_str` and `new_str`.\n\nIMPORTANT: cplace normalizes HTML/CSS on every save (CSS properties are alphabetized, hex colors become rgb(), whitespace is regularized). After this tool completes, the stored content may differ from what was submitted. The response includes the current stored form — use it (not your original input) when constructing the next old_str.',
245
273
  inputSchema: {
246
274
  context: LayoutContextSchema,
247
- old_str: z.string().describe("The exact text to find in the layout definition (must match exactly, including whitespace and line breaks)"),
248
- new_str: z.string().describe("The replacement text. Use empty string to delete the matched text."),
249
- replace_all: z.boolean().optional().default(false).describe("If true, replace all occurrences of old_str. If false (default), fails when old_str matches more than once.")
275
+ old_str: z
276
+ .string()
277
+ .describe('The exact text to find in the layout definition (must match exactly, including whitespace and line breaks)'),
278
+ new_str: z
279
+ .string()
280
+ .describe('The replacement text. Use empty string to delete the matched text.'),
281
+ replace_all: z
282
+ .boolean()
283
+ .optional()
284
+ .default(false)
285
+ .describe('If true, replace all occurrences of old_str. If false (default), fails when old_str matches more than once.'),
250
286
  },
251
- annotations: { title: "Edit Layout" }
252
- }
287
+ annotations: { title: 'Edit Layout' },
288
+ },
253
289
  };
254
290
  async function getLayoutOverview(client, context) {
255
291
  const endpoint = determineApiEndpoint(context);
@@ -261,22 +297,22 @@ export function registerLayoutTools(server, client) {
261
297
  server.registerTool(TOOL_EXECUTE_LAYOUT_SCRIPT, LAYOUT_TOOL_DEFINITIONS[TOOL_EXECUTE_LAYOUT_SCRIPT], async ({ context, script }) => {
262
298
  debugLogWithTag('EXECUTE_LAYOUT_SCRIPT', `Executing ${context.type} layout script (${script.length} chars)`);
263
299
  try {
264
- const endpoint = context.type === "page"
265
- ? "json/page/layoutScript"
266
- : "json/type/layoutScript";
300
+ const endpoint = context.type === 'page' ? 'json/page/layoutScript' : 'json/type/layoutScript';
267
301
  let requestBody;
268
- if (context.type === "page") {
302
+ if (context.type === 'page') {
269
303
  requestBody = {
270
304
  pageUID: context.pageUID,
271
- script
305
+ script,
272
306
  };
273
307
  }
274
308
  else {
275
309
  requestBody = {
276
310
  workspaceId: context.workspaceId,
277
311
  typeInternalName: context.typeInternalName,
278
- ...(context.alternativeLayoutName && { alternativeLayoutName: context.alternativeLayoutName }),
279
- script
312
+ ...(context.alternativeLayoutName && {
313
+ alternativeLayoutName: context.alternativeLayoutName,
314
+ }),
315
+ script,
280
316
  };
281
317
  }
282
318
  const rawResponse = await client.makeRawApiRequest(endpoint, 'POST', undefined, requestBody);
@@ -284,75 +320,87 @@ export function registerLayoutTools(server, client) {
284
320
  let response;
285
321
  if (rawResponse.success === true) {
286
322
  response = {
287
- operation: "EXECUTE_LAYOUT_SCRIPT",
323
+ operation: 'EXECUTE_LAYOUT_SCRIPT',
288
324
  success: true,
289
325
  status: rawResponse.status,
290
326
  context,
291
327
  result: rawResponse.result,
292
- ...(rawResponse.validationWarnings && { validationWarnings: rawResponse.validationWarnings })
328
+ ...(rawResponse.validationWarnings && {
329
+ validationWarnings: rawResponse.validationWarnings,
330
+ }),
293
331
  };
332
+ if (rawResponse.result?.rows) {
333
+ response.widgetIdSummary = extractWidgetIdSummary(rawResponse.result.rows);
334
+ }
294
335
  }
295
336
  else if (rawResponse.errors) {
296
337
  response = {
297
- operation: "EXECUTE_LAYOUT_SCRIPT",
338
+ operation: 'EXECUTE_LAYOUT_SCRIPT',
298
339
  success: false,
299
- status: "VALIDATION_ERROR",
340
+ status: 'VALIDATION_ERROR',
300
341
  context,
301
342
  errors: rawResponse.errors,
302
- hint: "If a widget type was not found, call cplace_list_widget_definitions to see available widget types."
343
+ hint: 'If a widget type was not found, call cplace_list_widget_definitions to see available widget types.',
303
344
  };
304
345
  }
305
346
  else {
306
347
  response = {
307
- operation: "EXECUTE_LAYOUT_SCRIPT",
348
+ operation: 'EXECUTE_LAYOUT_SCRIPT',
308
349
  success: false,
309
- status: "ERROR",
350
+ status: 'ERROR',
310
351
  context,
311
- errorMessage: rawResponse.errorMessage || "Unknown error"
352
+ errorMessage: rawResponse.errorMessage || 'Unknown error',
312
353
  };
313
354
  }
314
355
  const sizeCheck = checkResponseSize(response);
315
356
  if (sizeCheck.tooLarge) {
316
357
  if (response.result) {
317
358
  delete response.result;
318
- response.resultNote = "Layout was persisted successfully. Result omitted due to size. Use cplace_get_layout_overview to inspect.";
359
+ response.resultNote =
360
+ 'Layout was persisted successfully. Result omitted due to size — use widgetIdSummary for current widget IDs, or call cplace_get_layout_overview for the full layout.';
319
361
  const sizeCheck2 = checkResponseSize(response);
320
362
  if (sizeCheck2.tooLarge) {
321
363
  debugLogWithTag('EXECUTE_LAYOUT_SCRIPT', `Response still too large after stripping result: ${sizeCheck2.actualSize} chars`);
322
364
  return {
323
- content: [{
324
- type: "text",
325
- text: `Layout response too large (${sizeCheck2.actualSize} characters) even after stripping result. Use cplace_get_layout_overview to inspect the layout.`
326
- }],
327
- isError: true
365
+ content: [
366
+ {
367
+ type: 'text',
368
+ text: `Layout response too large (${sizeCheck2.actualSize} characters) even after stripping result. Use cplace_get_layout_overview to inspect the layout.`,
369
+ },
370
+ ],
371
+ isError: true,
328
372
  };
329
373
  }
330
374
  }
331
375
  else {
332
376
  debugLogWithTag('EXECUTE_LAYOUT_SCRIPT', `Error response too large: ${sizeCheck.actualSize} chars`);
333
377
  return {
334
- content: [{
335
- type: "text",
336
- text: `Layout response too large (${sizeCheck.actualSize} characters). Use cplace_get_layout_overview to inspect the layout.`
337
- }],
338
- isError: true
378
+ content: [
379
+ {
380
+ type: 'text',
381
+ text: `Layout response too large (${sizeCheck.actualSize} characters). Use cplace_get_layout_overview to inspect the layout.`,
382
+ },
383
+ ],
384
+ isError: true,
339
385
  };
340
386
  }
341
387
  }
342
388
  debugLogWithTag('EXECUTE_LAYOUT_SCRIPT', `Returning response with success=${response.success}, status=${response.status}`);
343
389
  return {
344
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
345
- ...(!response.success ? { isError: true } : {})
390
+ content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
391
+ ...(!response.success ? { isError: true } : {}),
346
392
  };
347
393
  }
348
394
  catch (error) {
349
395
  debugLogWithTag('EXECUTE_LAYOUT_SCRIPT', `Error: ${error instanceof Error ? error.message : String(error)}`);
350
396
  return {
351
- content: [{
352
- type: "text",
353
- text: `Error executing layout script: ${error instanceof Error ? error.message : String(error)}`
354
- }],
355
- isError: true
397
+ content: [
398
+ {
399
+ type: 'text',
400
+ text: `Error executing layout script: ${error instanceof Error ? error.message : String(error)}`,
401
+ },
402
+ ],
403
+ isError: true,
356
404
  };
357
405
  }
358
406
  });
@@ -371,24 +419,26 @@ export function registerLayoutTools(server, client) {
371
419
  id: widget.id,
372
420
  widgetType: widget.widgetType,
373
421
  configurationCount: widget.configuration?.length || 0,
374
- validationStatus: widget.validationStatus || null
375
- })) || []
376
- })) || []
377
- })) || []
422
+ validationStatus: widget.validationStatus || null,
423
+ })) || [],
424
+ })) || [],
425
+ })) || [],
378
426
  };
379
427
  debugLogWithTag('LAYOUT', `Retrieved ${context.type} layout overview successfully`);
380
428
  return {
381
- content: [{ type: "text", text: JSON.stringify(overview, null, 2) }]
429
+ content: [{ type: 'text', text: JSON.stringify(overview, null, 2) }],
382
430
  };
383
431
  }
384
432
  catch (error) {
385
433
  debugLogWithTag('LAYOUT', `Error getting layout overview: ${error instanceof Error ? error.message : String(error)}`);
386
434
  return {
387
- content: [{
388
- type: "text",
389
- text: `Error getting ${context.type} layout overview: ${error instanceof Error ? error.message : String(error)}`
390
- }],
391
- isError: true
435
+ content: [
436
+ {
437
+ type: 'text',
438
+ text: `Error getting ${context.type} layout overview: ${error instanceof Error ? error.message : String(error)}`,
439
+ },
440
+ ],
441
+ isError: true,
392
442
  };
393
443
  }
394
444
  });
@@ -398,145 +448,206 @@ export function registerLayoutTools(server, client) {
398
448
  const layoutResult = await getLayoutOverview(client, context);
399
449
  const foundWidget = findWidgetById(layoutResult.rows, widgetId);
400
450
  if (!foundWidget) {
401
- return {
402
- content: [{ type: "text", text: `Widget with ID ${widgetId} not found in ${context.type} layout` }]
403
- };
451
+ return buildStaleIdErrorResponse(widgetId, context.type);
404
452
  }
405
453
  debugLogWithTag('LAYOUT', `Retrieved widget details for: ${widgetId} from ${context.type} layout`);
406
454
  const response = {
407
455
  context,
408
456
  widgetId,
409
457
  widget: foundWidget,
410
- validationStatus: foundWidget.validationStatus || null
458
+ validationStatus: foundWidget.validationStatus || null,
411
459
  };
412
460
  return {
413
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
461
+ content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
414
462
  };
415
463
  }
416
464
  catch (error) {
417
465
  debugLogWithTag('LAYOUT', `Error getting widget details: ${error instanceof Error ? error.message : String(error)}`);
418
466
  return {
419
- content: [{
420
- type: "text",
421
- text: `Error getting widget details from ${context.type} layout: ${error instanceof Error ? error.message : String(error)}`
422
- }],
423
- isError: true
467
+ content: [
468
+ {
469
+ type: 'text',
470
+ text: `Error getting widget details from ${context.type} layout: ${error instanceof Error ? error.message : String(error)}`,
471
+ },
472
+ ],
473
+ isError: true,
424
474
  };
425
475
  }
426
476
  });
427
477
  server.registerTool(TOOL_GET_LAYOUT_SCRIPT, LAYOUT_TOOL_DEFINITIONS[TOOL_GET_LAYOUT_SCRIPT], async ({ context, widgetIds }) => {
428
478
  debugLogWithTag('GET_LAYOUT_SCRIPT', `Getting layout script for ${context.type} layout${widgetIds ? ` (widgets: ${widgetIds.join(', ')})` : ''}`);
429
479
  try {
430
- const endpoint = context.type === "page"
431
- ? "json/page/layoutScript"
432
- : "json/type/layoutScript";
480
+ if (widgetIds && widgetIds.length > 0) {
481
+ const layoutResult = await getLayoutOverview(client, context);
482
+ const missingIds = widgetIds.filter((id) => !findWidgetById(layoutResult.rows, id));
483
+ if (missingIds.length > 0) {
484
+ const missingList = missingIds.map((id) => `"${id}"`).join(', ');
485
+ return {
486
+ content: [
487
+ {
488
+ type: 'text',
489
+ text: `Widget ID(s) not found: ${missingList}. Widget IDs are regenerated after every layout save — call cplace_get_layout_overview to get current IDs.`,
490
+ },
491
+ ],
492
+ isError: true,
493
+ };
494
+ }
495
+ }
496
+ const endpoint = context.type === 'page' ? 'json/page/layoutScript' : 'json/type/layoutScript';
433
497
  const params = {
434
498
  ...buildRequestParams(context),
435
- ...(widgetIds && widgetIds.length > 0 && { widgetIds })
499
+ ...(widgetIds && widgetIds.length > 0 && { widgetIds }),
436
500
  };
437
501
  const rawResponse = await client.makeRawApiRequest(endpoint, 'GET', params);
438
502
  if (rawResponse.success !== true) {
439
503
  return {
440
- content: [{
441
- type: "text",
442
- text: `Error getting ${context.type} layout script: ${rawResponse.errorMessage || "Unknown error"}`
443
- }],
444
- isError: true
504
+ content: [
505
+ {
506
+ type: 'text',
507
+ text: `Error getting ${context.type} layout script: ${rawResponse.errorMessage || 'Unknown error'}`,
508
+ },
509
+ ],
510
+ isError: true,
445
511
  };
446
512
  }
447
513
  const response = {
448
514
  context,
449
515
  ...(widgetIds && { widgetIds }),
450
- script: rawResponse.script
516
+ script: rawResponse.script,
451
517
  };
452
518
  const sizeCheck = checkResponseSize(response);
453
519
  if (sizeCheck.tooLarge) {
454
520
  return {
455
- content: [{
456
- type: "text",
457
- text: `Layout script response too large (${sizeCheck.actualSize} characters). ${widgetIds ? 'Try requesting fewer widgets.' : 'Use cplace_get_layout_overview to inspect the layout structure instead, or use widgetIds to request specific widgets.'}`
458
- }],
459
- isError: true
521
+ content: [
522
+ {
523
+ type: 'text',
524
+ text: `Layout script response too large (${sizeCheck.actualSize} characters). ${widgetIds ? 'Try requesting fewer widgets.' : 'Use cplace_get_layout_overview to inspect the layout structure instead, or use widgetIds to request specific widgets.'}`,
525
+ },
526
+ ],
527
+ isError: true,
460
528
  };
461
529
  }
462
530
  return {
463
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
531
+ content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
464
532
  };
465
533
  }
466
534
  catch (error) {
467
535
  debugLogWithTag('GET_LAYOUT_SCRIPT', `Error: ${error instanceof Error ? error.message : String(error)}`);
468
536
  return {
469
- content: [{
470
- type: "text",
471
- text: `Error getting ${context.type} layout script: ${error instanceof Error ? error.message : String(error)}`
472
- }],
473
- isError: true
537
+ content: [
538
+ {
539
+ type: 'text',
540
+ text: `Error getting ${context.type} layout script: ${error instanceof Error ? error.message : String(error)}`,
541
+ },
542
+ ],
543
+ isError: true,
474
544
  };
475
545
  }
476
546
  });
477
547
  server.registerTool(TOOL_EDIT_LAYOUT, LAYOUT_TOOL_DEFINITIONS[TOOL_EDIT_LAYOUT], async ({ context, old_str, new_str, replace_all = false }) => {
478
548
  debugLogWithTag('EDIT_LAYOUT', `Editing ${context.type} layout`);
479
549
  try {
480
- const readEndpoint = context.type === "page"
481
- ? "json/page/layoutScript"
482
- : "json/type/layoutScript";
550
+ const readEndpoint = context.type === 'page' ? 'json/page/layoutScript' : 'json/type/layoutScript';
483
551
  const readParams = buildRequestParams(context);
484
552
  const rawResponse = await client.makeRawApiRequest(readEndpoint, 'GET', readParams);
485
553
  if (rawResponse.success !== true) {
486
554
  return {
487
- content: [{ type: "text", text: `Error reading layout definition: ${rawResponse.errorMessage || "Unknown error"}` }],
488
- isError: true
555
+ content: [
556
+ {
557
+ type: 'text',
558
+ text: `Error reading layout definition: ${rawResponse.errorMessage || 'Unknown error'}`,
559
+ },
560
+ ],
561
+ isError: true,
489
562
  };
490
563
  }
491
564
  const currentScript = rawResponse.script;
492
565
  if (currentScript === undefined || currentScript === null || currentScript === '') {
493
566
  return {
494
- content: [{ type: "text", text: `Error: No layout definition found for this ${context.type}. Use cplace_execute_layout_script to create a new layout.` }],
495
- isError: true
567
+ content: [
568
+ {
569
+ type: 'text',
570
+ text: `Error: No layout definition found for this ${context.type}. Use cplace_execute_layout_script to create a new layout.`,
571
+ },
572
+ ],
573
+ isError: true,
496
574
  };
497
575
  }
498
576
  const result = applyStrReplace(currentScript, old_str, new_str, replace_all);
499
577
  if (!result.success) {
500
578
  return {
501
- content: [{ type: "text", text: `Error: ${result.error}` }],
502
- isError: true
579
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
580
+ isError: true,
503
581
  };
504
582
  }
505
583
  let writeBody;
506
- if (context.type === "page") {
584
+ if (context.type === 'page') {
507
585
  writeBody = { pageUID: context.pageUID, script: result.newContent };
508
586
  }
509
587
  else {
510
588
  writeBody = {
511
589
  workspaceId: context.workspaceId,
512
590
  typeInternalName: context.typeInternalName,
513
- ...(context.alternativeLayoutName && { alternativeLayoutName: context.alternativeLayoutName }),
514
- script: result.newContent
591
+ ...(context.alternativeLayoutName && {
592
+ alternativeLayoutName: context.alternativeLayoutName,
593
+ }),
594
+ script: result.newContent,
515
595
  };
516
596
  }
517
597
  const writeResponse = await client.makeRawApiRequest(readEndpoint, 'POST', undefined, writeBody);
518
598
  if (writeResponse.success !== true) {
519
599
  const errorDetail = writeResponse.errors
520
600
  ? JSON.stringify(writeResponse.errors, null, 2)
521
- : writeResponse.errorMessage || "Unknown error";
601
+ : writeResponse.errorMessage || 'Unknown error';
522
602
  return {
523
- content: [{ type: "text", text: `Error writing layout after edit: ${errorDetail}` }],
524
- isError: true
603
+ content: [{ type: 'text', text: `Error writing layout after edit: ${errorDetail}` }],
604
+ isError: true,
525
605
  };
526
606
  }
607
+ const reReadResponse = await client.makeRawApiRequest(readEndpoint, 'GET', readParams);
608
+ const storedPreview = reReadResponse.success === true && reReadResponse.script
609
+ ? reReadResponse.script.length > 500
610
+ ? reReadResponse.script.substring(0, 500) + '...'
611
+ : reReadResponse.script
612
+ : null;
613
+ let widgetIdSummary = [];
614
+ try {
615
+ const overviewResult = await getLayoutOverview(client, context);
616
+ widgetIdSummary = extractWidgetIdSummary(overviewResult.rows);
617
+ }
618
+ catch (e) {
619
+ debugLogWithTag('EDIT_LAYOUT', `Warning: could not fetch widget ID summary: ${e instanceof Error ? e.message : String(e)}`);
620
+ }
527
621
  debugLogWithTag('EDIT_LAYOUT', `Successfully edited layout (${result.replacementCount} replacement(s)), status: ${writeResponse.status}`);
528
- const responseText = writeResponse.status === 'VALID_COMPLETE'
529
- ? `Successfully edited layout. Replaced ${result.replacementCount} occurrence(s). Layout is valid and complete.`
530
- : `Successfully edited layout. Replaced ${result.replacementCount} occurrence(s). Status: ${writeResponse.status}.${writeResponse.validationWarnings ? ' Warnings: ' + JSON.stringify(writeResponse.validationWarnings) : ''}`;
622
+ const editResponse = {
623
+ operation: 'EDIT_LAYOUT',
624
+ success: true,
625
+ status: writeResponse.status,
626
+ replacementCount: result.replacementCount,
627
+ context,
628
+ ...(writeResponse.validationWarnings && {
629
+ validationWarnings: writeResponse.validationWarnings,
630
+ }),
631
+ ...(storedPreview && {
632
+ normalizationNote: 'Server-side CSS normalization may have changed the stored form (properties alphabetized, hex→rgb, whitespace regularized).',
633
+ storedPreview,
634
+ }),
635
+ widgetIdSummary,
636
+ };
531
637
  return {
532
- content: [{ type: "text", text: responseText }]
638
+ content: [{ type: 'text', text: JSON.stringify(editResponse, null, 2) }],
533
639
  };
534
640
  }
535
641
  catch (error) {
536
642
  debugLogWithTag('EDIT_LAYOUT', `Error: ${error instanceof Error ? error.message : String(error)}`);
537
643
  return {
538
- content: [{ type: "text", text: `Error editing layout: ${error instanceof Error ? error.message : String(error)}` }],
539
- isError: true
644
+ content: [
645
+ {
646
+ type: 'text',
647
+ text: `Error editing layout: ${error instanceof Error ? error.message : String(error)}`,
648
+ },
649
+ ],
650
+ isError: true,
540
651
  };
541
652
  }
542
653
  });