@centrali-io/centrali-mcp 4.2.7 → 4.2.9

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.
@@ -155,11 +155,22 @@ function registerComputeTools(server, sdk) {
155
155
  }));
156
156
  server.tool("create_function", "Create a new compute function. Compute functions are JavaScript code blocks that run server-side.", {
157
157
  name: zod_1.z.string().describe("Display name for the function"),
158
- code: zod_1.z.string().describe("JavaScript source code. Must export an async function: module.exports = async (ctx) => { ... }"),
158
+ code: zod_1.z.string().describe("JavaScript source code. Must define 'async function run() { ... }'. Globals: api, triggerParams, executionParams. Do NOT use module.exports."),
159
159
  description: zod_1.z.string().optional().describe("Optional description of what the function does"),
160
- timeoutMs: zod_1.z.number().optional().describe("Execution timeout in milliseconds (default: 30000, min: 1000, max: 300000)"),
160
+ timeoutMs: zod_1.z.number().optional().describe("Execution timeout in milliseconds (default: 30000, max: 300000 / 5 minutes)"),
161
161
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ name, code, description, timeoutMs }) {
162
162
  try {
163
+ if (/module\.exports\s*=/.test(code)) {
164
+ return {
165
+ content: [
166
+ {
167
+ type: "text",
168
+ text: "Error: Do not use module.exports. Functions must define 'async function run() { ... }'. Globals available: api, triggerParams, executionParams.",
169
+ },
170
+ ],
171
+ isError: true,
172
+ };
173
+ }
163
174
  const input = { name, code };
164
175
  if (description !== undefined)
165
176
  input.description = description;
@@ -188,10 +199,21 @@ function registerComputeTools(server, sdk) {
188
199
  functionId: zod_1.z.string().describe("The compute function ID (UUID) to update"),
189
200
  name: zod_1.z.string().optional().describe("Updated display name"),
190
201
  description: zod_1.z.string().optional().describe("Updated description"),
191
- code: zod_1.z.string().optional().describe("Updated JavaScript source code"),
192
- timeoutMs: zod_1.z.number().optional().describe("Updated execution timeout in milliseconds (min: 1000, max: 300000)"),
202
+ code: zod_1.z.string().optional().describe("Updated JavaScript source code. Must define 'async function run() { ... }'. Do NOT use module.exports."),
203
+ timeoutMs: zod_1.z.number().optional().describe("Updated execution timeout in milliseconds (max: 300000 / 5 minutes)"),
193
204
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ functionId, name, description, code, timeoutMs }) {
194
205
  try {
206
+ if (code && /module\.exports\s*=/.test(code)) {
207
+ return {
208
+ content: [
209
+ {
210
+ type: "text",
211
+ text: "Error: Do not use module.exports. Functions must define 'async function run() { ... }'. Globals available: api, triggerParams, executionParams.",
212
+ },
213
+ ],
214
+ isError: true,
215
+ };
216
+ }
195
217
  const input = {};
196
218
  if (name !== undefined)
197
219
  input.name = name;
@@ -247,14 +269,25 @@ function registerComputeTools(server, sdk) {
247
269
  }
248
270
  }));
