@cplace/test-mcp-server 0.1.8 → 0.1.10

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 (56) hide show
  1. package/README.md +51 -6
  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/index.js +18 -949
  7. package/dist/index.js.map +1 -1
  8. package/dist/tools/common-schemas.d.ts +228 -0
  9. package/dist/tools/common-schemas.d.ts.map +1 -0
  10. package/dist/tools/common-schemas.js +68 -0
  11. package/dist/tools/common-schemas.js.map +1 -0
  12. package/dist/tools/pages.d.ts +4 -0
  13. package/dist/tools/pages.d.ts.map +1 -0
  14. package/dist/tools/pages.js +198 -0
  15. package/dist/tools/pages.js.map +1 -0
  16. package/dist/tools/references.d.ts +4 -0
  17. package/dist/tools/references.d.ts.map +1 -0
  18. package/dist/tools/references.js +57 -0
  19. package/dist/tools/references.js.map +1 -0
  20. package/dist/tools/search.d.ts +4 -0
  21. package/dist/tools/search.d.ts.map +1 -0
  22. package/dist/tools/search.js +304 -0
  23. package/dist/tools/search.js.map +1 -0
  24. package/dist/tools/system.d.ts +3 -0
  25. package/dist/tools/system.d.ts.map +1 -0
  26. package/dist/tools/system.js +22 -0
  27. package/dist/tools/system.js.map +1 -0
  28. package/dist/tools/type-layouts.d.ts +4 -0
  29. package/dist/tools/type-layouts.d.ts.map +1 -0
  30. package/dist/tools/type-layouts.js +56 -0
  31. package/dist/tools/type-layouts.js.map +1 -0
  32. package/dist/tools/users.d.ts +4 -0
  33. package/dist/tools/users.d.ts.map +1 -0
  34. package/dist/tools/users.js +62 -0
  35. package/dist/tools/users.js.map +1 -0
  36. package/dist/tools/version-history.d.ts +4 -0
  37. package/dist/tools/version-history.d.ts.map +1 -0
  38. package/dist/tools/version-history.js +142 -0
  39. package/dist/tools/version-history.js.map +1 -0
  40. package/dist/tools/widgets.d.ts +4 -0
  41. package/dist/tools/widgets.d.ts.map +1 -0
  42. package/dist/tools/widgets.js +516 -0
  43. package/dist/tools/widgets.js.map +1 -0
  44. package/dist/tools/workspace.d.ts +4 -0
  45. package/dist/tools/workspace.d.ts.map +1 -0
  46. package/dist/tools/workspace.js +118 -0
  47. package/dist/tools/workspace.js.map +1 -0
  48. package/dist/types.d.ts +2 -22
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/types.js +2 -9
  51. package/dist/types.js.map +1 -1
  52. package/dist/utils.d.ts +14 -2
  53. package/dist/utils.d.ts.map +1 -1
  54. package/dist/utils.js +66 -12
  55. package/dist/utils.js.map +1 -1
  56. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,967 +2,36 @@
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 { registerWorkspaceTools } from './tools/workspace.js';
7
+ import { registerPageTools } from './tools/pages.js';
8
+ import { registerSearchTools } from './tools/search.js';
9
+ import { registerUserTools } from './tools/users.js';
10
+ import { registerReferenceTools } from './tools/references.js';
11
+ import { registerWidgetTools } from './tools/widgets.js';
12
+ import { registerTypeLayoutTools } from './tools/type-layouts.js';
13
+ import { registerSystemTools } from './tools/system.js';
14
+ import { registerVersionHistoryTools } from './tools/version-history.js';
13
15
  dotenv.config();
14
16
  const config = {
15
17
  url: process.env.CPLACE_URL || '',
16
18
  apiToken: process.env.API_TOKEN || '',
17
19
  registerSystemInfoTool: process.env.REGISTER_SYSTEM_INFO_TOOL === 'true' || false,
18
- registerLayoutTools: process.env.REGISTER_LAYOUT_TOOLS === 'true' || false,
19
20
  };
20
21
  const client = new CplaceApiClient(config);
