@cplace/test-mcp-server 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -65
- package/dist/api.d.ts +9 -10
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +31 -4
- package/dist/api.js.map +1 -1
- package/dist/csvUtils.d.ts +7 -0
- package/dist/csvUtils.d.ts.map +1 -0
- package/dist/csvUtils.js +125 -0
- package/dist/csvUtils.js.map +1 -0
- package/dist/index.js +703 -24
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +17 -0
- package/dist/logger.js.map +1 -0
- package/dist/searchSchema.d.ts +245 -1
- package/dist/searchSchema.d.ts.map +1 -1
- package/dist/searchSchema.js +26 -25
- package/dist/searchSchema.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.js +7 -7
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,21 +7,45 @@ import { CplaceApiClient } from './api.js';
|
|
|
7
7
|
import SearchFilterSchema from "./searchSchema.js";
|
|
8
8
|
import { convertSearchFilterToCplaceFormat } from "./searchConversion.js";
|
|
9
9
|
import { filterSearchResult } from "./utils.js";
|
|
10
|
+
import { debugLogWithTag } from "./logger.js";
|
|
11
|
+
import { DEBUG_LOGGING } from "./logger.js";
|
|
12
|
+
import { convertPagesToCsv } from "./csvUtils.js";
|
|
10
13
|
dotenv.config();
|
|
11
14
|
const config = {
|
|
12
15
|
url: process.env.CPLACE_URL || '',
|
|
13
|
-
apiToken: process.env.API_TOKEN || ''
|
|
16
|
+
apiToken: process.env.API_TOKEN || '',
|
|
17
|
+
registerSystemInfoTool: process.env.REGISTER_SYSTEM_INFO_TOOL === 'true' || false,
|
|
18
|
+
registerLayoutTools: process.env.REGISTER_LAYOUT_TOOLS === 'true' || false,
|
|
14
19
|
};
|
|
15
20
|
const client = new CplaceApiClient(config);
|
|
16
21
|
const server = new McpServer({
|
|
17
22
|
name: "cplace-mcp-server",
|
|
18
23
|
version: "1.0.0"
|
|
19
24
|
});
|
|
25
|
+
if (config.registerSystemInfoTool) {
|
|
26
|
+
server.registerTool("cplace_system_info", {
|
|
27
|
+
description: "Get system information about the MCP server - including current working directory, CPLACE_URL setting, and debug state",
|
|
28
|
+
inputSchema: {},
|
|
29
|
+
annotations: { title: "System Information" }
|
|
30
|
+
}, async () => {
|
|
31
|
+
const systemInfo = {
|
|
32
|
+
currentWorkingDirectory: process.cwd(),
|
|
33
|
+
cplaceUrl: config.url || 'Not set',
|
|
34
|
+
debugLogging: DEBUG_LOGGING,
|
|
35
|
+
nodeVersion: process.version,
|
|
36
|
+
platform: process.platform
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: JSON.stringify(systemInfo, null, 2) }]
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
20
43
|
server.registerTool("cplace_list_workspaces", {
|
|
21
44
|
description: "Get a list of all workspaces with essential properties (id, name, displayName, totalPages, isFavorite, installed apps)",
|
|
22
45
|
inputSchema: {},
|
|
23
46
|
annotations: { title: "List Workspaces" }
|
|
24
47
|
}, async () => {
|
|
48
|
+
debugLogWithTag('WORKSPACES', 'Starting workspace list request');
|
|
25
49
|
try {
|
|
26
50
|
const workspaces = await client.makeApiRequest('json/workspaces');
|
|
27
51
|
const filteredWorkspaces = workspaces.map((workspace) => ({
|
|
@@ -32,6 +56,7 @@ server.registerTool("cplace_list_workspaces", {
|
|
|
32
56
|
isFavorite: workspace.isFavorite,
|
|
33
57
|
installedApps: workspace.apps?.installed?.map(app => app.name) || []
|
|
34
58
|
}));
|
|
59
|
+
debugLogWithTag('WORKSPACES', `Retrieved ${filteredWorkspaces.length} workspaces`);
|
|
35
60
|
return {
|
|
36
61
|
content: [{ type: "text", text: JSON.stringify(filteredWorkspaces, null, 2) }]
|
|
37
62
|
};
|
|
@@ -99,7 +124,7 @@ server.registerTool("cplace_get_page_by_id", {
|
|
|
99
124
|
annotations: { title: "Get Page by ID" }
|
|
100
125
|
}, async ({ id }) => {
|
|
101
126
|
try {
|
|
102
|
-
const result = await client.makeApiRequest('json/page', 'GET', { id });
|
|
127
|
+
const result = await client.makeApiRequest('json/page', 'GET', { pageUID: id });
|
|
103
128
|
return {
|
|
104
129
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
105
130
|
};
|
|
@@ -151,25 +176,78 @@ server.registerTool("cplace_search_person", {
|
|
|
151
176
|
server.registerTool("cplace_search_pages", {
|
|
152
177
|
description: "Search pages of a type in cplace.",
|
|
153
178
|
inputSchema: {
|
|
154
|
-
|
|
155
|
-
internalTypeName: z.string().describe(
|
|
156
|
-
|
|
179
|
+
workspaceId: z.string().optional().describe("The ID of the workspace to search in. If not provided, searches across all accessible workspaces."),
|
|
180
|
+
internalTypeName: z.string().describe(`The internal name of the type to filter by. Examples:
|
|
181
|
+
- "cf.cplace.solution.safe.feature" for Feature pages
|
|
182
|
+
- "cf.cplace.solution.safe.epic" for Epic pages
|
|
183
|
+
- "cf.cplace.solution.safe.story" for Story pages
|
|
184
|
+
- "cf.projectNavigator.project" for Project pages
|
|
185
|
+
- "cf.cplace.solution.safe.portfolio" for Portfolio pages
|
|
186
|
+
Use cplace_get_types_in_workspace to discover available types in a workspace.`),
|
|
187
|
+
search_filter: SearchFilterSchema.describe(`Search Filters using cplace filter format. Examples:
|
|
188
|
+
|
|
189
|
+
Basic string filter:
|
|
190
|
+
[{"attribute": "cf.cplace.workflow", "string": {"equals": "approved"}}]
|
|
191
|
+
|
|
192
|
+
Multiple conditions:
|
|
193
|
+
[
|
|
194
|
+
{"name": {"equals": "Project Alpha"}},
|
|
195
|
+
{"attribute": "cf.cplace.location", "reference": {"equals": "page/abc123"}}
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
Date range filter:
|
|
199
|
+
[
|
|
200
|
+
{"attribute": "cf.cplace.projectStart", "date": {"on_or_after": "2023-01-01"}},
|
|
201
|
+
{"attribute": "cf.cplace.projectFinish", "date": {"on_or_before": "2024-12-31"}}
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
Number comparison:
|
|
205
|
+
[{"attribute": "cf.cplace.estimatedCost", "number": {"greater_than": 1000}}]
|
|
206
|
+
|
|
207
|
+
Logical OR operator:
|
|
208
|
+
[{
|
|
209
|
+
"or": [
|
|
210
|
+
{"attribute": "cf.cplace.managementPriority", "string": {"equals": "Focus"}},
|
|
211
|
+
{"attribute": "cf.cplace.managementPriority", "string": {"equals": "Accelerate"}}
|
|
212
|
+
]
|
|
213
|
+
}]
|
|
214
|
+
|
|
215
|
+
Complex AND/OR combination:
|
|
216
|
+
[{
|
|
217
|
+
"and": [
|
|
218
|
+
{"attribute": "cf.cplace.workflow", "string": {"equals": "inProgress"}},
|
|
219
|
+
{
|
|
220
|
+
"or": [
|
|
221
|
+
{"attribute": "cf.cplace.priority", "string": {"equals": "High"}},
|
|
222
|
+
{"attribute": "cf.cplace.priority", "string": {"equals": "Critical"}}
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
}]
|
|
227
|
+
|
|
228
|
+
Boolean filter:
|
|
229
|
+
[{"attribute": "cf.cplace.isActive", "boolean": {"equals": true}}]
|
|
230
|
+
|
|
231
|
+
Empty/not empty checks:
|
|
232
|
+
[{"attribute": "cf.cplace.description", "string": {"is_not_empty": true}}]`),
|
|
157
233
|
limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
|
|
158
234
|
offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
|
|
235
|
+
responseFormat: z.enum(["minimal", "count"]).optional().describe(`Response format options:
|
|
236
|
+
- Default (not specified): Complete page data, respects 'attributes' parameter
|
|
237
|
+
- "minimal": Only core fields (id, name, url, type, workspace)
|
|
238
|
+
- "count": Only return the total count of matching pages`),
|
|
239
|
+
attributes: z.array(z.string()).optional().describe("Specific attribute names to include in response. If not provided, all attributes are included. Examples: ['cf.cplace.workflow', 'cf.cplace.priority']"),
|
|
159
240
|
},
|
|
160
241
|
annotations: { title: "Search Pages" }
|
|
161
|
-
}, async ({
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
content: [{ type: "text", text: "search_filter is required" }]
|
|
165
|
-
};
|
|
166
|
-
}
|
|
242
|
+
}, async ({ workspaceId, internalTypeName, search_filter, limit = 50, offset = 0, responseFormat, attributes, }) => {
|
|
243
|
+
debugLogWithTag('SEARCH', `Starting search with params: workspaceId=${workspaceId || 'none'}, type=${internalTypeName}, filter=${JSON.stringify(search_filter)}, limit=${limit}, offset=${offset}`);
|
|
167
244
|
try {
|
|
168
245
|
const cplaceFilter = convertSearchFilterToCplaceFormat(search_filter);
|
|
169
|
-
|
|
246
|
+
debugLogWithTag('SEARCH', `Converted filter: ${JSON.stringify(cplaceFilter)}`);
|
|
247
|
+
if (workspaceId) {
|
|
170
248
|
cplaceFilter.filters.unshift({
|
|
171
249
|
type: "Workspace",
|
|
172
|
-
workspaceIds: [
|
|
250
|
+
workspaceIds: [workspaceId]
|
|
173
251
|
});
|
|
174
252
|
}
|
|
175
253
|
if (internalTypeName) {
|
|
@@ -178,23 +256,43 @@ server.registerTool("cplace_search_pages", {
|
|
|
178
256
|
types: [internalTypeName]
|
|
179
257
|
});
|
|
180
258
|
}
|
|
259
|
+
debugLogWithTag('SEARCH', `Final cplace filter: ${JSON.stringify(cplaceFilter)}`);
|
|
181
260
|
const result = await client.makeApiRequest('json/search', 'GET', {
|
|
182
261
|
filter: JSON.stringify(cplaceFilter),
|
|
183
262
|
limit,
|
|
184
263
|
offset
|
|
185
264
|
});
|
|
265
|
+
if (responseFormat === "count") {
|
|
266
|
+
const countResult = {
|
|
267
|
+
count: result.pagination?.total || 0
|
|
268
|
+
};
|
|
269
|
+
return {
|
|
270
|
+
content: [{ type: "text", text: JSON.stringify(countResult, null, 2) }]
|
|
271
|
+
};
|
|
272
|
+
}
|
|
186
273
|
const filteredResults = result.results ? result.results.map((item) => filterSearchResult(item, {
|
|
187
|
-
minimal:
|
|
188
|
-
includeAttributes:
|
|
274
|
+
minimal: responseFormat === "minimal",
|
|
275
|
+
includeAttributes: responseFormat !== "minimal",
|
|
189
276
|
includeMetadata: false,
|
|
277
|
+
attributeFields: attributes,
|
|
190
278
|
truncateLargeValues: true
|
|
191
279
|
})) : [];
|
|
192
|
-
const filteredResult = {
|
|
280
|
+
const filteredResult = {
|
|
281
|
+
results: filteredResults,
|
|
282
|
+
pagination: {
|
|
283
|
+
total: result.pagination?.total || 0,
|
|
284
|
+
limit: result.pagination?.limit || limit,
|
|
285
|
+
offset: result.pagination?.offset || offset,
|
|
286
|
+
hasMore: result.pagination?.hasMore || false
|
|
287
|
+
},
|
|
288
|
+
humanReadableRepresentation: result.summary
|
|
289
|
+
};
|
|
193
290
|
return {
|
|
194
291
|
content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
|
|
195
292
|
};
|
|
196
293
|
}
|
|
197
294
|
catch (error) {
|
|
295
|
+
debugLogWithTag('SEARCH', `Error during search: ${error instanceof Error ? error.message : String(error)}`);
|
|
198
296
|
return {
|
|
199
297
|
content: [{ type: "text", text: `Error converting or executing search: ${error instanceof Error ? error.message : String(error)}` }]
|
|
200
298
|
};
|
|
@@ -203,13 +301,13 @@ server.registerTool("cplace_search_pages", {
|
|
|
203
301
|
server.registerTool("cplace_fulltext_search", {
|
|
204
302
|
description: "Make a fulltext search across all pages in cplace. Use this only if the user is not asking about specific types.",
|
|
205
303
|
inputSchema: {
|
|
206
|
-
|
|
304
|
+
workspaceId: z.string().optional().describe("The ID of the workspace to search in. If not provided, searches across all accessible workspaces."),
|
|
207
305
|
fulltext: z.string().describe("The fulltext search query to filter pages by. The results will include pages that conatin this string in their name, attributes, or content."),
|
|
208
306
|
limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
|
|
209
307
|
offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
|
|
210
308
|
},
|
|
211
309
|
annotations: { title: "Search Pages" }
|
|
212
|
-
}, async ({
|
|
310
|
+
}, async ({ workspaceId, fulltext, limit = 50, offset = 0, }) => {
|
|
213
311
|
if (!fulltext) {
|
|
214
312
|
return {
|
|
215
313
|
content: [{ type: "text", text: "fulltext is required" }]
|
|
@@ -217,7 +315,7 @@ server.registerTool("cplace_fulltext_search", {
|
|
|
217
315
|
}
|
|
218
316
|
try {
|
|
219
317
|
const result = await client.makeApiRequest('json/fulltext-search', 'GET', {
|
|
220
|
-
spaceId,
|
|
318
|
+
spaceId: workspaceId,
|
|
221
319
|
fulltext: `"${fulltext}"`,
|
|
222
320
|
limit,
|
|
223
321
|
offset
|
|
@@ -228,7 +326,16 @@ server.registerTool("cplace_fulltext_search", {
|
|
|
228
326
|
includeMetadata: false,
|
|
229
327
|
truncateLargeValues: true
|
|
230
328
|
})) : [];
|
|
231
|
-
const filteredResult = {
|
|
329
|
+
const filteredResult = {
|
|
330
|
+
results: filteredResults,
|
|
331
|
+
pagination: {
|
|
332
|
+
total: result.pagination?.total || 0,
|
|
333
|
+
limit: result.pagination?.limit || limit,
|
|
334
|
+
offset: result.pagination?.offset || offset,
|
|
335
|
+
hasMore: result.pagination?.hasMore || false
|
|
336
|
+
},
|
|
337
|
+
summary: result.summary
|
|
338
|
+
};
|
|
232
339
|
return {
|
|
233
340
|
content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
|
|
234
341
|
};
|
|
@@ -242,19 +349,19 @@ server.registerTool("cplace_fulltext_search", {
|
|
|
242
349
|
server.registerTool("cplace_get_all_pages_of_type", {
|
|
243
350
|
description: "Get all pages of a specific type in a workspace in cplace. ONLY use this if you need to retrieve all pages of a type.",
|
|
244
351
|
inputSchema: {
|
|
245
|
-
|
|
352
|
+
workspaceId: z.string().describe("The ID of the workspace to get pages from"),
|
|
246
353
|
typeName: z.string().describe("The internal name of the type to retrieve pages for"),
|
|
247
354
|
limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
|
|
248
355
|
offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
|
|
249
356
|
},
|
|
250
357
|
annotations: { title: "Get All Pages of Type" }
|
|
251
|
-
}, async ({
|
|
358
|
+
}, async ({ workspaceId, typeName, limit = 50, offset = 0, }) => {
|
|
252
359
|
try {
|
|
253
360
|
const cplaceFilter = {
|
|
254
361
|
filters: [
|
|
255
362
|
{
|
|
256
363
|
type: "Workspace",
|
|
257
|
-
workspaceIds: [
|
|
364
|
+
workspaceIds: [workspaceId]
|
|
258
365
|
},
|
|
259
366
|
{
|
|
260
367
|
type: "Type",
|
|
@@ -273,7 +380,16 @@ server.registerTool("cplace_get_all_pages_of_type", {
|
|
|
273
380
|
includeMetadata: false,
|
|
274
381
|
truncateLargeValues: true
|
|
275
382
|
})) : [];
|
|
276
|
-
const filteredResult = {
|
|
383
|
+
const filteredResult = {
|
|
384
|
+
results: filteredResults,
|
|
385
|
+
pagination: {
|
|
386
|
+
total: result.pagination?.total || 0,
|
|
387
|
+
limit: result.pagination?.limit || limit,
|
|
388
|
+
offset: result.pagination?.offset || offset,
|
|
389
|
+
hasMore: result.pagination?.hasMore || false
|
|
390
|
+
},
|
|
391
|
+
summary: result.summary
|
|
392
|
+
};
|
|
277
393
|
return {
|
|
278
394
|
content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
|
|
279
395
|
};
|
|
@@ -284,6 +400,569 @@ server.registerTool("cplace_get_all_pages_of_type", {
|
|
|
284
400
|
};
|
|
285
401
|
}
|
|
286
402
|
});
|
|
403
|
+
server.registerTool("cplace_search_pages_csv", {
|
|
404
|
+
description: "Same as cplace_search_pages but returns results in CSV format instead of JSON. Only use this tool after first calling cplace_search_pages to establish and validate the search parameters. The CSV result should always be stored as an artifact.",
|
|
405
|
+
inputSchema: {
|
|
406
|
+
workspaceId: z.string().optional().describe("Same as cplace_search_pages"),
|
|
407
|
+
internalTypeName: z.string().describe("Same as cplace_search_pages"),
|
|
408
|
+
search_filter: SearchFilterSchema.describe("Same as cplace_search_pages"),
|
|
409
|
+
limit: z.number().min(1).max(1000).default(50).describe("Same as cplace_search_pages"),
|
|
410
|
+
offset: z.number().min(0).max(10000).default(0).describe("Same as cplace_search_pages"),
|
|
411
|
+
attributes: z.array(z.string()).optional().describe("Same as cplace_search_pages"),
|
|
412
|
+
},
|
|
413
|
+
annotations: { title: "Search Pages (CSV Export)" }
|
|
414
|
+
}, async ({ workspaceId, internalTypeName, search_filter, limit = 50, offset = 0, attributes, }) => {
|
|
415
|
+
debugLogWithTag('CSV_SEARCH', `Starting CSV search with params: workspaceId=${workspaceId || 'none'}, type=${internalTypeName}, filter=${JSON.stringify(search_filter)}, limit=${limit}, offset=${offset}`);
|
|
416
|
+
try {
|
|
417
|
+
const cplaceFilter = convertSearchFilterToCplaceFormat(search_filter);
|
|
418
|
+
debugLogWithTag('CSV_SEARCH', `Converted filter: ${JSON.stringify(cplaceFilter)}`);
|
|
419
|
+
if (workspaceId) {
|
|
420
|
+
cplaceFilter.filters.unshift({
|
|
421
|
+
type: "Workspace",
|
|
422
|
+
workspaceIds: [workspaceId]
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
if (internalTypeName) {
|
|
426
|
+
cplaceFilter.filters.unshift({
|
|
427
|
+
type: "Type",
|
|
428
|
+
types: [internalTypeName]
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
debugLogWithTag('CSV_SEARCH', `Final cplace filter: ${JSON.stringify(cplaceFilter)}`);
|
|
432
|
+
const result = await client.makeApiRequest('json/search', 'GET', {
|
|
433
|
+
filter: JSON.stringify(cplaceFilter),
|
|
434
|
+
limit,
|
|
435
|
+
offset
|
|
436
|
+
});
|
|
437
|
+
const searchResults = result.results || [];
|
|
438
|
+
debugLogWithTag('CSV_SEARCH', `Retrieved ${searchResults.length} results for CSV conversion`);
|
|
439
|
+
const csvContent = await convertPagesToCsv(searchResults, client, attributes);
|
|
440
|
+
return {
|
|
441
|
+
content: [{ type: "text", text: csvContent }]
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
debugLogWithTag('CSV_SEARCH', `Error during CSV search: ${error instanceof Error ? error.message : String(error)}`);
|
|
446
|
+
return {
|
|
447
|
+
content: [{ type: "text", text: `Error converting or executing CSV search: ${error instanceof Error ? error.message : String(error)}` }]
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
if (config.registerLayoutTools) {
|
|
452
|
+
server.registerTool("cplace_list_widget_definitions", {
|
|
453
|
+
description: "Get a list of all available widget definitions in the system with their basic metadata including names, descriptions, and apps",
|
|
454
|
+
inputSchema: {},
|
|
455
|
+
annotations: { title: "List Widget Definitions" }
|
|
456
|
+
}, async () => {
|
|
457
|
+
debugLogWithTag('WIDGETS', 'Starting widget definitions list request');
|
|
458
|
+
try {
|
|
459
|
+
const result = await client.makeApiRequest('json/widget-definitions');
|
|
460
|
+
debugLogWithTag('WIDGETS', `Retrieved widget definitions`);
|
|
461
|
+
return {
|
|
462
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
return {
|
|
467
|
+
content: [{ type: "text", text: `Error retrieving widget definitions: ${error instanceof Error ? error.message : String(error)}` }]
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (config.registerLayoutTools) {
|
|
473
|
+
server.registerTool("cplace_get_widget_definition", {
|
|
474
|
+
description: "Get detailed information about a specific widget definition including its complete configuration schema with all attributes, constraints, and metadata",
|
|
475
|
+
inputSchema: {
|
|
476
|
+
widgetKind: z.string().describe("The widget kind identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')")
|
|
477
|
+
},
|
|
478
|
+
annotations: { title: "Get Widget Definition Details" }
|
|
479
|
+
}, async ({ widgetKind }) => {
|
|
480
|
+
debugLogWithTag('WIDGETS', `Starting widget definition request for: ${widgetKind}`);
|
|
481
|
+
try {
|
|
482
|
+
const result = await client.makeApiRequest('json/widget-definition', 'GET', { widgetKind });
|
|
483
|
+
debugLogWithTag('WIDGETS', `Retrieved widget definition for: ${widgetKind}`);
|
|
484
|
+
return {
|
|
485
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
return {
|
|
490
|
+
content: [{ type: "text", text: `Error retrieving widget definition for ${widgetKind}: ${error instanceof Error ? error.message : String(error)}` }]
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
if (config.registerLayoutTools) {
|
|
496
|
+
server.registerTool("cplace_get_page_layout_overview", {
|
|
497
|
+
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.",
|
|
498
|
+
inputSchema: {
|
|
499
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page to get layout for, e.g. 'page/kkt8ol745jqur4581kelm5ply'")
|
|
500
|
+
},
|
|
501
|
+
annotations: { title: "Get Page Layout Overview" }
|
|
502
|
+
}, async ({ pageUID }) => {
|
|
503
|
+
debugLogWithTag('LAYOUT', `Starting page layout overview request for: ${pageUID}`);
|
|
504
|
+
try {
|
|
505
|
+
const result = await client.makeApiRequest('json/pageLayout', 'GET', { pageUID });
|
|
506
|
+
const overview = {
|
|
507
|
+
rows: result.rows?.map((row, rowIndex) => ({
|
|
508
|
+
rowIndex,
|
|
509
|
+
columns: row.columns?.map((column, colIndex) => ({
|
|
510
|
+
columnIndex: colIndex,
|
|
511
|
+
proportion: column.proportion,
|
|
512
|
+
widgets: column.widgets?.map((widget) => ({
|
|
513
|
+
id: widget.id,
|
|
514
|
+
widgetType: widget.widgetType,
|
|
515
|
+
configurationCount: widget.configuration?.length || 0
|
|
516
|
+
})) || []
|
|
517
|
+
})) || []
|
|
518
|
+
})) || []
|
|
519
|
+
};
|
|
520
|
+
debugLogWithTag('LAYOUT', `Retrieved layout overview for: ${pageUID}`);
|
|
521
|
+
return {
|
|
522
|
+
content: [{ type: "text", text: JSON.stringify(overview, null, 2) }]
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
return {
|
|
527
|
+
content: [{ type: "text", text: `Error retrieving page layout overview for ${pageUID}: ${error instanceof Error ? error.message : String(error)}` }]
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
if (config.registerLayoutTools) {
|
|
533
|
+
server.registerTool("cplace_get_widget_details", {
|
|
534
|
+
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.",
|
|
535
|
+
inputSchema: {
|
|
536
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
|
|
537
|
+
widgetId: z.string().describe("The unique widget identifier from the layout (e.g., 'id_123', 'id_456')")
|
|
538
|
+
},
|
|
539
|
+
annotations: { title: "Get Widget Details" }
|
|
540
|
+
}, async ({ pageUID, widgetId }) => {
|
|
541
|
+
debugLogWithTag('LAYOUT', `Starting widget details request for widget ${widgetId} in page ${pageUID}`);
|
|
542
|
+
try {
|
|
543
|
+
const result = await client.makeApiRequest('json/pageLayout', 'GET', { pageUID });
|
|
544
|
+
let foundWidget = null;
|
|
545
|
+
const findWidget = (rows) => {
|
|
546
|
+
for (const row of rows || []) {
|
|
547
|
+
for (const column of row.columns || []) {
|
|
548
|
+
for (const widget of column.widgets || []) {
|
|
549
|
+
if (widget.id === widgetId) {
|
|
550
|
+
return widget;
|
|
551
|
+
}
|
|
552
|
+
if (widget.widgetsLayout?.rows) {
|
|
553
|
+
const nestedWidget = findWidget(widget.widgetsLayout.rows);
|
|
554
|
+
if (nestedWidget)
|
|
555
|
+
return nestedWidget;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return null;
|
|
561
|
+
};
|
|
562
|
+
foundWidget = findWidget(result.rows);
|
|
563
|
+
if (!foundWidget) {
|
|
564
|
+
return {
|
|
565
|
+
content: [{ type: "text", text: `Widget with ID ${widgetId} not found in page ${pageUID}` }]
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
debugLogWithTag('LAYOUT', `Retrieved widget details for: ${widgetId}`);
|
|
569
|
+
return {
|
|
570
|
+
content: [{ type: "text", text: JSON.stringify(foundWidget, null, 2) }]
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
return {
|
|
575
|
+
content: [{ type: "text", text: `Error retrieving widget details for ${widgetId} in page ${pageUID}: ${error instanceof Error ? error.message : String(error)}` }]
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
server.registerTool("cplace_get_current_user", {
|
|
581
|
+
description: "Get information about the currently logged-in user, including their attributes and metadata",
|
|
582
|
+
inputSchema: {},
|
|
583
|
+
annotations: { title: "Get Current User" }
|
|
584
|
+
}, async () => {
|
|
585
|
+
debugLogWithTag('CURRENT_USER', 'Starting current user request');
|
|
586
|
+
try {
|
|
587
|
+
const result = await client.makeApiRequest('json/current-user');
|
|
588
|
+
debugLogWithTag('CURRENT_USER', `Retrieved current user information`);
|
|
589
|
+
return {
|
|
590
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
return {
|
|
595
|
+
content: [{ type: "text", text: `Error retrieving current user information: ${error instanceof Error ? error.message : String(error)}` }]
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
server.registerTool("cplace_create_page", {
|
|
600
|
+
description: "Create a new page with specified attributes and content using the POST /page/create endpoint",
|
|
601
|
+
inputSchema: {
|
|
602
|
+
workspaceId: z.string().describe("The ID of the workspace/space to create the page in (e.g., 'workspace/abc123')"),
|
|
603
|
+
typeInternalName: z.string().describe("The internal name of the page type (e.g., 'cf.example.myType')"),
|
|
604
|
+
name: z.string().describe("The name of the new page"),
|
|
605
|
+
parentUID: z.string().optional().describe("The UID of the parent page for hierarchy (optional)"),
|
|
606
|
+
content: z.string().optional().describe("Wiki markup content for the page (optional)"),
|
|
607
|
+
attributes: z.record(z.any()).optional().describe(`Custom attribute values as key-value pairs. Supports:
|
|
608
|
+
- String values: "text"
|
|
609
|
+
- Numeric values: 42 or 3.14
|
|
610
|
+
- Boolean values: true/false
|
|
611
|
+
- Date values: "2024-01-15" or "2024-01-15T10:30:00"
|
|
612
|
+
- Array values: ["value1", "value2"] for multi-valued attributes
|
|
613
|
+
- Reference values: "page/ref123" for page references
|
|
614
|
+
- Localized string values: {"en": "English text", "de": "German text", "fr": "French text"}
|
|
615
|
+
- Multi-valued localized strings: [{"en": "Text 1", "de": "Text 1"}, {"en": "Text 2", "de": "Text 2"}]
|
|
616
|
+
|
|
617
|
+
The system automatically detects attribute types from the page type definition and handles:
|
|
618
|
+
- Type-aware processing based on actual attribute definitions
|
|
619
|
+
- Proper multiplicity validation (single vs multi-valued)
|
|
620
|
+
- Automatic type conversion and validation
|
|
621
|
+
- Enhanced error messages with attribute-specific details`)
|
|
622
|
+
},
|
|
623
|
+
annotations: { title: "Create Page" }
|
|
624
|
+
}, async ({ workspaceId, typeInternalName, name, parentUID, content, attributes }) => {
|
|
625
|
+
debugLogWithTag('CREATE_PAGE', `Starting page creation: ${name} of type ${typeInternalName} in workspace ${workspaceId}`);
|
|
626
|
+
try {
|
|
627
|
+
const requestBody = {
|
|
628
|
+
workspaceId,
|
|
629
|
+
typeInternalName,
|
|
630
|
+
name
|
|
631
|
+
};
|
|
632
|
+
if (parentUID) {
|
|
633
|
+
requestBody.parentUID = parentUID;
|
|
634
|
+
debugLogWithTag('CREATE_PAGE', `Including parentUID: ${parentUID}`);
|
|
635
|
+
}
|
|
636
|
+
if (content) {
|
|
637
|
+
requestBody.content = content;
|
|
638
|
+
debugLogWithTag('CREATE_PAGE', `Including content (${content.length} chars)`);
|
|
639
|
+
}
|
|
640
|
+
if (attributes && Object.keys(attributes).length > 0) {
|
|
641
|
+
requestBody.attributes = attributes;
|
|
642
|
+
debugLogWithTag('CREATE_PAGE', `Including attributes: ${JSON.stringify(Object.keys(attributes))}`);
|
|
643
|
+
}
|
|
644
|
+
debugLogWithTag('CREATE_PAGE', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
|
|
645
|
+
const result = await client.makeApiRequest('json/page/create', 'POST', undefined, requestBody);
|
|
646
|
+
debugLogWithTag('CREATE_PAGE', `Successfully created page: ${name}`);
|
|
647
|
+
return {
|
|
648
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
debugLogWithTag('CREATE_PAGE', `Error creating page: ${error instanceof Error ? error.message : String(error)}`);
|
|
653
|
+
return {
|
|
654
|
+
content: [{ type: "text", text: `Error creating page: ${error instanceof Error ? error.message : String(error)}` }]
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
server.registerTool("cplace_update_page", {
|
|
659
|
+
description: "Update an existing page's attributes, content, or structure using the PATCH /page/{pageId} endpoint",
|
|
660
|
+
inputSchema: {
|
|
661
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page to update"),
|
|
662
|
+
name: z.string().optional().describe("New name for the page (optional)"),
|
|
663
|
+
content: z.string().optional().describe("New wiki markup content (optional). Use empty string to remove content."),
|
|
664
|
+
parentUID: z.string().optional().describe("New parent page UID (optional - for moving pages). Use empty string to remove parent."),
|
|
665
|
+
attributes: z.record(z.any()).optional().describe(`Custom attributes to update as key-value pairs. Supports:
|
|
666
|
+
- String values: "text"
|
|
667
|
+
- Numeric values: 42 or 3.14
|
|
668
|
+
- Boolean values: true/false
|
|
669
|
+
- Date values: "2024-01-15" or "2024-01-15T10:30:00"
|
|
670
|
+
- Array values: ["value1", "value2"] for multi-valued attributes
|
|
671
|
+
- Reference values: "page/ref123" for page references
|
|
672
|
+
- Localized string values: {"en": "English text", "de": "German text", "fr": "French text"}
|
|
673
|
+
- Multi-valued localized strings: [{"en": "Text 1", "de": "Text 1"}, {"en": "Text 2", "de": "Text 2"}]
|
|
674
|
+
- Null values: null (removes the attribute value)
|
|
675
|
+
- Empty arrays: [] (removes all values for multi-valued attributes)
|
|
676
|
+
- New attributes: will be added to the page
|
|
677
|
+
|
|
678
|
+
The system automatically detects attribute types from the page type definition and handles:
|
|
679
|
+
- Type-aware processing based on actual attribute definitions
|
|
680
|
+
- Proper multiplicity validation (single vs multi-valued)
|
|
681
|
+
- Automatic type conversion and validation
|
|
682
|
+
- Enhanced error messages with attribute-specific details`)
|
|
683
|
+
},
|
|
684
|
+
annotations: { title: "Update Page" }
|
|
685
|
+
}, async ({ pageUID, name, content, parentUID, attributes }) => {
|
|
686
|
+
debugLogWithTag('UPDATE_PAGE', `Starting page update for: ${pageUID}`);
|
|
687
|
+
try {
|
|
688
|
+
const requestBody = {
|
|
689
|
+
pageUID
|
|
690
|
+
};
|
|
691
|
+
if (name !== undefined) {
|
|
692
|
+
requestBody.name = name;
|
|
693
|
+
}
|
|
694
|
+
if (content !== undefined) {
|
|
695
|
+
requestBody.content = content;
|
|
696
|
+
}
|
|
697
|
+
if (parentUID !== undefined) {
|
|
698
|
+
requestBody.parentUID = parentUID;
|
|
699
|
+
}
|
|
700
|
+
if (attributes && Object.keys(attributes).length > 0) {
|
|
701
|
+
requestBody.attributes = attributes;
|
|
702
|
+
}
|
|
703
|
+
if (Object.keys(requestBody).length === 1) {
|
|
704
|
+
return {
|
|
705
|
+
content: [{ type: "text", text: "No updates specified. At least one of name, content, parentId, or attributes must be provided." }]
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
const result = await client.makeApiRequest('json/page', 'PATCH', undefined, requestBody);
|
|
709
|
+
debugLogWithTag('UPDATE_PAGE', `Successfully updated page: ${pageUID}`);
|
|
710
|
+
return {
|
|
711
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
catch (error) {
|
|
715
|
+
debugLogWithTag('UPDATE_PAGE', `Error updating page: ${error instanceof Error ? error.message : String(error)}`);
|
|
716
|
+
return {
|
|
717
|
+
content: [{ type: "text", text: `Error updating page: ${error instanceof Error ? error.message : String(error)}` }]
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
server.registerTool("cplace_update_page_attributes_only", {
|
|
722
|
+
description: "Lightweight tool for updating only page attributes (no content or structural changes) using the same PATCH /page/{pageId} endpoint",
|
|
723
|
+
inputSchema: {
|
|
724
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page to update"),
|
|
725
|
+
attributes: z.record(z.any()).describe(`Attribute updates as key-value pairs. Supports all the same data types as the full update tool:
|
|
726
|
+
- String values: "text"
|
|
727
|
+
- Numeric values: 42 or 3.14
|
|
728
|
+
- Boolean values: true/false
|
|
729
|
+
- Date values: "2024-01-15" or "2024-01-15T10:30:00"
|
|
730
|
+
- Array values: ["value1", "value2"] for multi-valued attributes
|
|
731
|
+
- Reference values: "page/ref123" for page references
|
|
732
|
+
- Localized string values: {"en": "English text", "de": "German text", "fr": "French text"}
|
|
733
|
+
- Multi-valued localized strings: [{"en": "Text 1", "de": "Text 1"}, {"en": "Text 2", "de": "Text 2"}]
|
|
734
|
+
- Null values: null (removes the attribute value)
|
|
735
|
+
- Empty arrays: [] (removes all values for multi-valued attributes)
|
|
736
|
+
- New attributes: will be added to the page
|
|
737
|
+
|
|
738
|
+
The system automatically detects attribute types from the page type definition and handles:
|
|
739
|
+
- Type-aware processing based on actual attribute definitions
|
|
740
|
+
- Proper multiplicity validation (single vs multi-valued)
|
|
741
|
+
- Automatic type conversion and validation
|
|
742
|
+
- Enhanced error messages with attribute-specific details`)
|
|
743
|
+
},
|
|
744
|
+
annotations: { title: "Update Page Attributes Only" }
|
|
745
|
+
}, async ({ pageUID, attributes }) => {
|
|
746
|
+
debugLogWithTag('UPDATE_PAGE_ATTRS', `Starting attribute-only update for: ${pageUID}`);
|
|
747
|
+
try {
|
|
748
|
+
if (!attributes || Object.keys(attributes).length === 0) {
|
|
749
|
+
return {
|
|
750
|
+
content: [{ type: "text", text: "No attributes specified for update. The attributes parameter must contain at least one attribute." }]
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
const requestBody = {
|
|
754
|
+
pageUID,
|
|
755
|
+
attributes: attributes
|
|
756
|
+
};
|
|
757
|
+
const result = await client.makeApiRequest('json/page', 'PATCH', undefined, requestBody);
|
|
758
|
+
debugLogWithTag('UPDATE_PAGE_ATTRS', `Successfully updated attributes for page: ${pageUID}`);
|
|
759
|
+
return {
|
|
760
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
debugLogWithTag('UPDATE_PAGE_ATTRS', `Error updating page attributes: ${error instanceof Error ? error.message : String(error)}`);
|
|
765
|
+
return {
|
|
766
|
+
content: [{ type: "text", text: `Error updating page attributes: ${error instanceof Error ? error.message : String(error)}` }]
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
if (config.registerLayoutTools) {
|
|
771
|
+
server.registerTool("cplace_add_widget_to_layout", {
|
|
772
|
+
description: "Add a new widget to a page layout at a specific position",
|
|
773
|
+
inputSchema: {
|
|
774
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page to add the widget to"),
|
|
775
|
+
widgetType: z.string().describe("The widget type identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')"),
|
|
776
|
+
configuration: z.record(z.any()).optional().describe(`Widget-specific configuration object as key-value pairs. IMPORTANT: Check the widget definition schema first to understand required data types:
|
|
777
|
+
- localizedString attributes: use {"en": "English text", "de": "German text"} format
|
|
778
|
+
- boolean attributes: use true/false
|
|
779
|
+
- number attributes: use numeric values
|
|
780
|
+
- string attributes: use simple strings
|
|
781
|
+
|
|
782
|
+
RECOMMENDED WORKFLOW:
|
|
783
|
+
1. Use cplace_get_widget_definition() first to understand the configuration schema
|
|
784
|
+
2. Match your configuration values to the constraint types defined in the schema
|
|
785
|
+
3. Use appropriate data formats for each attribute type
|
|
786
|
+
|
|
787
|
+
Examples: {'title': {'en': 'My Widget', 'de': 'Mein Widget'}, 'showHeader': true, 'height': 600}`),
|
|
788
|
+
position: z.object({
|
|
789
|
+
rowIndex: z.number().min(0).describe("Row index in the layout (0-based)"),
|
|
790
|
+
columnIndex: z.number().min(0).describe("Column index within the row (0-based)"),
|
|
791
|
+
widgetIndex: z.number().min(0).describe("Widget index within the column (0-based)")
|
|
792
|
+
}).describe("Grid position where the widget should be added")
|
|
793
|
+
},
|
|
794
|
+
annotations: { title: "Add Widget to Layout" }
|
|
795
|
+
}, async ({ pageUID, widgetType, configuration, position }) => {
|
|
796
|
+
debugLogWithTag('LAYOUT_MODIFY', `Starting add widget operation: ${widgetType} to page ${pageUID} at position ${JSON.stringify(position)}`);
|
|
797
|
+
try {
|
|
798
|
+
const operation = {
|
|
799
|
+
type: "ADD",
|
|
800
|
+
widgetType,
|
|
801
|
+
position: {
|
|
802
|
+
rowIndex: position.rowIndex,
|
|
803
|
+
columnIndex: position.columnIndex,
|
|
804
|
+
widgetIndex: position.widgetIndex
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
if (configuration && Object.keys(configuration).length > 0) {
|
|
808
|
+
operation.configuration = configuration;
|
|
809
|
+
debugLogWithTag('LAYOUT_MODIFY', `Including configuration: ${JSON.stringify(Object.keys(configuration))}`);
|
|
810
|
+
}
|
|
811
|
+
const requestBody = {
|
|
812
|
+
pageUID,
|
|
813
|
+
operation
|
|
814
|
+
};
|
|
815
|
+
debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
|
|
816
|
+
const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
|
|
817
|
+
debugLogWithTag('LAYOUT_MODIFY', `Successfully added widget ${widgetType} to page ${pageUID}`);
|
|
818
|
+
return {
|
|
819
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
catch (error) {
|
|
823
|
+
debugLogWithTag('LAYOUT_MODIFY', `Error adding widget: ${error instanceof Error ? error.message : String(error)}`);
|
|
824
|
+
return {
|
|
825
|
+
content: [{ type: "text", text: `Error adding widget to layout: ${error instanceof Error ? error.message : String(error)}` }]
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
if (config.registerLayoutTools) {
|
|
831
|
+
server.registerTool("cplace_remove_widget_from_layout", {
|
|
832
|
+
description: "Remove an existing widget from a page layout",
|
|
833
|
+
inputSchema: {
|
|
834
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
|
|
835
|
+
widgetId: z.string().describe("The unique identifier of the widget to remove")
|
|
836
|
+
},
|
|
837
|
+
annotations: { title: "Remove Widget from Layout" }
|
|
838
|
+
}, async ({ pageUID, widgetId }) => {
|
|
839
|
+
debugLogWithTag('LAYOUT_MODIFY', `Starting remove widget operation: ${widgetId} from page ${pageUID}`);
|
|
840
|
+
try {
|
|
841
|
+
const requestBody = {
|
|
842
|
+
pageUID,
|
|
843
|
+
operation: {
|
|
844
|
+
type: "REMOVE",
|
|
845
|
+
widgetId
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
|
|
849
|
+
const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
|
|
850
|
+
debugLogWithTag('LAYOUT_MODIFY', `Successfully removed widget ${widgetId} from page ${pageUID}`);
|
|
851
|
+
return {
|
|
852
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
catch (error) {
|
|
856
|
+
debugLogWithTag('LAYOUT_MODIFY', `Error removing widget: ${error instanceof Error ? error.message : String(error)}`);
|
|
857
|
+
return {
|
|
858
|
+
content: [{ type: "text", text: `Error removing widget from layout: ${error instanceof Error ? error.message : String(error)}` }]
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
if (config.registerLayoutTools) {
|
|
864
|
+
server.registerTool("cplace_update_widget_in_layout", {
|
|
865
|
+
description: "Modify widget configuration or display properties",
|
|
866
|
+
inputSchema: {
|
|
867
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
|
|
868
|
+
widgetId: z.string().describe("The unique identifier of the widget to update"),
|
|
869
|
+
newConfiguration: z.record(z.any()).optional().describe(`Updated widget configuration as key-value pairs (optional). IMPORTANT: Check the widget definition schema first to understand required data types:
|
|
870
|
+
- localizedString attributes: use {"en": "English text", "de": "German text"} format
|
|
871
|
+
- boolean attributes: use true/false
|
|
872
|
+
- number attributes: use numeric values
|
|
873
|
+
- string attributes: use simple strings
|
|
874
|
+
|
|
875
|
+
RECOMMENDED WORKFLOW:
|
|
876
|
+
1. Use cplace_get_widget_definition() first to understand the configuration schema
|
|
877
|
+
2. Match your configuration values to the constraint types defined in the schema
|
|
878
|
+
3. Use appropriate data formats for each attribute type
|
|
879
|
+
|
|
880
|
+
Examples: {'title': {'en': 'My Widget', 'de': 'Mein Widget'}, 'showHeader': true, 'height': 600}`),
|
|
881
|
+
collapsed: z.boolean().optional().describe("Widget collapse state (optional)")
|
|
882
|
+
},
|
|
883
|
+
annotations: { title: "Update Widget in Layout" }
|
|
884
|
+
}, async ({ pageUID, widgetId, newConfiguration, collapsed }) => {
|
|
885
|
+
debugLogWithTag('LAYOUT_MODIFY', `Starting update widget operation: ${widgetId} in page ${pageUID}`);
|
|
886
|
+
try {
|
|
887
|
+
const operation = {
|
|
888
|
+
type: "UPDATE",
|
|
889
|
+
widgetId
|
|
890
|
+
};
|
|
891
|
+
if (newConfiguration && Object.keys(newConfiguration).length > 0) {
|
|
892
|
+
operation.newConfiguration = newConfiguration;
|
|
893
|
+
debugLogWithTag('LAYOUT_MODIFY', `Including new configuration: ${JSON.stringify(Object.keys(newConfiguration))}`);
|
|
894
|
+
}
|
|
895
|
+
if (collapsed !== undefined) {
|
|
896
|
+
operation.collapsed = collapsed;
|
|
897
|
+
debugLogWithTag('LAYOUT_MODIFY', `Setting collapsed state to: ${collapsed}`);
|
|
898
|
+
}
|
|
899
|
+
if (!operation.newConfiguration && operation.collapsed === undefined) {
|
|
900
|
+
return {
|
|
901
|
+
content: [{ type: "text", text: "No updates specified. At least one of newConfiguration or collapsed must be provided." }]
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const requestBody = {
|
|
905
|
+
pageUID,
|
|
906
|
+
operation
|
|
907
|
+
};
|
|
908
|
+
debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
|
|
909
|
+
const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
|
|
910
|
+
debugLogWithTag('LAYOUT_MODIFY', `Successfully updated widget ${widgetId} in page ${pageUID}`);
|
|
911
|
+
return {
|
|
912
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
catch (error) {
|
|
916
|
+
debugLogWithTag('LAYOUT_MODIFY', `Error updating widget: ${error instanceof Error ? error.message : String(error)}`);
|
|
917
|
+
return {
|
|
918
|
+
content: [{ type: "text", text: `Error updating widget in layout: ${error instanceof Error ? error.message : String(error)}` }]
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
if (config.registerLayoutTools) {
|
|
924
|
+
server.registerTool("cplace_move_widget_in_layout", {
|
|
925
|
+
description: "Relocate a widget to a different position in the layout",
|
|
926
|
+
inputSchema: {
|
|
927
|
+
pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
|
|
928
|
+
widgetId: z.string().describe("The unique identifier of the widget to move"),
|
|
929
|
+
newPosition: z.object({
|
|
930
|
+
rowIndex: z.number().min(0).describe("Target row index in the layout (0-based)"),
|
|
931
|
+
columnIndex: z.number().min(0).describe("Target column index within the row (0-based)"),
|
|
932
|
+
widgetIndex: z.number().min(0).describe("Target widget index within the column (0-based)")
|
|
933
|
+
}).describe("Target grid position for the widget")
|
|
934
|
+
},
|
|
935
|
+
annotations: { title: "Move Widget in Layout" }
|
|
936
|
+
}, async ({ pageUID, widgetId, newPosition }) => {
|
|
937
|
+
debugLogWithTag('LAYOUT_MODIFY', `Starting move widget operation: ${widgetId} in page ${pageUID} to position ${JSON.stringify(newPosition)}`);
|
|
938
|
+
try {
|
|
939
|
+
const requestBody = {
|
|
940
|
+
pageUID,
|
|
941
|
+
operation: {
|
|
942
|
+
type: "MOVE",
|
|
943
|
+
widgetId,
|
|
944
|
+
newPosition: {
|
|
945
|
+
rowIndex: newPosition.rowIndex,
|
|
946
|
+
columnIndex: newPosition.columnIndex,
|
|
947
|
+
widgetIndex: newPosition.widgetIndex
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
|
|
952
|
+
const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
|
|
953
|
+
debugLogWithTag('LAYOUT_MODIFY', `Successfully moved widget ${widgetId} in page ${pageUID}`);
|
|
954
|
+
return {
|
|
955
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
catch (error) {
|
|
959
|
+
debugLogWithTag('LAYOUT_MODIFY', `Error moving widget: ${error instanceof Error ? error.message : String(error)}`);
|
|
960
|
+
return {
|
|
961
|
+
content: [{ type: "text", text: `Error moving widget in layout: ${error instanceof Error ? error.message : String(error)}` }]
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
}
|
|
287
966
|
async function runServer() {
|
|
288
967
|
const transport = new StdioServerTransport();
|
|
289
968
|
await server.connect(transport);
|