249
271
  server.tool("test_function", "Test execute code without saving it as a function. Useful for validating function code before creating or updating.", {
250
- code: zod_1.z.string().describe("JavaScript code to test. Must export an async function: module.exports = async (ctx) => { ... }"),
272
+ code: zod_1.z.string().describe("JavaScript code to test. Must define 'async function run() { ... }'. Globals: api, triggerParams, executionParams. Do NOT use module.exports."),
251
273
  params: zod_1.z
252
274
  .record(zod_1.z.string(), zod_1.z.any())
253
275
  .optional()
254
276
  .describe("Optional parameters passed to the function as executionParams"),
255
- timeoutMs: zod_1.z.number().optional().describe("Execution timeout in milliseconds (default: 30000, min: 1000, max: 300000)"),
277
+ timeoutMs: zod_1.z.number().optional().describe("Execution timeout in milliseconds (default: 30000, max: 300000 / 5 minutes)"),
256
278
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ code, params, timeoutMs }) {
257
279
  try {
280
+ if (/module\.exports\s*=/.test(code)) {
281
+ return {
282
+ content: [
283
+ {
284
+ type: "text",
285
+ text: "Error: Do not use module.exports. Functions must define 'async function run() { ... }'. Globals available: api, triggerParams, executionParams.",
286
+ },
287
+ ],
288
+ isError: true,
289
+ };
290
+ }
258
291
  const testInput = { code };
259
292
  if (params !== undefined)
260
293
  testInput.params = params;
@@ -432,7 +432,7 @@ function registerDescribeTools(server) {
432
432
  description: "Create a new compute function",
433
433
  required_params: ["name", "code"],
434
434
  optional_params: ["description", "timeoutMs"],
435
- code_format: "module.exports = async (ctx) => { /* your code */ return result; }",
435
+ code_format: "async function run() { /* api, triggerParams, executionParams are globals */ return result; }",
436
436
  },
437
437
  update_function: {
438
438
  description: "Update an existing function. Partial updates supported.",
@@ -893,12 +893,27 @@ function registerDescribeTools(server) {
893
893
  inherit: "boolean — if true, inherits the workspace theme. Usually set to true.",
894
894
  },
895
895
  },
896
+ draft_vs_publish: {
897
+ description: "Drafts are lenient — most fields are optional and will default. Publishing validates strictly. Always provide the fields below to avoid 400 errors.",
898
+ always_required_even_in_drafts: [
899
+ "sections[*].id — MUST be a valid UUID",
900
+ "sections[*].blocks — array (can be empty)",
901
+ "blocks[*].id — MUST be a valid UUID",
902
+ ],
903
+ required_for_publish: [
904
+ "sections[*].kind — enum (see section_kinds)",
905
+ "sections[*].title — non-empty string",
906
+ "sections[*].layout — enum (see section_layouts)",
907
+ "blocks[*].blockType — non-empty string",
908
+ "theme.inherit — boolean",
909
+ ],
910
+ },
896
911
  section_shape: {
897
- id: "string — unique ID for the section (use a UUID or descriptive slug like 'metrics-strip')",
898
- kind: "'content' | 'metrics' | 'exceptions' | 'actions' | 'hero' | 'supporting'",
899
- title: "string — section heading displayed to users",
900
- layout: "'single-column' | 'two-column' | 'metric-strip' | 'stack'",
901
- blocks: "PageBlock[] — array of blocks in this section",
912
+ id: "string — MUST be a valid UUID (e.g., crypto.randomUUID()). Non-UUID strings will cause 400 errors.",
913
+ kind: "'content' | 'metrics' | 'exceptions' | 'actions' | 'hero' | 'supporting' — optional in drafts, required for publish",
914
+ title: "string — section heading displayed to users. Optional in drafts, required (non-empty) for publish.",
915
+ layout: "'single-column' | 'two-column' | 'metric-strip' | 'stack' — optional in drafts, required for publish",
916
+ blocks: "PageBlock[] — array of blocks in this section (required, can be empty)",
902
917
  },
903
918
  section_kinds: {
904
919
  hero: "Top of page — typically a title/summary or key metric. Use for detail pages.",
@@ -915,8 +930,8 @@ function registerDescribeTools(server) {
915
930
  stack: "Compact vertical stack",
916
931
  },
917
932
  block_shape: {
918
- id: "string — unique ID for the block (use a UUID)",
919
- blockType: "string — the type of UI component (see describe_page_blocks for all types)",
933
+ id: "string — MUST be a valid UUID. Non-UUID strings will cause 400 errors.",
934
+ blockType: "string — the type of UI component (see describe_page_blocks for all types). Optional in drafts, required (non-empty) for publish.",
920
935
  dataSource: "DataSource | null — where this block gets its data (see below)",
921
936
  presentation: "BlockPresentation | null — display options (density, columns, chart type, emphasis rules)",
922
937
  actions: "BlockAction[] | null — interactive actions (buttons, row clicks, form submissions). See describe_page_actions.",
@@ -954,16 +969,17 @@ function registerDescribeTools(server) {
954
969
  comparison: "{ type: 'previous-period' | 'target' | 'baseline', value: number } — for metrics comparison",
955
970
  nextActionLabel: "string — label for a suggested next action (e.g., 'View all orders')",
956
971
  },
972
+ IMPORTANT_all_ids_must_be_uuids: "Section IDs and block IDs MUST be valid UUIDs (e.g., 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'). Using slugs like 'main-content' will cause a 400 error.",
957
973
  example_list_page_definition: {
958
974
  sections: [
959
975
  {
960
- id: "main-content",
976
+ id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
961
977
  kind: "content",
962
978
  title: "All Customers",
963
979
  layout: "single-column",
964
980
  blocks: [
965
981
  {
966
- id: "customer-table",
982
+ id: "d290f1ee-6c54-4b01-90e6-d701748f0851",
967
983
  blockType: "data-table",
968
984
  dataSource: {
969
985
  type: "structure",
@@ -991,13 +1007,13 @@ function registerDescribeTools(server) {
991
1007
  example_dashboard_definition: {
992
1008
  sections: [
993
1009
  {
994
- id: "kpi-strip",
1010
+ id: "c73bcdcc-2669-4bf6-81d3-e4ae73fb11fd",
995
1011
  kind: "metrics",
996
1012
  title: "Key Metrics",
997
1013
  layout: "metric-strip",
998
1014
  blocks: [
999
1015
  {
1000
- id: "total-orders",
1016
+ id: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
1001
1017
  blockType: "metric-card",
1002
1018
  dataSource: {
1003
1019
  type: "structure",
@@ -1013,7 +1029,7 @@ function registerDescribeTools(server) {
1013
1029
  },
1014
1030
  },
1015
1031
  {
1016
- id: "total-revenue",
1032
+ id: "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
1017
1033
  blockType: "metric-card",
1018
1034
  dataSource: {
1019
1035
  type: "structure",
@@ -1033,13 +1049,13 @@ function registerDescribeTools(server) {
1033
1049
  ],
1034
1050
  },
1035
1051
  {
1036
- id: "charts",
1052
+ id: "6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b",
1037
1053
  kind: "content",
1038
1054
  title: "Trends",
1039
1055
  layout: "two-column",
1040
1056
  blocks: [
1041
1057
  {
1042
- id: "orders-by-status",
1058
+ id: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
1043
1059
  blockType: "chart",
1044
1060
  dataSource: {
1045
1061
  type: "structure",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centrali-io/centrali-mcp",
3
- "version": "4.2.7",
3
+ "version": "4.2.9",
4
4
  "description": "Centrali MCP Server - AI assistant integration for Centrali workspaces",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
@@ -174,12 +174,24 @@ export function registerComputeTools(server: McpServer, sdk: CentraliSDK) {
174
174
  "Create a new compute function. Compute functions are JavaScript code blocks that run server-side.",
175
175
  {
176
176
  name: z.string().describe("Display name for the function"),
177
- code: z.string().describe("JavaScript source code. Must export an async function: module.exports = async (ctx) => { ... }"),
177
+ code: z.string().describe("JavaScript source code. Must define 'async function run() { ... }'. Globals: api, triggerParams, executionParams. Do NOT use module.exports."),
178
178
  description: z.string().optional().describe("Optional description of what the function does"),
179
- timeoutMs: z.number().optional().describe("Execution timeout in milliseconds (default: 30000, min: 1000, max: 300000)"),
179
+ timeoutMs: z.number().optional().describe("Execution timeout in milliseconds (default: 30000, max: 300000 / 5 minutes)"),
180
180
  },
181
181
  async ({ name, code, description, timeoutMs }) => {
182
182
  try {
183
+ if (/module\.exports\s*=/.test(code)) {
184
+ return {
185
+ content: [
186
+ {
187
+ type: "text",
188
+ text: "Error: Do not use module.exports. Functions must define 'async function run() { ... }'. Globals available: api, triggerParams, executionParams.",
189
+ },
190
+ ],
191
+ isError: true,
192
+ };
193
+ }
194
+
183
195
  const input: Record<string, any> = { name, code };
184
196
  if (description !== undefined) input.description = description;
185
197
  if (timeoutMs !== undefined) input.timeoutMs = timeoutMs;
@@ -211,11 +223,23 @@ export function registerComputeTools(server: McpServer, sdk: CentraliSDK) {
211
223
  functionId: z.string().describe("The compute function ID (UUID) to update"),
212
224
  name: z.string().optional().describe("Updated display name"),
213
225
  description: z.string().optional().describe("Updated description"),
214
- code: z.string().optional().describe("Updated JavaScript source code"),
215
- timeoutMs: z.number().optional().describe("Updated execution timeout in milliseconds (min: 1000, max: 300000)"),
226
+ code: z.string().optional().describe("Updated JavaScript source code. Must define 'async function run() { ... }'. Do NOT use module.exports."),
227
+ timeoutMs: z.number().optional().describe("Updated execution timeout in milliseconds (max: 300000 / 5 minutes)"),
216
228
  },
217
229
  async ({ functionId, name, description, code, timeoutMs }) => {
218
230
  try {
231
+ if (code && /module\.exports\s*=/.test(code)) {
232
+ return {
233
+ content: [
234
+ {
235
+ type: "text",
236
+ text: "Error: Do not use module.exports. Functions must define 'async function run() { ... }'. Globals available: api, triggerParams, executionParams.",
237
+ },
238
+ ],
239
+ isError: true,
240
+ };
241
+ }
242
+
219
243
  const input: Record<string, any> = {};
220
244
  if (name !== undefined) input.name = name;
221
245
  if (description !== undefined) input.description = description;
@@ -277,15 +301,27 @@ export function registerComputeTools(server: McpServer, sdk: CentraliSDK) {
277
301
  "test_function",
278
302
  "Test execute code without saving it as a function. Useful for validating function code before creating or updating.",
279
303
  {
280
- code: z.string().describe("JavaScript code to test. Must export an async function: module.exports = async (ctx) => { ... }"),
304
+ code: z.string().describe("JavaScript code to test. Must define 'async function run() { ... }'. Globals: api, triggerParams, executionParams. Do NOT use module.exports."),
281
305
  params: z
282
306
  .record(z.string(), z.any())
283
307
  .optional()
284
308
  .describe("Optional parameters passed to the function as executionParams"),
285
- timeoutMs: z.number().optional().describe("Execution timeout in milliseconds (default: 30000, min: 1000, max: 300000)"),
309
+ timeoutMs: z.number().optional().describe("Execution timeout in milliseconds (default: 30000, max: 300000 / 5 minutes)"),
286
310
  },
287
311
  async ({ code, params, timeoutMs }) => {
288
312
  try {
313
+ if (/module\.exports\s*=/.test(code)) {
314
+ return {
315
+ content: [
316
+ {
317
+ type: "text",
318
+ text: "Error: Do not use module.exports. Functions must define 'async function run() { ... }'. Globals available: api, triggerParams, executionParams.",
319
+ },
320
+ ],
321
+ isError: true,
322
+ };
323
+ }
324
+
289
325
  const testInput: Record<string, any> = { code };
290
326
  if (params !== undefined) testInput.params = params;
291
327
  if (timeoutMs !== undefined) testInput.timeoutMs = timeoutMs;
@@ -502,7 +502,7 @@ export function registerDescribeTools(server: McpServer) {
502
502
  description: "Create a new compute function",
503
503
  required_params: ["name", "code"],
504
504
  optional_params: ["description", "timeoutMs"],
505
- code_format: "module.exports = async (ctx) => { /* your code */ return result; }",
505
+ code_format: "async function run() { /* api, triggerParams, executionParams are globals */ return result; }",
506
506
  },
507
507
  update_function: {
508
508
  description: "Update an existing function. Partial updates supported.",
@@ -1055,13 +1055,28 @@ export function registerDescribeTools(server: McpServer) {
1055
1055
  "boolean — if true, inherits the workspace theme. Usually set to true.",
1056
1056
  },
1057
1057
  },
1058
+ draft_vs_publish: {
1059
+ description: "Drafts are lenient — most fields are optional and will default. Publishing validates strictly. Always provide the fields below to avoid 400 errors.",
1060
+ always_required_even_in_drafts: [
1061
+ "sections[*].id — MUST be a valid UUID",
1062
+ "sections[*].blocks — array (can be empty)",
1063
+ "blocks[*].id — MUST be a valid UUID",
1064
+ ],
1065
+ required_for_publish: [
1066
+ "sections[*].kind — enum (see section_kinds)",
1067
+ "sections[*].title — non-empty string",
1068
+ "sections[*].layout — enum (see section_layouts)",
1069
+ "blocks[*].blockType — non-empty string",
1070
+ "theme.inherit — boolean",
1071
+ ],
1072
+ },
1058
1073
  section_shape: {
1059
- id: "string — unique ID for the section (use a UUID or descriptive slug like 'metrics-strip')",
1060
- kind: "'content' | 'metrics' | 'exceptions' | 'actions' | 'hero' | 'supporting'",
1061
- title: "string — section heading displayed to users",
1074
+ id: "string — MUST be a valid UUID (e.g., crypto.randomUUID()). Non-UUID strings will cause 400 errors.",
1075
+ kind: "'content' | 'metrics' | 'exceptions' | 'actions' | 'hero' | 'supporting' — optional in drafts, required for publish",
1076
+ title: "string — section heading displayed to users. Optional in drafts, required (non-empty) for publish.",
1062
1077
  layout:
1063
- "'single-column' | 'two-column' | 'metric-strip' | 'stack'",
1064
- blocks: "PageBlock[] — array of blocks in this section",
1078
+ "'single-column' | 'two-column' | 'metric-strip' | 'stack' — optional in drafts, required for publish",
1079
+ blocks: "PageBlock[] — array of blocks in this section (required, can be empty)",
1065
1080
  },
1066
1081
  section_kinds: {
1067
1082
  hero: "Top of page — typically a title/summary or key metric. Use for detail pages.",
@@ -1084,9 +1099,9 @@ export function registerDescribeTools(server: McpServer) {
1084
1099
  stack: "Compact vertical stack",
1085
1100
  },
1086
1101
  block_shape: {
1087
- id: "string — unique ID for the block (use a UUID)",
1102
+ id: "string — MUST be a valid UUID. Non-UUID strings will cause 400 errors.",
1088
1103
  blockType:
1089
- "string — the type of UI component (see describe_page_blocks for all types)",
1104
+ "string — the type of UI component (see describe_page_blocks for all types). Optional in drafts, required (non-empty) for publish.",
1090
1105
  dataSource:
1091
1106
  "DataSource | null — where this block gets its data (see below)",
1092
1107
  presentation:
@@ -1140,16 +1155,17 @@ export function registerDescribeTools(server: McpServer) {
1140
1155
  nextActionLabel:
1141
1156
  "string — label for a suggested next action (e.g., 'View all orders')",
1142
1157
  },
1158
+ IMPORTANT_all_ids_must_be_uuids: "Section IDs and block IDs MUST be valid UUIDs (e.g., 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'). Using slugs like 'main-content' will cause a 400 error.",
1143
1159
  example_list_page_definition: {
1144
1160
  sections: [
1145
1161
  {
1146
- id: "main-content",
1162
+ id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
1147
1163
  kind: "content",
1148
1164
  title: "All Customers",
1149
1165
  layout: "single-column",
1150
1166
  blocks: [
1151
1167
  {
1152
- id: "customer-table",
1168
+ id: "d290f1ee-6c54-4b01-90e6-d701748f0851",
1153
1169
  blockType: "data-table",
1154
1170
  dataSource: {
1155
1171
  type: "structure",
@@ -1177,13 +1193,13 @@ export function registerDescribeTools(server: McpServer) {
1177
1193
  example_dashboard_definition: {
1178
1194
  sections: [
1179
1195
  {
1180
- id: "kpi-strip",
1196
+ id: "c73bcdcc-2669-4bf6-81d3-e4ae73fb11fd",
1181
1197
  kind: "metrics",
1182
1198
  title: "Key Metrics",
1183
1199
  layout: "metric-strip",
1184
1200
  blocks: [
1185
1201
  {
1186
- id: "total-orders",
1202
+ id: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
1187
1203
  blockType: "metric-card",
1188
1204
  dataSource: {
1189
1205
  type: "structure",
@@ -1199,7 +1215,7 @@ export function registerDescribeTools(server: McpServer) {
1199
1215
  },
1200
1216
  },
1201
1217
  {
1202
- id: "total-revenue",
1218
+ id: "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
1203
1219
  blockType: "metric-card",
1204
1220
  dataSource: {
1205
1221
  type: "structure",
@@ -1219,13 +1235,13 @@ export function registerDescribeTools(server: McpServer) {
1219
1235
  ],
1220
1236
  },
1221
1237
  {
1222
- id: "charts",
1238
+ id: "6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b",
1223
1239
  kind: "content",
1224
1240
  title: "Trends",
1225
1241
  layout: "two-column",
1226
1242
  blocks: [
1227
1243
  {
1228
- id: "orders-by-status",
1244
+ id: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
1229
1245
  blockType: "chart",
1230
1246
  dataSource: {
1231
1247
  type: "structure",