@cplace/test-mcp-server 0.1.9 → 0.1.11

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 (77) hide show
  1. package/README.md +126 -20
  2. package/dist/api.d.ts +3 -2
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +4 -1
  5. package/dist/api.js.map +1 -1
  6. package/dist/conditional-registration.d.ts +4 -0
  7. package/dist/conditional-registration.d.ts.map +1 -0
  8. package/dist/conditional-registration.js +40 -0
  9. package/dist/conditional-registration.js.map +1 -0
  10. package/dist/index.js +49 -1129
  11. package/dist/index.js.map +1 -1
  12. package/dist/profiles.d.ts +10 -0
  13. package/dist/profiles.d.ts.map +1 -0
  14. package/dist/profiles.js +138 -0
  15. package/dist/profiles.js.map +1 -0
  16. package/dist/searchSchema.d.ts +44 -44
  17. package/dist/tools/change-listeners.d.ts +4 -0
  18. package/dist/tools/change-listeners.d.ts.map +1 -0
  19. package/dist/tools/change-listeners.js +98 -0
  20. package/dist/tools/change-listeners.js.map +1 -0
  21. package/dist/tools/common-schemas.d.ts +228 -0
  22. package/dist/tools/common-schemas.d.ts.map +1 -0
  23. package/dist/tools/common-schemas.js +68 -0
  24. package/dist/tools/common-schemas.js.map +1 -0
  25. package/dist/tools/pages.d.ts +4 -0
  26. package/dist/tools/pages.d.ts.map +1 -0
  27. package/dist/tools/pages.js +275 -0
  28. package/dist/tools/pages.js.map +1 -0
  29. package/dist/tools/ppt-export-profiles.d.ts +4 -0
  30. package/dist/tools/ppt-export-profiles.d.ts.map +1 -0
  31. package/dist/tools/ppt-export-profiles.js +242 -0
  32. package/dist/tools/ppt-export-profiles.js.map +1 -0
  33. package/dist/tools/references.d.ts +4 -0
  34. package/dist/tools/references.d.ts.map +1 -0
  35. package/dist/tools/references.js +57 -0
  36. package/dist/tools/references.js.map +1 -0
  37. package/dist/tools/search.d.ts +4 -0
  38. package/dist/tools/search.d.ts.map +1 -0
  39. package/dist/tools/search.js +304 -0
  40. package/dist/tools/search.js.map +1 -0
  41. package/dist/tools/system.d.ts +3 -0
  42. package/dist/tools/system.d.ts.map +1 -0
  43. package/dist/tools/system.js +22 -0
  44. package/dist/tools/system.js.map +1 -0
  45. package/dist/tools/type-layouts.d.ts +4 -0
  46. package/dist/tools/type-layouts.d.ts.map +1 -0
  47. package/dist/tools/type-layouts.js +56 -0
  48. package/dist/tools/type-layouts.js.map +1 -0
  49. package/dist/tools/users.d.ts +4 -0
  50. package/dist/tools/users.d.ts.map +1 -0
  51. package/dist/tools/users.js +62 -0
  52. package/dist/tools/users.js.map +1 -0
  53. package/dist/tools/validators.d.ts +4 -0
  54. package/dist/tools/validators.d.ts.map +1 -0
  55. package/dist/tools/validators.js +87 -0
  56. package/dist/tools/validators.js.map +1 -0
  57. package/dist/tools/version-history.d.ts +4 -0
  58. package/dist/tools/version-history.d.ts.map +1 -0
  59. package/dist/tools/version-history.js +142 -0
  60. package/dist/tools/version-history.js.map +1 -0
  61. package/dist/tools/widgets.d.ts +4 -0
  62. package/dist/tools/widgets.d.ts.map +1 -0
  63. package/dist/tools/widgets.js +516 -0
  64. package/dist/tools/widgets.js.map +1 -0
  65. package/dist/tools/workspace.d.ts +4 -0
  66. package/dist/tools/workspace.d.ts.map +1 -0
  67. package/dist/tools/workspace.js +396 -0
  68. package/dist/tools/workspace.js.map +1 -0
  69. package/dist/types.d.ts +2 -22
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js +2 -9
  72. package/dist/types.js.map +1 -1
  73. package/dist/utils.d.ts +15 -2
  74. package/dist/utils.d.ts.map +1 -1
  75. package/dist/utils.js +90 -12
  76. package/dist/utils.js.map +1 -1
  77. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2,14 +2,11 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import * as dotenv from 'dotenv';
5
- import { z } from "zod";
6
5
  import { CplaceApiClient } from './api.js';
7
- import SearchFilterSchema from "./searchSchema.js";
8
- import { convertSearchFilterToCplaceFormat } from "./searchConversion.js";
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";
6
+ import { parseProfiles, getActiveTools, TOOL_GROUPS } from './profiles.js';
7
+ import { registerActiveTools } from './conditional-registration.js';
8
+ import { registerSystemTools } from './tools/system.js';
9
+ import { debugLogWithTag } from './logger.js';
13
10
  dotenv.config();
