@centrali-io/centrali-mcp 4.4.8-rc.2 → 4.4.8-rc.4

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.
@@ -27,10 +27,10 @@ function registerDescribeTools(server) {
27
27
  type: "text",
28
28
  text: JSON.stringify({
29
29
  platform: "Centrali",
30
- description: "Centrali is a low-code backend platform. You define data structures (collections), write compute functions, build orchestration workflows, and publish pages — all through APIs.",
30
+ description: "Centrali is a low-code backend platform. You define data collections, write compute functions, build orchestration workflows, and publish pages — all through APIs.",
31
31
  domains: {
32
32
  collections: {
33
- summary: "Data schemas (formerly 'structures'). Define the shape of your data — fields, types, constraints, references.",
33
+ summary: "Data schemas. Define the shape of your data — fields, types, constraints, references.",
34
34
  describeWith: "describe_collections",
35
35
  tools: [
36
36
  "list_collections",
@@ -224,12 +224,11 @@ function registerDescribeTools(server) {
224
224
  naming_guide: {
225
225
  description: "Different tools use different parameter names for the same concept (collection identifier). This is a historical naming drift — all of these refer to the same thing.",
226
226
  aliases: {
227
- recordSlug: "Used by record tools (query_records, create_record, etc.). This is the collection's URL-safe slug, e.g., 'orders'.",
228
- structureSlug: "Used by validation and insights tools. Same value as recordSlug.",
227
+ recordSlug: "Used across all tools the collection's URL-safe identifier (e.g., 'orders').",
229
228
  collections: "Used by search_records. Same value as recordSlug. Accepts a string or array of strings.",
230
- structureIds: "Used by generate_starter_pages. This is the collection UUID (not the slug). Get it from list_collections → id field.",
229
+ collectionIds: "Used by generate_starter_pages. This is the collection UUID (not the slug). Get it from list_collections → id field.",
231
230
  },
232
- rule: "When a tool asks for recordSlug, structureSlug, or collections — use the collection's slug (e.g., 'orders'). When a tool asks for structureIds — use the collection's UUID.",
231
+ rule: "When a tool asks for recordSlug or collections — use the collection's slug (e.g., 'orders'). When a tool asks for collectionIds — use the collection's UUID (from list_collections → id).",
233
232
  },
234
233
  workflow: "Typical workflow: 1) Define collections → 2) Create records → 3) Write compute functions → 4) Wire orchestrations → 5) Build pages to surface data → 6) Publish pages for end users. When building an app, also: 7) Create a service account → 8) Grant least-privilege permissions via remediation → 9) Create publishable keys for the frontend.",
235
234
  app_credential_setup: {
@@ -294,14 +293,14 @@ function registerDescribeTools(server) {
294
293
  });
295
294
  }));
296
295
  // ── Collections ────────────────────────────────────────────────────
297
- server.tool("describe_collections", "Get the schema reference for Centrali collections (data structures). Explains field types, property shapes, constraints, and how to define data models.", {}, () => __awaiter(this, void 0, void 0, function* () {
296
+ server.tool("describe_collections", "Get the schema reference for Centrali collections. Explains field types, property shapes, constraints, and how to define data models.", {}, () => __awaiter(this, void 0, void 0, function* () {
298
297
  return ({
299
298
  content: [
300
299
  {
301
300
  type: "text",
302
301
  text: JSON.stringify({
303
302
  domain: "Collections",
304
- description: "Collections (formerly 'structures') define the schema for your data. Each collection has a name, a recordSlug (used in API calls), and an array of properties that define the fields.",
303
+ description: "Collections define the schema for your data. Each collection has a name, a recordSlug (used in API calls), and an array of properties that define the fields.",
305
304
  collection_shape: {
306
305
  id: "UUID — auto-generated",
307
306
  name: "string — display name (e.g., 'Customers')",
@@ -382,7 +381,7 @@ function registerDescribeTools(server) {
382
381
  description: "Records are rows of data stored in collections. All custom field values live under the 'data' namespace.",
383
382
  record_shape: {
384
383
  id: "UUID — auto-generated",
385
- structureId: "UUID — the collection this record belongs to",
384
+ collectionId: "UUID — the collection this record belongs to",
386
385
  data: "object — all custom field values keyed by field name",
387
386
  createdAt: "ISO 8601 datetime",
388
387
  updatedAt: "ISO 8601 datetime",
@@ -523,7 +522,7 @@ function registerDescribeTools(server) {
523
522
  "record_updated — fires after a record is modified",
524
523
  "record_deleted — fires after a record is deleted",
525
524
  ],
526
- config: "Specify the collection (structureId) and event type to listen for",
525
+ config: "Specify the collection (collectionId) and event type to listen for",
527
526
  },
528
527
  scheduled: {
529
528
  description: "Runs on a cron schedule (e.g., every hour, daily at midnight).",
@@ -752,7 +751,7 @@ function registerDescribeTools(server) {
752
751
  smart_query_shape: {
753
752
  id: "UUID",
754
753
  name: "string — display name",
755
- structureSlug: "string — the collection this query targets",
754
+ recordSlug: "string — the collection this query targets",
756
755
  description: "string | null",
757
756
  query: "object — the query definition with filters, sort, fields, and aggregations",
758
757
  variables: "object[] — declared variables with name, type, and default values",
@@ -995,7 +994,7 @@ function registerDescribeTools(server) {
995
994
  description: "Centrali's AI-powered anomaly detection scans your data for unusual patterns. Insights are generated automatically or on-demand via trigger_anomaly_analysis.",
996
995
  insight_shape: {
997
996
  id: "UUID",
998
- structureSlug: "string — the collection where the anomaly was detected",
997
+ recordSlug: "string — the collection where the anomaly was detected",
999
998
  type: "string — anomaly type (e.g., 'spike', 'drop', 'outlier', 'pattern_break')",
1000
999
  severity: "'info' | 'warning' | 'critical'",
1001
1000
  status: "'active' | 'acknowledged' | 'dismissed'",
@@ -1045,7 +1044,7 @@ function registerDescribeTools(server) {
1045
1044
  },
1046
1045
  suggestion_shape: {
1047
1046
  id: "UUID",
1048
- structureSlug: "string — the collection containing the issue",
1047
+ recordSlug: "string — the collection containing the issue",
1049
1048
  recordId: "UUID — the specific record with the issue",
1050
1049
  field: "string — the field name where the issue was found",
1051
1050
  issueType: "'typo' | 'format' | 'duplicate' | 'semantic' | 'type'",
@@ -37,10 +37,10 @@ function formatError(error, context) {
37
37
  }
38
38
  function registerInsightTools(server, sdk) {
39
39
  server.tool("list_insights", "List anomaly insights detected by Centrali's AI analysis. Insights flag unusual patterns in your data such as spikes, drops, or outliers.", {
40
- structureSlug: zod_1.z
40
+ recordSlug: zod_1.z
41
41
  .string()
42
42
  .optional()
43
- .describe("Filter insights by structure slug. If omitted, returns insights for all structures."),
43
+ .describe("Filter insights by collection record slug. If omitted, returns insights for all collections."),
44
44
  status: zod_1.z
45
45
  .enum(["active", "acknowledged", "dismissed"])
46
46
  .optional()
@@ -49,11 +49,11 @@ function registerInsightTools(server, sdk) {
49
49
  .enum(["info", "warning", "critical"])
50
50
  .optional()
51
51
  .describe("Filter by severity level"),
52
- }, (_a) => __awaiter(this, [_a], void 0, function* ({ structureSlug, status, severity }) {
52
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, status, severity }) {
53
53
  try {
54
54
  const options = {};
55
- if (structureSlug)
56
- options.structureSlug = structureSlug;
55
+ if (recordSlug)
56
+ options.structureSlug = recordSlug;
57
57
  if (status)
58
58
  options.status = status;
59
59
  if (severity)
@@ -127,14 +127,14 @@ function registerInsightTools(server, sdk) {
127
127
  };
128
128
  }
129
129
  }));
130
- server.tool("get_insights_summary", "Get a summary of anomaly insights in the workspace — counts by status and severity. Optionally filter by structure.", {
131
- structureSlug: zod_1.z
130
+ server.tool("get_insights_summary", "Get a summary of anomaly insights in the workspace — counts by status and severity. Optionally filter by collection.", {
131
+ recordSlug: zod_1.z
132
132
  .string()
133
133
  .optional()
134
- .describe("Filter summary to a specific structure. If omitted, returns workspace-wide summary."),
135
- }, (_a) => __awaiter(this, [_a], void 0, function* ({ structureSlug }) {
134
+ .describe("Filter summary to a specific collection's record slug. If omitted, returns workspace-wide summary."),
135
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug }) {
136
136
  try {
137
- const result = yield sdk.anomalyInsights.getSummary(structureSlug);
137
+ const result = yield sdk.anomalyInsights.getSummary(recordSlug);
138
138
  return {
139
139
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
140
140
  };
@@ -148,11 +148,11 @@ function registerInsightTools(server, sdk) {
148
148
  };
149
149
  }
150
150
  }));
151
- server.tool("trigger_anomaly_analysis", "Trigger AI-powered anomaly analysis for a structure. Starts a background analysis to detect unusual patterns. Check list_insights after the scan completes.", {
152
- structureSlug: zod_1.z.string().describe("The structure's record slug to analyze"),
153
- }, (_a) => __awaiter(this, [_a], void 0, function* ({ structureSlug }) {
151
+ server.tool("trigger_anomaly_analysis", "Trigger AI-powered anomaly analysis for a collection. Starts a background analysis to detect unusual patterns. Check list_insights after the scan completes.", {
152
+ recordSlug: zod_1.z.string().describe("The collection's record slug to analyze"),
153
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug }) {
154
154
  try {
155
- const result = yield sdk.anomalyInsights.triggerAnalysis(structureSlug);
155
+ const result = yield sdk.anomalyInsights.triggerAnalysis(recordSlug);
156
156
  return {
157
157
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
158
158
  };
@@ -162,7 +162,7 @@ function registerInsightTools(server, sdk) {
162
162
  content: [
163
163
  {
164
164
  type: "text",
165
- text: formatError(error, `triggering anomaly analysis for '${structureSlug}'`),
165
+ text: formatError(error, `triggering anomaly analysis for '${recordSlug}'`),
166
166
  },
167
167
  ],
168
168
  isError: true,
@@ -117,7 +117,7 @@ function registerPageTools(server, sdk, centraliUrl, workspaceId) {
117
117
  const { client } = createPagesClient(sdk, centraliUrl, workspaceId);
118
118
  const basePath = `/ws/${workspaceId}/api/v1/pages`;
119
119
  // ── Page CRUD ──────────────────────────────────────────────────────
120
- server.tool("list_pages", "List all pages in the workspace. Pages are pre-built UI views (lists, detail views, forms, dashboards) that surface data from structures.", {
120
+ server.tool("list_pages", "List all pages in the workspace. Pages are pre-built UI views (lists, detail views, forms, dashboards) that surface data from collections.", {
121
121
  page: zod_1.z.number().optional().describe("Page number (1-indexed, default: 1)"),
122
122
  pageSize: zod_1.z.number().optional().describe("Items per page (default: 20)"),
123
123
  pageType: zod_1.z
@@ -161,7 +161,7 @@ function registerPageTools(server, sdk, centraliUrl, workspaceId) {
161
161
  };
162
162
  }
163
163
  }));
164
- server.tool("create_page", `Create a new page in the workspace. A page is a UI view backed by data from structures. Specify the page type: 'list' for data tables, 'detail' for single-record views, 'form' for data entry, 'dashboard' for metrics and charts.
164
+ server.tool("create_page", `Create a new page in the workspace. A page is a UI view backed by data from collections. Specify the page type: 'list' for data tables, 'detail' for single-record views, 'form' for data entry, 'dashboard' for metrics and charts.
165
165
 
166
166
  Navigate-to-page actions can use config.useQueryParams: true to pass selected row fields as URL query params to the target page. Pair with paramConfig: { source: 'row', mode: 'selected', selectedFields: ['id'] } to control which fields are passed. Use config.paramMapping: { sourceField: 'targetParam' } to rename fields in the URL (e.g., { requestId: 'id' } passes ?id=<value> instead of ?requestId=<value>). The target detail page can then use variable bindings with { source: 'url', param: 'id' } to read those params.`, {
167
167
  name: zod_1.z.string().describe("Display name for the page (e.g., 'Customer List')"),
@@ -474,17 +474,17 @@ Common patterns:
474
474
  }
475
475
  }));
476
476
  // ── Assembly (AI-assisted page generation) ────────────────────────
477
- server.tool("generate_starter_pages", "Generate page proposals from structure IDs. Uses guided assembly to create page definitions (list, detail, form, dashboard) tailored to the data schema. Returns proposals that can be reviewed and accepted.", {
478
- structureIds: zod_1.z
477
+ server.tool("generate_starter_pages", "Generate page proposals from collection IDs. Uses guided assembly to create page definitions (list, detail, form, dashboard) tailored to the data schema. Returns proposals that can be reviewed and accepted.", {
478
+ collectionIds: zod_1.z
479
479
  .array(zod_1.z.string())
480
- .describe("Array of structure IDs (UUIDs) to generate pages for"),
480
+ .describe("Array of collection IDs (UUIDs) to generate pages for. Get these from list_collections → id field."),
481
481
  goals: zod_1.z
482
482
  .array(zod_1.z.string())
483
483
  .optional()
484
484
  .describe("Optional goals to guide page generation (e.g., 'customer management dashboard', 'order tracking')"),
485
- }, (_a) => __awaiter(this, [_a], void 0, function* ({ structureIds, goals }) {
485
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ collectionIds, goals }) {
486
486
  try {
487
- const body = { structureIds };
487
+ const body = { structureIds: collectionIds };
488
488
  if (goals)
489
489
  body.goals = goals;
490
490
  const resp = yield client.post(`${basePath}/generate-starter-pages`, body);
@@ -34,10 +34,10 @@ function formatError(error, context) {
34
34
  return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
35
35
  }
36
36
  function registerRecordTools(server, sdk) {
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).", {
37
+ server.tool("query_records", "Query records from a collection 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).", {
38
38
  recordSlug: zod_1.z
39
39
  .string()
40
- .describe("The structure's record slug (e.g., 'orders')"),
40
+ .describe("The collection's record slug (e.g., 'orders')"),
41
41
  filters: zod_1.z
42
42
  .record(zod_1.z.string(), zod_1.z.any())
43
43
  .optional()
@@ -82,8 +82,8 @@ function registerRecordTools(server, sdk) {
82
82
  };
83
83
  }
84
84
  }));
85
- server.tool("get_record", "Get a single record by its ID from a structure. Optionally expand reference fields.", {
86
- recordSlug: zod_1.z.string().describe("The structure's record slug"),
85
+ server.tool("get_record", "Get a single record by its ID from a collection. Optionally expand reference fields.", {
86
+ recordSlug: zod_1.z.string().describe("The collection's record slug"),
87
87
  id: zod_1.z.string().describe("The record ID (UUID)"),
88
88
  expand: zod_1.z
89
89
  .string()
@@ -111,10 +111,10 @@ function registerRecordTools(server, sdk) {
111
111
  };
112
112
  }
113
113
  }));
114
- server.tool("create_record", "Create a new record in a structure. Pass the record data as a JSON object with field names matching the structure's properties.", {
114
+ server.tool("create_record", "Create a new record in a collection. Pass the record data as a JSON object with field names matching the collection's properties.", {
115
115
  recordSlug: zod_1.z
116
116
  .string()
117
- .describe("The structure's record slug (e.g., 'orders')"),
117
+ .describe("The collection's record slug (e.g., 'orders')"),
118
118
  data: zod_1.z
119
119
  .record(zod_1.z.string(), zod_1.z.any())
120
120
  .describe("The record data object (field names as keys)"),
@@ -140,7 +140,7 @@ function registerRecordTools(server, sdk) {
140
140
  }
141
141
  }));
142
142
  server.tool("update_record", "Update an existing record by ID. Only include the fields you want to change.", {
143
- recordSlug: zod_1.z.string().describe("The structure's record slug"),
143
+ recordSlug: zod_1.z.string().describe("The collection's record slug"),
144
144
  id: zod_1.z.string().describe("The record ID (UUID) to update"),
145
145
  data: zod_1.z
146
146
  .record(zod_1.z.string(), zod_1.z.any())
@@ -167,7 +167,7 @@ function registerRecordTools(server, sdk) {
167
167
  }
168
168
  }));
169
169
  server.tool("delete_record", "Delete a record by ID. Performs a soft delete by default (can be restored). Set hard=true for permanent deletion.", {
170
- recordSlug: zod_1.z.string().describe("The structure's record slug"),
170
+ recordSlug: zod_1.z.string().describe("The collection's record slug"),
171
171
  id: zod_1.z.string().describe("The record ID (UUID) to delete"),
172
172
  hard: zod_1.z
173
173
  .boolean()
@@ -200,7 +200,7 @@ function registerRecordTools(server, sdk) {
200
200
  }
201
201
  }));
202
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"),
203
+ recordSlug: zod_1.z.string().describe("The collection's record slug"),
204
204
  match: zod_1.z
205
205
  .record(zod_1.z.string(), zod_1.z.any())
206
206
  .describe("Fields to match on when looking for an existing record (e.g. { sku: 'WIDGET-001' })"),
@@ -229,7 +229,7 @@ function registerRecordTools(server, sdk) {
229
229
  }
230
230
  }));
231
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"),
232
+ recordSlug: zod_1.z.string().describe("The collection's record slug"),
233
233
  ids: zod_1.z.array(zod_1.z.string()).describe("Array of record IDs (UUIDs) to fetch"),
234
234
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, ids }) {
235
235
  try {
@@ -248,7 +248,7 @@ function registerRecordTools(server, sdk) {
248
248
  }
249
249
  }));
250
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"),
251
+ recordSlug: zod_1.z.string().describe("The collection's record slug"),
252
252
  id: zod_1.z.string().describe("The record ID (UUID) to restore"),
253
253
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, id }) {
254
254
  try {
@@ -34,29 +34,22 @@ function formatError(error, context) {
34
34
  return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
35
35
  }
36
36
  function registerSearchTools(server, sdk) {
37
- server.tool("search_records", "Full-text search across records in the workspace. Powered by Meilisearch. Optionally filter by structure(s) and limit results.", {
37
+ server.tool("search_records", "Full-text search across records in the workspace. Powered by Meilisearch. Optionally filter by collection(s) and limit results.", {
38
38
  query: zod_1.z.string().describe("The search query string"),
39
39
  collections: zod_1.z
40
40
  .union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())])
41
41
  .optional()
42
- .describe("Filter by collection slug(s). Single slug string or array of slugs"),
43
- structures: zod_1.z
44
- .union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())])
45
- .optional()
46
- .describe("[DEPRECATED: use collections instead] Filter by structure slug(s). Single slug string or array of slugs"),
42
+ .describe("Filter by collection record slug(s). Single slug string or array of slugs"),
47
43
  limit: zod_1.z
48
44
  .number()
49
45
  .optional()
50
46
  .describe("Maximum results to return (default: 20, max: 100)"),
51
- }, (_a) => __awaiter(this, [_a], void 0, function* ({ query, collections, structures, limit }) {
47
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ query, collections, limit }) {
52
48
  try {
53
49
  const options = {};
54
50
  if (collections) {
55
51
  options.collections = collections;
56
52
  }
57
- else if (structures) {
58
- options.collections = structures;
59
- }
60
53
  if (limit)
61
54
  options.limit = limit;
62
55
  const result = yield sdk.search(query, Object.keys(options).length > 0 ? options : undefined);
@@ -853,4 +853,141 @@ function registerServiceAccountTools(server, sdk, centraliUrl, workspaceId, ownC
853
853
  };
854
854
  }
855
855
  }));
856
+ // ── Policies CRUD ──────────────────────────────────────────────────
857
+ // These tools give the MCP the ability to create, inspect, and delete
858
+ // access policies directly — making remediation safely reversible.
859
+ const getPoliciesClient = () => createIamClient(sdk, centraliUrl, workspaceId, "access/policies");
860
+ const getPermissionsClient = () => createIamClient(sdk, centraliUrl, workspaceId, "access/permissions");
861
+ const getResourcesClient = () => createIamClient(sdk, centraliUrl, workspaceId, "access/resources");
862
+ server.tool("list_policies", "List all access control policies in the workspace. Policies define who can do what — they bind roles/groups/principals to permissions with optional conditions.", {
863
+ page: zod_1.z.number().optional().describe("Page number (default: 1)"),
864
+ pageSize: zod_1.z.number().optional().describe("Results per page (default: 20)"),
865
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ page, pageSize }) {
866
+ try {
867
+ const params = {};
868
+ if (page !== undefined)
869
+ params.page = page;
870
+ if (pageSize !== undefined)
871
+ params.pageSize = pageSize;
872
+ const result = yield getPoliciesClient().get("/", { params });
873
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
874
+ }
875
+ catch (error) {
876
+ return { content: [{ type: "text", text: formatError(error, "listing policies") }], isError: true };
877
+ }
878
+ }));
879
+ server.tool("get_policy", "Get the full definition of an access control policy by ID.", {
880
+ policyId: zod_1.z.string().describe("The policy ID"),
881
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ policyId }) {
882
+ try {
883
+ const result = yield getPoliciesClient().get(`/${policyId}`);
884
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
885
+ }
886
+ catch (error) {
887
+ return { content: [{ type: "text", text: formatError(error, `getting policy '${policyId}'`) }], isError: true };
888
+ }
889
+ }));
890
+ server.tool("create_policy", "Create an access control policy. Policies grant or deny actions on resources to principals (users, service accounts, groups, roles).", {
891
+ policy: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).describe("The policy definition object. Must include: name, effect ('allow'|'deny'), principals, resources, actions. May include conditions."),
892
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ policy }) {
893
+ try {
894
+ const result = yield getPoliciesClient().post("/", policy);
895
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
896
+ }
897
+ catch (error) {
898
+ return { content: [{ type: "text", text: formatError(error, "creating policy") }], isError: true };
899
+ }
900
+ }));
901
+ server.tool("update_policy", "Update an existing access control policy by ID.", {
902
+ policyId: zod_1.z.string().describe("The policy ID to update"),
903
+ policy: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).describe("The updated policy definition"),
904
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ policyId, policy }) {
905
+ try {
906
+ const result = yield getPoliciesClient().put(`/${policyId}`, policy);
907
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
908
+ }
909
+ catch (error) {
910
+ return { content: [{ type: "text", text: formatError(error, `updating policy '${policyId}'`) }], isError: true };
911
+ }
912
+ }));
913
+ server.tool("delete_policy", "Delete an access control policy by ID. This immediately revokes the access it granted. Use this to undo apply_remediation.", {
914
+ policyId: zod_1.z.string().describe("The policy ID to delete"),
915
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ policyId }) {
916
+ try {
917
+ yield getPoliciesClient().delete(`/${policyId}`);
918
+ return { content: [{ type: "text", text: `Policy '${policyId}' deleted.` }] };
919
+ }
920
+ catch (error) {
921
+ return { content: [{ type: "text", text: formatError(error, `deleting policy '${policyId}'`) }], isError: true };
922
+ }
923
+ }));
924
+ // ── Permissions CRUD ───────────────────────────────────────────────
925
+ server.tool("list_permissions", "List all permission definitions in the workspace. Permissions are resource + action pairs (e.g., 'workspace::records' + 'create').", {
926
+ page: zod_1.z.number().optional().describe("Page number (default: 1)"),
927
+ pageSize: zod_1.z.number().optional().describe("Results per page (default: 20)"),
928
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ page, pageSize }) {
929
+ try {
930
+ const params = {};
931
+ if (page !== undefined)
932
+ params.page = page;
933
+ if (pageSize !== undefined)
934
+ params.pageSize = pageSize;
935
+ const result = yield getPermissionsClient().get("/", { params });
936
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
937
+ }
938
+ catch (error) {
939
+ return { content: [{ type: "text", text: formatError(error, "listing permissions") }], isError: true };
940
+ }
941
+ }));
942
+ server.tool("create_permission", "Create a new permission definition (resource + action pair).", {
943
+ permission: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).describe("The permission definition. Must include: resource (name), action."),
944
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ permission }) {
945
+ try {
946
+ const result = yield getPermissionsClient().post("/", permission);
947
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
948
+ }
949
+ catch (error) {
950
+ return { content: [{ type: "text", text: formatError(error, "creating permission") }], isError: true };
951
+ }
952
+ }));
953
+ server.tool("delete_permission", "Delete a permission definition by ID.", {
954
+ permissionId: zod_1.z.string().describe("The permission ID to delete"),
955
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ permissionId }) {
956
+ try {
957
+ yield getPermissionsClient().delete(`/${permissionId}`);
958
+ return { content: [{ type: "text", text: `Permission '${permissionId}' deleted.` }] };
959
+ }
960
+ catch (error) {
961
+ return { content: [{ type: "text", text: formatError(error, `deleting permission '${permissionId}'`) }], isError: true };
962
+ }
963
+ }));
964
+ // ── Resources ──────────────────────────────────────────────────────
965
+ server.tool("list_resources", "List all protected resource definitions in the workspace. Resources are the things permissions act on (e.g., 'workspace::records', 'workspace::functions').", {
966
+ page: zod_1.z.number().optional().describe("Page number (default: 1)"),
967
+ pageSize: zod_1.z.number().optional().describe("Results per page (default: 20)"),
968
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ page, pageSize }) {
969
+ try {
970
+ const params = {};
971
+ if (page !== undefined)
972
+ params.page = page;
973
+ if (pageSize !== undefined)
974
+ params.pageSize = pageSize;
975
+ const result = yield getResourcesClient().get("/", { params });
976
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
977
+ }
978
+ catch (error) {
979
+ return { content: [{ type: "text", text: formatError(error, "listing resources") }], isError: true };
980
+ }
981
+ }));
982
+ server.tool("get_resource", "Get details of a protected resource definition by ID.", {
983
+ resourceId: zod_1.z.string().describe("The resource ID"),
984
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ resourceId }) {
985
+ try {
986
+ const result = yield getResourcesClient().get(`/${resourceId}`);
987
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
988
+ }
989
+ catch (error) {
990
+ return { content: [{ type: "text", text: formatError(error, `getting resource '${resourceId}'`) }], isError: true };
991
+ }
992
+ }));
856
993
  }
@@ -34,11 +34,11 @@ function formatError(error, context) {
34
34
  return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
35
35
  }
36
36
  function registerSmartQueryTools(server, sdk) {
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.", {
37
+ server.tool("list_smart_queries", "List smart queries. Smart queries are reusable, parameterized queries defined in the Centrali console. Optionally filter by collection record slug.", {
38
38
  recordSlug: zod_1.z
39
39
  .string()
40
40
  .optional()
41
- .describe("Filter by structure slug. If omitted, lists all smart queries in the workspace"),
41
+ .describe("Filter by collection record slug. If omitted, lists all smart queries in the workspace"),
42
42
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug }) {
43
43
  try {
44
44
  const result = recordSlug
@@ -65,7 +65,7 @@ function registerSmartQueryTools(server, sdk) {
65
65
  server.tool("execute_smart_query", "Execute a smart query by ID and return the results. Smart queries can have parameterized variables using {{variableName}} syntax.", {
66
66
  recordSlug: zod_1.z
67
67
  .string()
68
- .describe("The structure's record slug the query belongs to"),
68
+ .describe("The collection's record slug the query belongs to"),
69
69
  queryId: zod_1.z.string().describe("The smart query ID (UUID) to execute"),
70
70
  variables: zod_1.z
71
71
  .record(zod_1.z.string(), zod_1.z.string())
@@ -94,7 +94,7 @@ function registerSmartQueryTools(server, sdk) {
94
94
  }
95
95
  }));
96
96
  server.tool("get_smart_query", "Get a smart query by ID. Returns the full query definition including filters, sort, and variable declarations.", {
97
- recordSlug: zod_1.z.string().describe("The structure's record slug the query belongs to"),
97
+ recordSlug: zod_1.z.string().describe("The collection's record slug the query belongs to"),
98
98
  queryId: zod_1.z.string().describe("The smart query ID (UUID)"),
99
99
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, queryId }) {
100
100
  try {
@@ -118,7 +118,7 @@ function registerSmartQueryTools(server, sdk) {
118
118
  }
119
119
  }));
120
120
  server.tool("create_smart_query", "Create a new smart query for a collection. Smart queries are reusable, parameterized queries with filter, sort, and variable support.", {
121
- recordSlug: zod_1.z.string().describe("The structure's record slug to create the query for"),
121
+ recordSlug: zod_1.z.string().describe("The collection's record slug to create the query for"),
122
122
  name: zod_1.z.string().describe("Display name for the smart query"),
123
123
  description: zod_1.z.string().optional().describe("Optional description"),
124
124
  queryDefinition: zod_1.z
@@ -149,7 +149,7 @@ function registerSmartQueryTools(server, sdk) {
149
149
  }
150
150
  }));
151
151
  server.tool("update_smart_query", "Update an existing smart query. Only include the fields you want to change.", {
152
- recordSlug: zod_1.z.string().describe("The structure's record slug the query belongs to"),
152
+ recordSlug: zod_1.z.string().describe("The collection's record slug the query belongs to"),
153
153
  queryId: zod_1.z.string().describe("The smart query ID (UUID) to update"),
154
154
  name: zod_1.z.string().optional().describe("Updated display name"),
155
155
  description: zod_1.z.string().optional().describe("Updated description"),
@@ -186,7 +186,7 @@ function registerSmartQueryTools(server, sdk) {
186
186
  }
187
187
  }));
188
188
  server.tool("delete_smart_query", "Delete a smart query by ID.", {
189
- recordSlug: zod_1.z.string().describe("The structure's record slug the query belongs to"),
189
+ recordSlug: zod_1.z.string().describe("The collection's record slug the query belongs to"),
190
190
  queryId: zod_1.z.string().describe("The smart query ID (UUID) to delete"),
191
191
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, queryId }) {
192
192
  try {
@@ -213,7 +213,7 @@ function registerSmartQueryTools(server, sdk) {
213
213
  }
214
214
  }));
215
215
  server.tool("test_smart_query", "Test execute a query definition without saving it. Useful for validating query syntax and previewing results before creating a smart query.", {
216
- recordSlug: zod_1.z.string().describe("The structure's record slug to test against"),
216
+ recordSlug: zod_1.z.string().describe("The collection's record slug to test against"),
217
217
  queryDefinition: zod_1.z
218
218
  .record(zod_1.z.string(), zod_1.z.any())
219
219
  .describe("The query definition to test (where, sort, limit, select, etc.)"),
@@ -36,16 +36,16 @@ function formatError(error, context) {
36
36
  return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
37
37
  }
38
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"),
39
+ server.tool("trigger_validation_scan", "Trigger an AI-powered data quality scan on a collection. 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
+ recordSlug: zod_1.z.string().describe("The collection's record slug to scan"),
41
41
  validationTypes: zod_1.z
42
42
  .array(zod_1.z.enum(["typo", "format", "duplicate", "semantic", "type"]))
43
43
  .optional()
44
44
  .describe("Specific validation types to run. If omitted, all types are run."),
45
- }, (_a) => __awaiter(this, [_a], void 0, function* ({ structureSlug, validationTypes }) {
45
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, validationTypes }) {
46
46
  try {
47
47
  const options = validationTypes ? { validationTypes } : undefined;
48
- const result = yield sdk.validation.triggerScan(structureSlug, options);
48
+ const result = yield sdk.validation.triggerScan(recordSlug, options);
49
49
  return {
50
50
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
51
51
  };
@@ -55,7 +55,7 @@ function registerValidationTools(server, sdk) {
55
55
  content: [
56
56
  {
57
57
  type: "text",
58
- text: formatError(error, `triggering validation scan for '${structureSlug}'`),
58
+ text: formatError(error, `triggering validation scan for '${recordSlug}'`),
59
59
  },
60
60
  ],
61
61
  isError: true,
@@ -63,10 +63,10 @@ function registerValidationTools(server, sdk) {
63
63
  }
64
64
  }));
65
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
66
+ recordSlug: zod_1.z
67
67
  .string()
68
68
  .optional()
69
- .describe("Filter suggestions to a specific structure"),
69
+ .describe("Filter suggestions to a specific collection's record slug"),
70
70
  status: zod_1.z
71
71
  .enum(["pending", "accepted", "rejected", "auto-applied"])
72
72
  .optional()
@@ -79,11 +79,11 @@ function registerValidationTools(server, sdk) {
79
79
  .number()
80
80
  .optional()
81
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 }) {
82
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, status, issueType, minConfidence }) {
83
83
  try {
84
84
  const options = {};
85
- if (structureSlug)
86
- options.structureSlug = structureSlug;
85
+ if (recordSlug)
86
+ options.structureSlug = recordSlug;
87
87
  if (status)
88
88
  options.status = status;
89
89
  if (issueType)
@@ -107,14 +107,14 @@ function registerValidationTools(server, sdk) {
107
107
  };
108
108
  }
109
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
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 collection.", {
111
+ recordSlug: zod_1.z
112
112
  .string()
113
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 }) {
114
+ .describe("Filter summary to a specific collection's record slug. If omitted, returns workspace-wide summary."),
115
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug }) {
116
116
  try {
117
- const result = yield sdk.validation.getSummary(structureSlug);
117
+ const result = yield sdk.validation.getSummary(recordSlug);
118
118
  return {
119
119
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
120
120
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centrali-io/centrali-mcp",
3
- "version": "4.4.8-rc.2",
3
+ "version": "4.4.8-rc.4",
4
4
  "description": "Centrali MCP Server - AI assistant integration for Centrali workspaces",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",