@centrali-io/centrali-mcp 3.1.4 → 3.1.5
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/dist/index.js +6 -0
- package/dist/resources/structures.js +75 -38
- package/dist/tools/compute.js +25 -3
- package/dist/tools/insights.d.ts +3 -0
- package/dist/tools/insights.js +172 -0
- package/dist/tools/orchestrations.d.ts +3 -0
- package/dist/tools/orchestrations.js +182 -0
- package/dist/tools/records.js +96 -5
- package/dist/tools/search.js +23 -1
- package/dist/tools/smart-queries.js +24 -2
- package/dist/tools/structures.js +24 -2
- package/dist/tools/validation.d.ts +3 -0
- package/dist/tools/validation.js +173 -0
- package/package.json +2 -2
- package/src/index.ts +6 -0
- package/src/resources/structures.ts +69 -36
- package/src/tools/compute.ts +27 -6
- package/src/tools/insights.ts +198 -0
- package/src/tools/orchestrations.ts +209 -0
- package/src/tools/records.ts +119 -10
- package/src/tools/search.ts +23 -2
- package/src/tools/smart-queries.ts +25 -4
- package/src/tools/structures.ts +25 -4
- package/src/tools/validation.ts +193 -0
package/dist/tools/records.js
CHANGED
|
@@ -11,6 +11,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.registerRecordTools = registerRecordTools;
|
|
13
13
|
const zod_1 = require("zod");
|
|
14
|
+
function formatError(error, context) {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
if (error && typeof error === 'object') {
|
|
17
|
+
const e = error;
|
|
18
|
+
if ('message' in e) {
|
|
19
|
+
let msg = `Error ${context}`;
|
|
20
|
+
if ('code' in e || 'status' in e) {
|
|
21
|
+
msg += `: [${(_b = (_a = e.code) !== null && _a !== void 0 ? _a : e.status) !== null && _b !== void 0 ? _b : 'ERROR'}] ${e.message}`;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
msg += `: ${e.message}`;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(e.fieldErrors) && e.fieldErrors.length > 0) {
|
|
27
|
+
msg += '\nField errors:\n' + e.fieldErrors
|
|
28
|
+
.map(f => ` ${f.field}: ${f.message}`)
|
|
29
|
+
.join('\n');
|
|
30
|
+
}
|
|
31
|
+
return msg;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
|
|
35
|
+
}
|
|
14
36
|
function registerRecordTools(server, sdk) {
|
|
15
37
|
server.tool("query_records", "Query records from a structure with optional filters, sorting, and pagination. Filters use 'data.' prefix for custom fields and bracket notation for operators (e.g., 'data.status': 'active', 'data.price[lte]': 100).", {
|
|
16
38
|
recordSlug: zod_1.z
|
|
@@ -53,7 +75,7 @@ function registerRecordTools(server, sdk) {
|
|
|
53
75
|
content: [
|
|
54
76
|
{
|
|
55
77
|
type: "text",
|
|
56
|
-
text: `
|
|
78
|
+
text: formatError(error, `querying records from '${recordSlug}'`),
|
|
57
79
|
},
|
|
58
80
|
],
|
|
59
81
|
isError: true,
|
|
@@ -82,7 +104,7 @@ function registerRecordTools(server, sdk) {
|
|
|
82
104
|
content: [
|
|
83
105
|
{
|
|
84
106
|
type: "text",
|
|
85
|
-
text: `
|
|
107
|
+
text: formatError(error, `getting record '${id}' from '${recordSlug}'`),
|
|
86
108
|
},
|
|
87
109
|
],
|
|
88
110
|
isError: true,
|
|
@@ -110,7 +132,7 @@ function registerRecordTools(server, sdk) {
|
|
|
110
132
|
content: [
|
|
111
133
|
{
|
|
112
134
|
type: "text",
|
|
113
|
-
text: `
|
|
135
|
+
text: formatError(error, `creating record in '${recordSlug}'`),
|
|
114
136
|
},
|
|
115
137
|
],
|
|
116
138
|
isError: true,
|
|
@@ -137,7 +159,7 @@ function registerRecordTools(server, sdk) {
|
|
|
137
159
|
content: [
|
|
138
160
|
{
|
|
139
161
|
type: "text",
|
|
140
|
-
text: `
|
|
162
|
+
text: formatError(error, `updating record '${id}' in '${recordSlug}'`),
|
|
141
163
|
},
|
|
142
164
|
],
|
|
143
165
|
isError: true,
|
|
@@ -170,9 +192,78 @@ function registerRecordTools(server, sdk) {
|
|
|
170
192
|
content: [
|
|
171
193
|
{
|
|
172
194
|
type: "text",
|
|
173
|
-
text: `
|
|
195
|
+
text: formatError(error, `deleting record '${id}' from '${recordSlug}'`),
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
isError: true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}));
|
|
202
|
+
server.tool("upsert_record", "Create or update a record atomically. Matches on the provided fields to find an existing record — updates it if found, creates it if not. Returns the record and whether it was 'created' or 'updated'.", {
|
|
203
|
+
recordSlug: zod_1.z.string().describe("The structure's record slug"),
|
|
204
|
+
match: zod_1.z
|
|
205
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
206
|
+
.describe("Fields to match on when looking for an existing record (e.g. { sku: 'WIDGET-001' })"),
|
|
207
|
+
data: zod_1.z
|
|
208
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
209
|
+
.describe("Full record data to create or update with"),
|
|
210
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, match, data }) {
|
|
211
|
+
try {
|
|
212
|
+
const result = yield sdk.upsertRecord(recordSlug, { match, data });
|
|
213
|
+
return {
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: "text",
|
|
217
|
+
text: JSON.stringify({ operation: result.operation, record: result.data }, null, 2),
|
|
174
218
|
},
|
|
175
219
|
],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
return {
|
|
224
|
+
content: [
|
|
225
|
+
{ type: "text", text: formatError(error, `upserting record in '${recordSlug}'`) },
|
|
226
|
+
],
|
|
227
|
+
isError: true,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
231
|
+
server.tool("get_records_by_ids", "Fetch multiple records by their IDs in a single request. Returns an array of records.", {
|
|
232
|
+
recordSlug: zod_1.z.string().describe("The structure's record slug"),
|
|
233
|
+
ids: zod_1.z.array(zod_1.z.string()).describe("Array of record IDs (UUIDs) to fetch"),
|
|
234
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, ids }) {
|
|
235
|
+
try {
|
|
236
|
+
const result = yield sdk.getRecordsByIds(recordSlug, ids);
|
|
237
|
+
return {
|
|
238
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
return {
|
|
243
|
+
content: [
|
|
244
|
+
{ type: "text", text: formatError(error, `fetching records by IDs from '${recordSlug}'`) },
|
|
245
|
+
],
|
|
246
|
+
isError: true,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}));
|
|
250
|
+
server.tool("restore_record", "Restore a soft-deleted record by ID. Only works on records deleted without hard=true.", {
|
|
251
|
+
recordSlug: zod_1.z.string().describe("The structure's record slug"),
|
|
252
|
+
id: zod_1.z.string().describe("The record ID (UUID) to restore"),
|
|
253
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, id }) {
|
|
254
|
+
try {
|
|
255
|
+
yield sdk.restoreRecord(recordSlug, id);
|
|
256
|
+
return {
|
|
257
|
+
content: [
|
|
258
|
+
{ type: "text", text: `Record '${id}' restored in '${recordSlug}'.` },
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
return {
|
|
264
|
+
content: [
|
|
265
|
+
{ type: "text", text: formatError(error, `restoring record '${id}' in '${recordSlug}'`) },
|
|
266
|
+
],
|
|
176
267
|
isError: true,
|
|
177
268
|
};
|
|
178
269
|
}
|
package/dist/tools/search.js
CHANGED
|
@@ -11,6 +11,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.registerSearchTools = registerSearchTools;
|
|
13
13
|
const zod_1 = require("zod");
|
|
14
|
+
function formatError(error, context) {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
if (error && typeof error === 'object') {
|
|
17
|
+
const e = error;
|
|
18
|
+
if ('message' in e) {
|
|
19
|
+
let msg = `Error ${context}`;
|
|
20
|
+
if ('code' in e || 'status' in e) {
|
|
21
|
+
msg += `: [${(_b = (_a = e.code) !== null && _a !== void 0 ? _a : e.status) !== null && _b !== void 0 ? _b : 'ERROR'}] ${e.message}`;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
msg += `: ${e.message}`;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(e.fieldErrors) && e.fieldErrors.length > 0) {
|
|
27
|
+
msg += '\nField errors:\n' + e.fieldErrors
|
|
28
|
+
.map(f => ` ${f.field}: ${f.message}`)
|
|
29
|
+
.join('\n');
|
|
30
|
+
}
|
|
31
|
+
return msg;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
|
|
35
|
+
}
|
|
14
36
|
function registerSearchTools(server, sdk) {
|
|
15
37
|
server.tool("search_records", "Full-text search across records in the workspace. Powered by Meilisearch. Optionally filter by structure(s) and limit results.", {
|
|
16
38
|
query: zod_1.z.string().describe("The search query string"),
|
|
@@ -41,7 +63,7 @@ function registerSearchTools(server, sdk) {
|
|
|
41
63
|
content: [
|
|
42
64
|
{
|
|
43
65
|
type: "text",
|
|
44
|
-
text:
|
|
66
|
+
text: formatError(error, "searching records"),
|
|
45
67
|
},
|
|
46
68
|
],
|
|
47
69
|
isError: true,
|
|
@@ -11,6 +11,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.registerSmartQueryTools = registerSmartQueryTools;
|
|
13
13
|
const zod_1 = require("zod");
|
|
14
|
+
function formatError(error, context) {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
if (error && typeof error === 'object') {
|
|
17
|
+
const e = error;
|
|
18
|
+
if ('message' in e) {
|
|
19
|
+
let msg = `Error ${context}`;
|
|
20
|
+
if ('code' in e || 'status' in e) {
|
|
21
|
+
msg += `: [${(_b = (_a = e.code) !== null && _a !== void 0 ? _a : e.status) !== null && _b !== void 0 ? _b : 'ERROR'}] ${e.message}`;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
msg += `: ${e.message}`;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(e.fieldErrors) && e.fieldErrors.length > 0) {
|
|
27
|
+
msg += '\nField errors:\n' + e.fieldErrors
|
|
28
|
+
.map(f => ` ${f.field}: ${f.message}`)
|
|
29
|
+
.join('\n');
|
|
30
|
+
}
|
|
31
|
+
return msg;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
|
|
35
|
+
}
|
|
14
36
|
function registerSmartQueryTools(server, sdk) {
|
|
15
37
|
server.tool("list_smart_queries", "List smart queries. Smart queries are reusable, parameterized queries defined in the Centrali console. Optionally filter by structure slug.", {
|
|
16
38
|
recordSlug: zod_1.z
|
|
@@ -33,7 +55,7 @@ function registerSmartQueryTools(server, sdk) {
|
|
|
33
55
|
content: [
|
|
34
56
|
{
|
|
35
57
|
type: "text",
|
|
36
|
-
text:
|
|
58
|
+
text: formatError(error, "listing smart queries"),
|
|
37
59
|
},
|
|
38
60
|
],
|
|
39
61
|
isError: true,
|
|
@@ -64,7 +86,7 @@ function registerSmartQueryTools(server, sdk) {
|
|
|
64
86
|
content: [
|
|
65
87
|
{
|
|
66
88
|
type: "text",
|
|
67
|
-
text: `
|
|
89
|
+
text: formatError(error, `executing smart query '${queryId}'`),
|
|
68
90
|
},
|
|
69
91
|
],
|
|
70
92
|
isError: true,
|
package/dist/tools/structures.js
CHANGED
|
@@ -11,6 +11,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.registerStructureTools = registerStructureTools;
|
|
13
13
|
const zod_1 = require("zod");
|
|
14
|
+
function formatError(error, context) {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
if (error && typeof error === 'object') {
|
|
17
|
+
const e = error;
|
|
18
|
+
if ('message' in e) {
|
|
19
|
+
let msg = `Error ${context}`;
|
|
20
|
+
if ('code' in e || 'status' in e) {
|
|
21
|
+
msg += `: [${(_b = (_a = e.code) !== null && _a !== void 0 ? _a : e.status) !== null && _b !== void 0 ? _b : 'ERROR'}] ${e.message}`;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
msg += `: ${e.message}`;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(e.fieldErrors) && e.fieldErrors.length > 0) {
|
|
27
|
+
msg += '\nField errors:\n' + e.fieldErrors
|
|
28
|
+
.map(f => ` ${f.field}: ${f.message}`)
|
|
29
|
+
.join('\n');
|
|
30
|
+
}
|
|
31
|
+
return msg;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
|
|
35
|
+
}
|
|
14
36
|
function registerStructureTools(server, sdk) {
|
|
15
37
|
server.tool("list_structures", "List all data structures (schemas) in the Centrali workspace. Returns name, slug, description, and property definitions for each structure.", {
|
|
16
38
|
page: zod_1.z.number().optional().describe("Page number (1-indexed)"),
|
|
@@ -29,7 +51,7 @@ function registerStructureTools(server, sdk) {
|
|
|
29
51
|
content: [
|
|
30
52
|
{
|
|
31
53
|
type: "text",
|
|
32
|
-
text:
|
|
54
|
+
text: formatError(error, "listing structures"),
|
|
33
55
|
},
|
|
34
56
|
],
|
|
35
57
|
isError: true,
|
|
@@ -54,7 +76,7 @@ function registerStructureTools(server, sdk) {
|
|
|
54
76
|
content: [
|
|
55
77
|
{
|
|
56
78
|
type: "text",
|
|
57
|
-
text: `
|
|
79
|
+
text: formatError(error, `getting structure '${recordSlug}'`),
|
|
58
80
|
},
|
|
59
81
|
],
|
|
60
82
|
isError: true,
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerValidationTools = registerValidationTools;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
function formatError(error, context) {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
if (error && typeof error === "object") {
|
|
17
|
+
const e = error;
|
|
18
|
+
if ("message" in e) {
|
|
19
|
+
let msg = `Error ${context}`;
|
|
20
|
+
if ("code" in e || "status" in e) {
|
|
21
|
+
msg += `: [${(_b = (_a = e.code) !== null && _a !== void 0 ? _a : e.status) !== null && _b !== void 0 ? _b : "ERROR"}] ${e.message}`;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
msg += `: ${e.message}`;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(e.fieldErrors) && e.fieldErrors.length > 0) {
|
|
27
|
+
msg +=
|
|
28
|
+
"\nField errors:\n" +
|
|
29
|
+
e.fieldErrors
|
|
30
|
+
.map((f) => ` ${f.field}: ${f.message}`)
|
|
31
|
+
.join("\n");
|
|
32
|
+
}
|
|
33
|
+
return msg;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
|
|
37
|
+
}
|
|
38
|
+
function registerValidationTools(server, sdk) {
|
|
39
|
+
server.tool("trigger_validation_scan", "Trigger an AI-powered data quality scan on a structure. Detects typos, format inconsistencies, duplicates, and other data issues. Returns a batchId — use get_validation_summary or list_validation_suggestions to see results after the scan completes.", {
|
|
40
|
+
structureSlug: zod_1.z.string().describe("The structure's record slug to scan"),
|
|
41
|
+
validationTypes: zod_1.z
|
|
42
|
+
.array(zod_1.z.enum(["typo", "format", "duplicate", "semantic", "type"]))
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Specific validation types to run. If omitted, all types are run."),
|
|
45
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ structureSlug, validationTypes }) {
|
|
46
|
+
try {
|
|
47
|
+
const options = validationTypes ? { validationTypes } : undefined;
|
|
48
|
+
const result = yield sdk.validation.triggerScan(structureSlug, options);
|
|
49
|
+
return {
|
|
50
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: "text",
|
|
58
|
+
text: formatError(error, `triggering validation scan for '${structureSlug}'`),
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
isError: true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
server.tool("list_validation_suggestions", "List data quality suggestions generated by validation scans. Each suggestion identifies an issue in a record and proposes a fix.", {
|
|
66
|
+
structureSlug: zod_1.z
|
|
67
|
+
.string()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Filter suggestions to a specific structure"),
|
|
70
|
+
status: zod_1.z
|
|
71
|
+
.enum(["pending", "accepted", "rejected", "auto-applied"])
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("Filter by suggestion status (default: all)"),
|
|
74
|
+
issueType: zod_1.z
|
|
75
|
+
.enum(["typo", "format", "duplicate", "semantic", "type"])
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Filter by issue type"),
|
|
78
|
+
minConfidence: zod_1.z
|
|
79
|
+
.number()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Minimum confidence score (0-1). Use 0.9 for high-confidence suggestions only."),
|
|
82
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ structureSlug, status, issueType, minConfidence }) {
|
|
83
|
+
try {
|
|
84
|
+
const options = {};
|
|
85
|
+
if (structureSlug)
|
|
86
|
+
options.structureSlug = structureSlug;
|
|
87
|
+
if (status)
|
|
88
|
+
options.status = status;
|
|
89
|
+
if (issueType)
|
|
90
|
+
options.issueType = issueType;
|
|
91
|
+
if (minConfidence !== undefined)
|
|
92
|
+
options.minConfidence = minConfidence;
|
|
93
|
+
const result = yield sdk.validation.listSuggestions(Object.keys(options).length > 0 ? options : undefined);
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: formatError(error, "listing validation suggestions"),
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
isError: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}));
|
|
110
|
+
server.tool("get_validation_summary", "Get a summary of data quality across the workspace — counts of pending, accepted, and rejected suggestions. Optionally filter by structure.", {
|
|
111
|
+
structureSlug: zod_1.z
|
|
112
|
+
.string()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe("Filter summary to a specific structure. If omitted, returns workspace-wide summary."),
|
|
115
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ structureSlug }) {
|
|
116
|
+
try {
|
|
117
|
+
const result = yield sdk.validation.getSummary(structureSlug);
|
|
118
|
+
return {
|
|
119
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{ type: "text", text: formatError(error, "getting validation summary") },
|
|
126
|
+
],
|
|
127
|
+
isError: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}));
|
|
131
|
+
server.tool("accept_validation_suggestion", "Accept a data quality suggestion and apply the fix to the record. The suggested correction is written to the record automatically.", {
|
|
132
|
+
suggestionId: zod_1.z.string().describe("The suggestion ID (UUID) to accept"),
|
|
133
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ suggestionId }) {
|
|
134
|
+
try {
|
|
135
|
+
const result = yield sdk.validation.accept(suggestionId);
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
return {
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: "text",
|
|
145
|
+
text: formatError(error, `accepting suggestion '${suggestionId}'`),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
isError: true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
server.tool("reject_validation_suggestion", "Reject a data quality suggestion, marking it as incorrect or not applicable. The record is left unchanged.", {
|
|
153
|
+
suggestionId: zod_1.z.string().describe("The suggestion ID (UUID) to reject"),
|
|
154
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ suggestionId }) {
|
|
155
|
+
try {
|
|
156
|
+
const result = yield sdk.validation.reject(suggestionId);
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return {
|
|
163
|
+
content: [
|
|
164
|
+
{
|
|
165
|
+
type: "text",
|
|
166
|
+
text: formatError(error, `rejecting suggestion '${suggestionId}'`),
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
isError: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}));
|
|
173
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centrali-io/centrali-mcp",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.5",
|
|
4
4
|
"description": "Centrali MCP Server - AI assistant integration for Centrali workspaces",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"author": "Blueinit",
|
|
26
26
|
"license": "ISC",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@centrali-io/centrali-sdk": "^3.1.
|
|
28
|
+
"@centrali-io/centrali-sdk": "^3.1.4",
|
|
29
29
|
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,9 @@ import { registerRecordTools } from "./tools/records.js";
|
|
|
8
8
|
import { registerSearchTools } from "./tools/search.js";
|
|
9
9
|
import { registerComputeTools } from "./tools/compute.js";
|
|
10
10
|
import { registerSmartQueryTools } from "./tools/smart-queries.js";
|
|
11
|
+
import { registerOrchestrationTools } from "./tools/orchestrations.js";
|
|
12
|
+
import { registerInsightTools } from "./tools/insights.js";
|
|
13
|
+
import { registerValidationTools } from "./tools/validation.js";
|
|
11
14
|
import { registerStructureResources } from "./resources/structures.js";
|
|
12
15
|
|
|
13
16
|
function getRequiredEnv(name: string): string {
|
|
@@ -43,6 +46,9 @@ async function main() {
|
|
|
43
46
|
registerSearchTools(server, sdk);
|
|
44
47
|
registerComputeTools(server, sdk);
|
|
45
48
|
registerSmartQueryTools(server, sdk);
|
|
49
|
+
registerOrchestrationTools(server, sdk);
|
|
50
|
+
registerInsightTools(server, sdk);
|
|
51
|
+
registerValidationTools(server, sdk);
|
|
46
52
|
|
|
47
53
|
// Register resources
|
|
48
54
|
registerStructureResources(server, sdk);
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { CentraliSDK } from "@centrali-io/centrali-sdk";
|
|
3
3
|
|
|
4
|
+
function formatError(error: unknown, context: string): string {
|
|
5
|
+
if (error && typeof error === 'object') {
|
|
6
|
+
const e = error as Record<string, any>;
|
|
7
|
+
if ('message' in e) {
|
|
8
|
+
let msg = `Error ${context}`;
|
|
9
|
+
if ('code' in e || 'status' in e) {
|
|
10
|
+
msg += `: [${e.code ?? e.status ?? 'ERROR'}] ${e.message}`;
|
|
11
|
+
} else {
|
|
12
|
+
msg += `: ${e.message}`;
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(e.fieldErrors) && e.fieldErrors.length > 0) {
|
|
15
|
+
msg += '\nField errors:\n' + (e.fieldErrors as Array<{field: string; message: string}>)
|
|
16
|
+
.map(f => ` ${f.field}: ${f.message}`)
|
|
17
|
+
.join('\n');
|
|
18
|
+
}
|
|
19
|
+
return msg;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
4
25
|
export function registerStructureResources(
|
|
5
26
|
server: McpServer,
|
|
6
27
|
sdk: CentraliSDK
|
|
@@ -15,23 +36,27 @@ export function registerStructureResources(
|
|
|
15
36
|
mimeType: "application/json",
|
|
16
37
|
},
|
|
17
38
|
async (uri) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
try {
|
|
40
|
+
const result = await sdk.structures.list({ limit: 100 });
|
|
41
|
+
const summary = (result.data || []).map((s) => ({
|
|
42
|
+
name: s.name,
|
|
43
|
+
recordSlug: s.recordSlug,
|
|
44
|
+
description: s.description || "",
|
|
45
|
+
propertyCount: s.properties?.length || 0,
|
|
46
|
+
status: s.status,
|
|
47
|
+
}));
|
|
48
|
+
return {
|
|
49
|
+
contents: [
|
|
50
|
+
{
|
|
51
|
+
uri: uri.href,
|
|
52
|
+
mimeType: "application/json",
|
|
53
|
+
text: JSON.stringify(summary, null, 2),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
} catch (error: unknown) {
|
|
58
|
+
throw new Error(formatError(error, "listing structures resource"));
|
|
59
|
+
}
|
|
35
60
|
}
|
|
36
61
|
);
|
|
37
62
|
|
|
@@ -40,15 +65,19 @@ export function registerStructureResources(
|
|
|
40
65
|
"structure-schema",
|
|
41
66
|
new ResourceTemplate("centrali://structures/{slug}", {
|
|
42
67
|
list: async () => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
try {
|
|
69
|
+
const result = await sdk.structures.list({ limit: 100 });
|
|
70
|
+
return {
|
|
71
|
+
resources: (result.data || []).map((s) => ({
|
|
72
|
+
uri: `centrali://structures/${s.recordSlug}`,
|
|
73
|
+
name: s.name,
|
|
74
|
+
description: s.description || `Schema for ${s.name}`,
|
|
75
|
+
mimeType: "application/json",
|
|
76
|
+
})),
|
|
77
|
+
};
|
|
78
|
+
} catch (error: unknown) {
|
|
79
|
+
throw new Error(formatError(error, "listing structure schemas"));
|
|
80
|
+
}
|
|
52
81
|
},
|
|
53
82
|
}),
|
|
54
83
|
{
|
|
@@ -56,16 +85,20 @@ export function registerStructureResources(
|
|
|
56
85
|
mimeType: "application/json",
|
|
57
86
|
},
|
|
58
87
|
async (uri, { slug }) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
88
|
+
try {
|
|
89
|
+
const result = await sdk.structures.getBySlug(slug as string);
|
|
90
|
+
return {
|
|
91
|
+
contents: [
|
|
92
|
+
{
|
|
93
|
+
uri: uri.href,
|
|
94
|
+
mimeType: "application/json",
|
|
95
|
+
text: JSON.stringify(result.data, null, 2),
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
} catch (error: unknown) {
|
|
100
|
+
throw new Error(formatError(error, `getting structure schema for '${slug}'`));
|
|
101
|
+
}
|
|
69
102
|
}
|
|
70
103
|
);
|
|
71
104
|
}
|