@enfyra/mcp-server 0.1.9 → 0.1.10

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/README.md CHANGED
@@ -265,7 +265,7 @@ The MCP server includes safety guards for LLM callers:
265
265
  - Generated code should use relation property names such as `conversation`, `sender`, and `member` instead of physical FK fields such as `conversationId`, `senderId`, or `memberId`.
266
266
  - Custom route tools reject `mainTableId` unless the route is the canonical table route.
267
267
  - `discover_enfyra_workflows` maps task intent to workflow surfaces before the agent loads detailed examples or guesses between similar tools.
268
- - Platform operation tools such as `api_endpoint_workflow`, `extension_workflow`, `create_api_endpoint`, `enable_route`, `disable_route`, `delete_route`, `public_route_methods`, `add_route_methods`, `set_table_graphql`, `ensure_guard`, `ensure_field_permission`, `ensure_column_rule`, `ensure_websocket_event`, `choose_flow_step_tool`, fixed-type flow step tools, `ensure_menu`, `ensure_page_extension`, `ensure_global_extension`, and `ensure_widget_extension` resolve metadata ids and validate code before saving.
268
+ - Platform operation tools such as `api_endpoint_workflow`, `extension_workflow`, `create_api_endpoint`, `enable_route`, `disable_route`, `delete_route`, `public_route_methods`, `add_route_methods`, `set_table_graphql`, `ensure_guard`, `ensure_field_permission`, `ensure_column_rule`, `ensure_websocket_event`, `choose_flow_step_tool`, fixed-type flow step tools, `ensure_menu`, `reorder_menus`, `ensure_page_extension`, `ensure_global_extension`, and `ensure_widget_extension` resolve metadata ids and validate code before saving.
269
269
  - Schema changes are serialized.
270
270
  - Destructive deletes return a preview before requiring `confirm=true`.
271
271
 
@@ -307,6 +307,8 @@ The MCP server exposes tools for workflow routing, metadata discovery, required
307
307
 
308
308
  Routes have two separate controls. `isEnabled` controls runtime registration: disabled routes return `404`. Use `enable_route` and `disable_route` for this lifecycle. `publicMethods` controls anonymous access for enabled routes; use `public_route_methods` and `private_route_methods` for that access boundary.
309
309
 
310
+ Use `reorder_menus` for menu order or parent changes. It calls the Enfyra 2.2.6 `/admin/menu/reorder` operation route so hierarchy validation and menu cache invalidation are handled by the server instead of PATCHing individual `enfyra_menu` records.
311
+
310
312
  Admin app page paths and API paths are different surfaces. A page extension path such as `/cloud/projects/:id` is a UI route unless an enabled Enfyra API route with that exact path exists. Use `test_rest_endpoint` only for actual API routes under `ENFYRA_API_URL`; verify page extensions through the app URL/browser or extension/menu metadata.
311
313
 
312
314
  For authenticated route access, use `audit_route_access` before changing permissions and `ensure_route_access` to grant access by route path plus role/user. For production script edits, use `trace_metadata_usage`, `get_script_source`, and `patch_script_source` so changes are targeted, hash-checked, and validated.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enfyra/mcp-server",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "MCP server for Enfyra - manage Enfyra instances from MCP-compatible coding tools",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -673,6 +673,42 @@ async function ensureMenu(apiUrl, {
673
673
  };
674
674
  }
675
675
 
676
+ async function reorderMenus(apiUrl, { updates, globalRulesAckKey }) {
677
+ assertGlobalRulesAck(globalRulesAckKey);
678
+ const seen = new Set();
679
+ const normalizedUpdates = updates.map((item, index) => {
680
+ const id = item?.id;
681
+ if (id === null || id === undefined || String(id).trim() === '') {
682
+ throw new Error(`updates[${index}].id is required.`);
683
+ }
684
+ const key = String(id);
685
+ if (seen.has(key)) throw new Error(`Duplicate menu id in reorder payload: ${key}`);
686
+ seen.add(key);
687
+ const order = Number(item.order);
688
+ if (!Number.isInteger(order) || order < 0) {
689
+ throw new Error(`updates[${index}].order must be a non-negative integer.`);
690
+ }
691
+ const parent = item.parent === undefined || item.parent === null || String(item.parent).trim() === ''
692
+ ? null
693
+ : item.parent;
694
+ return { id, order, parent };
695
+ });
696
+ const result = await fetchAPI(apiUrl, '/admin/menu/reorder', {
697
+ method: 'POST',
698
+ body: JSON.stringify({ updates: normalizedUpdates }),
699
+ });
700
+ return {
701
+ action: 'menus_reordered',
702
+ updates: normalizedUpdates,
703
+ result,
704
+ reload: {
705
+ attempted: false,
706
+ succeeded: true,
707
+ reason: '/admin/menu/reorder persists order/parent updates and emits enfyra_menu cache invalidation.',
708
+ },
709
+ };
710
+ }
711
+
676
712
  async function ensureExtension(apiUrl, {
677
713
  name,
678
714
  type,
@@ -2352,6 +2388,24 @@ export function registerPlatformOperationTools(server, ENFYRA_API_URL) {
2352
2388
  }),
2353
2389
  );
