@centrali-io/centrali-mcp 4.2.2 → 4.2.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.
@@ -184,28 +184,30 @@ function registerDescribeTools(server) {
184
184
  },
185
185
  property_shape: {
186
186
  name: "string — field name (e.g., 'email', 'totalAmount')",
187
- type: "string — the field data type (see field_types below)",
187
+ type: "string — one of: string, number, boolean, datetime, array, object, reference",
188
+ description: "string | null — optional human-readable description",
188
189
  required: "boolean — whether the field is mandatory",
189
- unique: "boolean — whether values must be unique across records",
190
- defaultValue: "any | null default value for new records",
191
- description: "string | null — field description",
192
- config: "object | null — type-specific configuration (e.g., min/max for numbers, options for select)",
190
+ nullable: "boolean — whether the field accepts null values",
191
+ isUnique: "boolean whether values must be unique across records",
192
+ immutable: "boolean — field cannot be changed after creation",
193
+ default: "any | null — default value for new records",
194
+ enum: "any[] | null — restrict to specific allowed values",
193
195
  },
194
196
  field_types: {
195
- text: "Short text string",
196
- long_text: "Multi-line text / rich text",
197
- number: "Numeric value (integer or decimal). Config: { min, max, precision }",
198
- boolean: "True/false toggle",
199
- date: "Date value (ISO 8601)",
200
- datetime: "Date and time value (ISO 8601 with time)",
201
- email: "Email address (validated format)",
202
- url: "URL (validated format)",
203
- select: "Single choice from predefined options. Config: { options: [{ label, value }] }",
204
- multi_select: "Multiple choices from predefined options. Config: { options: [{ label, value }] }",
205
- reference: "Foreign key to another collection. Config: { referenceSlug: 'other-collection-slug' }",
206
- file: "File attachment (stored in Azure Blob Storage)",
207
- json: "Arbitrary JSON object",
208
- auto_increment: "Auto-incrementing integer (read-only)",
197
+ string: "Text field. Options: minLength, maxLength, pattern (regex), renderAs ('textarea'|'secret'|'color'|'code'|'html'|'markdown'), enum (string[]), not (disallowed values), isSecret (boolean). Use enum for select-like behavior.",
198
+ number: "Numeric field (integer or decimal). Options: minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, enum (number[]), autoIncrement ({ startAt, incrementBy? }).",
199
+ boolean: "True/false toggle. Options: default (boolean), enum ([true]|[false]).",
200
+ datetime: "ISO 8601 date/time field. Options: earliestDate, latestDate, exclusiveEarliest, exclusiveLatest, default (ISO string), enum (ISO string[]).",
201
+ array: "Array of typed items. Required: items: { type: 'string'|'number'|'boolean'|'datetime'|'object' }. Options: minItems, maxItems, uniqueItems, itemSchema (PropertyDefinition[] when items.type='object').",
202
+ object: "Nested object. Options: properties (PropertyDefinition[]), requiredProperties (string[]), strictProperties (boolean — reject unknown fields).",
203
+ reference: "Foreign key to another collection. Required: target (collection recordSlug), relationship ('many-to-one'|'one-to-one'|'many-to-many'), onDelete ('restrict'|'cascade'|'set_null'). Options: targetField (default: 'id'), displayField, nullable (required if onDelete='set_null').",
204
+ },
205
+ examples: {
206
+ string_with_enum: '{ "name": "status", "type": "string", "required": true, "enum": ["active", "inactive", "archived"] }',
207
+ number_with_range: '{ "name": "price", "type": "number", "minimum": 0, "maximum": 10000 }',
208
+ reference: '{ "name": "categoryId", "type": "reference", "target": "categories", "relationship": "many-to-one", "onDelete": "restrict" }',
209
+ array_of_strings: '{ "name": "tags", "type": "array", "items": { "type": "string" } }',
210
+ nested_object: '{ "name": "address", "type": "object", "properties": [{ "name": "street", "type": "string" }, { "name": "city", "type": "string" }] }',
209
211
  },
210
212
  crud_tools: {
211
213
  create_collection: {
@@ -231,6 +233,10 @@ function registerDescribeTools(server) {
231
233
  "Use create_collection to define new data schemas with typed properties",
232
234
  "Properties array defines the fields — each needs at minimum a name and type",
233
235
  "Use schemaDiscoveryMode: 'flexible' to auto-add new fields when records are created",
236
+ "For enum/select behavior, use type 'string' with an 'enum' array — there is no 'select' type",
237
+ "For multi-line text, use type 'string' with renderAs: 'textarea'",
238
+ "For JSON blobs, use type 'object' with strictProperties: false",
239
+ "Property options go at the top level of the property object, NOT nested inside a 'config' key",
234
240
  ],
235
241
  }, null, 2),
