@centrali-io/centrali-mcp 4.2.6 → 4.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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;
@@ -454,4 +487,75 @@ function registerComputeTools(server, sdk) {
454
487
  };
455
488
  }
456
489
  }));
490
+ // ── Allowed Domains tools ──────────────────────────────────────────
491
+ server.tool("list_allowed_domains", "List all allowed domains for compute function HTTP requests. Functions can only call external APIs on domains in this allowlist.", {}, () => __awaiter(this, void 0, void 0, function* () {
492
+ try {
493
+ const result = yield sdk.allowedDomains.list();
494
+ return {
495
+ content: [
496
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
497
+ ],
498
+ };
499
+ }
500
+ catch (error) {
501
+ return {
502
+ content: [
503
+ {
504
+ type: "text",
505
+ text: formatError(error, "listing allowed domains"),
506
+ },
507
+ ],
508
+ isError: true,
509
+ };
510
+ }
511
+ }));
512
+ server.tool("add_allowed_domain", "Add a domain to the allowlist so compute functions can make HTTP requests to it. Supports wildcards (e.g., '*.example.com').", {
513
+ domain: zod_1.z.string().describe("Domain to allow (e.g., 'api.stripe.com' or '*.googleapis.com')"),
514
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ domain }) {
515
+ try {
516
+ const result = yield sdk.allowedDomains.add({ domain });
517
+ return {
518
+ content: [
519
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
520
+ ],
521
+ };
522
+ }
523
+ catch (error) {
524
+ return {
525
+ content: [
526
+ {
527
+ type: "text",
528
+ text: formatError(error, `adding allowed domain '${domain}'`),
529
+ },
530
+ ],
531
+ isError: true,
532
+ };
533
+ }
534
+ }));
535
+ server.tool("remove_allowed_domain", "Remove a domain from the allowlist. Compute functions will no longer be able to call this domain.", {
536
+ domainId: zod_1.z.string().describe("The allowed domain ID (UUID) to remove"),
537
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ domainId }) {
538
+ try {
539
+ yield sdk.allowedDomains.remove(domainId);
540
+ return {
541
+ content: [
542
+ {
543
+ type: "text",
544
+ text: `Allowed domain '${domainId}' removed successfully.`,
545
+ },
546
+ ],
547
+ };
548
+ }
549
+ catch (error) {
550
+ return {
551
+ content: [
552
+ {
553
+ type: "text",
554
+ text: formatError(error, `removing allowed domain '${domainId}'`),
555
+ },
556
+ ],
557
+ isError: true,
558
+ };
559
+ }
560
+ }));
457
561
  }
@@ -62,7 +62,7 @@ function registerDescribeTools(server) {
62
62
  tools: ["search_records"],
63
63
  },
