@centrali-io/centrali-mcp 4.2.1 → 4.2.3

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.
@@ -138,4 +138,375 @@ export function registerComputeTools(server: McpServer, sdk: CentraliSDK) {
138
138
  }
139
139
  }
140
140
  );
141
+
142
+ // ── Function CRUD tools ──────────────────────────────────────────
143
+
144
+ server.tool(
145
+ "get_function",
146
+ "Get a compute function by ID. Returns the full function definition including code.",
147
+ {
148
+ functionId: z.string().describe("The compute function ID (UUID)"),
149
+ },
150
+ async ({ functionId }) => {
151
+ try {
152
+ const result = await sdk.functions.get(functionId);
153
+ return {
154
+ content: [
155
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
156
+ ],
157
+ };
158
+ } catch (error: unknown) {
159
+ return {
160
+ content: [
161
+ {
162
+ type: "text",
163
+ text: formatError(error, `getting function '${functionId}'`),
164
+ },
165
+ ],
166
+ isError: true,
167
+ };
168
+ }
169
+ }
170
+ );
171
+
172
+ server.tool(
173
+ "create_function",
174
+ "Create a new compute function. Compute functions are JavaScript code blocks that run server-side.",
175
+ {
176
+ name: z.string().describe("Display name for the function"),
177
+ slug: z.string().describe("URL-safe unique identifier (e.g., 'process-order')"),
178
+ code: z.string().describe("JavaScript source code. Must export an async function: module.exports = async (ctx) => { ... }"),
179
+ description: z.string().optional().describe("Optional description of what the function does"),
180
+ timeout: z.number().optional().describe("Execution timeout in milliseconds (default: 30000)"),
181
+ },
182
+ async ({ name, slug, code, description, timeout }) => {
183
+ try {
184
+ const input: Record<string, any> = { name, slug, code };
185
+ if (description !== undefined) input.description = description;
186
+ if (timeout !== undefined) input.timeout = timeout;
187
+
188
+ const result = await sdk.functions.create(input as any);
189
+ return {
190
+ content: [
191
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
192
+ ],
193
+ };
194
+ } catch (error: unknown) {
195
+ return {
196
+ content: [
197
+ {
198
+ type: "text",
199
+ text: formatError(error, `creating function '${slug}'`),
200
+ },
201
+ ],
202
+ isError: true,
203
+ };
204
+ }
205
+ }
206
+ );
207
+
208
+ server.tool(
209
+ "update_function",
210
+ "Update an existing compute function by ID. Only include the fields you want to change.",
211
+ {
212
+ functionId: z.string().describe("The compute function ID (UUID) to update"),
213
+ name: z.string().optional().describe("Updated display name"),
214
+ description: z.string().optional().describe("Updated description"),
215
+ code: z.string().optional().describe("Updated JavaScript source code"),
216
+ timeout: z.number().optional().describe("Updated execution timeout in milliseconds"),
217
+ },
218
+ async ({ functionId, name, description, code, timeout }) => {
219
+ try {
220
+ const input: Record<string, any> = {};
221
+ if (name !== undefined) input.name = name;
222
+ if (description !== undefined) input.description = description;
223
+ if (code !== undefined) input.code = code;
224
+ if (timeout !== undefined) input.timeout = timeout;
225
+
226
+ const result = await sdk.functions.update(functionId, input as any);
227
+ return {
228
+ content: [
229
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
230
+ ],
231
+ };
232
+ } catch (error: unknown) {
233
+ return {
234
+ content: [
235
+ {
236
+ type: "text",
237
+ text: formatError(error, `updating function '${functionId}'`),
238
+ },
239
+ ],
240
+ isError: true,
241
+ };
242
+ }
243
+ }
244
+ );
245
+
246
+ server.tool(
247
+ "delete_function",
248
+ "Delete a compute function by ID.",
249
+ {
250
+ functionId: z.string().describe("The compute function ID (UUID) to delete"),
251
+ },
252
+ async ({ functionId }) => {
253
+ try {
254
+ await sdk.functions.delete(functionId);
255
+ return {
256
+ content: [
257
+ {
258
+ type: "text",
259
+ text: `Function '${functionId}' deleted successfully.`,
260
+ },
261
+ ],
262
+ };
263
+ } catch (error: unknown) {
264
+ return {
265
+ content: [
266
+ {
267
+ type: "text",
268
+ text: formatError(error, `deleting function '${functionId}'`),
269
+ },
270
+ ],
271
+ isError: true,
272
+ };
273
+ }
274
+ }
275
+ );
276
+
277
+ server.tool(
278
+ "test_function",
279
+ "Test execute code without saving it as a function. Useful for validating function code before creating or updating.",
280
+ {
281
+ code: z.string().describe("JavaScript code to test. Must export an async function: module.exports = async (ctx) => { ... }"),
282
+ input: z
283
+ .record(z.string(), z.any())
284
+ .optional()
285
+ .describe("Optional input data passed to the function as ctx.input"),
286
+ },
287
+ async ({ code, input }) => {
288
+ try {
289
+ const testInput: Record<string, any> = { code };
290
+ if (input !== undefined) testInput.input = input;
291
+
292
+ const result = await sdk.functions.testExecute(testInput as any);
293
+ return {
294
+ content: [
295
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
296
+ ],
297
+ };
298
+ } catch (error: unknown) {
299
+ return {
300
+ content: [
301
+ {
302
+ type: "text",
303
+ text: formatError(error, "test-executing function"),
304
+ },
305
+ ],
306
+ isError: true,
307
+ };
308
+ }
309
+ }
310
+ );
311
+
312
+ // ── Trigger CRUD tools ───────────────────────────────────────────
313
+
314
+ server.tool(
315
+ "get_trigger",
316
+ "Get a function trigger by ID. Returns full trigger details including execution type and metadata.",
317
+ {
318
+ triggerId: z.string().describe("The trigger ID (UUID)"),
319
+ },
320
+ async ({ triggerId }) => {
321
+ try {
322
+ const result = await sdk.triggers.getDetails(triggerId);
323
+ return {
324
+ content: [
325
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
326
+ ],
327
+ };
328
+ } catch (error: unknown) {
329
+ return {
330
+ content: [
331
+ {
332
+ type: "text",
333
+ text: formatError(error, `getting trigger '${triggerId}'`),
334
+ },
335
+ ],
336
+ isError: true,
337
+ };
338
+ }
339
+ }
340
+ );
341
+
342
+ server.tool(
343
+ "create_trigger",
344
+ "Create a new function trigger. Triggers define how and when a compute function is executed.",
345
+ {
346
+ name: z.string().describe("Display name for the trigger"),
347
+ functionId: z.string().describe("The compute function ID (UUID) to execute"),
348
+ executionType: z
349
+ .enum(["on-demand", "event-driven", "scheduled", "webhook"])
350
+ .describe("How the trigger fires: on-demand (manual), event-driven (data events), scheduled (cron), or webhook (HTTP)"),
351
+ description: z.string().optional().describe("Optional description"),
352
+ triggerMetadata: z
353
+ .record(z.string(), z.any())
354
+ .optional()
355
+ .describe("Type-specific configuration. For event-driven: { event, recordSlug }. For scheduled: { scheduleType, cronExpression, timezone }. For webhook: auto-generated URL."),
356
+ enabled: z.boolean().optional().describe("Whether the trigger is enabled (default: true)"),
357
+ },
358
+ async ({ name, functionId, executionType, description, triggerMetadata, enabled }) => {
359
+ try {
360
+ const input: Record<string, any> = { name, functionId, executionType };
361
+ if (description !== undefined) input.description = description;
362
+ if (triggerMetadata !== undefined) input.triggerMetadata = triggerMetadata;
363
+ if (enabled !== undefined) input.enabled = enabled;
364
+
365
+ const result = await sdk.triggers.create(input as any);
366
+ return {
367
+ content: [
368
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
369
+ ],
370
+ };
371
+ } catch (error: unknown) {
372
+ return {
373
+ content: [
374
+ {
375
+ type: "text",
376
+ text: formatError(error, `creating trigger '${name}'`),
377
+ },
378
+ ],
379
+ isError: true,
380
+ };
381
+ }
382
+ }
383
+ );
384
+
385
+ server.tool(
386
+ "update_trigger",
387
+ "Update an existing function trigger by ID. Only include the fields you want to change.",
388
+ {
389
+ triggerId: z.string().describe("The trigger ID (UUID) to update"),
390
+ name: z.string().optional().describe("Updated display name"),
391
+ description: z.string().optional().describe("Updated description"),
392
+ enabled: z.boolean().optional().describe("Enable or disable the trigger"),
393
+ triggerMetadata: z
394
+ .record(z.string(), z.any())
395
+ .optional()
396
+ .describe("Updated type-specific configuration"),
397
+ },
398
+ async ({ triggerId, name, description, enabled, triggerMetadata }) => {
399
+ try {
400
+ const input: Record<string, any> = {};
401
+ if (name !== undefined) input.name = name;
402
+ if (description !== undefined) input.description = description;
403
+ if (enabled !== undefined) input.enabled = enabled;
404
+ if (triggerMetadata !== undefined) input.triggerMetadata = triggerMetadata;
405
+
406
+ const result = await sdk.triggers.update(triggerId, input as any);
407
+ return {
408
+ content: [
409
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
410
+ ],
411
+ };
412
+ } catch (error: unknown) {
413
+ return {
414
+ content: [
415
+ {
416
+ type: "text",
417
+ text: formatError(error, `updating trigger '${triggerId}'`),
418
+ },
419
+ ],
420
+ isError: true,
421
+ };
422
+ }
423
+ }
424
+ );
425
+
426
+ server.tool(
427
+ "delete_trigger",
428
+ "Delete a function trigger by ID.",
429
+ {
430
+ triggerId: z.string().describe("The trigger ID (UUID) to delete"),
431
+ },
432
+ async ({ triggerId }) => {
433
+ try {
434
+ await sdk.triggers.delete(triggerId);
435
+ return {
436
+ content: [
437
+ {
438
+ type: "text",
439
+ text: `Trigger '${triggerId}' deleted successfully.`,
440
+ },
441
+ ],
442
+ };
443
+ } catch (error: unknown) {
444
+ return {
445
+ content: [
446
+ {
447
+ type: "text",
448
+ text: formatError(error, `deleting trigger '${triggerId}'`),
449
+ },
450
+ ],
451
+ isError: true,
452
+ };
453
+ }
454
+ }
455
+ );
456
+
457
+ server.tool(
458
+ "pause_trigger",
459
+ "Pause a function trigger. Paused triggers will not fire until resumed.",
460
+ {
461
+ triggerId: z.string().describe("The trigger ID (UUID) to pause"),
462
+ },
463
+ async ({ triggerId }) => {
464
+ try {
465
+ const result = await sdk.triggers.pauseTrigger(triggerId);
466
+ return {
467
+ content: [
468
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
469
+ ],
470
+ };
471
+ } catch (error: unknown) {
472
+ return {
473
+ content: [
474
+ {
475
+ type: "text",
476
+ text: formatError(error, `pausing trigger '${triggerId}'`),
477
+ },
478
+ ],
479
+ isError: true,
480
+ };
481
+ }
482
+ }
483
+ );
484
+
485
+ server.tool(
486
+ "resume_trigger",
487
+ "Resume a paused function trigger. The trigger will start firing again on matching events or schedules.",
488
+ {
489
+ triggerId: z.string().describe("The trigger ID (UUID) to resume"),
490
+ },
491
+ async ({ triggerId }) => {
492
+ try {
493
+ const result = await sdk.triggers.resumeTrigger(triggerId);
494
+ return {
495
+ content: [
496
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
497
+ ],
498
+ };
499
+ } catch (error: unknown) {
500
+ return {
501
+ content: [
502
+ {
503
+ type: "text",
504
+ text: formatError(error, `resuming trigger '${triggerId}'`),
505
+ },
506
+ ],
507
+ isError: true,
508
+ };
509
+ }
510
+ }
511
+ );
141
512
  }