@cplace/test-mcp-server 0.1.14 → 1.0.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 (149) hide show
  1. package/README.md +275 -75
  2. package/dist/api.d.ts +4 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js.map +1 -1
  5. package/dist/conditional-registration.d.ts.map +1 -1
  6. package/dist/conditional-registration.js +21 -3
  7. package/dist/conditional-registration.js.map +1 -1
  8. package/dist/index.js +82 -99
  9. package/dist/index.js.map +1 -1
  10. package/dist/preference-file.d.ts +16 -0
  11. package/dist/preference-file.d.ts.map +1 -0
  12. package/dist/preference-file.js +133 -0
  13. package/dist/preference-file.js.map +1 -0
  14. package/dist/profiles.d.ts +1 -3
  15. package/dist/profiles.d.ts.map +1 -1
  16. package/dist/profiles.js +60 -107
  17. package/dist/profiles.js.map +1 -1
  18. package/dist/searchConversion.d.ts.map +1 -1
  19. package/dist/searchConversion.js +18 -5
  20. package/dist/searchConversion.js.map +1 -1
  21. package/dist/searchSchema.d.ts +25 -25
  22. package/dist/searchSchema.d.ts.map +1 -1
  23. package/dist/searchSchema.js +5 -5
  24. package/dist/searchSchema.js.map +1 -1
  25. package/dist/token-hashing.d.ts +3 -0
  26. package/dist/token-hashing.d.ts.map +1 -0
  27. package/dist/token-hashing.js +18 -0
  28. package/dist/token-hashing.js.map +1 -0
  29. package/dist/tool-metadata.d.ts +2 -0
  30. package/dist/tool-metadata.d.ts.map +1 -0
  31. package/dist/tool-metadata.js +43 -0
  32. package/dist/tool-metadata.js.map +1 -0
  33. package/dist/tools/change-listeners.d.ts +30 -0
  34. package/dist/tools/change-listeners.d.ts.map +1 -1
  35. package/dist/tools/change-listeners.js +24 -18
  36. package/dist/tools/change-listeners.js.map +1 -1
  37. package/dist/tools/common-schemas.d.ts +2 -0
  38. package/dist/tools/common-schemas.d.ts.map +1 -1
  39. package/dist/tools/common-schemas.js +16 -0
  40. package/dist/tools/common-schemas.js.map +1 -1
  41. package/dist/tools/csv-export.d.ts +1849 -0
  42. package/dist/tools/csv-export.d.ts.map +1 -0
  43. package/dist/tools/csv-export.js +61 -0
  44. package/dist/tools/csv-export.js.map +1 -0
  45. package/dist/tools/generic-layouts.d.ts +332 -0
  46. package/dist/tools/generic-layouts.d.ts.map +1 -0
  47. package/dist/tools/generic-layouts.js +490 -0
  48. package/dist/tools/generic-layouts.js.map +1 -0
  49. package/dist/tools/get-operations.d.ts +16 -0
  50. package/dist/tools/get-operations.d.ts.map +1 -0
  51. package/dist/tools/get-operations.js +63 -0
  52. package/dist/tools/get-operations.js.map +1 -0
  53. package/dist/tools/pages.d.ts +55 -0
  54. package/dist/tools/pages.d.ts.map +1 -1
  55. package/dist/tools/pages.js +217 -209
  56. package/dist/tools/pages.js.map +1 -1
  57. package/dist/tools/ppt-export-schemas.d.ts +16 -16
  58. package/dist/tools/ppt-export.d.ts +1493 -0
  59. package/dist/tools/ppt-export.d.ts.map +1 -1
  60. package/dist/tools/ppt-export.js +23 -17
  61. package/dist/tools/ppt-export.js.map +1 -1
  62. package/dist/tools/profile-management.d.ts +20 -0
  63. package/dist/tools/profile-management.d.ts.map +1 -0
  64. package/dist/tools/profile-management.js +250 -0
  65. package/dist/tools/profile-management.js.map +1 -0
  66. package/dist/tools/references.d.ts +36 -0
  67. package/dist/tools/references.d.ts.map +1 -1
  68. package/dist/tools/references.js +48 -45
  69. package/dist/tools/references.js.map +1 -1
  70. package/dist/tools/richstring-widgets.d.ts +51 -0
  71. package/dist/tools/richstring-widgets.d.ts.map +1 -0
  72. package/dist/tools/richstring-widgets.js +173 -0
  73. package/dist/tools/richstring-widgets.js.map +1 -0
  74. package/dist/tools/schedule.d.ts +14 -0
  75. package/dist/tools/schedule.d.ts.map +1 -1
  76. package/dist/tools/schedule.js +7 -3
  77. package/dist/tools/schedule.js.map +1 -1
  78. package/dist/tools/script-logs.d.ts +29 -0
  79. package/dist/tools/script-logs.d.ts.map +1 -0
  80. package/dist/tools/script-logs.js +144 -0
  81. package/dist/tools/script-logs.js.map +1 -0
  82. package/dist/tools/search.d.ts +1870 -0
  83. package/dist/tools/search.d.ts.map +1 -1
  84. package/dist/tools/search.js +49 -75
  85. package/dist/tools/search.js.map +1 -1
  86. package/dist/tools/system.d.ts +2 -1
  87. package/dist/tools/system.d.ts.map +1 -1
  88. package/dist/tools/system.js +68 -2
  89. package/dist/tools/system.js.map +1 -1
  90. package/dist/tools/type-layouts.d.ts +37 -0
  91. package/dist/tools/type-layouts.d.ts.map +1 -1
  92. package/dist/tools/type-layouts.js +57 -11
  93. package/dist/tools/type-layouts.js.map +1 -1
  94. package/dist/tools/type-management.d.ts +134 -0
  95. package/dist/tools/type-management.d.ts.map +1 -0
  96. package/dist/tools/{workspace.js → type-management.js} +67 -307
  97. package/dist/tools/type-management.js.map +1 -0
  98. package/dist/tools/users.d.ts +19 -0
  99. package/dist/tools/users.d.ts.map +1 -1
  100. package/dist/tools/users.js +14 -28
  101. package/dist/tools/users.js.map +1 -1
  102. package/dist/tools/validators.d.ts +28 -0
  103. package/dist/tools/validators.d.ts.map +1 -1
  104. package/dist/tools/validators.js +21 -15
  105. package/dist/tools/validators.js.map +1 -1
  106. package/dist/tools/version-check.d.ts +4 -0
  107. package/dist/tools/version-check.d.ts.map +1 -0
  108. package/dist/tools/version-check.js +107 -0
  109. package/dist/tools/version-check.js.map +1 -0
  110. package/dist/tools/version-history.d.ts +48 -0
  111. package/dist/tools/version-history.d.ts.map +1 -1
  112. package/dist/tools/version-history.js +39 -29
  113. package/dist/tools/version-history.js.map +1 -1
  114. package/dist/tools/widgets.d.ts +182 -0
  115. package/dist/tools/widgets.d.ts.map +1 -1
  116. package/dist/tools/widgets.js +213 -590
  117. package/dist/tools/widgets.js.map +1 -1
  118. package/dist/tools/workflow-scripts.d.ts +40 -0
  119. package/dist/tools/workflow-scripts.d.ts.map +1 -0
  120. package/dist/tools/workflow-scripts.js +186 -0
  121. package/dist/tools/workflow-scripts.js.map +1 -0
  122. package/dist/tools/workflow.d.ts +70 -0
  123. package/dist/tools/workflow.d.ts.map +1 -0
  124. package/dist/tools/workflow.js +259 -0
  125. package/dist/tools/workflow.js.map +1 -0
  126. package/dist/tools/workspace-admin.d.ts +40 -0
  127. package/dist/tools/workspace-admin.d.ts.map +1 -0
  128. package/dist/tools/workspace-admin.js +125 -0
  129. package/dist/tools/workspace-admin.js.map +1 -0
  130. package/dist/tools/workspace-core.d.ts +45 -0
  131. package/dist/tools/workspace-core.d.ts.map +1 -0
  132. package/dist/tools/workspace-core.js +153 -0
  133. package/dist/tools/workspace-core.js.map +1 -0
  134. package/dist/types.d.ts +3 -3
  135. package/dist/types.d.ts.map +1 -1
  136. package/dist/url-parsing.d.ts +6 -0
  137. package/dist/url-parsing.d.ts.map +1 -0
  138. package/dist/url-parsing.js +25 -0
  139. package/dist/url-parsing.js.map +1 -0
  140. package/dist/utils/dateValidation.d.ts +5 -0
  141. package/dist/utils/dateValidation.d.ts.map +1 -0
  142. package/dist/utils/dateValidation.js +57 -0
  143. package/dist/utils/dateValidation.js.map +1 -0
  144. package/dist/utils.js +7 -7
  145. package/dist/utils.js.map +1 -1
  146. package/package.json +1 -1
  147. package/dist/tools/workspace.d.ts +0 -4
  148. package/dist/tools/workspace.d.ts.map +0 -1
  149. package/dist/tools/workspace.js.map +0 -1