64
64
  compute: {
65
- summary: "Server-side JavaScript functions with triggers (on-demand, event-driven, scheduled, http-trigger).",
65
+ summary: "Server-side JavaScript functions with triggers (on-demand, event-driven, scheduled, http-trigger) and domain allowlisting for external API calls.",
66
66
  describeWith: "describe_compute",
67
67
  tools: [
68
68
  "list_functions",
@@ -79,6 +79,9 @@ function registerDescribeTools(server) {
79
79
  "invoke_trigger",
80
80
  "pause_trigger",
81
81
  "resume_trigger",
82
+ "list_allowed_domains",
83
+ "add_allowed_domain",
84
+ "remove_allowed_domain",
82
85
  ],
83
86
  },
84
87
  smart_queries: {
@@ -429,7 +432,7 @@ function registerDescribeTools(server) {
429
432
  description: "Create a new compute function",
430
433
  required_params: ["name", "code"],
431
434
  optional_params: ["description", "timeoutMs"],
432
- code_format: "module.exports = async (ctx) => { /* your code */ return result; }",
435
+ code_format: "async function run() { /* api, triggerParams, executionParams are globals */ return result; }",
433
436
  },
434
437
  update_function: {
435
438
  description: "Update an existing function. Partial updates supported.",
@@ -478,6 +481,30 @@ function registerDescribeTools(server) {
478
481
  required_params: ["triggerId"],
479
482
  },
480
483
  },
484
+ allowed_domains: {
485
+ description: "Workspace-level allowlist controlling which external domains compute functions can call via api.httpGet/httpPost/etc. Functions attempting to reach unlisted domains get an error.",
486
+ domain_shape: {
487
+ id: "UUID",
488
+ domain: "string — the allowed domain (e.g., 'api.stripe.com' or '*.googleapis.com')",
489
+ createdAt: "ISO 8601 datetime",
490
+ createdBy: "UUID",
491
+ },
492
+ tools: {
493
+ list_allowed_domains: {
494
+ description: "List all allowed domains in the workspace",
495
+ },
496
+ add_allowed_domain: {
497
+ description: "Add a domain to the allowlist",
498
+ required_params: ["domain"],
499
+ },
500
+ remove_allowed_domain: {
501
+ description: "Remove a domain from the allowlist by ID",
502
+ required_params: ["domainId"],
503
+ },
504
+ },
505
+ wildcards: "Use '*.example.com' to allow all subdomains of example.com",
506
+ note: "Functions can only make HTTP requests to domains on this list. Add domains before creating functions that call external APIs.",
507
+ },
481
508
  sandbox_api: {
482
509
  description: "Functions receive an `api` object with built-in utilities. Key crypto methods:",
483
510
  crypto: {
@@ -497,6 +524,7 @@ function registerDescribeTools(server) {
497
524
  "Use test_function to validate code before creating or updating a function",
498
525
  "Use create_function + create_trigger together to set up a complete compute pipeline",
499
526
  "Use api.crypto.signJwt() for GitHub App, Google Cloud, or Azure AD authentication flows",
527
+ "Before creating functions that call external APIs, add the target domains with add_allowed_domain",
500
528
  ],
501
529
  }, null, 2),
502
530
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centrali-io/centrali-mcp",
3
- "version": "4.2.6",
3
+ "version": "4.2.8",
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;
@@ -510,4 +546,91 @@ export function registerComputeTools(server: McpServer, sdk: CentraliSDK) {
510
546
  }
511
547
  }
512
548
  );
549
+
550
+ // ── Allowed Domains tools ──────────────────────────────────────────
551
+
552
+ server.tool(
553
+ "list_allowed_domains",
554
+ "List all allowed domains for compute function HTTP requests. Functions can only call external APIs on domains in this allowlist.",
555
+ {},
556
+ async () => {
557
+ try {
558
+ const result = await sdk.allowedDomains.list();
559
+ return {
560
+ content: [
561
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
562
+ ],
563
+ };
564
+ } catch (error: unknown) {
565
+ return {
566
+ content: [
567
+ {
568
+ type: "text",
569
+ text: formatError(error, "listing allowed domains"),
570
+ },
571
+ ],
572
+ isError: true,
573
+ };
574
+ }
575
+ }
576
+ );
577
+
578
+ server.tool(
579
+ "add_allowed_domain",
580
+ "Add a domain to the allowlist so compute functions can make HTTP requests to it. Supports wildcards (e.g., '*.example.com').",
581
+ {
582
+ domain: z.string().describe("Domain to allow (e.g., 'api.stripe.com' or '*.googleapis.com')"),
583
+ },
584
+ async ({ domain }) => {
585
+ try {
586
+ const result = await sdk.allowedDomains.add({ domain });
587
+ return {
588
+ content: [
589
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
590
+ ],
591
+ };
592
+ } catch (error: unknown) {
593
+ return {
594
+ content: [
595
+ {
596
+ type: "text",
597
+ text: formatError(error, `adding allowed domain '${domain}'`),
598
+ },
599
+ ],
600
+ isError: true,
601
+ };
602
+ }
603
+ }
604
+ );
605
+
606
+ server.tool(
607
+ "remove_allowed_domain",
608
+ "Remove a domain from the allowlist. Compute functions will no longer be able to call this domain.",
609
+ {
610
+ domainId: z.string().describe("The allowed domain ID (UUID) to remove"),
611
+ },
612
+ async ({ domainId }) => {
613
+ try {
614
+ await sdk.allowedDomains.remove(domainId);
615
+ return {
616
+ content: [
617
+ {
618
+ type: "text",
619
+ text: `Allowed domain '${domainId}' removed successfully.`,
620
+ },
621
+ ],
622
+ };
623
+ } catch (error: unknown) {
624
+ return {
625
+ content: [
626
+ {
627
+ type: "text",
628
+ text: formatError(error, `removing allowed domain '${domainId}'`),
629
+ },
630
+ ],
631
+ isError: true,
632
+ };
633
+ }
634
+ }
635
+ );
513
636
  }