21
22
  const server = new McpServer({
22
23
  name: "cplace-mcp-server",
23
24
  version: "1.0.0"
24
25
  });
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
- }
43
- server.registerTool("cplace_list_workspaces", {
44
- description: "Get a list of all workspaces with essential properties (id, name, displayName, totalPages, isFavorite, installed apps)",
45
- inputSchema: {},
46
- annotations: { title: "List Workspaces" }
47
- }, async () => {
48
- debugLogWithTag('WORKSPACES', 'Starting workspace list request');
49
- try {
50
- const workspaces = await client.makeApiRequest('json/workspaces');
51
- const filteredWorkspaces = workspaces.map((workspace) => ({
52
- id: workspace.id,
53
- name: workspace.name,
54
- displayName: workspace.displayName,
55
- totalPages: workspace.totalPages,
56
- isFavorite: workspace.isFavorite,
57
- installedApps: workspace.apps?.installed?.map(app => app.name) || []
58
- }));
59
- debugLogWithTag('WORKSPACES', `Retrieved ${filteredWorkspaces.length} workspaces`);
60
- return {
61
- content: [{ type: "text", text: JSON.stringify(filteredWorkspaces, null, 2) }]
62
- };
63
- }
64
- catch (error) {
65
- return {
66
- content: [{ type: "text", text: `Error retrieving workspaces: ${error instanceof Error ? error.message : String(error)}` }]
67
- };
68
- }
69
- });
70
- server.registerTool("cplace_get_types_in_workspace", {
71
- description: "Get a list of all types available in a certain workspace",
72
- inputSchema: {
73
- workspaceId: z.string().describe("The ID of the workspace to get types for")
74
- },
75
- annotations: { title: "Get Types in Workspace" }
76
- }, async ({ workspaceId }) => {
77
- try {
78
- const workspaces = await client.makeApiRequest('json/workspaces');
79
- const workspace = workspaces.find((w) => w.id === workspaceId);
80
- if (workspace) {
81
- const types = workspace.typeDefinitions?.types || [];
82
- return {
83
- content: [{ type: "text", text: JSON.stringify(types, null, 2) }]
84
- };
85
- }
86
- return {
87
- content: [{ type: "text", text: `Workspace ${workspaceId} not found` }]
88
- };
89
- }
90
- catch (error) {
91
- return {
92
- content: [{ type: "text", text: `Error retrieving types: ${error instanceof Error ? error.message : String(error)}` }]
93
- };
94
- }
95
- });
96
- server.registerTool("cplace_get_type_datamodel", {
97
- description: "Get the datamodel of a type including its attributes, constraints, and permissions. Use this before performing a search to understand the type structure.",
98
- inputSchema: {
99
- workspaceId: z.string().describe("The ID of the workspace"),
100
- internalName: z.string().describe("The internal name of the type")
101
- },
102
- annotations: { title: "Get Type Details" }
103
- }, async ({ workspaceId, internalName }) => {
104
- try {
105
- const result = await client.makeApiRequest('json/type-definition', 'GET', {
106
- workspaceId,
107
- internalName
108
- });
109
- return {
110
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
111
- };
112
- }
113
- catch (error) {
114
- return {
115
- content: [{ type: "text", text: `Error retrieving type details: ${error instanceof Error ? error.message : String(error)}` }]
116
- };
117
- }
118
- });
119
- server.registerTool("cplace_get_page_by_id", {
120
- description: "Get comprehensive page information including relationships and metadata",
121
- inputSchema: {
122
- id: z.string().describe("The ID of the page to get, e.g. 'page/kkt8ol745jqur4581kelm5ply'")
123
- },
124
- annotations: { title: "Get Page by ID" }
125
- }, async ({ id }) => {
126
- try {
127
- const result = await client.makeApiRequest('json/page', 'GET', { pageUID: id });
128
- return {
129
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
130
- };
131
- }
132
- catch (error) {
133
- return {
134
- content: [{ type: "text", text: `Error retrieving page: ${error instanceof Error ? error.message : String(error)}` }]
135
- };
136
- }
137
- });
138
- server.registerTool("cplace_get_person_by_id", {
139
- description: "Get details about a person including their name and email.",
140
- inputSchema: {
141
- id: z.string().describe("The ID of the person to get, e.g. 'person/kkt8ol745jqur4581kelm5ply'")
142
- },
143
- annotations: { title: "Get Person by ID" }
144
- }, async ({ id }) => {
145
- try {
146
- const result = await client.makeApiRequest('json/person', 'GET', { id });
147
- return {
148
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
149
- };
150
- }
151
- catch (error) {
152
- return {
153
- content: [{ type: "text", text: `Error retrieving person: ${error instanceof Error ? error.message : String(error)}` }]
154
- };
155
- }
156
- });
157
- server.registerTool("cplace_search_person", {
158
- description: "Search a person by their name.",
159
- inputSchema: {
160
- name: z.string().describe("The name of the person or part of the name to search for"),
161
- },
162
- annotations: { title: "Get Person by ID" }
163
- }, async ({ name }) => {
164
- try {
165
- const result = await client.makeApiRequest('json/person', 'GET', { name });
166
- return {
167
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
168
- };
169
- }
170
- catch (error) {
171
- return {
172
- content: [{ type: "text", text: `Error searching for person: ${error instanceof Error ? error.message : String(error)}` }]
173
- };
174
- }
175
- });
176
- server.registerTool("cplace_search_pages", {
177
- description: "Search pages of a type in cplace.",
178
- inputSchema: {
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}}]`),
233
- limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
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']"),
240
- },
241
- annotations: { title: "Search Pages" }
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}`);
244
- try {
245
- const cplaceFilter = convertSearchFilterToCplaceFormat(search_filter);
246
- debugLogWithTag('SEARCH', `Converted filter: ${JSON.stringify(cplaceFilter)}`);
247
- if (workspaceId) {
248
- cplaceFilter.filters.unshift({
249
- type: "Workspace",
250
- workspaceIds: [workspaceId]
251
- });
252
- }
253
- if (internalTypeName) {
254
- cplaceFilter.filters.unshift({
255
- type: "Type",
256
- types: [internalTypeName]
257
- });
258
- }
259
- debugLogWithTag('SEARCH', `Final cplace filter: ${JSON.stringify(cplaceFilter)}`);
260
- const result = await client.makeApiRequest('json/search', 'GET', {
261
- filter: JSON.stringify(cplaceFilter),
262
- limit,
263
- offset
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
- }
273
- const filteredResults = result.results ? result.results.map((item) => filterSearchResult(item, {
274
- minimal: responseFormat === "minimal",
275
- includeAttributes: responseFormat !== "minimal",
276
- includeMetadata: false,
277
- attributeFields: attributes,
278
- truncateLargeValues: true
279
- })) : [];
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
- };
290
- return {
291
- content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
292
- };
293
- }
294
- catch (error) {
295
- debugLogWithTag('SEARCH', `Error during search: ${error instanceof Error ? error.message : String(error)}`);
296
- return {
297
- content: [{ type: "text", text: `Error converting or executing search: ${error instanceof Error ? error.message : String(error)}` }]
298
- };
299
- }
300
- });
301
- server.registerTool("cplace_fulltext_search", {
302
- description: "Make a fulltext search across all pages in cplace. Use this only if the user is not asking about specific types.",
303
- inputSchema: {
304
- workspaceId: z.string().optional().describe("The ID of the workspace to search in. If not provided, searches across all accessible workspaces."),
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."),
306
- limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
307
- offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
308
- },
309
- annotations: { title: "Search Pages" }
310
- }, async ({ workspaceId, fulltext, limit = 50, offset = 0, }) => {
311
- if (!fulltext) {
312
- return {
313
- content: [{ type: "text", text: "fulltext is required" }]
314
- };
315
- }
316
- try {
317
- const result = await client.makeApiRequest('json/fulltext-search', 'GET', {
318
- spaceId: workspaceId,
319
- fulltext: `"${fulltext}"`,
320
- limit,
321
- offset
322
- });
323
- const filteredResults = result.results ? result.results.map((item) => filterSearchResult(item, {
324
- minimal: false,
325
- includeAttributes: true,
326
- includeMetadata: false,
327
- truncateLargeValues: true
328
- })) : [];
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
- };
339
- return {
340
- content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
341
- };
342
- }
343
- catch (error) {
344
- return {
345
- content: [{ type: "text", text: `Error converting or executing search: ${error instanceof Error ? error.message : String(error)}` }]
346
- };
347
- }
348
- });
349
- server.registerTool("cplace_get_all_pages_of_type", {
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.",
351
- inputSchema: {
352
- workspaceId: z.string().describe("The ID of the workspace to get pages from"),
353
- typeName: z.string().describe("The internal name of the type to retrieve pages for"),
354
- limit: z.number().min(1).max(1000).default(50).describe("Maximum number of results to return (1-1000)"),
355
- offset: z.number().min(0).max(10000).default(0).describe("Number of results to skip for pagination, cannot be larger than 10000"),
356
- },
357
- annotations: { title: "Get All Pages of Type" }
358
- }, async ({ workspaceId, typeName, limit = 50, offset = 0, }) => {
359
- try {
360
- const cplaceFilter = {
361
- filters: [
362
- {
363
- type: "Workspace",
364
- workspaceIds: [workspaceId]
365
- },
366
- {
367
- type: "Type",
368
- types: [typeName]
369
- }
370
- ]
371
- };
372
- const result = await client.makeApiRequest('json/search', 'GET', {
373
- filter: JSON.stringify(cplaceFilter),
374
- limit,
375
- offset
376
- });
377
- const filteredResults = result.results ? result.results.map((item) => filterSearchResult(item, {
378
- minimal: false,
379
- includeAttributes: true,
380
- includeMetadata: false,
381
- truncateLargeValues: true
382
- })) : [];
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
- };
393
- return {
394
- content: [{ type: "text", text: JSON.stringify(filteredResult, null, 2) }]
395
- };
396
- }
397
- catch (error) {
398
- return {
399
- content: [{ type: "text", text: `Error retrieving pages: ${error instanceof Error ? error.message : String(error)}` }]
400
- };
401
- }
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
- }
26
+ registerWorkspaceTools(server, client);
27
+ registerPageTools(server, client);
28
+ registerSearchTools(server, client);
29
+ registerUserTools(server, client);
30
+ registerReferenceTools(server, client);
31
+ registerWidgetTools(server, client);
32
+ registerTypeLayoutTools(server, client);
33
+ registerVersionHistoryTools(server, client);
34
+ registerSystemTools(server, config);
966
35
  async function runServer() {
967
36
  const transport = new StdioServerTransport();
968
37
  await server.connect(transport);