@enfyra/mcp-server 0.0.84 → 0.0.86

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.
@@ -40,52 +40,52 @@ initAuth(ENFYRA_API_URL, ENFYRA_API_TOKEN);
40
40
  const CAPABILITY_AREAS = [
41
41
  {
42
42
  area: 'Schema and metadata',
43
- tables: ['table_definition', 'column_definition', 'relation_definition', 'schema_migration_definition'],
44
- workflow: 'Use table tools for table/column/relation schema changes. column_definition and session_definition are internal/no-route; do not CRUD them directly.',
43
+ tables: ['enfyra_table', 'enfyra_column', 'enfyra_relation', 'enfyra_schema_migration'],
44
+ workflow: 'Use table tools for table/column/relation schema changes. enfyra_column and enfyra_session are internal/no-route; do not CRUD them directly.',
45
45
  },
46
46
  {
47
47
  area: 'Dynamic REST API',
48
- tables: ['route_definition', 'route_handler_definition', 'pre_hook_definition', 'post_hook_definition', 'route_permission_definition', 'method_definition'],
49
- workflow: 'Create custom paths with create_route without mainTableId, then add handlers/hooks. mainTableId is only for canonical table routes like /table_name. Query method_definition before assigning route methods.',
48
+ tables: ['enfyra_route', 'enfyra_route_handler', 'enfyra_pre_hook', 'enfyra_post_hook', 'enfyra_route_permission', 'enfyra_method'],
49
+ workflow: 'Create custom paths with create_route without mainTableId, then add handlers/hooks. mainTableId is only for canonical table routes like /table_name. Query enfyra_method before assigning route methods.',
50
50
  },
51
51
  {
52
52
  area: 'Auth, roles, sessions, OAuth',
53
- tables: ['user_definition', 'role_definition', 'api_token_definition', 'session_definition', 'oauth_config_definition', 'oauth_account_definition'],
53
+ tables: ['enfyra_user', 'enfyra_role', 'enfyra_api_token', 'enfyra_session', 'enfyra_oauth_config', 'enfyra_oauth_account'],
54
54
  workflow: 'MCP auth exchanges ENFYRA_API_TOKEN through /auth/token/exchange. Configure an API token from Enfyra admin UI /me.',
55
55
  },
56
56
  {
57
57
  area: 'Guards and permissions',
58
- tables: ['guard_definition', 'guard_rule_definition', 'field_permission_definition', 'column_rule_definition'],
58
+ tables: ['enfyra_guard', 'enfyra_guard_rule', 'enfyra_field_permission', 'enfyra_column_rule'],
59
59
  workflow: 'Use route guard metadata for request gating, field permissions for record field access, and column rules for body validation.',
60
60
  },
61
61
  {
62
62
  area: 'GraphQL',
63
- tables: ['gql_definition'],
64
- workflow: 'Enable per table through gql_definition or update_table graphqlEnabled. GraphQL requires Bearer auth.',
63
+ tables: ['enfyra_graphql'],
64
+ workflow: 'Enable per table through enfyra_graphql or update_table graphqlEnabled. GraphQL requires Bearer auth.',
65
65
  },
66
66
  {
67
67
  area: 'Files and storage',
68
- tables: ['file_definition', 'file_permission_definition', 'folder_definition', 'storage_config_definition'],
68
+ tables: ['enfyra_file', 'enfyra_file_permission', 'enfyra_folder', 'enfyra_storage_config'],
69
69
  workflow: 'Use file endpoints/helpers for uploads and asset streaming; metadata tables describe files, permissions, folders, and storage backends.',
70
70
  },
71
71
  {
72
72
  area: 'WebSocket',
73
- tables: ['websocket_definition', 'websocket_event_definition'],
73
+ tables: ['enfyra_websocket', 'enfyra_websocket_event'],
74
74
  workflow: 'Socket.IO gateways/events are metadata-backed. Use admin test runner for handler scripts before relying on a real client.',
75
75
  },
76
76
  {
77
77
  area: 'Flows',
78
- tables: ['flow_definition', 'flow_step_definition', 'flow_execution_definition'],
78
+ tables: ['enfyra_flow', 'enfyra_flow_step', 'enfyra_flow_execution'],
79
79
  workflow: 'Create flows as small operation-sized steps via CRUD, test steps with test_flow_step/run_admin_test, trigger with trigger_flow. Split oversized scripts instead of adding more work to one step.',
80
80
  },
81
81
  {
82
82
  area: 'Extensions, menus, packages',
83
- tables: ['extension_definition', 'menu_definition', 'package_definition', 'bootstrap_script_definition'],
84
- workflow: 'Extensions are Vue SFC records. Use install_package for package_definition rather than raw CRUD.',
83
+ tables: ['enfyra_extension', 'enfyra_menu', 'enfyra_package', 'enfyra_bootstrap_script'],
84
+ workflow: 'Extensions are Vue SFC records. Use install_package for enfyra_package rather than raw CRUD.',
85
85
  },
86
86
  {
87
87
  area: 'Settings and platform config',
88
- tables: ['setting_definition', 'cors_origin_definition'],
88
+ tables: ['enfyra_setting', 'enfyra_cors_origin'],
89
89
  workflow: 'Settings and CORS origins are metadata-backed platform configuration.',
90
90
  },
91
91
  ];
@@ -129,14 +129,14 @@ const FIELD_PERMISSION_CONDITION_OPERATORS = [
129
129
  ];
130
130
 
131
131
  const SCRIPT_BACKED_TABLES = [
132
- 'route_handler_definition',
133
- 'pre_hook_definition',
134
- 'post_hook_definition',
135
- 'flow_step_definition',
136
- 'websocket_event_definition',
137
- 'websocket_definition',
138
- 'gql_definition',
139
- 'bootstrap_script_definition',
132
+ 'enfyra_route_handler',
133
+ 'enfyra_pre_hook',
134
+ 'enfyra_post_hook',
135
+ 'enfyra_flow_step',
136
+ 'enfyra_websocket_event',
137
+ 'enfyra_websocket',
138
+ 'enfyra_graphql',
139
+ 'enfyra_bootstrap_script',
140
140
  ];
141
141
 
