@centrali-io/centrali-mcp 4.2.0 → 4.2.2
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.
- package/README.md +25 -1
- package/dist/index.js +4 -0
- package/dist/tools/compute.js +323 -0
- package/dist/tools/describe.d.ts +10 -0
- package/dist/tools/describe.js +1485 -0
- package/dist/tools/orchestrations.js +146 -0
- package/dist/tools/pages.d.ts +3 -0
- package/dist/tools/pages.js +477 -0
- package/dist/tools/smart-queries.js +152 -0
- package/dist/tools/structures.js +117 -0
- package/package.json +2 -2
- package/src/index.ts +4 -0
- package/src/tools/compute.ts +371 -0
- package/src/tools/describe.ts +1798 -0
- package/src/tools/orchestrations.ts +167 -0
- package/src/tools/pages.ts +573 -0
- package/src/tools/smart-queries.ts +175 -0
- package/src/tools/structures.ts +123 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
MCP (Model Context Protocol) server for the Centrali platform. Lets AI assistants (Claude, Cursor, etc.) interact with Centrali workspaces — query data, manage records, search, trigger compute functions, and more.
|
|
4
4
|
|
|
5
|
-
Built on `@centrali-io/centrali-sdk`
|
|
5
|
+
Built on `@centrali-io/centrali-sdk` v4.2.0. Authenticates via service account client credentials.
|
|
6
6
|
|
|
7
7
|
## Setup
|
|
8
8
|
|
|
@@ -33,6 +33,7 @@ Add to your MCP client configuration (e.g., Claude Desktop, Cursor):
|
|
|
33
33
|
| `CENTRALI_CLIENT_ID` | Yes | Service account client ID |
|
|
34
34
|
| `CENTRALI_CLIENT_SECRET` | Yes | Service account client secret |
|
|
35
35
|
| `CENTRALI_WORKSPACE` | Yes | Workspace slug to operate in |
|
|
36
|
+
| `CENTRALI_PAGES_URL` | No | Override pages API URL (default: derived as `api.{domain}/pages`) |
|
|
36
37
|
|
|
37
38
|
## Available Tools
|
|
38
39
|
|
|
@@ -75,6 +76,29 @@ Add to your MCP client configuration (e.g., Claude Desktop, Cursor):
|
|
|
75
76
|
| `list_smart_queries` | List smart queries, optionally filtered by structure |
|
|
76
77
|
| `execute_smart_query` | Execute a smart query with optional variables |
|
|
77
78
|
|
|
79
|
+
### Pages
|
|
80
|
+
| Tool | Description |
|
|
81
|
+
|------|-------------|
|
|
82
|
+
| `list_pages` | List all pages in the workspace |
|
|
83
|
+
| `get_page` | Get a page by ID |
|
|
84
|
+
| `create_page` | Create a new page (list, detail, form, or dashboard) |
|
|
85
|
+
| `update_page` | Update page metadata (name, slug, description) |
|
|
86
|
+
| `delete_page` | Soft-delete a page |
|
|
87
|
+
| `save_page_draft` | Save or update a page's draft definition |
|
|
88
|
+
| `get_page_draft` | Get the current draft of a page |
|
|
89
|
+
| `list_page_versions` | List all versions of a page |
|
|
90
|
+
| `validate_page` | Validate a page draft before publishing |
|
|
91
|
+
| `publish_page` | Publish a page to its runtime URL |
|
|
92
|
+
| `unpublish_page` | Unpublish a page |
|
|
93
|
+
| `set_page_access_policy` | Set access policy (public, authenticated, role-gated) |
|
|
94
|
+
| `get_page_theme` | Get workspace page theme |
|
|
95
|
+
| `set_page_theme` | Set workspace page theme (colors, logo, font) |
|
|
96
|
+
| `get_navigation` | Get workspace navigation config |
|
|
97
|
+
| `set_navigation` | Set workspace navigation config |
|
|
98
|
+
| `delete_navigation` | Delete navigation config (revert to standalone pages) |
|
|
99
|
+
| `generate_starter_pages` | Generate page proposals from structure IDs |
|
|
100
|
+
| `accept_page_proposal` | Accept a generated proposal and create the page |
|
|
101
|
+
|
|
78
102
|
## Resources
|
|
79
103
|
|
|
80
104
|
| URI | Description |
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ const smart_queries_js_1 = require("./tools/smart-queries.js");
|
|
|
21
21
|
const orchestrations_js_1 = require("./tools/orchestrations.js");
|
|
22
22
|
const insights_js_1 = require("./tools/insights.js");
|
|
23
23
|
const validation_js_1 = require("./tools/validation.js");
|
|
24
|
+
const pages_js_1 = require("./tools/pages.js");
|
|
25
|
+
const describe_js_1 = require("./tools/describe.js");
|
|
24
26
|
const structures_js_2 = require("./resources/structures.js");
|
|
25
27
|
function getRequiredEnv(name) {
|
|
26
28
|
const value = process.env[name];
|
|
@@ -56,6 +58,8 @@ function main() {
|
|
|
56
58
|
(0, orchestrations_js_1.registerOrchestrationTools)(server, sdk);
|
|
57
59
|
(0, insights_js_1.registerInsightTools)(server, sdk);
|
|
58
60
|
(0, validation_js_1.registerValidationTools)(server, sdk);
|
|
61
|
+
(0, pages_js_1.registerPageTools)(server, sdk, baseUrl, workspaceId);
|
|
62
|
+
(0, describe_js_1.registerDescribeTools)(server);
|
|
59
63
|
// Register resources
|
|
60
64
|
(0, structures_js_2.registerCollectionResources)(server, sdk);
|
|
61
65
|
(0, structures_js_2.registerStructureResources)(server, sdk);
|
package/dist/tools/compute.js
CHANGED
|
@@ -129,4 +129,327 @@ function registerComputeTools(server, sdk) {
|
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
131
|
}));
|
|
132
|
+
// ── Function CRUD tools ──────────────────────────────────────────
|
|
133
|
+
server.tool("get_function", "Get a compute function by ID. Returns the full function definition including code.", {
|
|
134
|
+
functionId: zod_1.z.string().describe("The compute function ID (UUID)"),
|
|
135
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ functionId }) {
|
|
136
|
+
try {
|
|
137
|
+
const result = yield sdk.functions.get(functionId);
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: formatError(error, `getting function '${functionId}'`),
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}));
|
|
156
|
+
server.tool("create_function", "Create a new compute function. Compute functions are JavaScript code blocks that run server-side.", {
|
|
157
|
+
name: zod_1.z.string().describe("Display name for the function"),
|
|
158
|
+
slug: zod_1.z.string().describe("URL-safe unique identifier (e.g., 'process-order')"),
|
|
159
|
+
code: zod_1.z.string().describe("JavaScript source code. Must export an async function: module.exports = async (ctx) => { ... }"),
|
|
160
|
+
description: zod_1.z.string().optional().describe("Optional description of what the function does"),
|
|
161
|
+
timeout: zod_1.z.number().optional().describe("Execution timeout in milliseconds (default: 30000)"),
|
|
162
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ name, slug, code, description, timeout }) {
|
|
163
|
+
try {
|
|
164
|
+
const input = { name, slug, code };
|
|
165
|
+
if (description !== undefined)
|
|
166
|
+
input.description = description;
|
|
167
|
+
if (timeout !== undefined)
|
|
168
|
+
input.timeout = timeout;
|
|
169
|
+
const result = yield sdk.functions.create(input);
|
|
170
|
+
return {
|
|
171
|
+
content: [
|
|
172
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
content: [
|
|
179
|
+
{
|
|
180
|
+
type: "text",
|
|
181
|
+
text: formatError(error, `creating function '${slug}'`),
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
isError: true,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}));
|
|
188
|
+
server.tool("update_function", "Update an existing compute function by ID. Only include the fields you want to change.", {
|
|
189
|
+
functionId: zod_1.z.string().describe("The compute function ID (UUID) to update"),
|
|
190
|
+
name: zod_1.z.string().optional().describe("Updated display name"),
|
|
191
|
+
description: zod_1.z.string().optional().describe("Updated description"),
|
|
192
|
+
code: zod_1.z.string().optional().describe("Updated JavaScript source code"),
|
|
193
|
+
timeout: zod_1.z.number().optional().describe("Updated execution timeout in milliseconds"),
|
|
194
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ functionId, name, description, code, timeout }) {
|
|
195
|
+
try {
|
|
196
|
+
const input = {};
|
|
197
|
+
if (name !== undefined)
|
|
198
|
+
input.name = name;
|
|
199
|
+
if (description !== undefined)
|
|
200
|
+
input.description = description;
|
|
201
|
+
if (code !== undefined)
|
|
202
|
+
input.code = code;
|
|
203
|
+
if (timeout !== undefined)
|
|
204
|
+
input.timeout = timeout;
|
|
205
|
+
const result = yield sdk.functions.update(functionId, input);
|
|
206
|
+
return {
|
|
207
|
+
content: [
|
|
208
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
return {
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: "text",
|
|
217
|
+
text: formatError(error, `updating function '${functionId}'`),
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
isError: true,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}));
|
|
224
|
+
server.tool("delete_function", "Delete a compute function by ID.", {
|
|
225
|
+
functionId: zod_1.z.string().describe("The compute function ID (UUID) to delete"),
|
|
226
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ functionId }) {
|
|
227
|
+
try {
|
|
228
|
+
yield sdk.functions.delete(functionId);
|
|
229
|
+
return {
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: "text",
|
|
233
|
+
text: `Function '${functionId}' deleted successfully.`,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
return {
|
|
240
|
+
content: [
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
text: formatError(error, `deleting function '${functionId}'`),
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
isError: true,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}));
|
|
250
|
+
server.tool("test_function", "Test execute code without saving it as a function. Useful for validating function code before creating or updating.", {
|
|
251
|
+
code: zod_1.z.string().describe("JavaScript code to test. Must export an async function: module.exports = async (ctx) => { ... }"),
|
|
252
|
+
input: zod_1.z
|
|
253
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
254
|
+
.optional()
|
|
255
|
+
.describe("Optional input data passed to the function as ctx.input"),
|
|
256
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ code, input }) {
|
|
257
|
+
try {
|
|
258
|
+
const testInput = { code };
|
|
259
|
+
if (input !== undefined)
|
|
260
|
+
testInput.input = input;
|
|
261
|
+
const result = yield sdk.functions.testExecute(testInput);
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
265
|
+
],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
return {
|
|
270
|
+
content: [
|
|
271
|
+
{
|
|
272
|
+
type: "text",
|
|
273
|
+
text: formatError(error, "test-executing function"),
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
isError: true,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}));
|
|
280
|
+
// ── Trigger CRUD tools ───────────────────────────────────────────
|
|
281
|
+
server.tool("get_trigger", "Get a function trigger by ID. Returns full trigger details including execution type and metadata.", {
|
|
282
|
+
triggerId: zod_1.z.string().describe("The trigger ID (UUID)"),
|
|
283
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ triggerId }) {
|
|
284
|
+
try {
|
|
285
|
+
const result = yield sdk.triggers.getDetails(triggerId);
|
|
286
|
+
return {
|
|
287
|
+
content: [
|
|
288
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
289
|
+
],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
return {
|
|
294
|
+
content: [
|
|
295
|
+
{
|
|
296
|
+
type: "text",
|
|
297
|
+
text: formatError(error, `getting trigger '${triggerId}'`),
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
isError: true,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}));
|
|
304
|
+
server.tool("create_trigger", "Create a new function trigger. Triggers define how and when a compute function is executed.", {
|
|
305
|
+
name: zod_1.z.string().describe("Display name for the trigger"),
|
|
306
|
+
functionId: zod_1.z.string().describe("The compute function ID (UUID) to execute"),
|
|
307
|
+
executionType: zod_1.z
|
|
308
|
+
.enum(["on-demand", "event-driven", "scheduled", "webhook"])
|
|
309
|
+
.describe("How the trigger fires: on-demand (manual), event-driven (data events), scheduled (cron), or webhook (HTTP)"),
|
|
310
|
+
description: zod_1.z.string().optional().describe("Optional description"),
|
|
311
|
+
triggerMetadata: zod_1.z
|
|
312
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
313
|
+
.optional()
|
|
314
|
+
.describe("Type-specific configuration. For event-driven: { event, recordSlug }. For scheduled: { scheduleType, cronExpression, timezone }. For webhook: auto-generated URL."),
|
|
315
|
+
enabled: zod_1.z.boolean().optional().describe("Whether the trigger is enabled (default: true)"),
|
|
316
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ name, functionId, executionType, description, triggerMetadata, enabled }) {
|
|
317
|
+
try {
|
|
318
|
+
const input = { name, functionId, executionType };
|
|
319
|
+
if (description !== undefined)
|
|
320
|
+
input.description = description;
|
|
321
|
+
if (triggerMetadata !== undefined)
|
|
322
|
+
input.triggerMetadata = triggerMetadata;
|
|
323
|
+
if (enabled !== undefined)
|
|
324
|
+
input.enabled = enabled;
|
|
325
|
+
const result = yield sdk.triggers.create(input);
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
329
|
+
],
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
return {
|
|
334
|
+
content: [
|
|
335
|
+
{
|
|
336
|
+
type: "text",
|
|
337
|
+
text: formatError(error, `creating trigger '${name}'`),
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
isError: true,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}));
|
|
344
|
+
server.tool("update_trigger", "Update an existing function trigger by ID. Only include the fields you want to change.", {
|
|
345
|
+
triggerId: zod_1.z.string().describe("The trigger ID (UUID) to update"),
|
|
346
|
+
name: zod_1.z.string().optional().describe("Updated display name"),
|
|
347
|
+
description: zod_1.z.string().optional().describe("Updated description"),
|
|
348
|
+
enabled: zod_1.z.boolean().optional().describe("Enable or disable the trigger"),
|
|
349
|
+
triggerMetadata: zod_1.z
|
|
350
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
351
|
+
.optional()
|
|
352
|
+
.describe("Updated type-specific configuration"),
|
|
353
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ triggerId, name, description, enabled, triggerMetadata }) {
|
|
354
|
+
try {
|
|
355
|
+
const input = {};
|
|
356
|
+
if (name !== undefined)
|
|
357
|
+
input.name = name;
|
|
358
|
+
if (description !== undefined)
|
|
359
|
+
input.description = description;
|
|
360
|
+
if (enabled !== undefined)
|
|
361
|
+
input.enabled = enabled;
|
|
362
|
+
if (triggerMetadata !== undefined)
|
|
363
|
+
input.triggerMetadata = triggerMetadata;
|
|
364
|
+
const result = yield sdk.triggers.update(triggerId, input);
|
|
365
|
+
return {
|
|
366
|
+
content: [
|
|
367
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
368
|
+
],
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
return {
|
|
373
|
+
content: [
|
|
374
|
+
{
|
|
375
|
+
type: "text",
|
|
376
|
+
text: formatError(error, `updating trigger '${triggerId}'`),
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
isError: true,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}));
|
|
383
|
+
server.tool("delete_trigger", "Delete a function trigger by ID.", {
|
|
384
|
+
triggerId: zod_1.z.string().describe("The trigger ID (UUID) to delete"),
|
|
385
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ triggerId }) {
|
|
386
|
+
try {
|
|
387
|
+
yield sdk.triggers.delete(triggerId);
|
|
388
|
+
return {
|
|
389
|
+
content: [
|
|
390
|
+
{
|
|
391
|
+
type: "text",
|
|
392
|
+
text: `Trigger '${triggerId}' deleted successfully.`,
|
|
393
|
+
},
|
|
394
|
+
],
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
return {
|
|
399
|
+
content: [
|
|
400
|
+
{
|
|
401
|
+
type: "text",
|
|
402
|
+
text: formatError(error, `deleting trigger '${triggerId}'`),
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
isError: true,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}));
|
|
409
|
+
server.tool("pause_trigger", "Pause a function trigger. Paused triggers will not fire until resumed.", {
|
|
410
|
+
triggerId: zod_1.z.string().describe("The trigger ID (UUID) to pause"),
|
|
411
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ triggerId }) {
|
|
412
|
+
try {
|
|
413
|
+
const result = yield sdk.triggers.pauseTrigger(triggerId);
|
|
414
|
+
return {
|
|
415
|
+
content: [
|
|
416
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
417
|
+
],
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
return {
|
|
422
|
+
content: [
|
|
423
|
+
{
|
|
424
|
+
type: "text",
|
|
425
|
+
text: formatError(error, `pausing trigger '${triggerId}'`),
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
isError: true,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
}));
|
|
432
|
+
server.tool("resume_trigger", "Resume a paused function trigger. The trigger will start firing again on matching events or schedules.", {
|
|
433
|
+
triggerId: zod_1.z.string().describe("The trigger ID (UUID) to resume"),
|
|
434
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ triggerId }) {
|
|
435
|
+
try {
|
|
436
|
+
const result = yield sdk.triggers.resumeTrigger(triggerId);
|
|
437
|
+
return {
|
|
438
|
+
content: [
|
|
439
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
440
|
+
],
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
return {
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: "text",
|
|
448
|
+
text: formatError(error, `resuming trigger '${triggerId}'`),
|
|
449
|
+
},
|
|
450
|
+
],
|
|
451
|
+
isError: true,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}));
|
|
132
455
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Introspection tools that teach AI assistants the valid shapes, schemas,
|
|
4
|
+
* and domain concepts for every Centrali API. These tools return static
|
|
5
|
+
* documentation — no network calls, no auth required.
|
|
6
|
+
*
|
|
7
|
+
* Pattern inspired by Supabase MCP (list_tables / get_schemas) and
|
|
8
|
+
* Stripe MCP (search_stripe_documentation).
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerDescribeTools(server: McpServer): void;
|