14
11
  const config = {
15
12
  url: process.env.CPLACE_URL || '',
@@ -17,1140 +14,63 @@ const config = {
17
14
  registerSystemInfoTool: process.env.REGISTER_SYSTEM_INFO_TOOL === 'true' || false,
18
15
  };
19
16
  const client = new CplaceApiClient(config);
17
+ const activeGroups = parseProfiles(process.argv);
18
+ const activeTools = getActiveTools(activeGroups);
19
+ debugLogWithTag('MCP', '====================================');
20
+ debugLogWithTag('MCP', 'cplace MCP Server Profile Configuration');
21
+ debugLogWithTag('MCP', '====================================');
22
+ debugLogWithTag('MCP', `Active groups: ${Array.from(activeGroups).join(', ')}`);
23
+ debugLogWithTag('MCP', `Total tools to register: ${activeTools.size}`);
24
+ debugLogWithTag('MCP', '====================================');
20
25
  const server = new McpServer({
21
- name: "cplace-mcp-server",
26
+ name: `cplace-mcp-server`,
22
27
  version: "1.0.0"
23
28
  });
24
- if (config.registerSystemInfoTool) {
25
- server.registerTool("cplace_get_system_info", {
26
- description: "Get system information about the MCP server - including current working directory, CPLACE_URL setting, and debug state",
27
- inputSchema: {},
28
- annotations: { title: "Get System Information" }
29
- }, async () => {
30
- const systemInfo = {
31
- currentWorkingDirectory: process.cwd(),
32
- cplaceUrl: config.url || 'Not set',
33
- debugLogging: DEBUG_LOGGING,
34
- nodeVersion: process.version,
35
- platform: process.platform
36
- };
37
- return {
38
- content: [{ type: "text", text: JSON.stringify(systemInfo, null, 2) }]
39
- };
40
- });
41
- }
42
- server.registerTool("cplace_list_workspaces", {
43
- description: "Get a list of all workspaces with essential properties (id, name, displayName, totalPages, isFavorite, installed apps)",
29
+ registerActiveTools(server, client, activeGroups);
30
+ registerSystemTools(server, config);
31
+ server.registerTool("cplace_profile_info", {
32
+ description: "Get information about active MCP profiles and available groups",
44
33
  inputSchema: {},
45
- annotations: { title: "List Workspaces" }
34
+ annotations: { title: "Profile Information" }
46
35
  }, async () => {
47
- debugLogWithTag('WORKSPACES', 'Starting workspace list request');
48
- try {
49
- const workspaces = await client.makeApiRequest('json/workspaces');
50
- const filteredWorkspaces = workspaces.map((workspace) => ({
51
- id: workspace.id,
52
- name: workspace.name,
53
- displayName: workspace.displayName,
54
- totalPages: workspace.totalPages,
55
- isFavorite: workspace.isFavorite,
56
- installedApps: workspace.apps?.installed?.map(app => app.name) || []
57
- }));
58
- debugLogWithTag('WORKSPACES', `Retrieved ${filteredWorkspaces.length} workspaces`);
59
- return {
60
- content: [{ type: "text", text: JSON.stringify(filteredWorkspaces, null, 2) }]
61
- };
62
- }
63
- catch (error) {
64
- return {
65
- content: [{ type: "text", text: `Error retrieving workspaces: ${error instanceof Error ? error.message : String(error)}` }]
66
- };
67
- }
68
- });
69
- server.registerTool("cplace_list_types", {
70
- description: "List all types available in a workspace",
71
- inputSchema: {
72
- workspaceId: z.string().describe("The ID of the workspace to get types for")
73
- },
74
- annotations: { title: "List Types" }
75
- }, async ({ workspaceId }) => {
76
- try {
77
- const workspaces = await client.makeApiRequest('json/workspaces');
78
- const workspace = workspaces.find((w) => w.id === workspaceId);
79
- if (workspace) {
80
- const types = workspace.typeDefinitions?.types || [];
81
- return {
82
- content: [{ type: "text", text: JSON.stringify(types, null, 2) }]
83
- };
84
- }
85
- return {
86
- content: [{ type: "text", text: `Workspace ${workspaceId} not found` }]
87
- };
88
- }
89
- catch (error) {
90
- return {
91
- content: [{ type: "text", text: `Error retrieving types: ${error instanceof Error ? error.message : String(error)}` }]
92
- };
93
- }
94
- });
95
- server.registerTool("cplace_get_type_datamodel", {
96
- description: "Get the datamodel of a type including its attributes, constraints, and permissions. Use this before performing a search to understand the type structure.",
97
- inputSchema: {
98
- workspaceId: z.string().describe("The ID of the workspace"),
99
- internalName: z.string().describe("The internal name of the type")
100
- },
101
- annotations: { title: "Get Type Details" }
102
- }, async ({ workspaceId, internalName }) => {
103
- try {
104
- const result = await client.makeApiRequest('json/type-definition', 'GET', {
105
- workspaceId,
106
- internalName
107
- });
108
- return {
109
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
110
- };
111
- }
112
- catch (error) {
113
- return {
114
- content: [{ type: "text", text: `Error retrieving type details: ${error instanceof Error ? error.message : String(error)}` }]
115
- };
116
- }
117
- });
118
- server.registerTool("cplace_get_type_incoming_references", {
119
- description: "Get comprehensive information about all incoming references to a specific type definition. Identifies all attributes from other types that can reference pages of the target type, including custom attributes and built-in attributes.",
120
- inputSchema: {
121
- workspaceId: z.string().describe("The ID of the workspace containing the target type definition"),
122
- internalName: z.string().describe("The internal name of the target type definition")
123
- },
124
- annotations: { title: "Get Type Incoming References" }
125
- }, async ({ workspaceId, internalName }) => {
126
- debugLogWithTag('TYPE_REFS', `Starting incoming references request for type: ${internalName} in workspace: ${workspaceId}`);
127
- try {
128
- const result = await client.makeApiRequest('json/type/incoming-references', 'GET', {
129
- workspaceId,
130
- internalName
131
- });
132
- debugLogWithTag('TYPE_REFS', `Retrieved ${result.count || 0} incoming references for type: ${internalName}`);
133
- return {
134
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
135
- };
136
- }
137
- catch (error) {
138
- debugLogWithTag('TYPE_REFS', `Error retrieving incoming references: ${error instanceof Error ? error.message : String(error)}`);
139
- return {
140
- content: [{ type: "text", text: `Error retrieving type incoming references: ${error instanceof Error ? error.message : String(error)}` }]
141
- };
142
- }
143
- });
144
- server.registerTool("cplace_get_page_incoming_references", {
145
- description: "Get comprehensive information about all incoming references to a specific page. Identifies all pages that reference the target page through specific attributes, providing detailed information about the referencing pages and the attributes used for the references.",
146
- inputSchema: {
147
- pageUID: z.string().describe("The unique identifier (UID) of the target page"),
148
- includeReferences: z.string().describe("JSON string specifying which references to include. Must be a JSON array containing objects with 'sourceType' and 'attributeName' properties, e.g. '[{\"sourceType\":\"cf.example.Project\",\"attributeName\":\"relatedTask\"}]'")
149
- },
150
- annotations: { title: "Get Page Incoming References" }
151
- }, async ({ pageUID, includeReferences }) => {
152
- debugLogWithTag('PAGE_REFS', `Starting incoming references request for page: ${pageUID}`);
153
- try {
154
- const result = await client.makeApiRequest('json/page/incoming-references', 'GET', {
155
- pageUID,
156
- includeReferences
157
- });
158
- debugLogWithTag('PAGE_REFS', `Retrieved ${result.totalCount || 0} incoming references for page: ${pageUID}`);
159
- return {
160
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
161
- };
162
- }
163
- catch (error) {
164
- debugLogWithTag('PAGE_REFS', `Error retrieving incoming references: ${error instanceof Error ? error.message : String(error)}`);
165
- return {
166
- content: [{ type: "text", text: `Error retrieving page incoming references: ${error instanceof Error ? error.message : String(error)}` }]
167
- };
168
- }
169
- });
170
- server.registerTool("cplace_get_page_by_id", {
171
- description: "Get comprehensive page information including relationships and metadata",
172
- inputSchema: {
173
- id: z.string().describe("The ID of the page to get, e.g. 'page/kkt8ol745jqur4581kelm5ply'")
174
- },
175
- annotations: { title: "Get Page by ID" }
176
- }, async ({ id }) => {
177
- try {
178
- const result = await client.makeApiRequest('json/page', 'GET', { pageUID: id });
179
- return {
180
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
181
- };
182
- }
183
- catch (error) {
184
- return {
185
- content: [{ type: "text", text: `Error retrieving page: ${error instanceof Error ? error.message : String(error)}` }]
186
- };
187
- }
188
- });
189
- server.registerTool("cplace_get_person_by_id", {
190
- description: "Get details about a person including their name and email.",
191
- inputSchema: {
192
- id: z.string().describe("The ID of the person to get, e.g. 'person/kkt8ol745jqur4581kelm5ply'")
193
- },
194
- annotations: { title: "Get Person by ID" }
195
- }, async ({ id }) => {
196
- try {
197
- const result = await client.makeApiRequest('json/person', 'GET', { id });
198
- return {
199
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
200
- };
201
- }
202
- catch (error) {
203
- return {
204
- content: [{ type: "text", text: `Error retrieving person: ${error instanceof Error ? error.message : String(error)}` }]
205
- };
206
- }
207
- });
208
- server.registerTool("cplace_get_person_by_name", {
209
- description: "Get details about a person by searching for their name.",
210
- inputSchema: {
211
- name: z.string().describe("The name of the person or part of the name to search for"),
212
- },
213
- annotations: { title: "Get Person by Name" }
214
- }, async ({ name }) => {
215
- try {
216
- const result = await client.makeApiRequest('json/person', 'GET', { name });
217
- return {
218
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
219
- };
220
- }
221
- catch (error) {
222
- return {
223
- content: [{ type: "text", text: `Error searching for person: ${error instanceof Error ? error.message : String(error)}` }]
224
- };
225
- }
226
- });
227
- server.registerTool("cplace_search_pages", {
228
- description: "Search pages of a type in cplace.",
229
- inputSchema: {
230
- workspaceId: z.string().optional().describe("The ID of the workspace to search in. If not provided, searches across all accessible workspaces."),
231
- internalTypeName: z.string().describe(`The internal name of the type to filter by. Examples:
232
- - "cf.cplace.solution.safe.feature" for Feature pages
233
- - "cf.cplace.solution.safe.epic" for Epic pages
234
- - "cf.cplace.solution.safe.story" for Story pages
235
- - "cf.projectNavigator.project" for Project pages
236
- - "cf.cplace.solution.safe.portfolio" for Portfolio pages
237
- Use cplace_get_types_in_workspace to discover available types in a workspace.`),
238
- search_filter: SearchFilterSchema.describe(`Search Filters using cplace filter format. Examples:
239
-
240
- Basic string filter:
241
- [{"attribute": "cf.cplace.workflow", "string": {"equals": "approved"}}]
242
-
243
- Multiple conditions:
244
- [
245
- {"name": {"equals": "Project Alpha"}},
246
- {"attribute": "cf.cplace.location", "reference": {"equals": "page/abc123"}}
247
- ]
248
-
249
- Date range filter:
250
- [
251
- {"attribute": "cf.cplace.projectStart", "date": {"on_or_after": "2023-01-01"}},
252
- {"attribute": "cf.cplace.projectFinish", "date": {"on_or_before": "2024-12-31"}}
253
- ]
254
-
255
- Number comparison:
256
- [{"attribute": "cf.cplace.estimatedCost", "number": {"greater_than": 1000}}]
257
-
258
- Logical OR operator:
259
- [{
260
- "or": [
261
- {"attribute": "cf.cplace.managementPriority", "string": {"equals": "Focus"}},
262
- {"attribute": "cf.cplace.managementPriority", "string": {"equals": "Accelerate"}}
263
- ]
264
- }]
265
-
266
- Complex AND/OR combination:
267
- [{
268
- "and": [
269
- {"attribute": "cf.cplace.workflow", "string": {"equals": "inProgress"}},
270
- {
271
- "or": [
272
- {"attribute": "cf.cplace.priority", "string": {"equals": "High"}},
273
- {"attribute": "cf.cplace.priority", "string": {"equals": "Critical"}}
274
- ]
275
- }
276
- ]
277
- }]
278
-
279
- Boolean filter:
280
- [{"attribute": "cf.cplace.isActive", "boolean": {"equals": true}}]
281
-
282
- Empty/not empty checks:
283
- [{"attribute": "cf.cplace.description", "string": {"is_not_empty": true}}]`),
284
- limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
285
- offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
286
- responseFormat: z.enum(["minimal", "count"]).optional().describe(`Response format options:
287
- - Default (not specified): Complete page data, respects 'attributes' parameter
288
- - "minimal": Only core fields (id, name, url, type, workspace)
289
- - "count": Only return the total count of matching pages`),
290
- 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']"),
291
- },
292
- annotations: { title: "Search Pages" }
293
- }, async ({ workspaceId, internalTypeName, search_filter, limit = 50, offset = 0, responseFormat, attributes, }) => {
294
- debugLogWithTag('SEARCH', `Starting search with params: workspaceId=${workspaceId || 'none'}, type=${internalTypeName}, filter=${JSON.stringify(search_filter)}, limit=${limit}, offset=${offset}`);
295
- try {
296
- const cplaceFilter = convertSearchFilterToCplaceFormat(search_filter);
297
- debugLogWithTag('SEARCH', `Converted filter: ${JSON.stringify(cplaceFilter)}`);
298
- if (workspaceId) {
299
- cplaceFilter.filters.unshift({
300
- type: "Workspace",
301
- workspaceIds: [workspaceId]
302
- });
303
- }
304
- if (internalTypeName) {
305
- cplaceFilter.filters.unshift({
306
- type: "Type",
307
- types: [internalTypeName]
308
- });
309
- }
310
- debugLogWithTag('SEARCH', `Final cplace filter: ${JSON.stringify(cplaceFilter)}`);
311
- const result = await client.makeApiRequest('json/search', 'GET', {
312
- filter: JSON.stringify(cplaceFilter),
313
- limit,
314
- offset
315
- });
316
- if (responseFormat === "count") {
317
- const countResult = {
318
- count: result.pagination?.total || 0
319
- };
320
- return {
321
- content: [{ type: "text", text: JSON.stringify(countResult, null, 2) }]
322
- };
323
- }
324
- const filteredResults = result.results ? result.results.map((item) => filterSearchResult(item, {
325
- minimal: responseFormat === "minimal",
326
- includeAttributes: responseFormat !== "minimal",
327
- includeMetadata: false,
328
- attributeFields: attributes,
329
- truncateLargeValues: true
330
- })) : [];
331
- const filteredResult = {
332
- results: filteredResults,
333
- pagination: {
334
- total: result.pagination?.total || 0,
335
- limit: result.pagination?.limit || limit,
336
- offset: result.pagination?.offset || offset,
337
- hasMore: result.pagination?.hasMore || false
338
- },
339
- humanReadableRepresentation: result.summary,
340
- cplaceJson: result.cplaceJson
341
- };
342
- return {
343
- content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
344
- };
345
- }
346
- catch (error) {
347
- debugLogWithTag('SEARCH', `Error during search: ${error instanceof Error ? error.message : String(error)}`);
348
- return {
349
- content: [{ type: "text", text: `Error converting or executing search: ${error instanceof Error ? error.message : String(error)}` }]
350
- };
351
- }
352
- });
353
- server.registerTool("cplace_search_pages_fulltext", {
354
- description: "Make a fulltext search across all pages in cplace. Use this when searching for text content within pages.",
355
- inputSchema: {
356
- workspaceId: z.string().optional().describe("The ID of the workspace to search in. If not provided, searches across all accessible workspaces."),
357
- fulltext: z.string().describe("The fulltext search query to filter pages by. The results will include pages that contain this string in their name, attributes, or content."),
358
- limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
359
- offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
360
- },
361
- annotations: { title: "Search Pages by Fulltext" }
362
- }, async ({ workspaceId, fulltext, limit = 50, offset = 0, }) => {
363
- if (!fulltext) {
364
- return {
365
- content: [{ type: "text", text: "fulltext is required" }]
366
- };
367
- }
368
- try {
369
- const result = await client.makeApiRequest('json/fulltext-search', 'GET', {
370
- spaceId: workspaceId,
371
- fulltext: `"${fulltext}"`,
372
- limit,
373
- offset
374
- });
375
- const filteredResults = result.results ? result.results.map((item) => filterSearchResult(item, {
376
- minimal: false,
377
- includeAttributes: true,
378
- includeMetadata: false,
379
- truncateLargeValues: true
380
- })) : [];
381
- const filteredResult = {
382
- results: filteredResults,
383
- pagination: {
384
- total: result.pagination?.total || 0,
385
- limit: result.pagination?.limit || limit,
386
- offset: result.pagination?.offset || offset,
387
- hasMore: result.pagination?.hasMore || false
388
- },
389
- summary: result.summary,
390
- cplaceJson: result.cplaceJson
391
- };
392
- return {
393
- content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
394
- };
395
- }
396
- catch (error) {
397
- return {
398
- content: [{ type: "text", text: `Error converting or executing search: ${error instanceof Error ? error.message : String(error)}` }]
399
- };
400
- }
401
- });
402
- server.registerTool("cplace_list_pages_of_type", {
403
- description: "List all pages of a specific type in a workspace in cplace. Use this to retrieve multiple pages of the same type.",
404
- inputSchema: {
405
- workspaceId: z.string().describe("The ID of the workspace to get pages from"),
406
- typeName: z.string().describe("The internal name of the type to retrieve pages for"),
407
- limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
408
- offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
409
- },
410
- annotations: { title: "List Pages of Type" }
411
- }, async ({ workspaceId, typeName, limit = 50, offset = 0, }) => {
412
- try {
413
- const cplaceFilter = {
414
- filters: [
415
- {
416
- type: "Workspace",
417
- workspaceIds: [workspaceId]
418
- },
419
- {
420
- type: "Type",
421
- types: [typeName]
422
- }
423
- ]
424
- };
425
- const result = await client.makeApiRequest('json/search', 'GET', {
426
- filter: JSON.stringify(cplaceFilter),
427
- limit,
428
- offset
429
- });
430
- const filteredResults = result.results ? result.results.map((item) => filterSearchResult(item, {
431
- minimal: false,
432
- includeAttributes: true,
433
- includeMetadata: false,
434
- truncateLargeValues: true
435
- })) : [];
436
- const filteredResult = {
437
- results: filteredResults,
438
- pagination: {
439
- total: result.pagination?.total || 0,
440
- limit: result.pagination?.limit || limit,
441
- offset: result.pagination?.offset || offset,
442
- hasMore: result.pagination?.hasMore || false
443
- },
444
- summary: result.summary
445
- };
446
- return {
447
- content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
448
- };
449
- }
450
- catch (error) {
451
- return {
452
- content: [{ type: "text", text: `Error retrieving pages: ${error instanceof Error ? error.message : String(error)}` }]
453
- };
454
- }
455
- });
456
- server.registerTool("cplace_search_pages_csv", {
457
- 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.",
458
- inputSchema: {
459
- workspaceId: z.string().optional().describe("Same as cplace_search_pages"),
460
- internalTypeName: z.string().describe("Same as cplace_search_pages"),
461
- search_filter: SearchFilterSchema.describe("Same as cplace_search_pages"),
462
- limit: z.number().min(1).max(1000).default(50).describe("Same as cplace_search_pages"),
463
- offset: z.number().min(0).max(10000).default(0).describe("Same as cplace_search_pages"),
464
- attributes: z.array(z.string()).optional().describe("Same as cplace_search_pages"),
465
- },
466
- annotations: { title: "Search Pages (CSV Export)" }
467
- }, async ({ workspaceId, internalTypeName, search_filter, limit = 50, offset = 0, attributes, }) => {
468
- debugLogWithTag('CSV_SEARCH', `Starting CSV search with params: workspaceId=${workspaceId || 'none'}, type=${internalTypeName}, filter=${JSON.stringify(search_filter)}, limit=${limit}, offset=${offset}`);
469
- try {
470
- const cplaceFilter = convertSearchFilterToCplaceFormat(search_filter);
471
- debugLogWithTag('CSV_SEARCH', `Converted filter: ${JSON.stringify(cplaceFilter)}`);
472
- if (workspaceId) {
473
- cplaceFilter.filters.unshift({
474
- type: "Workspace",
475
- workspaceIds: [workspaceId]
476
- });
477
- }
478
- if (internalTypeName) {
479
- cplaceFilter.filters.unshift({
480
- type: "Type",
481
- types: [internalTypeName]
482
- });
483
- }
484
- debugLogWithTag('CSV_SEARCH', `Final cplace filter: ${JSON.stringify(cplaceFilter)}`);
485
- const result = await client.makeApiRequest('json/search', 'GET', {
486
- filter: JSON.stringify(cplaceFilter),
487
- limit,
488
- offset
489
- });
490
- const searchResults = result.results || [];
491
- debugLogWithTag('CSV_SEARCH', `Retrieved ${searchResults.length} results for CSV conversion`);
492
- const csvContent = await convertPagesToCsv(searchResults, client, attributes);
493
- return {
494
- content: [{ type: "text", text: csvContent }]
495
- };
496
- }
497
- catch (error) {
498
- debugLogWithTag('CSV_SEARCH', `Error during CSV search: ${error instanceof Error ? error.message : String(error)}`);
499
- return {
500
- content: [{ type: "text", text: `Error converting or executing CSV search: ${error instanceof Error ? error.message : String(error)}` }]
501
- };
502
- }
503
- });
504
- server.registerTool("cplace_list_widget_definitions", {
505
- 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",
506
- inputSchema: {
507
- embeddingContext: z.enum(["AS_WIDGET", "INSIDE_RICHSTRING", "INSIDE_WIDGET"]).default("AS_WIDGET").describe(`Filter widgets by embedding context support:
508
- - AS_WIDGET (default): Widgets that support full widget embedding with frame and title
509
- - INSIDE_RICHSTRING: Widgets that can be embedded in rich text content without frame
510
- - INSIDE_WIDGET: Widgets that can be embedded inside other widgets without frame`)
511
- },
512
- annotations: { title: "List Widget Definitions" }
513
- }, async ({ embeddingContext = "AS_WIDGET" }) => {
514
- debugLogWithTag('WIDGETS', `Starting widget definitions list request with embeddingContext: ${embeddingContext}`);
515
- try {
516
- const result = await client.makeApiRequest('json/widget-definitions', 'GET', { embeddingContext });
517
- debugLogWithTag('WIDGETS', `Retrieved widget definitions for context: ${embeddingContext}`);
518
- return {
519
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
520
- };
521
- }
522
- catch (error) {
523
- return {
524
- content: [{ type: "text", text: `Error retrieving widget definitions: ${error instanceof Error ? error.message : String(error)}` }]
525
- };
526
- }
527
- });
528
- server.registerTool("cplace_get_widget_definition", {
529
- description: "Get detailed information about a specific widget definition including its complete configuration schema with all attributes, constraints, and metadata",
530
- inputSchema: {
531
- widgetKind: z.string().describe("The widget kind identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')")
532
- },
533
- annotations: { title: "Get Widget Definition Details" }
534
- }, async ({ widgetKind }) => {
535
- debugLogWithTag('WIDGETS', `Starting widget definition request for: ${widgetKind}`);
536
- try {
537
- const result = await client.makeApiRequest('json/widget-definition', 'GET', { widgetKind });
538
- debugLogWithTag('WIDGETS', `Retrieved widget definition for: ${widgetKind}`);
539
- return {
540
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
541
- };
542
- }
543
- catch (error) {
544
- return {
545
- content: [{ type: "text", text: `Error retrieving widget definition for ${widgetKind}: ${error instanceof Error ? error.message : String(error)}` }]
546
- };
547
- }
548
- });
549
- server.registerTool("cplace_get_page_layout_overview", {
550
- 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.",
551
- inputSchema: {
552
- pageUID: z.string().describe("The unique identifier (UID) of the page to get layout for, e.g. 'page/kkt8ol745jqur4581kelm5ply'")
553
- },
554
- annotations: { title: "Get Page Layout Overview" }
555
- }, async ({ pageUID }) => {
556
- debugLogWithTag('LAYOUT', `Starting page layout overview request for: ${pageUID}`);
557
- try {
558
- const result = await client.makeApiRequest('json/pageLayout', 'GET', { pageUID });
559
- const overview = {
560
- rows: result.rows?.map((row, rowIndex) => ({
561
- rowIndex,
562
- columns: row.columns?.map((column, colIndex) => ({
563
- columnIndex: colIndex,
564
- proportion: column.proportion,
565
- widgets: column.widgets?.map((widget) => ({
566
- id: widget.id,
567
- widgetType: widget.widgetType,
568
- configurationCount: widget.configuration?.length || 0
569
- })) || []
570
- })) || []
571
- })) || []
572
- };
573
- debugLogWithTag('LAYOUT', `Retrieved page layout overview for: ${pageUID}`);
574
- return {
575
- content: [{ type: "text", text: JSON.stringify(overview, null, 2) }]
576
- };
577
- }
578
- catch (error) {
579
- return {
580
- content: [{ type: "text", text: `Error retrieving page layout overview for ${pageUID}: ${error instanceof Error ? error.message : String(error)}` }]
581
- };
582
- }
583
- });
584
- server.registerTool("cplace_get_widget_details", {
585
- 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.",
586
- inputSchema: {
587
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
588
- widgetId: z.string().describe("The unique widget identifier from the layout (e.g., 'id_123', 'id_456')")
589
- },
590
- annotations: { title: "Get Widget Details" }
591
- }, async ({ pageUID, widgetId }) => {
592
- debugLogWithTag('LAYOUT', `Starting widget details request for widget ${widgetId} in page ${pageUID}`);
593
- try {
594
- const result = await client.makeApiRequest('json/pageLayout', 'GET', { pageUID });
595
- let foundWidget = null;
596
- const findWidget = (rows) => {
597
- for (const row of rows || []) {
598
- for (const column of row.columns || []) {
599
- for (const widget of column.widgets || []) {
600
- if (widget.id === widgetId) {
601
- return widget;
602
- }
603
- if (widget.widgetsLayout?.rows) {
604
- const nestedWidget = findWidget(widget.widgetsLayout.rows);
605
- if (nestedWidget)
606
- return nestedWidget;
607
- }
36
+ const groupDetails = Array.from(activeGroups).map(name => ({
37
+ name,
38
+ description: TOOL_GROUPS[name]?.description || 'Unknown group',
39
+ toolCount: TOOL_GROUPS[name]?.tools.length || 0,
40
+ tools: TOOL_GROUPS[name]?.tools || []
41
+ }));
42
+ const allGroups = Object.entries(TOOL_GROUPS).map(([name, group]) => ({
43
+ name,
44
+ description: group.description,
45
+ toolCount: group.tools.length,
46
+ active: activeGroups.has(name)
47
+ }));
48
+ return {
49
+ content: [{
50
+ type: "text",
51
+ text: JSON.stringify({
52
+ active_groups: Array.from(activeGroups),
53
+ total_active_tools: activeTools.size,
54
+ active_group_details: groupDetails,
55
+ all_available_groups: allGroups,
56
+ usage_examples: {
57
+ minimal: "No --profiles argument (core only)",
58
+ page_editing: "--profiles=pages_write",
59
+ dashboard_dev: "--profiles=pages_write+layouts",
60
+ type_dev: "--profiles=types_write+low_code",
61
+ full_access: "--profiles=all"
608
62
  }
609
- }
610
- }
611
- return null;
612
- };
613
- foundWidget = findWidget(result.rows);
614
- if (!foundWidget) {
615
- return {
616
- content: [{ type: "text", text: `Widget with ID ${widgetId} not found in page ${pageUID}` }]
617
- };
618
- }
619
- debugLogWithTag('LAYOUT', `Retrieved widget details for: ${widgetId} in page`);
620
- return {
621
- content: [{ type: "text", text: JSON.stringify(foundWidget, null, 2) }]
622
- };
623
- }
624
- catch (error) {
625
- return {
626
- content: [{ type: "text", text: `Error retrieving widget details for ${widgetId} in page ${pageUID}: ${error instanceof Error ? error.message : String(error)}` }]
627
- };
628
- }
629
- });
630
- server.registerTool("cplace_get_current_user", {
631
- description: "Get information about the currently logged-in user, including their attributes and metadata",
632
- inputSchema: {},
633
- annotations: { title: "Get Current User" }
634
- }, async () => {
635
- debugLogWithTag('CURRENT_USER', 'Starting current user request');
636
- try {
637
- const result = await client.makeApiRequest('json/current-user');
638
- debugLogWithTag('CURRENT_USER', `Retrieved current user information`);
639
- return {
640
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
641
- };
642
- }
643
- catch (error) {
644
- return {
645
- content: [{ type: "text", text: `Error retrieving current user information: ${error instanceof Error ? error.message : String(error)}` }]
646
- };
647
- }
648
- });
649
- server.registerTool("cplace_create_page", {
650
- description: "Create a new page with specified attributes and content using the POST /page/create endpoint",
651
- inputSchema: {
652
- workspaceId: z.string().describe("The ID of the workspace/space to create the page in (e.g., 'workspace/abc123')"),
653
- typeInternalName: z.string().describe("The internal name of the page type (e.g., 'cf.example.myType')"),
654
- name: z.string().describe("The name of the new page"),
655
- parentUID: z.string().optional().describe("The UID of the parent page for hierarchy (optional)"),
656
- content: z.string().optional().describe("Wiki markup content for the page (optional)"),
657
- attributes: z.record(z.any()).optional().describe(`Custom attribute values as key-value pairs. Supports:
658
- - String values: "text"
659
- - Numeric values: 42 or 3.14
660
- - Boolean values: true/false
661
- - Date values: "2024-01-15" or "2024-01-15T10:30:00"
662
- - Array values: ["value1", "value2"] for multi-valued attributes
663
- - Reference values: "page/ref123" for page references
664
- - Localized string values: {"en": "English text", "de": "German text", "fr": "French text"}
665
- - Multi-valued localized strings: [{"en": "Text 1", "de": "Text 1"}, {"en": "Text 2", "de": "Text 2"}]
666
-
667
- The system automatically detects attribute types from the page type definition and handles:
668
- - Type-aware processing based on actual attribute definitions
669
- - Proper multiplicity validation (single vs multi-valued)
670
- - Automatic type conversion and validation
671
- - Enhanced error messages with attribute-specific details`)
672
- },
673
- annotations: { title: "Create Page" }
674
- }, async ({ workspaceId, typeInternalName, name, parentUID, content, attributes }) => {
675
- debugLogWithTag('CREATE_PAGE', `Starting page creation: ${name} of type ${typeInternalName} in workspace ${workspaceId}`);
676
- try {
677
- const requestBody = {
678
- workspaceId,
679
- typeInternalName,
680
- name
681
- };
682
- if (parentUID) {
683
- requestBody.parentUID = parentUID;
684
- debugLogWithTag('CREATE_PAGE', `Including parentUID: ${parentUID}`);
685
- }
686
- if (content) {
687
- requestBody.content = content;
688
- debugLogWithTag('CREATE_PAGE', `Including content (${content.length} chars)`);
689
- }
690
- if (attributes && Object.keys(attributes).length > 0) {
691
- requestBody.attributes = attributes;
692
- debugLogWithTag('CREATE_PAGE', `Including attributes: ${JSON.stringify(Object.keys(attributes))}`);
693
- }
694
- debugLogWithTag('CREATE_PAGE', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
695
- const result = await client.makeApiRequest('json/page/create', 'POST', undefined, requestBody);
696
- debugLogWithTag('CREATE_PAGE', `Successfully created page: ${name}`);
697
- return {
698
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
699
- };
700
- }
701
- catch (error) {
702
- debugLogWithTag('CREATE_PAGE', `Error creating page: ${error instanceof Error ? error.message : String(error)}`);
703
- return {
704
- content: [{ type: "text", text: `Error creating page: ${error instanceof Error ? error.message : String(error)}` }]
705
- };
706
- }
707
- });
708
- server.registerTool("cplace_update_page", {
709
- description: "Update an existing page's attributes, content, or structure using the PATCH /page/{pageId} endpoint",
710
- inputSchema: {
711
- pageUID: z.string().describe("The unique identifier (UID) of the page to update"),
712
- name: z.string().optional().describe("New name for the page (optional)"),
713
- content: z.string().optional().describe("New wiki markup content (optional). Use empty string to remove content."),
714
- parentUID: z.string().optional().describe("New parent page UID (optional - for moving pages). Use empty string to remove parent."),
715
- attributes: z.record(z.any()).optional().describe(`Custom attributes to update as key-value pairs. Supports:
716
- - String values: "text"
717
- - Numeric values: 42 or 3.14
718
- - Boolean values: true/false
719
- - Date values: "2024-01-15" or "2024-01-15T10:30:00"
720
- - Array values: ["value1", "value2"] for multi-valued attributes
721
- - Reference values: "page/ref123" for page references
722
- - Localized string values: {"en": "English text", "de": "German text", "fr": "French text"}
723
- - Multi-valued localized strings: [{"en": "Text 1", "de": "Text 1"}, {"en": "Text 2", "de": "Text 2"}]
724
- - Null values: null (removes the attribute value)
725
- - Empty arrays: [] (removes all values for multi-valued attributes)
726
- - New attributes: will be added to the page
727
-
728
- The system automatically detects attribute types from the page type definition and handles:
729
- - Type-aware processing based on actual attribute definitions
730
- - Proper multiplicity validation (single vs multi-valued)
731
- - Automatic type conversion and validation
732
- - Enhanced error messages with attribute-specific details`)
733
- },
734
- annotations: { title: "Update Page" }
735
- }, async ({ pageUID, name, content, parentUID, attributes }) => {
736
- debugLogWithTag('UPDATE_PAGE', `Starting page update for: ${pageUID}`);
737
- try {
738
- const requestBody = {
739
- pageUID
740
- };
741
- if (name !== undefined) {
742
- requestBody.name = name;
743
- }
744
- if (content !== undefined) {
745
- requestBody.content = content;
746
- }
747
- if (parentUID !== undefined) {
748
- requestBody.parentUID = parentUID;
749
- }
750
- if (attributes && Object.keys(attributes).length > 0) {
751
- requestBody.attributes = attributes;
752
- }
753
- if (Object.keys(requestBody).length === 1) {
754
- return {
755
- content: [{ type: "text", text: "No updates specified. At least one of name, content, parentId, or attributes must be provided." }]
756
- };
757
- }
758
- const result = await client.makeApiRequest('json/page', 'PATCH', undefined, requestBody);
759
- debugLogWithTag('UPDATE_PAGE', `Successfully updated page: ${pageUID}`);
760
- return {
761
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
762
- };
763
- }
764
- catch (error) {
765
- debugLogWithTag('UPDATE_PAGE', `Error updating page: ${error instanceof Error ? error.message : String(error)}`);
766
- return {
767
- content: [{ type: "text", text: `Error updating page: ${error instanceof Error ? error.message : String(error)}` }]
768
- };
769
- }
770
- });
771
- server.registerTool("cplace_update_page_attributes", {
772
- description: "Update page attributes (no content or structural changes) using the PATCH /page/{pageId} endpoint",
773
- inputSchema: {
774
- pageUID: z.string().describe("The unique identifier (UID) of the page to update"),
775
- attributes: z.record(z.any()).describe(`Attribute updates as key-value pairs. Supports all the same data types as the full update tool:
776
- - String values: "text"
777
- - Numeric values: 42 or 3.14
778
- - Boolean values: true/false
779
- - Date values: "2024-01-15" or "2024-01-15T10:30:00"
780
- - Array values: ["value1", "value2"] for multi-valued attributes
781
- - Reference values: "page/ref123" for page references
782
- - Localized string values: {"en": "English text", "de": "German text", "fr": "French text"}
783
- - Multi-valued localized strings: [{"en": "Text 1", "de": "Text 1"}, {"en": "Text 2", "de": "Text 2"}]
784
- - Null values: null (removes the attribute value)
785
- - Empty arrays: [] (removes all values for multi-valued attributes)
786
- - New attributes: will be added to the page
787
-
788
- The system automatically detects attribute types from the page type definition and handles:
789
- - Type-aware processing based on actual attribute definitions
790
- - Proper multiplicity validation (single vs multi-valued)
791
- - Automatic type conversion and validation
792
- - Enhanced error messages with attribute-specific details`)
793
- },
794
- annotations: { title: "Update Page Attributes" }
795
- }, async ({ pageUID, attributes }) => {
796
- debugLogWithTag('UPDATE_PAGE_ATTRS', `Starting attribute-only update for: ${pageUID}`);
797
- try {
798
- if (!attributes || Object.keys(attributes).length === 0) {
799
- return {
800
- content: [{ type: "text", text: "No attributes specified for update. The attributes parameter must contain at least one attribute." }]
801
- };
802
- }
803
- const requestBody = {
804
- pageUID,
805
- attributes: attributes
806
- };
807
- const result = await client.makeApiRequest('json/page', 'PATCH', undefined, requestBody);
808
- debugLogWithTag('UPDATE_PAGE_ATTRS', `Successfully updated attributes for page: ${pageUID}`);
809
- return {
810
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
811
- };
812
- }
813
- catch (error) {
814
- debugLogWithTag('UPDATE_PAGE_ATTRS', `Error updating page attributes: ${error instanceof Error ? error.message : String(error)}`);
815
- return {
816
- content: [{ type: "text", text: `Error updating page attributes: ${error instanceof Error ? error.message : String(error)}` }]
817
- };
818
- }
819
- });
820
- server.registerTool("cplace_add_widget_to_layout", {
821
- description: "Add a new widget to a page layout at a specific position. Supports both page-level layouts (default) and embedded layouts within container widgets like AttributesGroup.",
822
- inputSchema: {
823
- pageUID: z.string().describe("The unique identifier (UID) of the page to add the widget to"),
824
- widgetType: z.string().describe("The widget type identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki', 'cf.platform.singleAttribute')"),
825
- 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:
826
- - localizedString attributes: use {"en": "English text", "de": "German text"} format
827
- - boolean attributes: use true/false
828
- - number attributes: use numeric values
829
- - string attributes: use simple strings
830
-
831
- RECOMMENDED WORKFLOW:
832
- 1. Use cplace_get_widget_definition() first to understand the configuration schema
833
- 2. Match your configuration values to the constraint types defined in the schema
834
- 3. Use appropriate data formats for each attribute type
835
-
836
- Examples: {'title': {'en': 'My Widget', 'de': 'Mein Widget'}, 'showHeader': true, 'height': 600}
837
- For embedded attribute widgets: {'cf.platform.quotedAttributeName': "'cf.cplace.myAttribute'", 'cf.platform.withLabel': true}`),
838
- position: z.object({
839
- rowIndex: z.number().min(0).describe("Row index in the layout (0-based)"),
840
- columnIndex: z.number().min(0).describe("Column index within the row (0-based)"),
841
- widgetIndex: z.number().min(0).describe("Widget index within the column (0-based)")
842
- }).describe("Grid position where the widget should be added"),
843
- layoutContext: z.enum(["page", "widget"]).default("page").describe("Layout context: 'page' for page-level layout (default), 'widget' for embedded layout within a container widget"),
844
- 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"),
845
- 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")
846
- },
847
- annotations: { title: "Add Widget to Layout" }
848
- }, async ({ pageUID, widgetType, configuration, position, layoutContext = "page", containerWidgetId, layoutAttributeName }) => {
849
- debugLogWithTag('LAYOUT_MODIFY', `Starting add widget operation: ${widgetType} to ${layoutContext === "widget" ? "embedded layout in container " + containerWidgetId : "page layout"} ${pageUID} at position ${JSON.stringify(position)}`);
850
- try {
851
- if (layoutContext === "widget") {
852
- if (!containerWidgetId) {
853
- return {
854
- content: [{ type: "text", text: "containerWidgetId is required when layoutContext is 'widget'" }]
855
- };
856
- }
857
- debugLogWithTag('LAYOUT_MODIFY', `Adding to embedded layout in container widget: ${containerWidgetId}`);
858
- }
859
- const operation = {
860
- type: "ADD",
861
- widgetType,
862
- position: {
863
- rowIndex: position.rowIndex,
864
- columnIndex: position.columnIndex,
865
- widgetIndex: position.widgetIndex
866
- }
867
- };
868
- if (configuration && Object.keys(configuration).length > 0) {
869
- operation.configuration = configuration;
870
- debugLogWithTag('LAYOUT_MODIFY', `Including configuration: ${JSON.stringify(Object.keys(configuration))}`);
871
- }
872
- const requestBody = {
873
- pageUID,
874
- layoutContext,
875
- operation
876
- };
877
- if (layoutContext === "widget") {
878
- requestBody.containerWidgetId = containerWidgetId;
879
- if (layoutAttributeName) {
880
- requestBody.layoutAttributeName = layoutAttributeName;
881
- }
882
- }
883
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
884
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
885
- debugLogWithTag('LAYOUT_MODIFY', `Successfully added widget ${widgetType} to page ${pageUID}`);
886
- return {
887
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
888
- };
889
- }
890
- catch (error) {
891
- debugLogWithTag('LAYOUT_MODIFY', `Error adding widget: ${error instanceof Error ? error.message : String(error)}`);
892
- return {
893
- content: [{ type: "text", text: `Error adding widget to layout: ${error instanceof Error ? error.message : String(error)}` }]
894
- };
895
- }
896
- });
897
- server.registerTool("cplace_remove_widget_from_layout", {
898
- description: "Remove an existing widget from a page layout",
899
- inputSchema: {
900
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
901
- widgetId: z.string().describe("The unique identifier of the widget to remove")
902
- },
903
- annotations: { title: "Remove Widget from Layout" }
904
- }, async ({ pageUID, widgetId }) => {
905
- debugLogWithTag('LAYOUT_MODIFY', `Starting remove widget operation: ${widgetId} from page ${pageUID}`);
906
- try {
907
- const requestBody = {
908
- pageUID,
909
- operation: {
910
- type: "REMOVE",
911
- widgetId
912
- }
913
- };
914
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
915
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
916
- debugLogWithTag('LAYOUT_MODIFY', `Successfully removed widget ${widgetId} from page ${pageUID}`);
917
- return {
918
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
919
- };
920
- }
921
- catch (error) {
922
- debugLogWithTag('LAYOUT_MODIFY', `Error removing widget: ${error instanceof Error ? error.message : String(error)}`);
923
- return {
924
- content: [{ type: "text", text: `Error removing widget from layout: ${error instanceof Error ? error.message : String(error)}` }]
925
- };
926
- }
927
- });
928
- server.registerTool("cplace_update_widget_in_layout", {
929
- description: "Modify widget configuration or display properties",
930
- inputSchema: {
931
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
932
- widgetId: z.string().describe("The unique identifier of the widget to update"),
933
- 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:
934
- - localizedString attributes: use {"en": "English text", "de": "German text"} format
935
- - boolean attributes: use true/false
936
- - number attributes: use numeric values
937
- - string attributes: use simple strings
938
-
939
- RECOMMENDED WORKFLOW:
940
- 1. Use cplace_get_widget_definition() first to understand the configuration schema
941
- 2. Match your configuration values to the constraint types defined in the schema
942
- 3. Use appropriate data formats for each attribute type
943
-
944
- Examples: {'title': {'en': 'My Widget', 'de': 'Mein Widget'}, 'showHeader': true, 'height': 600}`),
945
- collapsed: z.boolean().optional().describe("Widget collapse state (optional)")
946
- },
947
- annotations: { title: "Update Widget in Layout" }
948
- }, async ({ pageUID, widgetId, newConfiguration, collapsed }) => {
949
- debugLogWithTag('LAYOUT_MODIFY', `Starting update widget operation: ${widgetId} in page ${pageUID}`);
950
- try {
951
- const operation = {
952
- type: "UPDATE",
953
- widgetId
954
- };
955
- if (newConfiguration && Object.keys(newConfiguration).length > 0) {
956
- operation.newConfiguration = newConfiguration;
957
- debugLogWithTag('LAYOUT_MODIFY', `Including new configuration: ${JSON.stringify(Object.keys(newConfiguration))}`);
958
- }
959
- if (collapsed !== undefined) {
960
- operation.collapsed = collapsed;
961
- debugLogWithTag('LAYOUT_MODIFY', `Setting collapsed state to: ${collapsed}`);
962
- }
963
- if (!operation.newConfiguration && operation.collapsed === undefined) {
964
- return {
965
- content: [{ type: "text", text: "No updates specified. At least one of newConfiguration or collapsed must be provided." }]
966
- };
967
- }
968
- const requestBody = {
969
- pageUID,
970
- operation
971
- };
972
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
973
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
974
- debugLogWithTag('LAYOUT_MODIFY', `Successfully updated widget ${widgetId} in page ${pageUID}`);
975
- return {
976
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
977
- };
978
- }
979
- catch (error) {
980
- debugLogWithTag('LAYOUT_MODIFY', `Error updating widget: ${error instanceof Error ? error.message : String(error)}`);
981
- return {
982
- content: [{ type: "text", text: `Error updating widget in layout: ${error instanceof Error ? error.message : String(error)}` }]
983
- };
984
- }
985
- });
986
- server.registerTool("cplace_move_widget_in_layout", {
987
- description: "Relocate a widget to a different position in the layout",
988
- inputSchema: {
989
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the widget"),
990
- widgetId: z.string().describe("The unique identifier of the widget to move"),
991
- newPosition: z.object({
992
- rowIndex: z.number().min(0).describe("Target row index in the layout (0-based)"),
993
- columnIndex: z.number().min(0).describe("Target column index within the row (0-based)"),
994
- widgetIndex: z.number().min(0).describe("Target widget index within the column (0-based)")
995
- }).describe("Target grid position for the widget")
996
- },
997
- annotations: { title: "Move Widget in Layout" }
998
- }, async ({ pageUID, widgetId, newPosition }) => {
999
- debugLogWithTag('LAYOUT_MODIFY', `Starting move widget operation: ${widgetId} in page ${pageUID} to position ${JSON.stringify(newPosition)}`);
1000
- try {
1001
- const requestBody = {
1002
- pageUID,
1003
- operation: {
1004
- type: "MOVE",
1005
- widgetId,
1006
- newPosition: {
1007
- rowIndex: newPosition.rowIndex,
1008
- columnIndex: newPosition.columnIndex,
1009
- widgetIndex: newPosition.widgetIndex
1010
- }
1011
- }
1012
- };
1013
- debugLogWithTag('LAYOUT_MODIFY', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
1014
- const result = await client.makeApiRequest('json/pageLayout', 'PATCH', undefined, requestBody);
1015
- debugLogWithTag('LAYOUT_MODIFY', `Successfully moved widget ${widgetId} in page ${pageUID}`);
1016
- return {
1017
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1018
- };
1019
- }
1020
- catch (error) {
1021
- debugLogWithTag('LAYOUT_MODIFY', `Error moving widget: ${error instanceof Error ? error.message : String(error)}`);
1022
- return {
1023
- content: [{ type: "text", text: `Error moving widget in layout: ${error instanceof Error ? error.message : String(error)}` }]
1024
- };
1025
- }
1026
- });
1027
- server.registerTool("cplace_get_embedded_layout", {
1028
- 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.",
1029
- inputSchema: {
1030
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the container widget"),
1031
- containerWidgetId: z.string().describe("The ID of the container widget that contains the embedded layout"),
1032
- layoutAttributeName: z.string().describe("The name of the attribute that defines the embedded layout structure")
1033
- },
1034
- annotations: { title: "Get Embedded Layout" }
1035
- }, async ({ pageUID, containerWidgetId, layoutAttributeName }) => {
1036
- debugLogWithTag('EMBEDDED_LAYOUT', `Starting embedded widget layout request for container ${containerWidgetId} in page ${pageUID}`);
1037
- try {
1038
- const result = await client.makeApiRequest('json/embeddedWidgetLayout', 'GET', {
1039
- pageUID,
1040
- containerWidgetId,
1041
- layoutAttributeName
1042
- });
1043
- debugLogWithTag('EMBEDDED_LAYOUT', `Retrieved embedded widget layout for container: ${containerWidgetId}`);
1044
- return {
1045
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1046
- };
1047
- }
1048
- catch (error) {
1049
- return {
1050
- content: [{ type: "text", text: `Error retrieving embedded widget layout: ${error instanceof Error ? error.message : String(error)}` }]
1051
- };
1052
- }
1053
- });
1054
- server.registerTool("cplace_add_widget_to_embedded_layout", {
1055
- 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.",
1056
- inputSchema: {
1057
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the container widget"),
1058
- containerWidgetId: z.string().describe("The ID of the container widget that contains the embedded layout to modify"),
1059
- layoutAttributeName: z.string().describe("The name of the attribute that defines the embedded layout structure"),
1060
- widgetType: z.string().describe("Widget type identifier (e.g., 'cf.cplace.platform.table', 'cf.platform.wiki')"),
1061
- position: z.object({
1062
- rowIndex: z.number().min(0).describe("Row index in the embedded layout (0-based)"),
1063
- columnIndex: z.number().min(0).describe("Column index within the row (0-based)"),
1064
- widgetIndex: z.number().min(0).describe("Widget index within the column (0-based)")
1065
- }).describe("Position where the widget should be added in the embedded layout"),
1066
- configuration: z.record(z.any()).optional().describe(`Widget configuration as key-value pairs. IMPORTANT: Check the widget definition schema first to understand required data types:
1067
- - localizedString attributes: use {"en": "English text", "de": "German text"} format
1068
- - boolean attributes: use true/false
1069
- - number attributes: use numeric values
1070
- - string attributes: use simple strings
1071
-
1072
- RECOMMENDED WORKFLOW:
1073
- 1. Use cplace_get_widget_definition() first to understand the configuration schema
1074
- 2. Match your configuration values to the constraint types defined in the schema
1075
- 3. Use appropriate data formats for each attribute type
1076
-
1077
- Examples: {'title': {'en': 'Embedded Widget', 'de': 'Eingebettetes Widget'}, 'showHeader': true}`)
1078
- },
1079
- annotations: { title: "Add Widget to Embedded Layout" }
1080
- }, async ({ pageUID, containerWidgetId, layoutAttributeName, widgetType, position, configuration }) => {
1081
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Starting add widget to embedded layout: ${widgetType} to container ${containerWidgetId} in page ${pageUID}`);
1082
- try {
1083
- const operation = {
1084
- type: "ADD",
1085
- widgetType,
1086
- position
1087
- };
1088
- if (configuration && Object.keys(configuration).length > 0) {
1089
- operation.configuration = configuration;
1090
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Including configuration: ${JSON.stringify(Object.keys(configuration))}`);
1091
- }
1092
- const requestBody = {
1093
- pageUID,
1094
- containerWidgetId,
1095
- layoutAttributeName,
1096
- operation
1097
- };
1098
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
1099
- const result = await client.makeApiRequest('json/embeddedWidgetLayout', 'PATCH', undefined, requestBody);
1100
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Successfully added widget ${widgetType} to embedded layout`);
1101
- return {
1102
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1103
- };
1104
- }
1105
- catch (error) {
1106
- debugLogWithTag('EMBEDDED_LAYOUT_ADD', `Error adding widget to embedded layout: ${error instanceof Error ? error.message : String(error)}`);
1107
- return {
1108
- content: [{ type: "text", text: `Error adding widget to embedded layout: ${error instanceof Error ? error.message : String(error)}` }]
1109
- };
1110
- }
1111
- });
1112
- server.registerTool("cplace_remove_widget_from_embedded_layout", {
1113
- 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.",
1114
- inputSchema: {
1115
- pageUID: z.string().describe("The unique identifier (UID) of the page containing the container widget"),
1116
- containerWidgetId: z.string().describe("The ID of the container widget that contains the embedded layout to modify"),
1117
- layoutAttributeName: z.string().describe("The name of the attribute that defines the embedded layout structure"),
1118
- widgetId: z.string().describe("The ID of the existing widget to remove from the embedded layout")
1119
- },
1120
- annotations: { title: "Remove Widget from Embedded Layout" }
1121
- }, async ({ pageUID, containerWidgetId, layoutAttributeName, widgetId }) => {
1122
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Starting remove widget from embedded layout: ${widgetId} from container ${containerWidgetId} in page ${pageUID}`);
1123
- try {
1124
- const requestBody = {
1125
- pageUID,
1126
- containerWidgetId,
1127
- layoutAttributeName,
1128
- operation: {
1129
- type: "REMOVE",
1130
- widgetId
1131
- }
1132
- };
1133
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Request body: ${JSON.stringify(requestBody, null, 2)}`);
1134
- const result = await client.makeApiRequest('json/embeddedWidgetLayout', 'PATCH', undefined, requestBody);
1135
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Successfully removed widget ${widgetId} from embedded layout`);
1136
- return {
1137
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1138
- };
1139
- }
1140
- catch (error) {
1141
- debugLogWithTag('EMBEDDED_LAYOUT_REMOVE', `Error removing widget from embedded layout: ${error instanceof Error ? error.message : String(error)}`);
1142
- return {
1143
- content: [{ type: "text", text: `Error removing widget from embedded layout: ${error instanceof Error ? error.message : String(error)}` }]
1144
- };
1145
- }
63
+ }, null, 2)
64
+ }]
65
+ };
1146
66
  });
1147
67
  async function runServer() {
1148
68
  const transport = new StdioServerTransport();
1149
69
  await server.connect(transport);
1150
- console.error("cplace MCP Server running on stdio");
70
+ debugLogWithTag('MCP', 'Server running on stdio');
1151
71
  }
1152
72
  runServer().catch((error) => {
1153
- console.error("Fatal error running server:", error);
73
+ debugLogWithTag('MCP', `Fatal error running server: ${error}`);
1154
74
  process.exit(1);
1155
75
  });
1156
76
  //# sourceMappingURL=index.js.map