236
242
  },
@@ -135,23 +135,23 @@ function registerCollectionTools(server, sdk) {
135
135
  };
136
136
  }
137
137
  }));
138
- server.tool("create_collection", "Create a new data collection (schema). Define the name, slug, description, and properties (fields) for the collection.", {
138
+ server.tool("create_collection", "Create a new data collection (schema). Define the name, recordSlug, description, and properties (fields) for the collection.", {
139
139
  name: zod_1.z.string().describe("Display name for the collection (e.g., 'Customers')"),
140
- slug: zod_1.z.string().describe("URL-safe identifier used in API calls (e.g., 'customers')"),
140
+ recordSlug: zod_1.z.string().describe("URL-safe identifier used in API calls (e.g., 'customers'). This is the recordSlug, not 'slug'."),
141
141
  description: zod_1.z.string().optional().describe("Optional description of the collection"),
142
142
  properties: zod_1.z
143
143
  .array(zod_1.z.record(zod_1.z.string(), zod_1.z.any()))
144
144
  .optional()
145
- .describe("Array of property definitions. Each property has name, type, and optional config (required, unique, defaultValue, description, config)"),
145
+ .describe("Array of property definitions. Each property needs 'name' and 'type' (string|number|boolean|datetime|array|object|reference). Type-specific options go at the top level, NOT inside a 'config' key. Use describe_collections for full schema reference."),
146
146
  enableVersioning: zod_1.z.boolean().optional().describe("Enable record versioning (default: false)"),
147
147
  schemaDiscoveryMode: zod_1.z
148
148
  .enum(["strict", "flexible"])
149
149
  .optional()
150
150
  .describe("Schema discovery mode: 'strict' rejects unknown fields, 'flexible' auto-adds them"),
151
151
  tags: zod_1.z.array(zod_1.z.string()).optional().describe("Optional tags for organizing collections"),
152
- }, (_a) => __awaiter(this, [_a], void 0, function* ({ name, slug, description, properties, enableVersioning, schemaDiscoveryMode, tags }) {
152
+ }, (_a) => __awaiter(this, [_a], void 0, function* ({ name, recordSlug, description, properties, enableVersioning, schemaDiscoveryMode, tags }) {
153
153
  try {
154
- const input = { name, slug };
154
+ const input = { name, recordSlug };
155
155
  if (description !== undefined)
156
156
  input.description = description;
157
157
  if (properties !== undefined)
@@ -174,7 +174,7 @@ function registerCollectionTools(server, sdk) {
174
174
  content: [
175
175
  {
176
176
  type: "text",
177
- text: formatError(error, `creating collection '${slug}'`),
177
+ text: formatError(error, `creating collection '${recordSlug}'`),
178
178
  },
179
179
  ],
180
180
  isError: true,
@@ -191,7 +191,11 @@ function registerCollectionTools(server, sdk) {
191
191
  .describe("Updated array of property definitions (replaces existing properties)"),
192
192
  enableVersioning: zod_1.z.boolean().optional().describe("Enable or disable record versioning"),
193
193
  tags: zod_1.z.array(zod_1.z.string()).optional().describe("Updated tags"),
194
- defaultTtlSeconds: zod_1.z.number().nullable().optional().describe("Default TTL in seconds for new records. Set to null to clear."),
194
+ defaultTtlSeconds: zod_1.z
195
+ .number()
196
+ .nullable()
197
+ .optional()
198
+ .describe("Default TTL in seconds for new records. Set to null to clear."),
195
199
  }, (_a) => __awaiter(this, [_a], void 0, function* ({ collectionId, name, description, properties, enableVersioning, tags, defaultTtlSeconds }) {
196
200
  try {
197
201
  const input = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centrali-io/centrali-mcp",
3
- "version": "4.2.2",
3
+ "version": "4.2.4",
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": "^4.2.0",
28
+ "@centrali-io/centrali-sdk": "^4.2.4",
29
29
  "@modelcontextprotocol/sdk": "^1.12.1"
30
30
  },
31
31
  "devDependencies": {
@@ -201,32 +201,41 @@ export function registerDescribeTools(server: McpServer) {
201
201
  },
202
202
  property_shape: {
203
203
  name: "string — field name (e.g., 'email', 'totalAmount')",
204
- type: "string — the field data type (see field_types below)",
204
+ type: "string — one of: string, number, boolean, datetime, array, object, reference",
205
+ description: "string | null — optional human-readable description",
205
206
  required: "boolean — whether the field is mandatory",
206
- unique: "boolean — whether values must be unique across records",
207
- defaultValue: "any | null default value for new records",
208
- description: "string | null — field description",
209
- config:
210
- "object | null — type-specific configuration (e.g., min/max for numbers, options for select)",
207
+ nullable: "boolean — whether the field accepts null values",
208
+ isUnique: "boolean whether values must be unique across records",
209
+ immutable: "boolean — field cannot be changed after creation",
210
+ default: "any | null — default value for new records",
211
+ enum: "any[] | null — restrict to specific allowed values",
211
212
  },
212
213
  field_types: {
213
- text: "Short text string",
214
- long_text: "Multi-line text / rich text",
215
- number: "Numeric value (integer or decimal). Config: { min, max, precision }",
216
- boolean: "True/false toggle",
217
- date: "Date value (ISO 8601)",
218
- datetime: "Date and time value (ISO 8601 with time)",
219
- email: "Email address (validated format)",
220
- url: "URL (validated format)",
221
- select:
222
- "Single choice from predefined options. Config: { options: [{ label, value }] }",
223
- multi_select:
224
- "Multiple choices from predefined options. Config: { options: [{ label, value }] }",
214
+ string:
215
+ "Text field. Options: minLength, maxLength, pattern (regex), renderAs ('textarea'|'secret'|'color'|'code'|'html'|'markdown'), enum (string[]), not (disallowed values), isSecret (boolean). Use enum for select-like behavior.",
216
+ number:
217
+ "Numeric field (integer or decimal). Options: minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, enum (number[]), autoIncrement ({ startAt, incrementBy? }).",
218
+ boolean: "True/false toggle. Options: default (boolean), enum ([true]|[false]).",
219
+ datetime:
220
+ "ISO 8601 date/time field. Options: earliestDate, latestDate, exclusiveEarliest, exclusiveLatest, default (ISO string), enum (ISO string[]).",
221
+ array:
222
+ "Array of typed items. Required: items: { type: 'string'|'number'|'boolean'|'datetime'|'object' }. Options: minItems, maxItems, uniqueItems, itemSchema (PropertyDefinition[] when items.type='object').",
223
+ object:
224
+ "Nested object. Options: properties (PropertyDefinition[]), requiredProperties (string[]), strictProperties (boolean — reject unknown fields).",
225
225
  reference:
226
- "Foreign key to another collection. Config: { referenceSlug: 'other-collection-slug' }",
227
- file: "File attachment (stored in Azure Blob Storage)",
228
- json: "Arbitrary JSON object",
229
- auto_increment: "Auto-incrementing integer (read-only)",
226
+ "Foreign key to another collection. Required: target (collection recordSlug), relationship ('many-to-one'|'one-to-one'|'many-to-many'), onDelete ('restrict'|'cascade'|'set_null'). Options: targetField (default: 'id'), displayField, nullable (required if onDelete='set_null').",
227
+ },
228
+ examples: {
229
+ string_with_enum:
230
+ '{ "name": "status", "type": "string", "required": true, "enum": ["active", "inactive", "archived"] }',
231
+ number_with_range:
232
+ '{ "name": "price", "type": "number", "minimum": 0, "maximum": 10000 }',
233
+ reference:
234
+ '{ "name": "categoryId", "type": "reference", "target": "categories", "relationship": "many-to-one", "onDelete": "restrict" }',
235
+ array_of_strings:
236
+ '{ "name": "tags", "type": "array", "items": { "type": "string" } }',
237
+ nested_object:
238
+ '{ "name": "address", "type": "object", "properties": [{ "name": "street", "type": "string" }, { "name": "city", "type": "string" }] }',
230
239
  },
231
240
  crud_tools: {
232
241
  create_collection: {
@@ -252,6 +261,10 @@ export function registerDescribeTools(server: McpServer) {
252
261
  "Use create_collection to define new data schemas with typed properties",
253
262
  "Properties array defines the fields — each needs at minimum a name and type",
254
263
  "Use schemaDiscoveryMode: 'flexible' to auto-add new fields when records are created",
264
+ "For enum/select behavior, use type 'string' with an 'enum' array — there is no 'select' type",
265
+ "For multi-line text, use type 'string' with renderAs: 'textarea'",
266
+ "For JSON blobs, use type 'object' with strictProperties: false",
267
+ "Property options go at the top level of the property object, NOT nested inside a 'config' key",
255
268
  ],
256
269
  },
257
270
  null,
@@ -146,15 +146,17 @@ export function registerCollectionTools(server: McpServer, sdk: CentraliSDK) {
146
146
 
147
147
  server.tool(
148
148
  "create_collection",
149
- "Create a new data collection (schema). Define the name, slug, description, and properties (fields) for the collection.",
149
+ "Create a new data collection (schema). Define the name, recordSlug, description, and properties (fields) for the collection.",
150
150
  {
151
151
  name: z.string().describe("Display name for the collection (e.g., 'Customers')"),
152
- slug: z.string().describe("URL-safe identifier used in API calls (e.g., 'customers')"),
152
+ recordSlug: z.string().describe("URL-safe identifier used in API calls (e.g., 'customers'). This is the recordSlug, not 'slug'."),
153
153
  description: z.string().optional().describe("Optional description of the collection"),
154
154
  properties: z
155
155
  .array(z.record(z.string(), z.any()))
156
156
  .optional()
157
- .describe("Array of property definitions. Each property has name, type, and optional config (required, unique, defaultValue, description, config)"),
157
+ .describe(
158
+ "Array of property definitions. Each property needs 'name' and 'type' (string|number|boolean|datetime|array|object|reference). Type-specific options go at the top level, NOT inside a 'config' key. Use describe_collections for full schema reference."
159
+ ),
158
160
  enableVersioning: z.boolean().optional().describe("Enable record versioning (default: false)"),
159
161
  schemaDiscoveryMode: z
160
162
  .enum(["strict", "flexible"])
@@ -162,15 +164,14 @@ export function registerCollectionTools(server: McpServer, sdk: CentraliSDK) {
162
164
  .describe("Schema discovery mode: 'strict' rejects unknown fields, 'flexible' auto-adds them"),
163
165
  tags: z.array(z.string()).optional().describe("Optional tags for organizing collections"),
164
166
  },
165
- async ({ name, slug, description, properties, enableVersioning, schemaDiscoveryMode, tags }) => {
167
+ async ({ name, recordSlug, description, properties, enableVersioning, schemaDiscoveryMode, tags }) => {
166
168
  try {
167
- const input: Record<string, any> = { name, slug };
169
+ const input: Record<string, any> = { name, recordSlug };
168
170
  if (description !== undefined) input.description = description;
169
171
  if (properties !== undefined) input.properties = properties;
170
172
  if (enableVersioning !== undefined) input.enableVersioning = enableVersioning;
171
173
  if (schemaDiscoveryMode !== undefined) input.schemaDiscoveryMode = schemaDiscoveryMode;
172
174
  if (tags !== undefined) input.tags = tags;
173
-
174
175
  const result = await sdk.collections.create(input as any);
175
176
  return {
176
177
  content: [
@@ -182,7 +183,7 @@ export function registerCollectionTools(server: McpServer, sdk: CentraliSDK) {
182
183
  content: [
183
184
  {
184
185
  type: "text",
185
- text: formatError(error, `creating collection '${slug}'`),
186
+ text: formatError(error, `creating collection '${recordSlug}'`),
186
187
  },
187
188
  ],
188
189
  isError: true,
@@ -204,7 +205,11 @@ export function registerCollectionTools(server: McpServer, sdk: CentraliSDK) {
204
205
  .describe("Updated array of property definitions (replaces existing properties)"),
205
206
  enableVersioning: z.boolean().optional().describe("Enable or disable record versioning"),
206
207
  tags: z.array(z.string()).optional().describe("Updated tags"),
207
- defaultTtlSeconds: z.number().nullable().optional().describe("Default TTL in seconds for new records. Set to null to clear."),
208
+ defaultTtlSeconds: z
209
+ .number()
210
+ .nullable()
211
+ .optional()
212
+ .describe("Default TTL in seconds for new records. Set to null to clear."),
208
213
  },
209
214
  async ({ collectionId, name, description, properties, enableVersioning, tags, defaultTtlSeconds }) => {
210
215
  try {
@@ -215,7 +220,6 @@ export function registerCollectionTools(server: McpServer, sdk: CentraliSDK) {
215
220
  if (enableVersioning !== undefined) input.enableVersioning = enableVersioning;
216
221
  if (tags !== undefined) input.tags = tags;
217
222
  if (defaultTtlSeconds !== undefined) input.defaultTtlSeconds = defaultTtlSeconds;
218
-
219
223
  const result = await sdk.collections.update(collectionId, input as any);
220
224
  return {
221
225
  content: [