@@ -2,8 +2,14 @@ import { z } from "zod";
2
2
  import { debugLogWithTag } from "../logger.js";
3
3
  import { transformMultiplicityToBoolean } from '../utils.js';
4
4
  import { PositionSchema, WIDGET_CONFIG_DESCRIPTION } from './common-schemas.js';
5
- export function registerWidgetTools(server, client) {
6
- server.registerTool("cplace_list_widget_definitions", {
5
+ const TOOL_LIST_WIDGET_DEFINITIONS = 'cplace_list_widget_definitions';
6
+ const TOOL_GET_WIDGET_DEFINITION = 'cplace_get_widget_definition';
7
+ const TOOL_GET_WIDGET_DETAILS = 'cplace_get_widget_details';
8
+ const TOOL_GET_EMBEDDED_LAYOUT = 'cplace_get_embedded_layout';
9
+ const TOOL_ADD_WIDGET_TO_EMBEDDED_LAYOUT = 'cplace_add_widget_to_embedded_layout';
10
+ const TOOL_REMOVE_WIDGET_FROM_EMBEDDED_LAYOUT = 'cplace_remove_widget_from_embedded_layout';
11
+ export const WIDGET_TOOL_DEFINITIONS = {
12
+ [TOOL_LIST_WIDGET_DEFINITIONS]: {
7
13
  description: "Get a list of all available widget definitions in the system that support the specified embedding context, with their basic metadata including names, descriptions, and apps",
8
14
  inputSchema: {
9
15
  embeddingContext: z.enum(["AS_WIDGET", "INSIDE_RICHSTRING", "INSIDE_WIDGET"]).default("AS_WIDGET").describe(`Filter widgets by embedding context support:
@@ -12,7 +18,109 @@ export function registerWidgetTools(server, client) {
12
18
  - INSIDE_WIDGET: Widgets that can be embedded inside other widgets without frame`)
13
19
  },
14
20
  annotations: { title: "List Widget Definitions" }
15
- }, async ({ embeddingContext = "AS_WIDGET" }) => {
21
+ },
22
+ [TOOL_GET_WIDGET_DEFINITION]: {
23
+ description: "Get detailed widget configuration schema.\n\n🔍 ALWAYS USE BEFORE ADDING/CONFIGURING WIDGETS to understand:\n- Required vs optional attributes\n- Correct attribute names (often not intuitive!)\n- Expected data types and formats\n- Available enumeration values\n\nCOMMON GOTCHAS:\n- Bar charts use 'attributeName' not 'attribute'\n- Connected charts need 'tableWidgetId' to link to tables\n- Attribute names often need single quotes: \"'cf.cplace.myAttribute'\"\n- LocalizedString format: {\"en\": \"English\", \"de\": \"German\"}\n\nWidget kinds: 'cf.platform.connectedBarChart', 'cf.platform.wiki', etc.",
24
+ inputSchema: {
25
+ widgetKind: z.string().describe("The widget kind identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')")
26
+ },
27
+ annotations: { title: "Get Widget Definition Details" }
28
+ },
29
+ [TOOL_GET_WIDGET_DETAILS]: {
30
+ description: "Get detailed configuration for a specific widget within a page layout. Use this after cplace_get_page_layout_overview to examine specific widget configurations.",
31
+ inputSchema: {
32
+ pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
33
+ widgetId: z.string().describe("The unique widget identifier from the layout (e.g., 'id_123', 'id_456')")
34
+ },
35
+ annotations: { title: "Get Widget Details" }
36
+ },
37
+ [TOOL_GET_EMBEDDED_LAYOUT]: {
38
+ description: "Get embedded widget layout structure for page or type. Retrieves the widget layout structure of embedded widgets within container widgets like AttributesGroup widgets.",
39
+ inputSchema: {
40
+ context: z.union([
41
+ z.object({
42
+ type: z.literal("page"),
43
+ pageUID: z.string().describe("Page UID (e.g., 'page/abc123')"),
44
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
45
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
46
+ }),
47
+ z.object({
48
+ type: z.literal("type"),
49
+ workspaceId: z.string().describe("Workspace ID (e.g., 'workspace123')"),
50
+ typeInternalName: z.string().describe("Type internal name (e.g., 'cf.example.myType')"),
51
+ alternativeLayoutName: z.string().optional().describe("Alternative layout name (optional)"),
52
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
53
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
54
+ })
55
+ ]).describe("Embedded layout target - specify whether operating on page or type embedded layout")
56
+ },
57
+ annotations: { title: "Get Embedded Layout" }
58
+ },
59
+ [TOOL_ADD_WIDGET_TO_EMBEDDED_LAYOUT]: {
60
+ description: "Add widget to page or type embedded layout. Enables adding widgets to nested layouts like those within AttributesGroup widgets.",
61
+ inputSchema: {
62
+ context: z.union([
63
+ z.object({
64
+ type: z.literal("page"),
65
+ pageUID: z.string().describe("Page UID (e.g., 'page/abc123')"),
66
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
67
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
68
+ }),
69
+ z.object({
70
+ type: z.literal("type"),
71
+ workspaceId: z.string().describe("Workspace ID (e.g., 'workspace123')"),
72
+ typeInternalName: z.string().describe("Type internal name (e.g., 'cf.example.myType')"),
73
+ alternativeLayoutName: z.string().optional().describe("Alternative layout name (optional)"),
74
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
75
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
76
+ })
77
+ ]).describe("Embedded layout target - specify whether operating on page or type embedded layout"),
78
+ widgetType: z.string().describe("Widget type identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')"),
79
+ position: PositionSchema,
80
+ configuration: z.record(z.any()).optional().describe(WIDGET_CONFIG_DESCRIPTION)
81
+ },
82
+ annotations: { title: "Add Widget to Embedded Layout" }
83
+ },
84
+ [TOOL_REMOVE_WIDGET_FROM_EMBEDDED_LAYOUT]: {
85
+ description: "Remove widget from page or type embedded layout. Enables removing widgets from nested layouts like those within AttributesGroup widgets.",
86
+ inputSchema: {
87
+ context: z.union([
88
+ z.object({
89
+ type: z.literal("page"),
90
+ pageUID: z.string().describe("Page UID (e.g., 'page/abc123')"),
91
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
92
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
93
+ }),
94
+ z.object({
95
+ type: z.literal("type"),
96
+ workspaceId: z.string().describe("Workspace ID (e.g., 'workspace123')"),
97
+ typeInternalName: z.string().describe("Type internal name (e.g., 'cf.example.myType')"),
98
+ alternativeLayoutName: z.string().optional().describe("Alternative layout name (optional)"),
99
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
100
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
101
+ })
102
+ ]).describe("Embedded layout target - specify whether operating on page or type embedded layout"),
103
+ widgetId: z.string().describe("The ID of the existing widget to remove from the embedded layout")
104
+ },
105
+ annotations: { title: "Remove Widget from Embedded Layout" }
106
+ }
107
+ };
108
+ export function registerWidgetTools(server, client) {
109
+ const WIDGET_ATTRIBUTE_ENHANCEMENTS = {
110
+ 'cf.cplace.platform.attributesGroup': {
111
+ 'cf.cplace.platform.attributesGroup.pageSelection': {
112
+ validValues: {
113
+ 'embedding': 'Use the current embedding page (the page containing this widget)',
114
+ 'parent': 'Use the parent page of the embedding page',
115
+ 'absolute': 'Use an absolute page reference (requires cf.cplace.platform.attributesGroup.absoluteSelection)',
116
+ 'relative': 'Use a relative reference through an attribute (requires cf.cplace.platform.attributesGroup.relativeSelection)'
117
+ },
118
+ defaultValue: 'embedding',
119
+ note: 'This attribute determines which page\'s attributes are displayed in the widget'
120
+ }
121
+ },
122
+ };
123
+ server.registerTool(TOOL_LIST_WIDGET_DEFINITIONS, WIDGET_TOOL_DEFINITIONS[TOOL_LIST_WIDGET_DEFINITIONS], async ({ embeddingContext = "AS_WIDGET" }) => {
16
124
  debugLogWithTag('WIDGETS', `Starting widget definitions list request with embeddingContext: ${embeddingContext}`);
17
125
  try {
18
126
  const result = await client.makeApiRequest('json/widget-definitions', 'GET', { embeddingContext });
@@ -31,27 +139,7 @@ export function registerWidgetTools(server, client) {
31
139
  };
32
140
  }
33
141
  });
34
- const WIDGET_ATTRIBUTE_ENHANCEMENTS = {
35
- 'cf.cplace.platform.attributesGroup': {
36
- 'cf.cplace.platform.attributesGroup.pageSelection': {
37
- validValues: {
38
- 'embedding': 'Use the current embedding page (the page containing this widget)',
39
- 'parent': 'Use the parent page of the embedding page',
40
- 'absolute': 'Use an absolute page reference (requires cf.cplace.platform.attributesGroup.absoluteSelection)',
41
- 'relative': 'Use a relative reference through an attribute (requires cf.cplace.platform.attributesGroup.relativeSelection)'
42
- },
43
- defaultValue: 'embedding',
44
- note: 'This attribute determines which page\'s attributes are displayed in the widget'
45
- }
46
- },
47
- };
48
- server.registerTool("cplace_get_widget_definition", {
49
- description: "Get detailed widget configuration schema.\n\n🔍 ALWAYS USE BEFORE ADDING/CONFIGURING WIDGETS to understand:\n- Required vs optional attributes\n- Correct attribute names (often not intuitive!)\n- Expected data types and formats\n- Available enumeration values\n\nCOMMON GOTCHAS:\n- Bar charts use 'attributeName' not 'attribute'\n- Connected charts need 'tableWidgetId' to link to tables\n- Attribute names often need single quotes: \"'cf.cplace.myAttribute'\"\n- LocalizedString format: {\"en\": \"English\", \"de\": \"German\"}\n\nWidget kinds: 'cf.platform.connectedBarChart', 'cf.platform.wiki', etc.",
50
- inputSchema: {
51
- widgetKind: z.string().describe("The widget kind identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')")
52
- },
53
- annotations: { title: "Get Widget Definition Details" }
54
- }, async ({ widgetKind }) => {
142
+ server.registerTool(TOOL_GET_WIDGET_DEFINITION, WIDGET_TOOL_DEFINITIONS[TOOL_GET_WIDGET_DEFINITION], async ({ widgetKind }) => {
55
143
  debugLogWithTag('WIDGETS', `Starting widget definition request for: ${widgetKind}`);
56
144
  try {
57
145
  const result = await client.makeApiRequest('json/widget-definition', 'GET', { widgetKind });
@@ -84,53 +172,7 @@ export function registerWidgetTools(server, client) {
84
172
  };
85
173
  }
86
174
  });
87
- server.registerTool("cplace_get_page_layout_overview", {
88
- description: "Get the high-level layout structure of a page including rows, columns, and widget summaries. Provides an overview of the page layout grid without detailed widget configurations.",
89
- inputSchema: {
90
- pageUID: z.string().describe("The unique identifier (UID) of the page to get layout for, e.g. 'page/kkt8ol745jqur4581kelm5ply'")
91
- },
92
- annotations: { title: "Get Page Layout Overview" }
93
- }, async ({ pageUID }) => {
94
- debugLogWithTag('LAYOUT', `Starting page layout overview request for: ${pageUID}`);
95
- try {
96
- const result = await client.makeApiRequest('json/pageLayout', 'GET', { pageUID });
97
- const overview = {
98
- rows: result.rows?.map((row, rowIndex) => ({
99
- rowIndex,
100
- columns: row.columns?.map((column, colIndex) => ({
101
- columnIndex: colIndex,
102
- proportion: column.proportion,
103
- widgets: column.widgets?.map((widget) => ({
104
- id: widget.id,
105
- widgetType: widget.widgetType,
106
- configurationCount: widget.configuration?.length || 0
107
- })) || []
108
- })) || []
109
- })) || []
110
- };
111
- debugLogWithTag('LAYOUT', `Retrieved page layout overview for: ${pageUID}`);
112
- return {
113
- content: [{ type: "text", text: JSON.stringify(overview, null, 2) }]
114
- };
115
- }
116
- catch (error) {
117
- return {
118
- content: [{
119
- type: "text",
120
- text: `Error retrieving page layout overview for ${pageUID}: ${error instanceof Error ? error.message : String(error)}`
121
- }],
122
- isError: true
123
- };
124
- }
125
- });
126
- server.registerTool("cplace_get_widget_details", {
127
- description: "Get detailed configuration for a specific widget within a page layout. Use this after cplace_get_page_layout_overview to examine specific widget configurations.",
128
- inputSchema: {
129
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
130
- widgetId: z.string().describe("The unique widget identifier from the layout (e.g., 'id_123', 'id_456')")
131
- },
132
- annotations: { title: "Get Widget Details" }
133
- }, async ({ pageUID, widgetId }) => {
175
+ server.registerTool(TOOL_GET_WIDGET_DETAILS, WIDGET_TOOL_DEFINITIONS[TOOL_GET_WIDGET_DETAILS], async ({ pageUID, widgetId }) => {
134
176
  debugLogWithTag('LAYOUT', `Starting widget details request for widget ${widgetId} in page ${pageUID}`);
135
177
  try {
136
178
  const result = await client.makeApiRequest('json/pageLayout', 'GET', { pageUID });
@@ -159,582 +201,163 @@ export function registerWidgetTools(server, client) {
159
201
  };
160
202
  }
161
203
  debugLogWithTag('LAYOUT', `Retrieved widget details for: ${widgetId} in page`);
162
- return {
163
- content: [{ type: "text", text: JSON.stringify(foundWidget, null, 2) }]
164
- };
165
- }
166
- catch (error) {
167
- return {
168
- content: [{
169
- type: "text",
170
- text: `Error retrieving widget details for ${widgetId} in page ${pageUID}: ${error instanceof Error ? error.message : String(error)}`
171
- }],
172
- isError: true
173
- };
174
- }
175
- });
176
- server.registerTool("cplace_add_widget_to_layout", {
177
- description: "Add a new widget to a page layout at a specific position.\n\n⚠️ REQUIRED BEFORE USE:\n- ALWAYS call cplace_get_widget_definition FIRST to understand required fields\n- Many widgets have non-obvious required attributes that will cause validation errors\n- Widget configurations must match the schema exactly\n\nCRITICAL BEHAVIOR:\n- Row creation: ❌ NOT automatic - use cplace_add_row_to_page_layout to create rows first\n- Column creation: ❌ NOT automatic - columnIndex only references existing columns\n- Invalid columnIndex: Falls back to last existing column (no error)\n\nTO CREATE SIDE-BY-SIDE LAYOUTS:\n1. First use cplace_add_row_to_page_layout to create multi-column structure\n2. Then add widgets to the created columns\n\nEXAMPLES:\n- Add to new row 0: Creates single-column row, widget goes to column 0\n- Add to column 1 when only column 0 exists: Widget goes to column 0 (fallback)\n- Add to pre-created 6/6 row: Works correctly if columns exist\n\nTIP: Always check current layout with cplace_get_page_layout_overview before adding widgets.\n\nSupports both page-level layouts (default) and embedded layouts within container widgets like AttributesGroup.",
178
- inputSchema: {
179
- pageUID: z.string().describe("The unique identifier (UID) of the page to add the widget to"),
180
- widgetType: z.string().describe("The widget type identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki', 'cf.platform.singleAttribute')"),
181
- configuration: z.record(z.any()).optional().describe(`${WIDGET_CONFIG_DESCRIPTION}
182
-
183
- WIDGET-SPECIFIC PATTERNS:
184
- - Connected widgets (bar/pie charts): Use 'tableWidgetId' to connect to existing table widget
185
- Example: {'tableWidgetId': 'id_abc123', 'title': {'en': 'Chart Title'}}
186
- - Attribute widgets: Use 'cf.platform.quotedAttributeName' with single quotes
187
- Example: {'cf.platform.quotedAttributeName': "'cf.cplace.myAttribute'", 'cf.platform.withLabel': true}
188
- - Search widgets: Use 'search' with JSON search string
189
- Example: {'search': '{"filters":[{"types":["test.project"]}]}', 'title': {'en': 'Results'}}
190
-
191
- For embedded attribute widgets: {'cf.platform.quotedAttributeName': "'cf.cplace.myAttribute'", 'cf.platform.withLabel': true}`),
192
- position: PositionSchema.extend({}).describe("Grid position where the widget should be added"),
193
- layoutContext: z.enum(["page", "widget"]).default("page").describe("Layout context: 'page' for page-level layout (default), 'widget' for embedded layout within a container widget"),
194
- containerWidgetId: z.string().optional().describe("Required when layoutContext is 'widget'. The unique identifier of the container widget (e.g., AttributesGroup widget) that contains the embedded layout to modify"),
195
- layoutAttributeName: z.string().optional().describe("Optional layout attribute name for embedded layouts. If not provided, it will be auto-detected based on the container widget type")
196
- },
197
- annotations: { title: "Add Widget to Layout" }
198
- }, async ({ pageUID, widgetType, configuration, position, layoutContext = "page", containerWidgetId, layoutAttributeName }) => {
199
- debugLogWithTag('LAYOUT_MODIFY', `Starting add widget operation: ${widgetType} to ${layoutContext === "widget" ? "embedded layout in container " + containerWidgetId : "page layout"} ${pageUID} at position ${JSON.stringify(position)}`);
200
- try {
201
- if (layoutContext === "widget") {
202
- if (!containerWidgetId) {
203
- return {
204
- content: [{ type: "text", text: "containerWidgetId is required when layoutContext is 'widget'" }]
205
- };
206
- }
207
- debugLogWithTag('LAYOUT_MODIFY', `Adding to embedded layout in container widget: ${containerWidgetId}`);
208
- }
209
- const operation = {
210
- type: "ADD",
211
- widgetType,
212
- position: {
213
- rowIndex: position.rowIndex,
214
- columnIndex: position.columnIndex,
215
- widgetIndex: position.widgetIndex
216
- }
217
- };
218
- if (configuration && Object.keys(configuration).length > 0) {
219
- operation.configuration = configuration;
220
- debugLogWithTag('LAYOUT_MODIFY', `Including configuration: ${JSON.stringify(Object.keys(configuration))}`);
221
- }
222
- const requestBody = {
223
- pageUID,
224
- layoutContext,
225
- operation
226
- };
227
- if (layoutContext === "widget") {
228
- requestBody.containerWidgetId = containerWidgetId;
229
- if (layoutAttributeName) {
230
- requestBody.layoutAttributeName = layoutAttributeName;
231
- }
232
- }
233
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
234
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
235
- debugLogWithTag('LAYOUT_MODIFY', `Successfully added widget ${widgetType} to page ${pageUID}`);
236
- const response = {
237
- operation: "ADD_WIDGET",
238
- status: "SUCCESS",
239
- pageUID,
240
- widgetType,
241
- position,
242
- layoutContext,
243
- ...(layoutContext === "widget" && containerWidgetId && { containerWidgetId }),
244
- message: layoutContext === "widget"
245
- ? "Widget added successfully to embedded layout"
246
- : "Widget added successfully"
247
- };
248
- return {
249
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
250
- };
251
- }
252
- catch (error) {
253
- debugLogWithTag('LAYOUT_MODIFY', `Error adding widget: ${error instanceof Error ? error.message : String(error)}`);
254
- const errorResponse = {
255
- operation: "ADD_WIDGET",
256
- status: "ERROR",
257
- pageUID,
258
- widgetType,
259
- position,
260
- layoutContext,
261
- ...(layoutContext === "widget" && containerWidgetId && { containerWidgetId }),
262
- error: error instanceof Error ? error.message : String(error),
263
- message: "Failed to add widget"
264
- };
265
- return {
266
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
267
- isError: true
268
- };
269
- }
270
- });
271
- server.registerTool("cplace_remove_widget_from_layout", {
272
- description: "Remove an existing widget from a page layout",
273
- inputSchema: {
274
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
275
- widgetId: z.string().describe("The unique identifier of the widget to remove")
276
- },
277
- annotations: { title: "Remove Widget from Layout" }
278
- }, async ({ pageUID, widgetId }) => {
279
- debugLogWithTag('LAYOUT_MODIFY', `Starting remove widget operation: ${widgetId} from page ${pageUID}`);
280
- try {
281
- const requestBody = {
282
- pageUID,
283
- operation: {
284
- type: "REMOVE",
285
- widgetId
286
- }
287
- };
288
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
289
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
290
- debugLogWithTag('LAYOUT_MODIFY', `Successfully removed widget ${widgetId} from page ${pageUID}`);
291
204
  const response = {
292
- operation: "REMOVE_WIDGET",
293
- status: "SUCCESS",
294
- pageUID,
295
205
  widgetId,
296
- message: "Widget removed successfully"
297
- };
298
- return {
299
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
300
- };
301
- }
302
- catch (error) {
303
- debugLogWithTag('LAYOUT_MODIFY', `Error removing widget: ${error instanceof Error ? error.message : String(error)}`);
304
- const errorResponse = {
305
- operation: "REMOVE_WIDGET",
306
- status: "ERROR",
307
206
  pageUID,
308
- widgetId,
309
- error: error instanceof Error ? error.message : String(error),
310
- message: "Failed to remove widget"
311
- };
312
- return {
313
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
314
- isError: true
315
- };
316
- }
317
- });
318
- server.registerTool("cplace_update_widget_in_layout", {
319
- description: "Modify widget configuration or display properties.\nUSE CASES:\n- Restore configuration after widget moves (configurations may be lost during moves)\n- Update widget connections (e.g., change tableWidgetId after layout modifications)\n- Modify display properties (titles, collapse state, etc.)\n\nTIP: Widget IDs may change after layout operations - always use current IDs from latest layout overview.",
320
- inputSchema: {
321
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
322
- widgetId: z.string().describe("The unique identifier of the widget to update"),
323
- newConfiguration: z.record(z.any()).optional().describe(`Updated widget configuration as key-value pairs (optional). ${WIDGET_CONFIG_DESCRIPTION}`),
324
- collapsed: z.boolean().optional().describe("Widget collapse state (optional)")
325
- },
326
- annotations: { title: "Update Widget in Layout" }
327
- }, async ({ pageUID, widgetId, newConfiguration, collapsed }) => {
328
- debugLogWithTag('LAYOUT_MODIFY', `Starting update widget operation: ${widgetId} in page ${pageUID}`);
329
- try {
330
- const operation = {
331
- type: "UPDATE",
332
- widgetId
333
- };
334
- if (newConfiguration && Object.keys(newConfiguration).length > 0) {
335
- operation.newConfiguration = newConfiguration;
336
- debugLogWithTag('LAYOUT_MODIFY', `Including new configuration: ${JSON.stringify(Object.keys(newConfiguration))}`);
337
- }
338
- if (collapsed !== undefined) {
339
- operation.collapsed = collapsed;
340
- debugLogWithTag('LAYOUT_MODIFY', `Setting collapsed state to: ${collapsed}`);
341
- }
342
- if (!operation.newConfiguration && operation.collapsed === undefined) {
343
- return {
344
- content: [{ type: "text", text: "No updates specified. At least one of newConfiguration or collapsed must be provided." }]
345
- };
346
- }
347
- const requestBody = {
348
- pageUID,
349
- operation
350
- };
351
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
352
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
353
- debugLogWithTag('LAYOUT_MODIFY', `Successfully updated widget ${widgetId} in page ${pageUID}`);
354
- const response = {
355
- operation: "UPDATE_WIDGET",
356
- status: "SUCCESS",
357
- pageUID,
358
- widgetId,
359
- message: "Widget updated successfully"
360
- };
361
- return {
362
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
363
- };
364
- }
365
- catch (error) {
366
- debugLogWithTag('LAYOUT_MODIFY', `Error updating widget: ${error instanceof Error ? error.message : String(error)}`);
367
- const errorResponse = {
368
- operation: "UPDATE_WIDGET",
369
- status: "ERROR",
370
- pageUID,
371
- widgetId,
372
- error: error instanceof Error ? error.message : String(error),
373
- message: "Failed to update widget"
374
- };
375
- return {
376
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
377
- isError: true
378
- };
379
- }
380
- });
381
- server.registerTool("cplace_move_widget_in_layout", {
382
- description: "Relocate a widget to a different position in the layout.\n\nMOVE BEHAVIOR:\n- ✅ Can move between existing rows and columns\n- ❌ Cannot create new columns (only references existing ones)\n- Invalid columnIndex: Falls back to last column in target row\n\nCRITICAL WARNINGS:\n- Widget configurations may be LOST during moves\n- Widget IDs may CHANGE - always get new IDs from response\n- Connected widgets (charts) may lose their tableWidgetId references\n\nPOST-MOVE CHECKLIST:\n1. Get new widget ID from response\n2. Use cplace_get_widget_details to check if configuration was preserved\n3. If lost, use cplace_update_widget_in_layout to restore configuration\n4. Update any widgets that referenced the moved widget's old ID\n\nTIP: For complex layouts, consider removing and re-adding widgets instead of moving.",
383
- inputSchema: {
384
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
385
- widgetId: z.string().describe("The unique identifier of the widget to move"),
386
- newPosition: PositionSchema.extend({}).describe("Target grid position for the widget")
387
- },
388
- annotations: { title: "Move Widget in Layout" }
389
- }, async ({ pageUID, widgetId, newPosition }) => {
390
- debugLogWithTag('LAYOUT_MODIFY', `Starting move widget operation: ${widgetId} in page ${pageUID} to position ${JSON.stringify(newPosition)}`);
391
- try {
392
- const requestBody = {
393
- pageUID,
394
- operation: {
395
- type: "MOVE",
396
- widgetId,
397
- newPosition: {
398
- rowIndex: newPosition.rowIndex,
399
- columnIndex: newPosition.columnIndex,
400
- widgetIndex: newPosition.widgetIndex
401
- }
402
- }
403
- };
404
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
405
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
406
- debugLogWithTag('LAYOUT_MODIFY', `Successfully moved widget ${widgetId} in page ${pageUID}`);
407
- let newWidgetId = widgetId;
408
- let widgetIdChanged = false;
409
- try {
410
- const layoutString = result.result;
411
- const layout = JSON.parse(layoutString);
412
- if (layout.rows && layout.rows[newPosition.rowIndex]) {
413
- const targetRow = layout.rows[newPosition.rowIndex];
414
- if (targetRow.columns && targetRow.columns[newPosition.columnIndex]) {
415
- const targetColumn = targetRow.columns[newPosition.columnIndex];
416
- if (targetColumn.widgets && targetColumn.widgets[newPosition.widgetIndex]) {
417
- const movedWidget = targetColumn.widgets[newPosition.widgetIndex];
418
- if (movedWidget.id && movedWidget.id !== widgetId) {
419
- newWidgetId = movedWidget.id;
420
- widgetIdChanged = true;
421
- }
422
- }
423
- }
424
- }
425
- }
426
- catch (parseError) {
427
- debugLogWithTag('LAYOUT_MODIFY', `Could not parse layout to detect ID change: ${parseError}`);
428
- }
429
- const response = {
430
- operation: "MOVE_WIDGET",
431
- status: "SUCCESS",
432
- pageUID,
433
- oldWidgetId: widgetId,
434
- newWidgetId,
435
- newPosition,
436
- widgetIdChanged,
437
- message: widgetIdChanged
438
- ? "Widget moved successfully - widget ID changed, please update any references"
439
- : "Widget moved successfully"
207
+ widget: foundWidget,
208
+ validationStatus: foundWidget.validationStatus || null
440
209
  };
441
210
  return {
442
211
  content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
443
212
  };
444
213
  }
445
- catch (error) {
446
- debugLogWithTag('LAYOUT_MODIFY', `Error moving widget: ${error instanceof Error ? error.message : String(error)}`);
447
- const errorResponse = {
448
- operation: "MOVE_WIDGET",
449
- status: "ERROR",
450
- pageUID,
451
- oldWidgetId: widgetId,
452
- newPosition,
453
- error: error instanceof Error ? error.message : String(error),
454
- message: "Failed to move widget"
455
- };
456
- return {
457
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
458
- isError: true
459
- };
460
- }
461
- });
462
- server.registerTool("cplace_get_embedded_layout", {
463
- description: "Get the widget layout structure of an embedded widget within a container widget on a specific page. This endpoint accesses nested widget layouts embedded within other widgets, such as AttributesGroup widgets.",
464
- inputSchema: {
465
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the container widget"),
466
- containerWidgetId: z.string().describe("The ID of the container widget that contains the embedded layout"),
467
- layoutAttributeName: z.string().describe("The name of the attribute that defines the embedded layout structure")
468
- },
469
- annotations: { title: "Get Embedded Layout" }
470
- }, async ({ pageUID, containerWidgetId, layoutAttributeName }) => {
471
- debugLogWithTag('EMBEDDED_LAYOUT', `Starting embedded widget layout request for container ${containerWidgetId} in page ${pageUID}`);
472
- try {
473
- const result = await client.makeApiRequest('json/page/embeddedWidgetLayout', 'GET', {
474
- pageUID,
475
- containerWidgetId,
476
- layoutAttributeName
477
- });
478
- debugLogWithTag('EMBEDDED_LAYOUT', `Retrieved embedded widget layout for container: ${containerWidgetId}`);
479
- return {
480
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
481
- };
482
- }
483
214
  catch (error) {
484
215
  return {
485
216
  content: [{
486
217
  type: "text",
487
- text: `Error retrieving embedded widget layout: ${error instanceof Error ? error.message : String(error)}`
218
+ text: `Error retrieving widget details for ${widgetId} in page ${pageUID}: ${error instanceof Error ? error.message : String(error)}`
488
219
  }],
489
220
  isError: true
490
221
  };
491
222
  }
492
223
  });
493
- server.registerTool("cplace_add_widget_to_embedded_layout", {
494
- description: "Add a new widget to an embedded layout within a container widget. This enables adding widgets to nested layouts like those within AttributesGroup widgets.\n\nSAME LIMITATIONS AS PAGE LAYOUTS:\n- Column creation: ❌ Never happens via widget placement\n- Invalid columnIndex falls back to last existing column\n- Can only add widgets to existing column structures in embedded layouts",
495
- inputSchema: {
496
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the container widget"),
497
- containerWidgetId: z.string().describe("The ID of the container widget that contains the embedded layout to modify"),
498
- layoutAttributeName: z.string().describe("The name of the attribute that defines the embedded layout structure"),
499
- widgetType: z.string().describe("Widget type identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')"),
500
- position: PositionSchema.extend({}).describe("Position where the widget should be added in the embedded layout"),
501
- configuration: z.record(z.any()).optional().describe(`Widget configuration as key-value pairs. ${WIDGET_CONFIG_DESCRIPTION}
502
-
503
- Examples: {'title': {'en': 'Embedded Widget', 'de': 'Eingebettetes Widget'}, 'showHeader': true}`)
504
- },
505
- annotations: { title: "Add Widget to Embedded Layout" }
506
- }, async ({ pageUID, containerWidgetId, layoutAttributeName, widgetType, position, configuration }) => {
507
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Starting add widget to embedded layout: ${widgetType} to container ${containerWidgetId} in page ${pageUID}`);
508
- try {
509
- const operation = {
510
- type: "ADD",
511
- widgetType,
512
- position
513
- };
514
- if (configuration && Object.keys(configuration).length > 0) {
515
- operation.configuration = configuration;
516
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Including configuration: ${JSON.stringify(Object.keys(configuration))}`);
517
- }
518
- const requestBody = {
519
- pageUID,
520
- containerWidgetId,
521
- layoutAttributeName,
522
- operation
523
- };
524
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
525
- const result = await client.makeApiRequest('json/page/embeddedWidgetLayout', 'PATCH', undefined, requestBody);
526
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Successfully added widget ${widgetType} to embedded layout`);
527
- const response = {
528
- operation: "ADD_EMBEDDED_WIDGET",
529
- status: "SUCCESS",
530
- pageUID,
531
- containerWidgetId,
532
- layoutAttributeName,
533
- widgetType,
534
- position,
535
- message: "Widget added successfully to embedded layout"
536
- };
224
+ const EmbeddedLayoutContextSchema = z.union([
225
+ z.object({
226
+ type: z.literal("page"),
227
+ pageUID: z.string().describe("Page UID (e.g., 'page/abc123')"),
228
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
229
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
230
+ }),
231
+ z.object({
232
+ type: z.literal("type"),
233
+ workspaceId: z.string().describe("Workspace ID (e.g., 'workspace123')"),
234
+ typeInternalName: z.string().describe("Type internal name (e.g., 'cf.example.myType')"),
235
+ alternativeLayoutName: z.string().optional().describe("Alternative layout name (optional)"),
236
+ containerWidgetId: z.string().describe("Container widget ID (e.g., 'id_abc123')"),
237
+ layoutAttributeName: z.string().describe("Layout attribute name (e.g., 'detailsLayout')")
238
+ })
239
+ ]).describe("Embedded layout target - specify whether operating on page or type embedded layout");
240
+ function determineEmbeddedApiEndpoint(context) {
241
+ return context.type === "page"
242
+ ? "json/page/embeddedWidgetLayout"
243
+ : "json/type/embeddedWidgetLayout";
244
+ }
245
+ function buildEmbeddedRequestParams(context, additionalParams = {}) {
246
+ const commonParams = {
247
+ containerWidgetId: context.containerWidgetId,
248
+ layoutAttributeName: context.layoutAttributeName,
249
+ ...additionalParams
250
+ };
251
+ if (context.type === "page") {
537
252
  return {
538
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
253
+ pageUID: context.pageUID,
254
+ ...commonParams
539
255
  };
540
256
  }
541
- catch (error) {
542
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Error adding widget to embedded layout: ${error instanceof Error ? error.message : String(error)}`);
543
- const errorResponse = {
544
- operation: "ADD_EMBEDDED_WIDGET",
545
- status: "ERROR",
546
- pageUID,
547
- containerWidgetId,
548
- layoutAttributeName,
549
- widgetType,
550
- position,
551
- error: error instanceof Error ? error.message : String(error),
552
- message: "Failed to add widget to embedded layout"
553
- };
257
+ else {
554
258
  return {
555
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
556
- isError: true
259
+ workspaceId: context.workspaceId,
260
+ typeInternalName: context.typeInternalName,
261
+ ...(context.alternativeLayoutName && { alternativeLayoutName: context.alternativeLayoutName }),
262
+ ...commonParams
557
263
  };
558
264
  }
559
- });
560
- server.registerTool("cplace_remove_widget_from_embedded_layout", {
561
- description: "Remove an existing widget from an embedded layout within a container widget. This enables removing widgets from nested layouts like those within AttributesGroup widgets.",
562
- inputSchema: {
563
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the container widget"),
564
- containerWidgetId: z.string().describe("The ID of the container widget that contains the embedded layout to modify"),
565
- layoutAttributeName: z.string().describe("The name of the attribute that defines the embedded layout structure"),
566
- widgetId: z.string().describe("The ID of the existing widget to remove from the embedded layout")
567
- },
568
- annotations: { title: "Remove Widget from Embedded Layout" }
569
- }, async ({ pageUID, containerWidgetId, layoutAttributeName, widgetId }) => {
570
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Starting remove widget from embedded layout: ${widgetId} from container ${containerWidgetId} in page ${pageUID}`);
265
+ }
266
+ function normalizeEmbeddedResponse(result, operation) {
267
+ const operationVerb = operation === "ADD" ? "added" : "removed";
268
+ return `Widget ${operationVerb} successfully`;
269
+ }
270
+ server.registerTool(TOOL_GET_EMBEDDED_LAYOUT, WIDGET_TOOL_DEFINITIONS[TOOL_GET_EMBEDDED_LAYOUT], async ({ context }) => {
571
271
  try {
572
- const requestBody = {
573
- pageUID,
574
- containerWidgetId,
575
- layoutAttributeName,
576
- operation: {
577
- type: "REMOVE",
578
- widgetId
579
- }
580
- };
581
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
582
- const result = await client.makeApiRequest('json/page/embeddedWidgetLayout', 'PATCH', undefined, requestBody);
583
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Successfully removed widget ${widgetId} from embedded layout`);
584
- const response = {
585
- operation: "REMOVE_EMBEDDED_WIDGET",
586
- status: "SUCCESS",
587
- pageUID,
588
- containerWidgetId,
589
- layoutAttributeName,
590
- widgetId,
591
- message: "Widget removed successfully from embedded layout"
592
- };
272
+ debugLogWithTag('EMBEDDED_LAYOUT', `Getting embedded layout for ${context.type} context: ${JSON.stringify({ context, endpoint: determineEmbeddedApiEndpoint(context) })}`);
273
+ const endpoint = determineEmbeddedApiEndpoint(context);
274
+ const requestParams = buildEmbeddedRequestParams(context);
275
+ const result = await client.makeApiRequest(endpoint, 'GET', requestParams);
276
+ debugLogWithTag('EMBEDDED_LAYOUT', `Successfully retrieved embedded layout for ${context.type} context: ${JSON.stringify({ context, hasResult: !!result })}`);
593
277
  return {
594
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
278
+ content: [{
279
+ type: "text",
280
+ text: typeof result === 'string' ? result : JSON.stringify(result, null, 2)
281
+ }]
595
282
  };
596
283
  }
597
284
  catch (error) {
598
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Error removing widget from embedded layout: ${error instanceof Error ? error.message : String(error)}`);
599
- const errorResponse = {
600
- operation: "REMOVE_EMBEDDED_WIDGET",
601
- status: "ERROR",
602
- pageUID,
603
- containerWidgetId,
604
- layoutAttributeName,
605
- widgetId,
606
- error: error instanceof Error ? error.message : String(error),
607
- message: "Failed to remove widget from embedded layout"
608
- };
285
+ debugLogWithTag('EMBEDDED_LAYOUT', `Failed to get embedded layout for ${context.type} context: ${JSON.stringify({ context, error: error instanceof Error ? error.message : String(error) })}`);
609
286
  return {
610
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
287
+ content: [{
288
+ type: "text",
289
+ text: `Failed to get embedded layout: ${error instanceof Error ? error.message : String(error)}`
290
+ }],
611
291
  isError: true
612
292
  };
613
293
  }
614
294
  });
615
- server.registerTool("cplace_compact_page_layout", {
616
- description: "Remove all empty rows from a page layout, effectively compacting the layout by keeping only rows that contain widgets. This operation automatically identifies and removes rows where all columns have no widgets while preserving the order and content of non-empty rows and maintaining all widget configurations and positions.",
617
- inputSchema: {
618
- pageUID: z.string().describe("The unique identifier (UID) of the page to compact the layout for")
619
- },
620
- annotations: { title: "Compact Page Layout" }
621
- }, async ({ pageUID }) => {
622
- debugLogWithTag('LAYOUT_MODIFY', `Starting compact layout operation for page ${pageUID}`);
295
+ server.registerTool(TOOL_ADD_WIDGET_TO_EMBEDDED_LAYOUT, WIDGET_TOOL_DEFINITIONS[TOOL_ADD_WIDGET_TO_EMBEDDED_LAYOUT], async ({ context, widgetType, position, configuration }) => {
623
296
  try {
297
+ debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Adding widget to ${context.type} embedded layout: ${JSON.stringify({ context, widgetType, position, hasConfiguration: !!configuration })}`);
298
+ const endpoint = determineEmbeddedApiEndpoint(context);
299
+ const requestParams = buildEmbeddedRequestParams(context);
300
+ const operationType = "ADD";
624
301
  const requestBody = {
625
- pageUID,
302
+ ...requestParams,
626
303
  operation: {
627
- type: "COMPACT"
304
+ type: operationType,
305
+ widgetType,
306
+ position,
307
+ ...(configuration && Object.keys(configuration).length > 0 && { configuration })
628
308
  }
629
309
  };
630
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
631
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
632
- debugLogWithTag('LAYOUT_MODIFY', `Successfully compacted layout for page ${pageUID}`);
633
- let emptyRowsRemoved = 0;
634
- try {
635
- const originalLayoutResult = await client.makeApiRequest('json/pageLayout', 'GET', { pageUID });
636
- const originalLayout = JSON.parse(originalLayoutResult.result || '{}');
637
- const originalRowCount = originalLayout.rows ? originalLayout.rows.length : 0;
638
- const compactedLayoutString = result.result;
639
- const compactedLayout = JSON.parse(compactedLayoutString);
640
- const compactedRowCount = compactedLayout.rows ? compactedLayout.rows.length : 0;
641
- emptyRowsRemoved = Math.max(0, originalRowCount - compactedRowCount);
642
- }
643
- catch (parseError) {
644
- debugLogWithTag('LAYOUT_MODIFY', `Could not calculate compaction statistics: ${parseError}`);
645
- }
646
- const response = {
647
- operation: "COMPACT_LAYOUT",
648
- status: "SUCCESS",
649
- pageUID,
650
- emptyRowsRemoved,
651
- message: emptyRowsRemoved > 0
652
- ? `Layout compacted successfully - removed ${emptyRowsRemoved} empty row${emptyRowsRemoved === 1 ? '' : 's'}`
653
- : "Layout was already compact - no empty rows found"
654
- };
310
+ debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Sending ADD request to ${endpoint}: ${JSON.stringify({ context, operationType, requestBodyKeys: Object.keys(requestBody) })}`);
311
+ const result = await client.makeApiRequest(endpoint, 'PATCH', undefined, requestBody);
312
+ debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Successfully added widget to ${context.type} embedded layout: ${JSON.stringify({ context, widgetType, success: true })}`);
655
313
  return {
656
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
314
+ content: [{
315
+ type: "text",
316
+ text: normalizeEmbeddedResponse(result, "ADD")
317
+ }]
657
318
  };
658
319
  }
659
320
  catch (error) {
660
- debugLogWithTag('LAYOUT_MODIFY', `Error compacting layout: ${error instanceof Error ? error.message : String(error)}`);
661
- const errorResponse = {
662
- operation: "COMPACT_LAYOUT",
663
- status: "ERROR",
664
- pageUID,
665
- error: error instanceof Error ? error.message : String(error),
666
- message: "Failed to compact layout"
667
- };
321
+ debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Failed to add widget to ${context.type} embedded layout: ${JSON.stringify({ context, widgetType, error: error instanceof Error ? error.message : String(error) })}`);
668
322
  return {
669
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
323
+ content: [{
324
+ type: "text",
325
+ text: `Failed to add widget: ${error instanceof Error ? error.message : String(error)}`
326
+ }],
670
327
  isError: true
671
328
  };
672
329
  }
673
330
  });
674
- server.registerTool("cplace_add_row_to_page_layout", {
675
- description: "Create a new row with specific column layout in a page layout.\n\n⭐ ESSENTIAL FOR MULTI-COLUMN LAYOUTS ⭐\nThis is the ONLY way to create multi-column structures. Widget placement alone cannot create columns.\n\nCOMMON WORKFLOW:\n1. Use this tool to create desired column structure (e.g., [6,6] for side-by-side)\n2. Use cplace_add_widget_to_layout to place widgets in the columns\n3. Use cplace_compact_page_layout to remove any empty rows\n\nROW INSERTION BEHAVIOR:\n- rowIndex < existing rows: Inserts and shifts others down\n- rowIndex >= existing rows: Appends at end\n\nCOLUMN PROPORTIONS (must sum to 12):\n- [12]: Single full-width column\n- [6, 6]: Two equal columns (most common for side-by-side)\n- [4, 4, 4]: Three equal columns\n- [3, 9]: Sidebar + main content\n- [2, 8, 2]: Content with sidebars",
676
- inputSchema: {
677
- pageUID: z.string().describe("The unique identifier (UID) of the page to add the row to"),
678
- rowIndex: z.number().min(0).describe("Row index where to insert the new row (0-based). If rowIndex >= current row count, adds at the end. Otherwise, inserts at position and shifts existing rows down."),
679
- columnProportions: z.array(z.number().min(1)).min(1).describe("Array of column width proportions that must sum to 12 (Bootstrap grid system). Examples: [12] (single column), [6,6] (two equal columns), [4,4,4] (three equal columns), [3,9] (sidebar + main)")
680
- },
681
- annotations: { title: "Add Row to Page Layout" }
682
- }, async ({ pageUID, rowIndex, columnProportions }) => {
683
- debugLogWithTag('LAYOUT_MODIFY', `Starting add row operation: row ${rowIndex} with proportions ${JSON.stringify(columnProportions)} to page ${pageUID}`);
331
+ server.registerTool(TOOL_REMOVE_WIDGET_FROM_EMBEDDED_LAYOUT, WIDGET_TOOL_DEFINITIONS[TOOL_REMOVE_WIDGET_FROM_EMBEDDED_LAYOUT], async ({ context, widgetId }) => {
684
332
  try {
333
+ debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Removing widget from ${context.type} embedded layout: ${JSON.stringify({ context, widgetId })}`);
334
+ const endpoint = determineEmbeddedApiEndpoint(context);
335
+ const requestParams = buildEmbeddedRequestParams(context);
336
+ const operationType = "REMOVE";
685
337
  const requestBody = {
686
- pageUID,
338
+ ...requestParams,
687
339
  operation: {
688
- type: "ADD_ROW",
689
- rowIndex,
690
- columnProportions
691
- }
692
- };
693
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
694
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
695
- debugLogWithTag('LAYOUT_MODIFY', `Successfully added row ${rowIndex} with ${columnProportions.length} columns to page ${pageUID}`);
696
- const columnsCreated = columnProportions.length;
697
- const columnSum = columnProportions.reduce((sum, prop) => sum + prop, 0);
698
- let insertionType = "appended";
699
- try {
700
- const layoutString = result.result;
701
- const layout = JSON.parse(layoutString);
702
- if (layout.rows && layout.rows.length > rowIndex + 1) {
703
- insertionType = "inserted";
340
+ type: operationType,
341
+ widgetId
704
342
  }
705
- }
706
- catch (parseError) {
707
- debugLogWithTag('LAYOUT_MODIFY', `Could not determine insertion type: ${parseError}`);
708
- }
709
- const response = {
710
- operation: "ADD_ROW",
711
- status: "SUCCESS",
712
- pageUID,
713
- rowIndex,
714
- columnProportions,
715
- columnsCreated,
716
- insertionType,
717
- message: insertionType === "inserted"
718
- ? `Row added successfully at index ${rowIndex} with ${columnsCreated} column${columnsCreated === 1 ? '' : 's'} [${columnProportions.join(',')}]`
719
- : `Row added successfully at end with ${columnsCreated} column${columnsCreated === 1 ? '' : 's'} [${columnProportions.join(',')}]`
720
343
  };
344
+ debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Sending REMOVE request to ${endpoint}: ${JSON.stringify({ context, operationType, widgetId })}`);
345
+ const result = await client.makeApiRequest(endpoint, 'PATCH', undefined, requestBody);
346
+ debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Successfully removed widget from ${context.type} embedded layout: ${JSON.stringify({ context, widgetId, success: true })}`);
721
347
  return {
722
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
348
+ content: [{
349
+ type: "text",
350
+ text: normalizeEmbeddedResponse(result, "REMOVE")
351
+ }]
723
352
  };
724
353
  }
725
354
  catch (error) {
726
- debugLogWithTag('LAYOUT_MODIFY', `Error adding row: ${error instanceof Error ? error.message : String(error)}`);
727
- const errorResponse = {
728
- operation: "ADD_ROW",
729
- status: "ERROR",
730
- pageUID,
731
- rowIndex,
732
- columnProportions,
733
- error: error instanceof Error ? error.message : String(error),
734
- message: "Failed to add row"
735
- };
355
+ debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Failed to remove widget from ${context.type} embedded layout: ${JSON.stringify({ context, widgetId, error: error instanceof Error ? error.message : String(error) })}`);
736
356
  return {
737
- content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2) }],
357
+ content: [{
358
+ type: "text",
359
+ text: `Failed to remove widget: ${error instanceof Error ? error.message : String(error)}`
360
+ }],
738
361
  isError: true
739
362
  };
740
363
  }