@enfyra/mcp-server 0.0.56 → 0.0.57
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/package.json
CHANGED
package/src/lib/mcp-examples.js
CHANGED
|
@@ -203,10 +203,21 @@ create_column({
|
|
|
203
203
|
defaultValue: "pending",
|
|
204
204
|
isPublished: true,
|
|
205
205
|
description: "Email verification state controlled by server hooks."
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
create_column({
|
|
209
|
+
tableId: "<project_env_table_id>",
|
|
210
|
+
name: "value",
|
|
211
|
+
type: "text",
|
|
212
|
+
isNullable: false,
|
|
213
|
+
isPublished: false,
|
|
214
|
+
isEncrypted: true,
|
|
215
|
+
description: "Encrypted environment value."
|
|
206
216
|
})`,
|
|
207
217
|
notes: [
|
|
208
218
|
'Run schema-changing calls sequentially. Do not parallelize create_column calls.',
|
|
209
219
|
'create_column fetches table_definition and patches only real persisted columns with id/_id; generated metadata projections such as createdAt, updatedAt, or relation FK display fields are skipped.',
|
|
220
|
+
'Use isEncrypted=true for encryption at rest. Add isUpdatable=false separately only when the field should be immutable.',
|
|
210
221
|
'Use hooks or field permissions to prevent clients from updating server-owned fields.',
|
|
211
222
|
],
|
|
212
223
|
},
|
|
@@ -139,7 +139,7 @@ export function buildMcpServerInstructions(apiBaseUrl) {
|
|
|
139
139
|
'### Dynamic script syntax preference',
|
|
140
140
|
'- When writing server-side Enfyra scripts, prefer template macros over raw `$ctx` access: use `@BODY`, `@QUERY`, `@PARAMS`, `@USER`, `@REPOS`, `@HELPERS`, `@SOCKET`, `@TRIGGER`, `@DATA`, `@ERROR`, and `@THROW400`–`@THROW503`.',
|
|
141
141
|
'- Use Enfyra native throw helpers for intentional errors: `@THROW400("message")`, `@THROW403()`, `@THROW404("resource", id)`, or `$ctx.$throw[400]("message")`. Do not generate `throw new Error(...)` for user/domain errors in handlers, hooks, flows, websocket events, OAuth scripts, or admin-generated scripts.',
|
|
142
|
-
'- For encrypted
|
|
142
|
+
'- For regular app data that must be encrypted at rest, create the column with `isEncrypted=true`; Enfyra database-query hooks will encrypt on insert/update and decrypt after select. `isEncrypted` does not imply immutability; use `isUpdatable=false` separately only when the field itself must be immutable. Do not filter or sort on encrypted fields. Legacy Cloud control-plane fields named `*_encrypted` may still use route pre-hooks until migrated.',
|
|
143
143
|
'- ASV exposes `$helpers.$encrypt.encrypt/decrypt` for encrypted strings and `$helpers.$ssh.generateKeyPair` for SSH keys. Do not generate `$helpers.$secrets` usage.',
|
|
144
144
|
'- Script-backed records use one shared persistence contract: `sourceCode` is the editable source, `scriptLanguage` controls compilation, and `compiledCode` is generated by the server from `sourceCode`. Do not hand-edit or send stale `compiledCode` from generated tools; save `sourceCode`/`scriptLanguage` through `PATCH /<script_table>/<id>` and let the server persist generated `compiledCode` internally. Public metadata may mark `compiledCode` non-updatable, but the server engine must still preserve the generated value after normalization.',
|
|
145
145
|
'- For route handlers specifically, the field is also `sourceCode`. Older names such as `logic` are wrong for current Enfyra REST CRUD and will be rejected. Use MCP `create_handler` so it writes `sourceCode` and resolves method ids correctly.',
|
|
@@ -205,7 +205,7 @@ export function buildMcpServerInstructions(apiBaseUrl) {
|
|
|
205
205
|
'- To check which tables have canonical CRUD routes, call `get_all_routes` and look for `mainTable`. Custom routes intentionally have no `mainTable`; inspect their handlers/hooks to see which repos they touch.',
|
|
206
206
|
'- **Tables confirmed to have REST routes (system):** `bootstrap_script_definition`, `column_rule_definition`, `cors_origin_definition`, `extension_definition`, `field_permission_definition`, `file_definition`, `file_permission_definition`, `flow_definition`, `flow_execution_definition`, `flow_step_definition`, `folder_definition`, `gql_definition`, `guard_definition`, `guard_rule_definition`, `menu_definition`, `method_definition`, `oauth_account_definition`, `oauth_config_definition`, `package_definition`, `post_hook_definition`, `pre_hook_definition`, `relation_definition`, `role_definition`, `route_definition`, `route_handler_definition`, `route_permission_definition`, `schema_migration_definition`, `setting_definition`, `storage_config_definition`, `table_definition`, `user_definition`, `websocket_definition`, `websocket_event_definition`.',
|
|
207
207
|
'- **Tables without REST routes (internal/system only):** `column_definition`, `session_definition`. Columns are managed indirectly via cascade on `table_definition` (POST/PATCH with columns arrays). The `create_table`, `create_column`/`add_column`, `update_column`, and `delete_column`/`remove_column` MCP tools handle this automatically by reading full table metadata first.',
|
|
208
|
-
'- Use `create_column`/`add_column` for new scalar fields. These tools accept column metadata such as `isNullable`, `isUnique`, `isPublished`, `isPrimary`, `isGenerated`, `isSystem`, `defaultValue`, `description`, and `options`; set `isPublished=false` directly when creating secret/internal fields
|
|
208
|
+
'- Use `create_column`/`add_column` for new scalar fields. These tools accept column metadata such as `isNullable`, `isUnique`, `isPublished`, `isUpdatable`, `isEncrypted`, `isPrimary`, `isGenerated`, `isSystem`, `defaultValue`, `description`, and `options`; set `isUpdatable=false` for immutable fields and set `isPublished=false` directly when creating secret/internal fields. `isEncrypted=true` encrypts stored values at rest but does not change `isUpdatable`; encrypted fields must not be used for filter/sort. When patching an existing table, only persisted columns with an `id`/`_id` belong in the cascade payload; metadata projections such as `createdAt`, `updatedAt`, or relation-derived FK display fields without an id are not valid column-definition patch rows. Never rebuild a schema cascade from `table_definition?fields=columns.*`, because nested relation fields may be paginated/truncated.',
|
|
209
209
|
'- Prefer `create_relation`/`add_relation` and `delete_relation`/`remove_relation` for relation schema changes because they preserve the full table relation list, serialize schema mutations, verify unrelated relation ids survived, and handle schema-confirm retry. Direct `create_record` on `relation_definition` only edits metadata and is not the canonical schema migration path; generic record mutations also reject physical FK/junction fields on `relation_definition`.',
|
|
210
210
|
'- Destructive schema tools and generic `delete_record` return a preview unless `confirm=true` is passed. This applies to `delete_record`, `delete_table`, `delete_column`/`remove_column`, and `delete_relation`/`remove_relation`; do not add `confirm=true` until the user has explicitly approved the destructive operation.',
|
|
211
211
|
'',
|
package/src/lib/table-tools.js
CHANGED
|
@@ -208,12 +208,14 @@ async function verifyRelationCascade(ENFYRA_API_URL, tableId, beforeIds, {
|
|
|
208
208
|
return afterRelations;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
function buildColumnDefinition({
|
|
211
|
+
export function buildColumnDefinition({
|
|
212
212
|
name,
|
|
213
213
|
type,
|
|
214
214
|
isNullable,
|
|
215
215
|
isUnique,
|
|
216
216
|
isPublished,
|
|
217
|
+
isUpdatable,
|
|
218
|
+
isEncrypted,
|
|
217
219
|
isPrimary,
|
|
218
220
|
isGenerated,
|
|
219
221
|
isSystem,
|
|
@@ -225,6 +227,8 @@ function buildColumnDefinition({
|
|
|
225
227
|
if (isNullable !== undefined) column.isNullable = isNullable;
|
|
226
228
|
if (isUnique !== undefined) column.isUnique = isUnique;
|
|
227
229
|
if (isPublished !== undefined) column.isPublished = isPublished;
|
|
230
|
+
if (isUpdatable !== undefined) column.isUpdatable = isUpdatable;
|
|
231
|
+
if (isEncrypted !== undefined) column.isEncrypted = isEncrypted;
|
|
228
232
|
if (isPrimary !== undefined) column.isPrimary = isPrimary;
|
|
229
233
|
if (isGenerated !== undefined) column.isGenerated = isGenerated;
|
|
230
234
|
if (isSystem !== undefined) column.isSystem = isSystem;
|
|
@@ -378,6 +382,8 @@ export function registerTableTools(server, ENFYRA_API_URL) {
|
|
|
378
382
|
isNullable: z.boolean().optional().default(true).describe('Set to false if column cannot be null.'),
|
|
379
383
|
isUnique: z.boolean().optional().default(false).describe('Set to true for unique constraint.'),
|
|
380
384
|
isPublished: z.boolean().optional().describe('Set column visibility baseline. Use false for secrets and internal fields.'),
|
|
385
|
+
isUpdatable: z.boolean().optional().describe('Set false for immutable fields that cannot be updated after creation. Independent from isEncrypted.'),
|
|
386
|
+
isEncrypted: z.boolean().optional().describe('Set true to encrypt this column at the Enfyra database-query layer. This does not change isUpdatable. Encrypted fields cannot be filtered or sorted.'),
|
|
381
387
|
isPrimary: z.boolean().optional().describe('Set true only for primary key columns; normally only create_table auto id uses this.'),
|
|
382
388
|
isGenerated: z.boolean().optional().describe('Set true only for generated columns such as auto id.'),
|
|
383
389
|
isSystem: z.boolean().optional().describe('Set true only for system-managed columns. Avoid for normal app fields.'),
|
|
@@ -448,7 +454,7 @@ export function registerTableTools(server, ENFYRA_API_URL) {
|
|
|
448
454
|
name: z.string().describe('Table name (e.g., "user_definition", "my_custom_table"). Must be unique, lowercase with underscores.'),
|
|
449
455
|
description: z.string().optional().describe('Description of what this table stores.'),
|
|
450
456
|
isSingleRecord: z.boolean().optional().describe('Set to true for single-record tables such as settings/config. This is passed directly to table_definition create.'),
|
|
451
|
-
columns: z.string().optional().describe('JSON array of column definitions to create with the table (cascade). Each column: { name, type, isNullable?, isUnique?, defaultValue?, description?, options? }. The `id` column is always auto-included. Example: [{"name":"title","type":"varchar"},{"name":"
|
|
457
|
+
columns: z.string().optional().describe('JSON array of column definitions to create with the table (cascade). Each column: { name, type, isNullable?, isUnique?, isPublished?, isUpdatable?, isEncrypted?, defaultValue?, description?, options? }. Set isEncrypted=true for values encrypted at rest; set isUpdatable=false separately only when the field should be immutable. The `id` column is always auto-included. Example: [{"name":"title","type":"varchar"},{"name":"api_key","type":"varchar","isEncrypted":true,"isPublished":false}]'),
|
|
452
458
|
relations: z.string().optional().describe('JSON array of relation definitions to create with the table in the same cascade call. Each relation: { targetTable, type, propertyName, inversePropertyName?, mappedBy?, isNullable?, onDelete?, description? }. targetTable can be an id or {"id": <id>}. Do not include physical FK/junction columns such as fkCol, foreignKeyColumn, sourceColumn, targetColumn, junctionSourceColumn, or junctionTargetColumn; Enfyra derives them and hides FK columns from app schema. Example: [{"targetTable":2,"type":"many-to-one","propertyName":"author","inversePropertyName":"posts","isNullable":false,"onDelete":"CASCADE"}]'),
|
|
453
459
|
indexes: z.string().optional().describe('JSON array of logical index field groups. Each group can be ["fieldA","fieldB"] or {"value":["fieldA","fieldB"]}. Relation property names are allowed. Example: [["member","isRead","conversation"],["conversation","member","isRead"]]'),
|
|
454
460
|
uniques: z.string().optional().describe('JSON array of logical unique field groups. Each group can be ["fieldA","fieldB"] or {"value":["fieldA","fieldB"]}. Example: [["message","member"]]'),
|
|
@@ -611,11 +617,12 @@ export function registerTableTools(server, ENFYRA_API_URL) {
|
|
|
611
617
|
type: z.string().optional().describe('New column type.'),
|
|
612
618
|
isNullable: z.boolean().optional().describe('Set nullable.'),
|
|
613
619
|
isPublished: z.boolean().optional().describe('Set column visibility baseline. false = unpublished (omitted from response unless allowed by field permission rules).'),
|
|
620
|
+
isUpdatable: z.boolean().optional().describe('Set false for immutable fields that should be stripped from update payloads.'),
|
|
614
621
|
defaultValue: z.string().optional().describe('New default value as JSON string.'),
|
|
615
622
|
description: z.string().optional().describe('New description.'),
|
|
616
623
|
options: z.string().optional().describe('New options as JSON string.'),
|
|
617
624
|
},
|
|
618
|
-
async ({ tableId, columnId, name, type, isNullable, isPublished, defaultValue, description, options }) => withSchemaQueue(async () => {
|
|
625
|
+
async ({ tableId, columnId, name, type, isNullable, isPublished, isUpdatable, defaultValue, description, options }) => withSchemaQueue(async () => {
|
|
619
626
|
const tableData = await fetchTableWithDetails(ENFYRA_API_URL, tableId);
|
|
620
627
|
if (!tableData) {
|
|
621
628
|
return { content: [{ type: 'text', text: `Error: Table with ID ${tableId} not found.` }] };
|
|
@@ -634,6 +641,7 @@ export function registerTableTools(server, ENFYRA_API_URL) {
|
|
|
634
641
|
if (type !== undefined) rest.type = type;
|
|
635
642
|
if (isNullable !== undefined) rest.isNullable = isNullable;
|
|
636
643
|
if (isPublished !== undefined) rest.isPublished = isPublished;
|
|
644
|
+
if (isUpdatable !== undefined) rest.isUpdatable = isUpdatable;
|
|
637
645
|
if (defaultValue !== undefined) rest.defaultValue = defaultValue;
|
|
638
646
|
if (description !== undefined) rest.description = description;
|
|
639
647
|
if (options !== undefined) rest.options = JSON.parse(options);
|
package/src/mcp-server-entry.mjs
CHANGED
|
@@ -175,6 +175,8 @@ function summarizeTable(table) {
|
|
|
175
175
|
isPrimary: !!column.isPrimary,
|
|
176
176
|
isNullable: column.isNullable,
|
|
177
177
|
isPublished: column.isPublished,
|
|
178
|
+
isUpdatable: column.isUpdatable !== false,
|
|
179
|
+
isEncrypted: column.isEncrypted === true,
|
|
178
180
|
})),
|
|
179
181
|
relations: (table.relations || []).map((relation) => ({
|
|
180
182
|
id: relation.id ?? relation._id,
|