142
142
  const SCRIPT_SOURCE_FIELDS = [
@@ -510,7 +510,7 @@ function sourcePreview(source, aroundText) {
510
510
  }
511
511
 
512
512
  function scriptRecordLabel(tableName, record) {
513
- const method = record.method?.name || record.method?.method || null;
513
+ const method = record.method?.name || null;
514
514
  const route = record.route?.path || null;
515
515
  const flow = record.flow?.name || null;
516
516
  const gateway = record.gateway?.path || null;
@@ -530,21 +530,21 @@ function scriptRecordLabel(tableName, record) {
530
530
  function scriptTraceFields(tableName) {
531
531
  const common = 'id,_id,name,key,eventName,sourceCode,handlerScript,connectionHandlerScript,code,scriptLanguage';
532
532
  const byTable = {
533
- route_handler_definition: `${common},route.id,route.path,method.id,method.name`,
534
- pre_hook_definition: `${common},route.id,route.path,methods.id,methods.name,isGlobal`,
535
- post_hook_definition: `${common},route.id,route.path,methods.id,methods.name,isGlobal`,
536
- flow_step_definition: `${common},flow.id,flow.name`,
537
- websocket_event_definition: `${common},gateway.id,gateway.path`,
538
- websocket_definition: `${common},path`,
539
- gql_definition: `${common},table.id,table.name`,
540
- bootstrap_script_definition: common,
533
+ enfyra_route_handler: `${common},route.id,route.path,method.id,method.name`,
534
+ enfyra_pre_hook: `${common},route.id,route.path,methods.id,methods.name,isGlobal`,
535
+ enfyra_post_hook: `${common},route.id,route.path,methods.id,methods.name,isGlobal`,
536
+ enfyra_flow_step: `${common},flow.id,flow.name`,
537
+ enfyra_websocket_event: `${common},gateway.id,gateway.path`,
538
+ enfyra_websocket: `${common},path`,
539
+ enfyra_graphql: `${common},table.id,table.name`,
540
+ enfyra_bootstrap_script: common,
541
541
  };
542
542
  return byTable[tableName] || '*';
543
543
  }
544
544
 
545
545
  async function findMethodRecordByName(method) {
546
546
  const filter = encodeURIComponent(JSON.stringify({ name: { _eq: method } }));
547
- const result = await fetchAPI(ENFYRA_API_URL, `/method_definition?filter=${filter}&limit=1&fields=id,_id,name,buttonColor,textColor,isSystem`);
547
+ const result = await fetchAPI(ENFYRA_API_URL, `/enfyra_method?filter=${filter}&limit=1&fields=id,_id,name,buttonColor,textColor,isSystem`);
548
548
  return unwrapData(result)[0] || null;
549
549
  }
550
550
 
@@ -581,7 +581,7 @@ server.tool('get_all_metadata', 'Get concise metadata summary for all tables. Us
581
581
  });
582
582
 
583
583
  server.tool('get_table_metadata', 'Get concise metadata for a specific table by name', {
584
- tableName: z.string().describe('Table name (e.g., "user_definition", "route_definition")'),
584
+ tableName: z.string().describe('Table name (e.g., "enfyra_user", "enfyra_route")'),
585
585
  includeFull: z.boolean().optional().default(false).describe('Return full raw table metadata. Default false to keep MCP context small.'),
586
586
  }, async ({ tableName, includeFull }) => {
587
587
  const result = await fetchAPI(ENFYRA_API_URL, `/metadata/${tableName}`);
@@ -622,8 +622,8 @@ server.tool(
622
622
  async () => {
623
623
  const metadata = await fetchAPI(ENFYRA_API_URL, '/metadata');
624
624
  const [routesResult, methodsResult] = await Promise.all([
625
- fetchAPI(ENFYRA_API_URL, '/route_definition?fields=path,mainTable.name,availableMethods.*,publicMethods.*&limit=1000'),
626
- fetchAPI(ENFYRA_API_URL, '/method_definition?limit=100'),
625
+ fetchAPI(ENFYRA_API_URL, '/enfyra_route?fields=path,mainTable.name,availableMethods.*,publicMethods.*&limit=1000'),
626
+ fetchAPI(ENFYRA_API_URL, '/enfyra_method?limit=100'),
627
627
  ]);
628
628
 
629
629
  const tables = normalizeTables(metadata);
@@ -631,9 +631,9 @@ server.tool(
631
631
  const routes = summarizeRoutes(routesResult);
632
632
  const routeTables = new Set(routes.map((route) => route.mainTable).filter(Boolean));
633
633
  const noRouteTables = tableNames.filter((name) => !routeTables.has(name));
634
- const relationTable = tables.find((table) => table?.name === 'relation_definition');
635
- const tableDefinition = tables.find((table) => table?.name === 'table_definition');
636
- const gqlDefinition = tables.find((table) => table?.name === 'gql_definition');
634
+ const relationTable = tables.find((table) => table?.name === 'enfyra_relation');
635
+ const tableDefinition = tables.find((table) => table?.name === 'enfyra_table');
636
+ const gqlDefinition = tables.find((table) => table?.name === 'enfyra_graphql');
637
637
  const routeTableList = [...routeTables].sort();
638
638
 
639
639
  const payload = {
@@ -643,7 +643,7 @@ server.tool(
643
643
  routes: routes.length,
644
644
  methods: methodsResult?.data?.length || 0,
645
645
  },
646
- methods: (methodsResult?.data || []).map((method) => ({ id: method.id || method._id, name: method.name, method: method.name })),
646
+ methods: (methodsResult?.data || []).map((method) => ({ id: method.id || method._id, name: method.name })),
647
647
  capabilityAreas: CAPABILITY_AREAS.map((item) => ({
648
648
  ...item,
649
649
  presentTables: item.tables.filter((table) => tableNames.includes(table)),
@@ -659,12 +659,12 @@ server.tool(
659
659
  customRouteWorkflow: 'For a new endpoint use create_route without mainTableId, then create_handler/create_pre_hook/create_post_hook. Do not create a table just to get a path.',
660
660
  },
661
661
  schemaManagement: {
662
- createTable: 'POST /table_definition supports isSingleRecord at create time and supports columns and relations arrays in the same cascade call. MCP create_table exposes isSingleRecord, columns, and relations directly. It does not accept alias at create time; table name drives the default route/schema behavior.',
663
- updateTable: 'PATCH /table_definition/:id is the canonical path for table property changes and column/relation schema changes.',
664
- columns: 'column_definition has no REST route; use create_table/create_column/update_column/delete_column.',
665
- relations: routeTables.has('relation_definition')
666
- ? 'relation_definition has a REST route for reads/metadata, but canonical schema migration is create_relation/delete_relation or table_definition PATCH with the full relations array. Relation onDelete accepts CASCADE, SET NULL, or RESTRICT.'
667
- : 'Use create_relation/delete_relation or table_definition PATCH with the full relations array. Relation onDelete accepts CASCADE, SET NULL, or RESTRICT.',
662
+ createTable: 'POST /enfyra_table supports isSingleRecord at create time and supports columns and relations arrays in the same cascade call. MCP create_table exposes isSingleRecord, columns, and relations directly. It does not accept alias at create time; table name drives the default route/schema behavior.',
663
+ updateTable: 'PATCH /enfyra_table/:id is the canonical path for table property changes and column/relation schema changes.',
664
+ columns: 'enfyra_column has no REST route; use create_table/create_column/update_column/delete_column.',
665
+ relations: routeTables.has('enfyra_relation')
666
+ ? 'enfyra_relation has a REST route for reads/metadata, but canonical schema migration is create_relation/delete_relation or enfyra_table PATCH with the full relations array. Relation onDelete accepts CASCADE, SET NULL, or RESTRICT.'
667
+ : 'Use create_relation/delete_relation or enfyra_table PATCH with the full relations array. Relation onDelete accepts CASCADE, SET NULL, or RESTRICT.',
668
668
  relationCascadeFkContract: 'Do not ask for or send physical FK/junction column names in relation create/update payloads. Enfyra derives fk/junction columns from relation propertyName/table metadata and hides FK columns from app schema/forms. Use targetTable, type, propertyName, inversePropertyName or mappedBy, isNullable, onDelete.',
669
669
  tableDefinitionRelations: (tableDefinition?.relations || []).map((rel) => rel.propertyName),
670
670
  relationDefinitionRelations: (relationTable?.relations || []).map((rel) => rel.propertyName),
@@ -677,10 +677,10 @@ server.tool(
677
677
  graphql: {
678
678
  endpoint: `${ENFYRA_API_URL.replace(/\/$/, '')}/graphql`,
679
679
  schemaEndpoint: `${ENFYRA_API_URL.replace(/\/$/, '')}/graphql-schema`,
680
- enablement: 'A table appears in GraphQL when gql_definition has an enabled row for that table. REST route availableMethods does not enable GraphQL.',
680
+ enablement: 'A table appears in GraphQL when enfyra_graphql has an enabled row for that table. REST route availableMethods does not enable GraphQL.',
681
681
  auth: 'GraphQL currently requires Authorization: Bearer <accessToken>; REST publicMethods does not make GraphQL anonymous.',
682
- management: routeTables.has('gql_definition')
683
- ? 'Use update_table graphqlEnabled or create/update records on gql_definition, then reload_graphql if needed.'
682
+ management: routeTables.has('enfyra_graphql')
683
+ ? 'Use update_table graphqlEnabled or create/update records on enfyra_graphql, then reload_graphql if needed.'
684
684
  : 'Use update_table graphqlEnabled, then reload_graphql if needed.',
685
685
  gqlDefinitionColumns: (gqlDefinition?.columns || []).map((column) => column.name),
686
686
  },
@@ -711,13 +711,13 @@ server.tool(
711
711
  settingsResult,
712
712
  meResult,
713
713
  ] = await Promise.all([
714
- fetchAPI(ENFYRA_API_URL, '/route_definition?fields=path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled&limit=1000'),
715
- fetchAPI(ENFYRA_API_URL, '/method_definition?limit=100'),
716
- fetchAPI(ENFYRA_API_URL, '/gql_definition?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
717
- fetchAPI(ENFYRA_API_URL, '/flow_definition?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
718
- fetchAPI(ENFYRA_API_URL, '/websocket_definition?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
719
- fetchAPI(ENFYRA_API_URL, '/storage_config_definition?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
720
- fetchAPI(ENFYRA_API_URL, '/setting_definition?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
714
+ fetchAPI(ENFYRA_API_URL, '/enfyra_route?fields=path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled&limit=1000'),
715
+ fetchAPI(ENFYRA_API_URL, '/enfyra_method?limit=100'),
716
+ fetchAPI(ENFYRA_API_URL, '/enfyra_graphql?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
717
+ fetchAPI(ENFYRA_API_URL, '/enfyra_flow?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
718
+ fetchAPI(ENFYRA_API_URL, '/enfyra_websocket?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
719
+ fetchAPI(ENFYRA_API_URL, '/enfyra_storage_config?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
720
+ fetchAPI(ENFYRA_API_URL, '/enfyra_setting?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
721
721
  fetchAPI(ENFYRA_API_URL, '/me').catch((error) => ({ error: String(error.message || error), data: [] })),
722
722
  ]);
723
723
 
@@ -746,7 +746,7 @@ server.tool(
746
746
  storageConfigs: storageResult?.data?.length || 0,
747
747
  settings: settingsResult?.data?.length || 0,
748
748
  },
749
- methods: (methodsResult?.data || []).map((method) => ({ id: method.id || method._id, name: method.name, method: method.name })),
749
+ methods: (methodsResult?.data || []).map((method) => ({ id: method.id || method._id, name: method.name })),
750
750
  routeRuntime: {
751
751
  routePattern: 'GET/POST /<route-path>; PATCH/DELETE /<route-path>/:id; no dynamic GET /<route-path>/:id.',
752
752
  adminRoutes: adminRoutes.map((route) => route.path).sort(),
@@ -786,7 +786,7 @@ server.tool(
786
786
  },
787
787
  async ({ tableName }) => {
788
788
  const metadata = await fetchAPI(ENFYRA_API_URL, '/metadata');
789
- const routesResult = await fetchAPI(ENFYRA_API_URL, '/route_definition?fields=path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled&limit=1000');
789
+ const routesResult = await fetchAPI(ENFYRA_API_URL, '/enfyra_route?fields=path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled&limit=1000');
790
790
  const tables = normalizeTables(metadata);
791
791
  const routes = summarizeRoutes(routesResult);
792
792
  const table = tableName ? tables.find((item) => item.name === tableName) : null;
@@ -826,8 +826,8 @@ server.tool(
826
826
  backendNotes: {
827
827
  primaryKey: 'SQL commonly uses id; Mongo uses _id. Use table metadata primary column when available.',
828
828
  relationNames: 'API relation operations use relation propertyName, not physical FK column names.',
829
- relationCascadeFkContract: 'When creating relations through create_table/create_relation/table_definition PATCH, never provide fkCol/fkColumn/foreignKeyColumn/sourceColumn/targetColumn/junction*Column. These are physical implementation details derived by Enfyra and hidden from app schema/forms.',
830
- graphql: 'GraphQL query args also accept filter/sort/page/limit, but GraphQL requires Bearer auth and table enablement via gql_definition.',
829
+ relationCascadeFkContract: 'When creating relations through create_table/create_relation/enfyra_table PATCH, never provide fkCol/fkColumn/foreignKeyColumn/sourceColumn/targetColumn/junction*Column. These are physical implementation details derived by Enfyra and hidden from app schema/forms.',
830
+ graphql: 'GraphQL query args also accept filter/sort/page/limit, but GraphQL requires Bearer auth and table enablement via enfyra_graphql.',
831
831
  },
832
832
  table: tableName
833
833
  ? {
@@ -901,7 +901,7 @@ server.tool(
901
901
  throws: '@THROW400 through @THROW503 and @THROW map to $ctx.$throw helpers.',
902
902
  helpers: {
903
903
  crypto: '$ctx.$helpers.$crypto exposes bounded runtime crypto helpers: randomUUID(), randomBytes(size, encoding), sha256(value, encoding), hmacSha256(value, secret, encoding), and generateSshKeyPair(comment). Use generateSshKeyPair for SSH key material. Do not use legacy $ctx.$helpers.$ssh.',
904
- files: '$ctx.$storage.$upload and $ctx.$storage.$update accept file: @UPLOADED_FILE for request uploads and stream from the server temp file path. $ctx.$storage.$registerFile creates a file_definition record for an object that already exists in storage without uploading bytes. Use buffer only for small generated/transformed files; do not use @UPLOADED_FILE.buffer.',
904
+ files: '$ctx.$storage.$upload and $ctx.$storage.$update accept file: @UPLOADED_FILE for request uploads and stream from the server temp file path. $ctx.$storage.$registerFile creates a enfyra_file record for an object that already exists in storage without uploading bytes. Use buffer only for small generated/transformed files; do not use @UPLOADED_FILE.buffer.',
905
905
  },
906
906
  env: '$ctx.$env exposes a sanitized process env snapshot with exact sensitive keys removed: DB_URI, DB_REPLICA_URIS, REDIS_URI, SECRET_KEY, and ADMIN_PASSWORD. Store app secrets in unpublished isEncrypted fields instead of reading them from $env.',
907
907
  },
@@ -948,7 +948,7 @@ server.tool(
948
948
  extensionVueSfc: {
949
949
  runs: 'Frontend extension code, not server sandbox.',
950
950
  data: ['Vue/Nuxt composables', 'Enfyra composables', 'auto-resolved UI components'],
951
- caveat: 'No import statements; save as extension_definition Vue SFC record.',
951
+ caveat: 'No import statements; save as enfyra_extension Vue SFC record.',
952
952
  },
953
953
  },
954
954
  helpers: {
@@ -961,7 +961,7 @@ server.tool(
961
961
  },
962
962
  socketInHttpOrFlow: 'HTTP/flow context can emitToUser/emitToRoom/emitToGateway/broadcast, but cannot reply/join/leave/disconnect/emitToCurrentRoom/broadcastToRoom because there is no bound socket. emitToRoom requires an explicit gateway path: emitToRoom(path, room, event, data).',
963
963
  packages: 'Server packages installed through install_package are exposed as $ctx.$pkgs.packageName in server scripts.',
964
- files: 'Upload helpers are on $storage; raw create_record on file_definition is not equivalent to multipart upload/storage rollback. For multipart request files, pass file: @UPLOADED_FILE to @STORAGE.$upload/@STORAGE.$update so Enfyra streams from disk-backed temp storage. Use @STORAGE.$registerFile only when the object already exists in storage and the script should create the file_definition record without uploading bytes. Use buffer only for small generated files.',
964
+ files: 'Upload helpers are on $storage; raw create_record on enfyra_file is not equivalent to multipart upload/storage rollback. For multipart request files, pass file: @UPLOADED_FILE to @STORAGE.$upload/@STORAGE.$update so Enfyra streams from disk-backed temp storage. Use @STORAGE.$registerFile only when the object already exists in storage and the script should create the enfyra_file record without uploading bytes. Use buffer only for small generated files.',
965
965
  },
966
966
  adminTesting: {
967
967
  flowStep: 'Use test_flow_step or run_admin_test(kind=flow_step).',
@@ -985,7 +985,7 @@ server.tool(
985
985
  'Auth: publicMethods on a route can allow a method without Bearer; otherwise JWT + routePermissions — see server instructions.',
986
986
  'If path might differ from table name, use get_all_routes before asserting a URL.',
987
987
  'Same mapping as MCP tool → HTTP: query_table=GET /table?..., create_record=POST /table, update_record=PATCH /table/id, delete_record=DELETE /table/id.',
988
- 'GraphQL: see graphqlHttpUrl / graphqlSchemaUrl in response; enable per table via gql_definition/update_table graphqlEnabled and send Bearer auth.',
988
+ 'GraphQL: see graphqlHttpUrl / graphqlSchemaUrl in response; enable per table via enfyra_graphql/update_table graphqlEnabled and send Bearer auth.',
989
989
  ].join(' '),
990
990
  {},
991
991
  async () => {
@@ -1292,19 +1292,19 @@ server.tool(
1292
1292
  'update_script_source',
1293
1293
  [
1294
1294
  'Update sourceCode on a script-backed record without forcing the caller to JSON-escape long code.',
1295
- 'Use this for flow_step_definition, route_handler_definition, pre_hook_definition, post_hook_definition, websocket_event_definition, websocket_definition, gql_definition, and bootstrap_script_definition.',
1295
+ 'Use this for enfyra_flow_step, enfyra_route_handler, enfyra_pre_hook, enfyra_post_hook, enfyra_websocket_event, enfyra_websocket, enfyra_graphql, and enfyra_bootstrap_script.',
1296
1296
  'The tool validates sourceCode through /admin/script/validate before saving and never accepts compiledCode.',
1297
1297
  ].join(' '),
1298
1298
  {
1299
1299
  tableName: z.enum([
1300
- 'route_handler_definition',
1301
- 'pre_hook_definition',
1302
- 'post_hook_definition',
1303
- 'flow_step_definition',
1304
- 'websocket_event_definition',
1305
- 'websocket_definition',
1306
- 'gql_definition',
1307
- 'bootstrap_script_definition',
1300
+ 'enfyra_route_handler',
1301
+ 'enfyra_pre_hook',
1302
+ 'enfyra_post_hook',
1303
+ 'enfyra_flow_step',
1304
+ 'enfyra_websocket_event',
1305
+ 'enfyra_websocket',
1306
+ 'enfyra_graphql',
1307
+ 'enfyra_bootstrap_script',
1308
1308
  ]).describe('Script-backed table to update'),
1309
1309
  id: z.string().describe('Record ID to update'),
1310
1310
  sourceCode: z.string().describe('Editable script sourceCode. Pass the raw code string; do not JSON-escape it yourself.'),
@@ -1370,20 +1370,19 @@ server.tool('delete_record', 'Delete a record by ID', {
1370
1370
 
1371
1371
  server.tool(
1372
1372
  'list_methods',
1373
- 'List method_definition records with their UI colors. Use this before creating route methods or method-colored UI.',
1373
+ 'List enfyra_method records with their UI colors. Use this before creating route methods or method-colored UI.',
1374
1374
  {},
1375
1375
  async () => {
1376
- const result = await fetchAPI(ENFYRA_API_URL, '/method_definition?fields=id,_id,name,buttonColor,textColor,isSystem&sort=name&limit=0');
1376
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_method?fields=id,_id,name,buttonColor,textColor,isSystem&sort=name&limit=0');
1377
1377
  const methods = unwrapData(result).map((method) => ({
1378
1378
  id: getId(method),
1379
1379
  name: method.name,
1380
- method: method.name,
1381
1380
  buttonColor: method.buttonColor,
1382
1381
  textColor: method.textColor,
1383
1382
  isSystem: method.isSystem === true,
1384
1383
  }));
1385
1384
  return { content: [{ type: 'text', text: JSON.stringify({
1386
- tableName: 'method_definition',
1385
+ tableName: 'enfyra_method',
1387
1386
  methods,
1388
1387
  appUi: '/settings/methods',
1389
1388
  }, null, 2) }] };
@@ -1392,7 +1391,7 @@ server.tool(
1392
1391
 
1393
1392
  server.tool(
1394
1393
  'create_method',
1395
- 'Create a method_definition record with app badge colors. Prefer this over generic create_record for method_definition.',
1394
+ 'Create a enfyra_method record with app badge colors. Prefer this over generic create_record for enfyra_method.',
1396
1395
  {
1397
1396
  method: z.string().describe('Uppercase method name, e.g. GET, POST, PUT, CUSTOM_METHOD. Must start with A-Z and contain only A-Z, 0-9, or underscore.'),
1398
1397
  buttonColor: z.string().describe('Badge background color as full hex, e.g. #dbeafe.'),
@@ -1411,15 +1410,14 @@ server.tool(
1411
1410
  textColor: normalizeHexColorInput(textColor, 'textColor'),
1412
1411
  isSystem: isSystem === true,
1413
1412
  };
1414
- const result = await fetchAPI(ENFYRA_API_URL, '/method_definition', {
1413
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_method', {
1415
1414
  method: 'POST',
1416
1415
  body: JSON.stringify(body),
1417
1416
  });
1418
1417
  _methodMap = null;
1419
1418
  return { content: [{ type: 'text', text: JSON.stringify({
1420
- ...summarizeMutationResult(result, 'created', 'method_definition'),
1419
+ ...summarizeMutationResult(result, 'created', 'enfyra_method'),
1421
1420
  name: normalizedMethod,
1422
- method: normalizedMethod,
1423
1421
  appUi: '/settings/methods',
1424
1422
  }, null, 2) }] };
1425
1423
  },
@@ -1427,7 +1425,7 @@ server.tool(
1427
1425
 
1428
1426
  server.tool(
1429
1427
  'update_method',
1430
- 'Update a method_definition record color pair, and optionally rename non-system methods. Prefer this over generic update_record for method_definition.',
1428
+ 'Update a enfyra_method record color pair, and optionally rename non-system methods. Prefer this over generic update_record for enfyra_method.',
1431
1429
  {
1432
1430
  id: z.string().optional().describe('Method record id. If omitted, method is used to find the record.'),
1433
1431
  method: z.string().optional().describe('Existing method name to find, or new name when id is provided.'),
@@ -1459,13 +1457,13 @@ server.tool(
1459
1457
  throw new Error('Provide buttonColor, textColor, or a new method name.');
1460
1458
  }
1461
1459
 
1462
- const result = await fetchAPI(ENFYRA_API_URL, `/method_definition/${encodeURIComponent(String(targetId))}`, {
1460
+ const result = await fetchAPI(ENFYRA_API_URL, `/enfyra_method/${encodeURIComponent(String(targetId))}`, {
1463
1461
  method: 'PATCH',
1464
1462
  body: JSON.stringify(body),
1465
1463
  });
1466
1464
  _methodMap = null;
1467
1465
  return { content: [{ type: 'text', text: JSON.stringify({
1468
- ...summarizeMutationResult(result, 'updated', 'method_definition'),
1466
+ ...summarizeMutationResult(result, 'updated', 'enfyra_method'),
1469
1467
  id: targetId,
1470
1468
  appUi: '/settings/methods',
1471
1469
  }, null, 2) }] };
@@ -1474,7 +1472,7 @@ server.tool(
1474
1472
 
1475
1473
  server.tool(
1476
1474
  'delete_method',
1477
- 'Preview or delete a method_definition record. Only delete unused custom methods; system/default methods should be kept.',
1475
+ 'Preview or delete a enfyra_method record. Only delete unused custom methods; system/default methods should be kept.',
1478
1476
  {
1479
1477
  id: z.string().optional().describe('Method record id. If omitted, method is used to find the record.'),
1480
1478
  method: z.string().optional().describe('Method name to find when id is omitted.'),
@@ -1491,27 +1489,26 @@ server.tool(
1491
1489
  }
1492
1490
  if (!confirm) {
1493
1491
  if (!target) {
1494
- const primaryKey = await getPrimaryFieldName('method_definition');
1492
+ const primaryKey = await getPrimaryFieldName('enfyra_method');
1495
1493
  const filter = encodeURIComponent(JSON.stringify({ [primaryKey]: { _eq: targetId } }));
1496
- const result = await fetchAPI(ENFYRA_API_URL, `/method_definition?filter=${filter}&limit=1&fields=id,_id,name,buttonColor,textColor,isSystem`);
1494
+ const result = await fetchAPI(ENFYRA_API_URL, `/enfyra_method?filter=${filter}&limit=1&fields=id,_id,name,buttonColor,textColor,isSystem`);
1497
1495
  target = unwrapData(result)[0] || null;
1498
1496
  }
1499
1497
  return { content: [{ type: 'text', text: JSON.stringify({
1500
1498
  action: 'delete_method_preview',
1501
1499
  id: targetId,
1502
1500
  name: target?.name,
1503
- method: target?.name,
1504
1501
  isSystem: target?.isSystem === true,
1505
1502
  destructive: true,
1506
1503
  warning: 'Only delete unused custom methods. Deleting a method can affect route method relations.',
1507
1504
  next: 'Call delete_method again with confirm=true to delete.',
1508
1505
  }, null, 2) }] };
1509
1506
  }
1510
- const result = await fetchAPI(ENFYRA_API_URL, `/method_definition/${encodeURIComponent(String(targetId))}`, { method: 'DELETE' });
1507
+ const result = await fetchAPI(ENFYRA_API_URL, `/enfyra_method/${encodeURIComponent(String(targetId))}`, { method: 'DELETE' });
1511
1508
  _methodMap = null;
1512
1509
  return { content: [{ type: 'text', text: JSON.stringify({
1513
1510
  action: 'deleted',
1514
- tableName: 'method_definition',
1511
+ tableName: 'enfyra_method',
1515
1512
  id: targetId,
1516
1513
  statusCode: result?.statusCode,
1517
1514
  success: result?.success,
@@ -1588,7 +1585,7 @@ server.tool(
1588
1585
  let _methodMap = null;
1589
1586
  async function getMethodMap() {
1590
1587
  if (_methodMap) return _methodMap;
1591
- const result = await fetchAPI(ENFYRA_API_URL, '/method_definition?limit=0');
1588
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_method?limit=0');
1592
1589
  _methodMap = {};
1593
1590
  for (const m of result.data) {
1594
1591
  _methodMap[m.name] = m.id || m._id;
@@ -1616,7 +1613,6 @@ function withMethodNames(records, methodIdNameMap, field = 'methods') {
1616
1613
  ? record[field].map((item) => ({
1617
1614
  ...item,
1618
1615
  name: item.name || methodIdNameMap[String(getId(item))] || null,
1619
- method: item.name || methodIdNameMap[String(getId(item))] || null,
1620
1616
  }))
1621
1617
  : record?.[field],
1622
1618
  }));
@@ -1638,15 +1634,15 @@ async function collectRestDefinitionState() {
1638
1634
  methodIdNameMap,
1639
1635
  ] = await Promise.all([
1640
1636
  getMetadataTables(),
1641
- fetchAll('/route_definition?limit=1000'),
1642
- fetchAll('/route_handler_definition?limit=1000'),
1643
- fetchAll('/pre_hook_definition?limit=1000'),
1644
- fetchAll('/post_hook_definition?limit=1000'),
1645
- fetchAll('/route_permission_definition?limit=1000'),
1646
- fetchAll('/guard_definition?limit=1000'),
1647
- fetchAll('/guard_rule_definition?limit=1000'),
1648
- fetchAll('/field_permission_definition?limit=1000'),
1649
- fetchAll('/column_rule_definition?limit=1000'),
1637
+ fetchAll('/enfyra_route?limit=1000'),
1638
+ fetchAll('/enfyra_route_handler?limit=1000'),
1639
+ fetchAll('/enfyra_pre_hook?limit=1000'),
1640
+ fetchAll('/enfyra_post_hook?limit=1000'),
1641
+ fetchAll('/enfyra_route_permission?limit=1000'),
1642
+ fetchAll('/enfyra_guard?limit=1000'),
1643
+ fetchAll('/enfyra_guard_rule?limit=1000'),
1644
+ fetchAll('/enfyra_field_permission?limit=1000'),
1645
+ fetchAll('/enfyra_column_rule?limit=1000'),
1650
1646
  getMethodIdNameMap(),
1651
1647
  ]);
1652
1648
 
@@ -1674,7 +1670,6 @@ function enrichRoute(route, state) {
1674
1670
  method: item.method ? {
1675
1671
  ...item.method,
1676
1672
  name: state.methodIdNameMap[String(getId(item.method))] || item.method.name || null,
1677
- method: state.methodIdNameMap[String(getId(item.method))] || item.method.name || null,
1678
1673
  } : item.method,
1679
1674
  }, 'sourceCode'));
1680
1675
  const routePreHooks = withMethodNames(
@@ -1703,21 +1698,18 @@ function enrichRoute(route, state) {
1703
1698
  ? route.availableMethods.map((method) => ({
1704
1699
  ...method,
1705
1700
  name: method.name || state.methodIdNameMap[String(getId(method))] || null,
1706
- method: method.name || state.methodIdNameMap[String(getId(method))] || null,
1707
1701
  }))
1708
1702
  : route.availableMethods,
1709
1703
  publicMethods: Array.isArray(route.publicMethods)
1710
1704
  ? route.publicMethods.map((method) => ({
1711
1705
  ...method,
1712
1706
  name: method.name || state.methodIdNameMap[String(getId(method))] || null,
1713
- method: method.name || state.methodIdNameMap[String(getId(method))] || null,
1714
1707
  }))
1715
1708
  : route.publicMethods,
1716
1709
  skipRoleGuardMethods: Array.isArray(route.skipRoleGuardMethods)
1717
1710
  ? route.skipRoleGuardMethods.map((method) => ({
1718
1711
  ...method,
1719
1712
  name: method.name || state.methodIdNameMap[String(getId(method))] || null,
1720
- method: method.name || state.methodIdNameMap[String(getId(method))] || null,
1721
1713
  }))
1722
1714
  : route.skipRoleGuardMethods,
1723
1715
  handlers: routeHandlers,
@@ -1791,8 +1783,8 @@ server.tool(
1791
1783
  'Returns the backing table, available/public methods, handlers, hooks, route permissions, guards, and exact REST URL pattern.',
1792
1784
  ].join(' '),
1793
1785
  {
1794
- path: z.string().optional().describe('Route path, e.g. /user_definition'),
1795
- routeId: z.union([z.string(), z.number()]).optional().describe('route_definition id. Use either path or routeId.'),
1786
+ path: z.string().optional().describe('Route path, e.g. /enfyra_user'),
1787
+ routeId: z.union([z.string(), z.number()]).optional().describe('enfyra_route id. Use either path or routeId.'),
1796
1788
  },
1797
1789
  async ({ path, routeId }) => {
1798
1790
  if (!path && !routeId) throw new Error('Provide path or routeId');
@@ -1962,11 +1954,11 @@ server.tool(
1962
1954
  'test_rest_endpoint',
1963
1955
  [
1964
1956
  'Execute a real REST request against the configured Enfyra API base.',
1965
- 'Use this after inspecting a route or changing handlers/hooks/guards. Pass paths like /table_definition?limit=1, not external URLs.',
1957
+ 'Use this after inspecting a route or changing handlers/hooks/guards. Pass paths like /enfyra_table?limit=1, not external URLs.',
1966
1958
  ].join(' '),
1967
1959
  {
1968
- method: z.string().optional().default('GET').describe('HTTP method name. Must exist in method_definition.name for Enfyra route-backed calls.'),
1969
- path: z.string().describe('Enfyra API path, e.g. /route_definition?limit=1'),
1960
+ method: z.string().optional().default('GET').describe('HTTP method name. Must exist in enfyra_method.name for Enfyra route-backed calls.'),
1961
+ path: z.string().describe('Enfyra API path, e.g. /enfyra_route?limit=1'),
1970
1962
  query: z.string().optional().describe('Optional query params JSON object, merged onto path query string'),
1971
1963
  body: z.string().optional().describe('Optional JSON request body string'),
1972
1964
  headers: z.string().optional().describe('Optional headers JSON object'),
@@ -2033,7 +2025,7 @@ server.tool('get_all_routes', 'List route definitions with minimal fields. Call
2033
2025
  fields: 'id,path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled',
2034
2026
  limit: '1000',
2035
2027
  });
2036
- const result = await fetchAPI(ENFYRA_API_URL, `/route_definition?${queryParams.toString()}`);
2028
+ const result = await fetchAPI(ENFYRA_API_URL, `/enfyra_route?${queryParams.toString()}`);
2037
2029
  const routeLimit = limit || 50;
2038
2030
  const q = search ? search.toLowerCase() : null;
2039
2031
  const allRoutes = summarizeRoutes(result);
@@ -2063,8 +2055,8 @@ server.tool(
2063
2055
  [
2064
2056
  '**Use this when the user wants a new REST API route or path** — not `create_table`. Custom routes must omit `mainTableId`.',
2065
2057
  '`mainTableId` is only a marker for canonical table routes such as `/orders`; do not set it for `/orders/stats`, `/reports/summary`, `/auth/login`, or any custom path.',
2066
- 'Do NOT create a new table_definition only to expose an endpoint; create a route without `mainTableId`, then have the handler/hook query explicit repos such as `$ctx.$repos.orders`.',
2067
- 'availableMethods = which REST verbs the route responds to. publicMethods = which REST verbs are public (no auth). GraphQL is enabled separately through gql_definition/update_table graphqlEnabled.',
2058
+ 'Do NOT create a new enfyra_table only to expose an endpoint; create a route without `mainTableId`, then have the handler/hook query explicit repos such as `$ctx.$repos.orders`.',
2059
+ 'availableMethods = which REST verbs the route responds to. publicMethods = which REST verbs are public (no auth). GraphQL is enabled separately through enfyra_graphql/update_table graphqlEnabled.',
2068
2060
  'After creation the tool auto-reloads routes. Then create handlers for specific methods via create_handler on this route id.',
2069
2061
  'Flow: create_route → create_handler (per method) → optionally create_pre_hook / create_post_hook → test via HTTP or admin test APIs (see server instructions).',
2070
2062
  ].join(' '),
@@ -2072,7 +2064,7 @@ server.tool(
2072
2064
  path: z.string().describe('URL path, must start with / (e.g., "/my-endpoint")'),
2073
2065
  mainTableId: z.union([z.string(), z.number()]).optional().describe('Only set for the canonical table route `/<table_name>`. Omit for every custom route.'),
2074
2066
  methods: z.array(z.string())
2075
- .describe('HTTP method names this route supports (availableMethods). Each value must exist in method_definition.name. Common: ["GET","POST","PATCH","DELETE"].'),
2067
+ .describe('HTTP method names this route supports (availableMethods). Each value must exist in enfyra_method.name. Common: ["GET","POST","PATCH","DELETE"].'),
2076
2068
  publicMethods: z.array(z.string()).optional()
2077
2069
  .describe('Methods accessible WITHOUT auth token. Omit = all methods require auth.'),
2078
2070
  isEnabled: z.boolean().optional().default(true).describe('Enable route immediately'),
@@ -2099,7 +2091,7 @@ server.tool(
2099
2091
  body.publicMethods = resolveMethodIds(methodMap, publicMethods);
2100
2092
  }
2101
2093
 
2102
- const result = await fetchAPI(ENFYRA_API_URL, '/route_definition', {
2094
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_route', {
2103
2095
  method: 'POST',
2104
2096
  body: JSON.stringify(body),
2105
2097
  });
@@ -2117,7 +2109,7 @@ server.tool(
2117
2109
  publicMethods: publicMethods || [],
2118
2110
  },
2119
2111
  routeReload,
2120
- next: `Use create_handler({ routeId: ${JSON.stringify(getId(created))}, method: "GET", sourceCode }) for custom code. Create extra method_definition.name rows first for custom methods such as PUT.`,
2112
+ next: `Use create_handler({ routeId: ${JSON.stringify(getId(created))}, method: "GET", sourceCode }) for custom code. Create extra enfyra_method.name rows first for custom methods such as PUT.`,
2121
2113
  }, null, 2) }] };
2122
2114
  },
2123
2115
  );
@@ -2135,7 +2127,7 @@ server.tool(
2135
2127
  {
2136
2128
  routeId: z.union([z.string(), z.number()]).describe('Route definition ID'),
2137
2129
  method: z.string().optional()
2138
- .describe('Single method_definition.name to create. Prefer this for one handler.'),
2130
+ .describe('Single enfyra_method.name to create. Prefer this for one handler.'),
2139
2131
  methods: z.array(z.string()).optional()
2140
2132
  .describe('Batch create multiple handlers. Use only when the same sourceCode applies to every method.'),
2141
2133
  sourceCode: z.string().describe('Handler JavaScript sourceCode. Do not use logic; backend CRUD rejects logic.'),
@@ -2147,7 +2139,7 @@ server.tool(
2147
2139
  if (methodNames.length === 0) throw new Error('Provide method or methods');
2148
2140
  const methodMap = await getMethodMap();
2149
2141
  const results = [];
2150
- const scriptValidation = await validateScriptSourceIfPresent(fetchAPI, ENFYRA_API_URL, 'route_handler_definition', {
2142
+ const scriptValidation = await validateScriptSourceIfPresent(fetchAPI, ENFYRA_API_URL, 'enfyra_route_handler', {
2151
2143
  sourceCode,
2152
2144
  scriptLanguage,
2153
2145
  });
@@ -2159,7 +2151,7 @@ server.tool(
2159
2151
  const body = { route: { id: routeId }, method: { id: methodId }, sourceCode, scriptLanguage };
2160
2152
  if (timeout) body.timeout = timeout;
2161
2153
 
2162
- const result = await fetchAPI(ENFYRA_API_URL, '/route_handler_definition', {
2154
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_route_handler', {
2163
2155
  method: 'POST',
2164
2156
  body: JSON.stringify(body),
2165
2157
  });
@@ -2206,12 +2198,12 @@ server.tool(
2206
2198
  async ({ routeId, name, code, scriptLanguage, methods, priority, isEnabled }) => {
2207
2199
  const methodMap = await getMethodMap();
2208
2200
  const methodNames = methods || ['GET', 'POST', 'PATCH', 'DELETE'];
2209
- const scriptValidation = await validateScriptSourceIfPresent(fetchAPI, ENFYRA_API_URL, 'pre_hook_definition', {
2201
+ const scriptValidation = await validateScriptSourceIfPresent(fetchAPI, ENFYRA_API_URL, 'enfyra_pre_hook', {
2210
2202
  sourceCode: code,
2211
2203
  scriptLanguage,
2212
2204
  });
2213
2205
 
2214
- const result = await fetchAPI(ENFYRA_API_URL, '/pre_hook_definition', {
2206
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_pre_hook', {
2215
2207
  method: 'POST',
2216
2208
  body: JSON.stringify({
2217
2209
  route: { id: routeId },
@@ -2260,12 +2252,12 @@ server.tool(
2260
2252
  async ({ routeId, name, code, scriptLanguage, methods, priority, isEnabled }) => {
2261
2253
  const methodMap = await getMethodMap();
2262
2254
  const methodNames = methods || ['GET', 'POST', 'PATCH', 'DELETE'];
2263
- const scriptValidation = await validateScriptSourceIfPresent(fetchAPI, ENFYRA_API_URL, 'post_hook_definition', {
2255
+ const scriptValidation = await validateScriptSourceIfPresent(fetchAPI, ENFYRA_API_URL, 'enfyra_post_hook', {
2264
2256
  sourceCode: code,
2265
2257
  scriptLanguage,
2266
2258
  });
2267
2259
 
2268
- const result = await fetchAPI(ENFYRA_API_URL, '/post_hook_definition', {
2260
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_post_hook', {
2269
2261
  method: 'POST',
2270
2262
  body: JSON.stringify({
2271
2263
  route: { id: routeId },
@@ -2320,7 +2312,7 @@ server.tool(
2320
2312
  isEnabled,
2321
2313
  column: { id: getId(column) },
2322
2314
  };
2323
- const result = await fetchAPI(ENFYRA_API_URL, '/column_rule_definition', {
2315
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_column_rule', {
2324
2316
  method: 'POST',
2325
2317
  body: JSON.stringify(body),
2326
2318
  });
@@ -2367,7 +2359,7 @@ server.tool(
2367
2359
  } else {
2368
2360
  body.relation = { id: getId(resolveFieldOrThrow(table, relationName, 'relation')) };
2369
2361
  }
2370
- const result = await fetchAPI(ENFYRA_API_URL, '/field_permission_definition', {
2362
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_field_permission', {
2371
2363
  method: 'POST',
2372
2364
  body: JSON.stringify(body),
2373
2365
  });
@@ -2382,9 +2374,9 @@ server.tool(
2382
2374
  'Use this when a non-root role/user should access an authenticated route. publicMethods are for public access; route permissions are for authenticated role/user access.',
2383
2375
  ].join(' '),
2384
2376
  {
2385
- path: z.string().optional().describe('Route path, e.g. /user_definition'),
2377
+ path: z.string().optional().describe('Route path, e.g. /enfyra_user'),
2386
2378
  routeId: z.union([z.string(), z.number()]).optional().describe('Route id. Use either path or routeId.'),
2387
- methods: z.array(z.string()).describe('REST method names this permission allows. Each value must exist in method_definition.name.'),
2379
+ methods: z.array(z.string()).describe('REST method names this permission allows. Each value must exist in enfyra_method.name.'),
2388
2380
  roleId: z.union([z.string(), z.number()]).optional().describe('Role id scope'),
2389
2381
  allowedUserIds: z.array(z.union([z.string(), z.number()])).optional().describe('Specific user ids scope'),
2390
2382
  description: z.string().optional().describe('Admin note'),
@@ -2395,7 +2387,7 @@ server.tool(
2395
2387
  if (!roleId && (!allowedUserIds || allowedUserIds.length === 0)) {
2396
2388
  throw new Error('Provide roleId or allowedUserIds');
2397
2389
  }
2398
- const routes = await fetchAll('/route_definition?limit=1000');
2390
+ const routes = await fetchAll('/enfyra_route?limit=1000');
2399
2391
  const route = routes.find((item) => (
2400
2392
  routeId ? sameId(getId(item), routeId) : item.path === normalizeRestPath(path)
2401
2393
  ));
@@ -2409,7 +2401,7 @@ server.tool(
2409
2401
  ...(roleId ? { role: { id: roleId } } : {}),
2410
2402
  ...(allowedUserIds?.length ? { allowedUsers: allowedUserIds.map((id) => ({ id })) } : {}),
2411
2403
  };
2412
- const result = await fetchAPI(ENFYRA_API_URL, '/route_permission_definition', {
2404
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_route_permission', {
2413
2405
  method: 'POST',
2414
2406
  body: JSON.stringify(body),
2415
2407
  });
@@ -2447,9 +2439,9 @@ server.tool(
2447
2439
  if (roleId && roleName) throw new Error('Provide roleId or roleName, not both.');
2448
2440
 
2449
2441
  const [routes, routePermissions, roles, methodIdNameMap] = await Promise.all([
2450
- fetchAll('/route_definition?limit=1000'),
2451
- fetchAll('/route_permission_definition?limit=1000'),
2452
- fetchAll('/role_definition?limit=1000'),
2442
+ fetchAll('/enfyra_route?limit=1000'),
2443
+ fetchAll('/enfyra_route_permission?limit=1000'),
2444
+ fetchAll('/enfyra_role?limit=1000'),
2453
2445
  getMethodIdNameMap(),
2454
2446
  ]);
2455
2447
 
@@ -2466,8 +2458,8 @@ server.tool(
2466
2458
  const expectedMethods = normalizeMethodNames(methods || []);
2467
2459
  const payload = {
2468
2460
  guidance: {
2469
- publicAccess: 'publicMethods bypass RoleGuard and do not require route_permission_definition.',
2470
- authenticatedAccess: 'For non-public methods, Enfyra admin UI PermissionGate and backend RoleGuard both expect enabled route_permission_definition rows with matching route + HTTP method.',
2461
+ publicAccess: 'publicMethods bypass RoleGuard and do not require enfyra_route_permission.',
2462
+ authenticatedAccess: 'For non-public methods, Enfyra admin UI PermissionGate and backend RoleGuard both expect enabled enfyra_route_permission rows with matching route + HTTP method.',
2471
2463
  directUserAccess: 'allowedRoutePermissions on /me represent direct user-scoped route permissions; role.routePermissions represent role-scoped permissions.',
2472
2464
  },
2473
2465
  expectedScope: {
@@ -2492,7 +2484,7 @@ server.tool(
2492
2484
  'ensure_route_access',
2493
2485
  [
2494
2486
  'Create or update authenticated route access for one role/user scope.',
2495
- 'Use this instead of raw route_permission_definition CRUD when fixing 403s. It resolves roleName/route/method ids, validates route.availableMethods, merges existing permission methods by default, and reloads routes.',
2487
+ 'Use this instead of raw enfyra_route_permission CRUD when fixing 403s. It resolves roleName/route/method ids, validates route.availableMethods, merges existing permission methods by default, and reloads routes.',
2496
2488
  ].join(' '),
2497
2489
  {
2498
2490
  path: z.string().optional().describe('Route path, e.g. /orders'),
@@ -2514,9 +2506,9 @@ server.tool(
2514
2506
  }
2515
2507
 
2516
2508
  const [routes, routePermissions, roles, methodMap, methodIdNameMap] = await Promise.all([
2517
- fetchAll('/route_definition?limit=1000'),
2518
- fetchAll('/route_permission_definition?limit=1000'),
2519
- fetchAll('/role_definition?limit=1000'),
2509
+ fetchAll('/enfyra_route?limit=1000'),
2510
+ fetchAll('/enfyra_route_permission?limit=1000'),
2511
+ fetchAll('/enfyra_role?limit=1000'),
2520
2512
  getMethodMap(),
2521
2513
  getMethodIdNameMap(),
2522
2514
  ]);
@@ -2547,7 +2539,7 @@ server.tool(
2547
2539
  methods: methodRefs,
2548
2540
  ...(description !== undefined ? { description } : {}),
2549
2541
  };
2550
- result = await fetchAPI(ENFYRA_API_URL, `/route_permission_definition/${encodeURIComponent(String(getId(existing)))}`, {
2542
+ result = await fetchAPI(ENFYRA_API_URL, `/enfyra_route_permission/${encodeURIComponent(String(getId(existing)))}`, {
2551
2543
  method: 'PATCH',
2552
2544
  body: JSON.stringify(patchBody),
2553
2545
  });
@@ -2561,7 +2553,7 @@ server.tool(
2561
2553
  ...(scope.roleId ? { role: { id: scope.roleId } } : {}),
2562
2554
  ...(scope.allowedUserIds.length ? { allowedUsers: scope.allowedUserIds.map((id) => ({ id })) } : {}),
2563
2555
  };
2564
- result = await fetchAPI(ENFYRA_API_URL, '/route_permission_definition', {
2556
+ result = await fetchAPI(ENFYRA_API_URL, '/enfyra_route_permission', {
2565
2557
  method: 'POST',
2566
2558
  body: JSON.stringify(createBody),
2567
2559
  });
@@ -2619,7 +2611,7 @@ server.tool(
2619
2611
  async ({ name, position, routeId, path, methods, combinator, priority, isGlobal, isEnabled, description, rules }) => {
2620
2612
  let route = null;
2621
2613
  if (!isGlobal && (routeId || path)) {
2622
- const routes = await fetchAll('/route_definition?limit=1000');
2614
+ const routes = await fetchAll('/enfyra_route?limit=1000');
2623
2615
  route = routes.find((item) => (
2624
2616
  routeId ? sameId(getId(item), routeId) : item.path === normalizeRestPath(path)
2625
2617
  ));
@@ -2637,7 +2629,7 @@ server.tool(
2637
2629
  ...(route ? { route: { id: getId(route) } } : {}),
2638
2630
  ...(methods?.length ? { methods: resolveMethodIds(methodMap, methods) } : {}),
2639
2631
  };
2640
- const guard = await fetchAPI(ENFYRA_API_URL, '/guard_definition', {
2632
+ const guard = await fetchAPI(ENFYRA_API_URL, '/enfyra_guard', {
2641
2633
  method: 'POST',
2642
2634
  body: JSON.stringify(guardBody),
2643
2635
  });
@@ -2653,7 +2645,7 @@ server.tool(
2653
2645
  guard: { id: resultRecordId(guard) },
2654
2646
  ...(Array.isArray(rule.userIds) && rule.userIds.length ? { users: rule.userIds.map((id) => ({ id })) } : {}),
2655
2647
  };
2656
- createdRules.push(await fetchAPI(ENFYRA_API_URL, '/guard_rule_definition', {
2648
+ createdRules.push(await fetchAPI(ENFYRA_API_URL, '/enfyra_guard_rule', {
2657
2649
  method: 'POST',
2658
2650
  body: JSON.stringify(ruleBody),
2659
2651
  }));
@@ -2756,7 +2748,7 @@ server.tool('get_current_user', 'Get current authenticated user info', {}, async
2756
2748
  });
2757
2749
 
2758
2750
  server.tool('get_all_roles', 'Get all role definitions', {}, async () => {
2759
- const result = await fetchAPI(ENFYRA_API_URL, '/role_definition?limit=100');
2751
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_role?limit=100');
2760
2752
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
2761
2753
  });
2762
2754
 
@@ -2808,7 +2800,7 @@ server.tool(
2808
2800
  server.tool(
2809
2801
  'install_package',
2810
2802
  [
2811
- 'Install an NPM package on Enfyra. Searches NPM registry for exact version, then creates package_definition record.',
2803
+ 'Install an NPM package on Enfyra. Searches NPM registry for exact version, then creates enfyra_package record.',
2812
2804
  'Enfyra handles the actual yarn add internally based on type.',
2813
2805
  'Type "Server" = available in handlers/hooks as $ctx.$pkgs.packageName.',
2814
2806
  'Type "App" = available in extensions via getPackages().',
@@ -2838,7 +2830,7 @@ server.tool(
2838
2830
 
2839
2831
  // Step 2: Check if already installed (same name AND type)
2840
2832
  const checkFilter = JSON.stringify({ name: { _eq: name }, type: { _eq: type } });
2841
- const existing = await fetchAPI(ENFYRA_API_URL, `/package_definition?filter=${encodeURIComponent(checkFilter)}&limit=1`);
2833
+ const existing = await fetchAPI(ENFYRA_API_URL, `/enfyra_package?filter=${encodeURIComponent(checkFilter)}&limit=1`);
2842
2834
  if (existing.data && existing.data.length > 0) {
2843
2835
  return {
2844
2836
  content: [{
@@ -2853,7 +2845,7 @@ server.tool(
2853
2845
  const userId = me.data?.[0]?.id || me.data?.[0]?._id;
2854
2846
  if (!userId) throw new Error('Cannot get current user ID');
2855
2847
 
2856
- // Step 4: Install via package_definition
2848
+ // Step 4: Install via enfyra_package
2857
2849
  const body = {
2858
2850
  name,
2859
2851
  version: pkgVersion,
@@ -2862,7 +2854,7 @@ server.tool(
2862
2854
  installedBy: { id: userId },
2863
2855
  };
2864
2856
 
2865
- const result = await fetchAPI(ENFYRA_API_URL, '/package_definition', {
2857
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_package', {
2866
2858
  method: 'POST',
2867
2859
  body: JSON.stringify(body),
2868
2860
  });
@@ -2901,7 +2893,7 @@ server.tool('create_menu', 'Create a menu item in the navigation. Use permission
2901
2893
  if (body.path && !body.path.startsWith('/')) {
2902
2894
  body.path = '/' + body.path;
2903
2895
  }
2904
- const result = await fetchAPI(ENFYRA_API_URL, '/menu_definition', { method: 'POST', body: JSON.stringify(body) });
2896
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_menu', { method: 'POST', body: JSON.stringify(body) });
2905
2897
  const created = firstDataRecord(result);
2906
2898
  return { content: [{ type: 'text', text: `Menu created (ID: ${getId(created)}):\n${JSON.stringify(result, null, 2)}` }] };
2907
2899
  });
@@ -2916,7 +2908,7 @@ server.tool(
2916
2908
  name: z.string().describe('Extension name (unique)'),
2917
2909
  type: z.enum(['page', 'widget', 'global']).describe('Extension type: page = full page linked to menu; widget = embed via Widget component; global = shell-level lifecycle component'),
2918
2910
  code: z.string().describe('Vue SFC string — <template> + <script setup>, NO import statements'),
2919
- menuId: z.string().optional().describe('Required for type=page — menu_definition id from create_menu. Omit for widget/global'),
2911
+ menuId: z.string().optional().describe('Required for type=page — enfyra_menu id from create_menu. Omit for widget/global'),
2920
2912
  isEnabled: z.boolean().optional().default(true).describe('Enable extension'),
2921
2913
  description: z.string().optional().describe('Extension description'),
2922
2914
  version: z.string().optional().default('1.0.0').describe('Extension version'),
@@ -2924,7 +2916,7 @@ server.tool(
2924
2916
  async (data) => {
2925
2917
  const body = { ...data };
2926
2918
  if (body.type === 'page' && !body.menuId) {
2927
- throw new Error('menuId is required for type=page. Create or find a menu_definition record first.');
2919
+ throw new Error('menuId is required for type=page. Create or find a enfyra_menu record first.');
2928
2920
  }
2929
2921
  if (body.type !== 'page' && body.menuId) {
2930
2922
  throw new Error('menuId is only valid for type=page. Omit menuId for widget/global extensions.');
@@ -2933,7 +2925,7 @@ server.tool(
2933
2925
  body.menu = { id: body.menuId };
2934
2926
  delete body.menuId;
2935
2927
  }
2936
- const result = await fetchAPI(ENFYRA_API_URL, '/extension_definition', { method: 'POST', body: JSON.stringify(body) });
2928
+ const result = await fetchAPI(ENFYRA_API_URL, '/enfyra_extension', { method: 'POST', body: JSON.stringify(body) });
2937
2929
  const created = firstDataRecord(result);
2938
2930
  return { content: [{ type: 'text', text: `Extension created (ID: ${getId(created)}). Open Enfyra admin tabs should update through the realtime reload contract.\n${JSON.stringify(result, null, 2)}` }] };
2939
2931
  },