2354
2390
 
2391
+ server.tool(
2392
+ 'reorder_menus',
2393
+ [
2394
+ 'Business operation: reorder Enfyra admin menus and optionally move menus under a new parent.',
2395
+ 'Uses the server /admin/menu/reorder route introduced in Enfyra 2.2.6 instead of PATCHing each enfyra_menu record.',
2396
+ 'The server validates duplicate ids, non-negative integer order, dropdown-only parents, /data child restrictions, system menu parent locks, cycle prevention, persistence, and menu cache invalidation.',
2397
+ ].join(' '),
2398
+ {
2399
+ updates: z.array(z.object({
2400
+ id: z.union([z.string(), z.number()]).describe('Menu id to reorder.'),
2401
+ order: z.number().int().nonnegative().describe('Sibling order index. Must be a non-negative integer.'),
2402
+ parent: z.union([z.string(), z.number(), z.null()]).optional().describe('New parent menu id, or null for a root menu. Parent must be a Dropdown Menu.'),
2403
+ })).min(1).describe('Menu order/parent updates, usually the changed siblings from drag-and-drop.'),
2404
+ globalRulesAckKey: globalRulesAckParam(z),
2405
+ },
2406
+ async (input) => jsonText(await reorderMenus(ENFYRA_API_URL, input)),
2407
+ );
2408
+
2355
2409
  server.tool(
2356
2410
  'ensure_page_extension',
2357
2411
  'Business operation: create or update one page extension attached to an existing menu. Validates extension code before save. Call get_extension_theme_contract first for UI work.',
@@ -67,7 +67,7 @@ export const TOOL_WORKFLOWS = [
67
67
  firstTools: ['get_enfyra_required_knowledge', 'get_extension_theme_contract', 'inspect_feature'],
68
68
  inspectTools: ['inspect_feature', 'trace_metadata_usage', 'get_script_source'],
69
69
  knowledgeTools: ['get_enfyra_required_knowledge', 'get_extension_theme_contract', 'get_theme_class_reference'],
70
- writeTools: ['extension_workflow', 'ensure_menu', 'ensure_page_extension', 'ensure_global_extension', 'ensure_widget_extension'],
70
+ writeTools: ['extension_workflow', 'ensure_menu', 'reorder_menus', 'ensure_page_extension', 'ensure_global_extension', 'ensure_widget_extension'],
71
71
  verifyTools: ['validate_extension_code', 'inspect_feature'],
72
72
  avoidTools: [
73
73
  {
@@ -82,6 +82,12 @@ export const TOOL_WORKFLOWS = [
82
82
  useInstead: 'notification summary/realtime shell signal plus destination-page fetch on click',
83
83
  reason: 'Shell notifications should not fetch messages, tickets, orders, or jobs lists solely for a badge.',
84
84
  },
85
+ {
86
+ tool: 'update_record/PATCH enfyra_menu for order or parent changes',
87
+ when: 'drag-and-drop or programmatic menu ordering changes sibling order or parent',
88
+ useInstead: 'reorder_menus',
89
+ reason: 'The Enfyra 2.2.6 /admin/menu/reorder route validates menu hierarchy constraints and emits menu cache invalidation.',
90
+ },
85
91
  ],
86
92
  requiredAck: ['globalRulesAckKey', 'extensionAckKey when saving extension code'],
87
93
  exampleCategories: ['extensions'],
@@ -89,6 +95,7 @@ export const TOOL_WORKFLOWS = [
89
95
  'Call get_extension_theme_contract before writing or reviewing UI.',
90
96
  'Inspect the existing menu/extension/global shell registration.',
91
97
  'Use extension_workflow with apply=false when page/menu wiring or shell notification behavior needs multiple steps.',
98
+ 'Use reorder_menus for menu order/parent changes instead of patching individual enfyra_menu records.',
92
99
  'Choose count only when the source already owns an exact count; choose dot/chip for new-attention signals.',
93
100
  'Validate extension code or use an ensure_*_extension tool that validates before saving.',
94
101
  ],
@@ -173,6 +173,12 @@ const MCP_PERMISSION_REQUIREMENTS = [
173
173
  route: '/admin/reload/routes',
174
174
  methods: ['POST'],
175
175
  },
176
+ {
177
+ area: 'menu reorder',
178
+ tools: ['reorder_menus'],
179
+ route: '/admin/menu/reorder',
180
+ methods: ['POST'],
181
+ },
176
182
  {
177
183
  area: 'metadata cache reload',
178
184
  tools: ['reload_metadata'],