@@ -62,7 +62,7 @@ export function registerDescribeTools(server: McpServer) {
62
62
  },
63
63
  compute: {
64
64
  summary:
65
- "Server-side JavaScript functions with triggers (on-demand, event-driven, scheduled, http-trigger).",
65
+ "Server-side JavaScript functions with triggers (on-demand, event-driven, scheduled, http-trigger) and domain allowlisting for external API calls.",
66
66
  describeWith: "describe_compute",
67
67
  tools: [
68
68
  "list_functions",
@@ -79,6 +79,9 @@ export function registerDescribeTools(server: McpServer) {
79
79
  "invoke_trigger",
80
80
  "pause_trigger",
81
81
  "resume_trigger",
82
+ "list_allowed_domains",
83
+ "add_allowed_domain",
84
+ "remove_allowed_domain",
82
85
  ],
83
86
  },
84
87
  smart_queries: {
@@ -499,7 +502,7 @@ export function registerDescribeTools(server: McpServer) {
499
502
  description: "Create a new compute function",
500
503
  required_params: ["name", "code"],
501
504
  optional_params: ["description", "timeoutMs"],
502
- code_format: "module.exports = async (ctx) => { /* your code */ return result; }",
505
+ code_format: "async function run() { /* api, triggerParams, executionParams are globals */ return result; }",
503
506
  },
504
507
  update_function: {
505
508
  description: "Update an existing function. Partial updates supported.",
@@ -548,6 +551,30 @@ export function registerDescribeTools(server: McpServer) {
548
551
  required_params: ["triggerId"],
549
552
  },
550
553
  },
554
+ allowed_domains: {
555
+ description: "Workspace-level allowlist controlling which external domains compute functions can call via api.httpGet/httpPost/etc. Functions attempting to reach unlisted domains get an error.",
556
+ domain_shape: {
557
+ id: "UUID",
558
+ domain: "string — the allowed domain (e.g., 'api.stripe.com' or '*.googleapis.com')",
559
+ createdAt: "ISO 8601 datetime",
560
+ createdBy: "UUID",
561
+ },
562
+ tools: {
563
+ list_allowed_domains: {
564
+ description: "List all allowed domains in the workspace",
565
+ },
566
+ add_allowed_domain: {
567
+ description: "Add a domain to the allowlist",
568
+ required_params: ["domain"],
569
+ },
570
+ remove_allowed_domain: {
571
+ description: "Remove a domain from the allowlist by ID",
572
+ required_params: ["domainId"],
573
+ },
574
+ },
575
+ wildcards: "Use '*.example.com' to allow all subdomains of example.com",
576
+ note: "Functions can only make HTTP requests to domains on this list. Add domains before creating functions that call external APIs.",
577
+ },
551
578
  sandbox_api: {
552
579
  description: "Functions receive an `api` object with built-in utilities. Key crypto methods:",
553
580
  crypto: {
@@ -567,6 +594,7 @@ export function registerDescribeTools(server: McpServer) {
567
594
  "Use test_function to validate code before creating or updating a function",
568
595
  "Use create_function + create_trigger together to set up a complete compute pipeline",
569
596
  "Use api.crypto.signJwt() for GitHub App, Google Cloud, or Azure AD authentication flows",
597
+ "Before creating functions that call external APIs, add the target domains with add_allowed_domain",
570
598
  ],
571
599
  },
572
600
  null,