@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
|
@@ -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.',
|
package/src/lib/tool-routing.js
CHANGED
|
@@ -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
|
],
|
package/src/mcp-server-entry.mjs
CHANGED
|
@@ -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'],
|