@enfyra/mcp-server 0.1.13 → 0.1.15

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.
Files changed (71) hide show
  1. package/README.md +14 -1
  2. package/dist/index.d.ts +5 -0
  3. package/{src/index.mjs → dist/index.js} +8 -10
  4. package/dist/index.js.map +1 -0
  5. package/dist/lib/auth.d.ts +34 -0
  6. package/dist/lib/auth.js +161 -0
  7. package/dist/lib/auth.js.map +1 -0
  8. package/dist/lib/config-local.d.ts +1 -0
  9. package/dist/lib/config-local.js +719 -0
  10. package/dist/lib/config-local.js.map +1 -0
  11. package/dist/lib/extension-search-tools.d.ts +59 -0
  12. package/dist/lib/extension-search-tools.js +483 -0
  13. package/dist/lib/extension-search-tools.js.map +1 -0
  14. package/dist/lib/fetch.d.ts +28 -0
  15. package/dist/lib/fetch.js +106 -0
  16. package/dist/lib/fetch.js.map +1 -0
  17. package/dist/lib/mcp-examples.d.ts +99 -0
  18. package/dist/lib/mcp-examples.js +2285 -0
  19. package/dist/lib/mcp-examples.js.map +1 -0
  20. package/dist/lib/mcp-instructions.d.ts +11 -0
  21. package/dist/lib/mcp-instructions.js +78 -0
  22. package/dist/lib/mcp-instructions.js.map +1 -0
  23. package/dist/lib/mutation-guards.d.ts +33 -0
  24. package/dist/lib/mutation-guards.js +106 -0
  25. package/dist/lib/mutation-guards.js.map +1 -0
  26. package/dist/lib/platform-operation-tools.d.ts +12 -0
  27. package/dist/lib/platform-operation-tools.js +2304 -0
  28. package/dist/lib/platform-operation-tools.js.map +1 -0
  29. package/dist/lib/required-knowledge.d.ts +32 -0
  30. package/dist/lib/required-knowledge.js +181 -0
  31. package/dist/lib/required-knowledge.js.map +1 -0
  32. package/dist/lib/response-format.d.ts +7 -0
  33. package/dist/lib/response-format.js +179 -0
  34. package/dist/lib/response-format.js.map +1 -0
  35. package/dist/lib/route-guards.d.ts +1 -0
  36. package/dist/lib/route-guards.js +19 -0
  37. package/dist/lib/route-guards.js.map +1 -0
  38. package/dist/lib/route-permission-tools.d.ts +91 -0
  39. package/dist/lib/route-permission-tools.js +151 -0
  40. package/dist/lib/route-permission-tools.js.map +1 -0
  41. package/dist/lib/source-artifacts.d.ts +27 -0
  42. package/dist/lib/source-artifacts.js +82 -0
  43. package/dist/lib/source-artifacts.js.map +1 -0
  44. package/dist/lib/table-tools.d.ts +62 -0
  45. package/dist/lib/table-tools.js +774 -0
  46. package/dist/lib/table-tools.js.map +1 -0
  47. package/dist/lib/tool-routing.d.ts +297 -0
  48. package/dist/lib/tool-routing.js +585 -0
  49. package/dist/lib/tool-routing.js.map +1 -0
  50. package/dist/lib/types.d.ts +17 -0
  51. package/dist/lib/types.js +2 -0
  52. package/dist/lib/types.js.map +1 -0
  53. package/dist/mcp-server-entry.d.ts +4 -0
  54. package/dist/mcp-server-entry.js +2787 -0
  55. package/dist/mcp-server-entry.js.map +1 -0
  56. package/package.json +17 -10
  57. package/src/lib/auth.js +0 -179
  58. package/src/lib/config-local.mjs +0 -718
  59. package/src/lib/fetch.js +0 -111
  60. package/src/lib/mcp-examples.js +0 -2289
  61. package/src/lib/mcp-instructions.js +0 -80
  62. package/src/lib/mutation-guards.js +0 -118
  63. package/src/lib/platform-operation-tools.js +0 -2616
  64. package/src/lib/required-knowledge.js +0 -188
  65. package/src/lib/response-format.js +0 -187
  66. package/src/lib/route-guards.js +0 -24
  67. package/src/lib/route-permission-tools.js +0 -160
  68. package/src/lib/source-artifacts.js +0 -82
  69. package/src/lib/table-tools.js +0 -907
  70. package/src/lib/tool-routing.js +0 -589
  71. package/src/mcp-server-entry.mjs +0 -3177
@@ -1,589 +0,0 @@
1
- export const WORKFLOW_SURFACES = [
2
- 'api-endpoint',
3
- 'extension',
4
- 'schema',
5
- 'record-data',
6
- 'dynamic-script',
7
- 'route-access',
8
- 'guards-permissions-rules',
9
- 'flow',
10
- 'websocket',
11
- 'graphql',
12
- 'package',
13
- 'cache',
14
- 'logs-debug',
15
- 'auth-context',
16
- ];
17
-
18
- const ALL_DETAILS = ['summary', 'plan', 'full'];
19
-
20
- export const TOOL_WORKFLOWS = [
21
- {
22
- key: 'api-endpoint',
23
- title: 'Custom REST endpoint with handler',
24
- useWhen: [
25
- 'Creating or changing a custom API path with handler behavior.',
26
- 'Changing public/private method access on a custom endpoint.',
27
- 'Adding route permissions for endpoint access.',
28
- ],
29
- keywords: ['endpoint', 'api', 'route handler', 'handler', 'webhook', 'custom route', 'rest path'],
30
- firstTools: ['get_enfyra_required_knowledge', 'discover_script_contexts', 'inspect_route'],
31
- inspectTools: ['inspect_route', 'get_all_routes', 'trace_metadata_usage'],
32
- knowledgeTools: ['get_enfyra_required_knowledge', 'discover_script_contexts'],
33
- writeTools: ['api_endpoint_workflow', 'create_api_endpoint', 'enable_route', 'public_route_methods', 'private_route_methods', 'ensure_route_access'],
34
- verifyTools: ['test_rest_endpoint', 'run_admin_test'],
35
- avoidTools: [
36
- {
37
- tool: 'create_route',
38
- when: 'a handler-backed endpoint or route permission plan is needed',
39
- useInstead: 'api_endpoint_workflow',
40
- reason: 'The workflow validates source, reads live route/handler/access state, applies one safe step at a time, and returns nextSteps.',
41
- },
42
- {
43
- tool: 'create_table',
44
- when: 'the user asked for custom behavior at an API path',
45
- useInstead: 'api_endpoint_workflow',
46
- reason: 'Tables create persisted data models; custom route handlers own behavior endpoints.',
47
- },
48
- ],
49
- requiredAck: ['globalRulesAckKey', 'dynamicCodeAckKey when saving handler source'],
50
- exampleCategories: ['handlers-hooks', 'permissions-rls'],
51
- nextStepTemplate: [
52
- 'Inspect the route or run api_endpoint_workflow with apply=false.',
53
- 'Read required knowledge before apply/applyAll.',
54
- 'Apply one pending step or use applyAll only when the plan is fully understood.',
55
- 'Verify the endpoint with test_rest_endpoint or run_admin_test.',
56
- ],
57
- },
58
- {
59
- key: 'extension',
60
- title: 'Admin extension, menu, shell notification, or account panel UI',
61
- useWhen: [
62
- 'Creating or changing Enfyra admin page/widget/global extensions.',
63
- 'Adding menu entries, menu notification dots/counts, account panel rows, or shell actions.',
64
- 'Reviewing extension UI theme/layout/component usage.',
65
- ],
66
- keywords: ['extension', 'menu', 'account panel', 'notification', 'chip', 'badge', 'sidebar', 'shell', 'page ui', 'widget'],
67
- firstTools: ['get_enfyra_required_knowledge', 'get_extension_theme_contract', 'inspect_feature'],
68
- inspectTools: ['inspect_feature', 'trace_metadata_usage', 'get_script_source'],
69
- knowledgeTools: ['get_enfyra_required_knowledge', 'get_extension_theme_contract', 'get_theme_class_reference'],
70
- writeTools: ['extension_workflow', 'ensure_menu', 'reorder_menus', 'update_extension_code', 'ensure_page_extension', 'ensure_global_extension', 'ensure_widget_extension'],
71
- verifyTools: ['validate_extension_code', 'inspect_feature'],
72
- avoidTools: [
73
- {
74
- tool: 'create_record/update_record on enfyra_extension',
75
- when: 'creating or changing extension code',
76
- useInstead: 'update_extension_code for an existing extension id/name, or extension_workflow/ensure_*_extension for create/wire flows',
77
- reason: 'Extension operation tools validate local guards plus /enfyra_extension/preview and save only after validation succeeds.',
78
- },
79
- {
80
- tool: 'query_table on destination domain lists',
81
- when: 'decorating menu/account panel notifications',
82
- useInstead: 'notification summary/realtime shell signal plus destination-page fetch on click',
83
- reason: 'Shell notifications should not fetch messages, tickets, orders, or jobs lists solely for a badge.',
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
- },
91
- ],
92
- requiredAck: ['globalRulesAckKey', 'extensionAckKey when saving extension code'],
93
- exampleCategories: ['extensions'],
94
- nextStepTemplate: [
95
- 'Call get_extension_theme_contract before writing or reviewing UI.',
96
- 'Inspect the existing menu/extension/global shell registration.',
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.',
99
- 'Choose count only when the source already owns an exact count; choose dot/chip for new-attention signals.',
100
- 'Validate extension code or use an ensure_*_extension tool that validates before saving.',
101
- ],
102
- },
103
- {
104
- key: 'schema',
105
- title: 'Table, column, relation, validation, or schema metadata change',
106
- useWhen: [
107
- 'Creating or changing tables, columns, relations, indexes, or validation metadata.',
108
- 'Designing relation direction or inverse relation exposure.',
109
- 'Changing GraphQL/table schema flags alongside metadata.',
110
- ],
111
- keywords: ['schema', 'table', 'column', 'relation', 'field', 'index', 'validation', 'inverse'],
112
- firstTools: ['get_enfyra_required_knowledge', 'inspect_table', 'get_all_tables'],
113
- inspectTools: ['inspect_table', 'get_table_metadata', 'get_all_tables'],
114
- knowledgeTools: ['get_enfyra_required_knowledge', 'get_enfyra_examples'],
115
- writeTools: ['create_table', 'update_table', 'delete_table', 'create_column', 'update_column', 'delete_column', 'create_relation', 'delete_relation', 'ensure_column_rule'],
116
- verifyTools: ['inspect_table', 'get_table_metadata'],
117
- avoidTools: [
118
- {
119
- tool: 'create_record/update_record on enfyra_column or enfyra_relation',
120
- when: 'changing schema metadata',
121
- useInstead: 'table/column/relation schema tools',
122
- reason: 'Schema tools resolve table ids, preserve relation contracts, and reject physical FK names.',
123
- },
124
- {
125
- tool: 'manual inversePropertyName',
126
- when: 'there is no concrete response/UI/deep-query/aggregate need for reverse traversal',
127
- useInstead: 'owning relation only',
128
- reason: 'Relation design stays minimal unless the reverse field is actually used.',
129
- },
130
- ],
131
- requiredAck: ['globalRulesAckKey'],
132
- exampleCategories: ['schema-relations'],
133
- nextStepTemplate: [
134
- 'Inspect the existing table and relations.',
135
- 'Decide owner relation and whether an inverse is actually needed.',
136
- 'Apply schema tool changes with globalRulesAckKey.',
137
- 'Re-inspect metadata instead of assuming the saved shape.',
138
- ],
139
- },
140
- {
141
- key: 'record-data',
142
- title: 'Route-backed table data query or CRUD',
143
- useWhen: [
144
- 'Reading or mutating normal route-backed records.',
145
- 'Counting records or finding one row by filter.',
146
- 'Testing filters, fields, deep relations, pagination, or aggregate query shape.',
147
- ],
148
- keywords: ['record', 'crud', 'query', 'count', 'filter', 'aggregate', 'deep', 'sort', 'pagination'],
149
- firstTools: ['inspect_table', 'discover_query_capabilities'],
150
- inspectTools: ['inspect_table', 'get_table_metadata', 'discover_query_capabilities'],
151
- knowledgeTools: ['get_enfyra_required_knowledge for writes'],
152
- writeTools: ['create_record', 'update_record', 'delete_record'],
153
- verifyTools: ['find_one_record', 'query_table', 'count_records'],
154
- avoidTools: [
155
- {
156
- tool: 'query_table without limit or all=true',
157
- when: 'listing records',
158
- useInstead: 'query_table with a bounded limit or all=true for intentional complete reads',
159
- reason: 'List/query tools require explicit paging intent.',
160
- },
161
- {
162
- tool: 'generic CRUD on internal/no-route system tables',
163
- when: 'changing schema, sessions, columns, or other no-route internals',
164
- useInstead: 'specific schema/platform tools',
165
- reason: 'Generic CRUD is for route-backed tables only.',
166
- },
167
- ],
168
- requiredAck: ['globalRulesAckKey for writes', 'dynamicCodeAckKey for script-backed sourceCode writes', 'extensionAckKey for extension code writes'],
169
- exampleCategories: ['queries-deep'],
170
- nextStepTemplate: [
171
- 'Inspect table metadata and choose fields explicitly.',
172
- 'Use bounded pagination or all=true deliberately.',
173
- 'For writes, read required knowledge and use metadata-backed field names only.',
174
- 'Re-read with explicit fields after mutation when saved shape matters.',
175
- ],
176
- },
177
- {
178
- key: 'dynamic-script',
179
- title: 'Dynamic server code: handlers, hooks, scripts, or source patches',
180
- useWhen: [
181
- 'Writing or reviewing handler, hook, flow step, websocket, GraphQL, or bootstrap sourceCode.',
182
- 'Editing an existing script-backed metadata record.',
183
- 'Debugging macro, repository, or validation behavior.',
184
- ],
185
- keywords: ['sourcecode', 'script', 'hook', 'pre hook', 'post hook', 'compiledcode', 'macro', 'repos', 'bootstrap'],
186
- firstTools: ['get_enfyra_required_knowledge', 'discover_script_contexts', 'trace_metadata_usage'],
187
- inspectTools: ['trace_metadata_usage', 'get_script_source', 'discover_script_contexts'],
188
- knowledgeTools: ['get_enfyra_required_knowledge', 'discover_script_contexts'],
189
- writeTools: ['patch_script_source', 'update_script_source', 'create_handler', 'create_pre_hook', 'create_post_hook', 'api_endpoint_workflow'],
190
- verifyTools: ['validate_dynamic_script', 'run_admin_test', 'test_rest_endpoint', 'test_flow_step'],
191
- avoidTools: [
192
- {
193
- tool: 'update_record with compiledCode',
194
- when: 'editing dynamic scripts',
195
- useInstead: 'patch_script_source or update_script_source with sourceCode',
196
- reason: 'compiledCode is generated and may differ because macros expand.',
197
- },
198
- {
199
- tool: 'throw new Error for intentional user/domain failures',
200
- when: 'writing generated dynamic server code',
201
- useInstead: '@THROW400-style macros or native $ctx.$throw helpers',
202
- reason: 'Intentional domain errors should use the platform error contract.',
203
- },
204
- ],
205
- requiredAck: ['globalRulesAckKey', 'dynamicCodeAckKey'],
206
- exampleCategories: ['handlers-hooks'],
207
- nextStepTemplate: [
208
- 'Discover script context macros for the surface.',
209
- 'Read existing source through trace_metadata_usage/get_script_source when patching.',
210
- 'Validate source before save unless the chosen write tool already validates.',
211
- 'Verify behavior with the route/test runner that matches the script surface.',
212
- ],
213
- },
214
- {
215
- key: 'route-access',
216
- title: 'Authenticated route access and public/private method state',
217
- useWhen: [
218
- 'Debugging 403/401 route access.',
219
- 'Granting role/user access to a route method.',
220
- 'Changing publicMethods or availableMethods.',
221
- ],
222
- keywords: ['permission', '403', '401', 'role', 'route access', 'public method', 'private method', 'available method'],
223
- firstTools: ['get_permission_profile', 'inspect_route', 'audit_route_access'],
224
- inspectTools: ['get_permission_profile', 'inspect_route', 'audit_route_access'],
225
- knowledgeTools: ['get_enfyra_required_knowledge'],
226
- writeTools: ['ensure_route_access', 'add_route_methods', 'replace_route_methods', 'remove_route_methods', 'public_route_methods', 'private_route_methods', 'enable_route', 'disable_route'],
227
- verifyTools: ['audit_route_access', 'test_rest_endpoint'],
228
- avoidTools: [
229
- {
230
- tool: 'raw enfyra_route_permission CRUD',
231
- when: 'granting route access',
232
- useInstead: 'ensure_route_access',
233
- reason: 'The operation tool resolves roles/methods and merges existing method grants safely.',
234
- },
235
- {
236
- tool: 'public_route_methods',
237
- when: 'the desired behavior is authenticated user access',
238
- useInstead: 'ensure_route_access',
239
- reason: 'publicMethods grants anonymous access; route permissions grant authenticated access.',
240
- },
241
- ],
242
- requiredAck: ['globalRulesAckKey for writes'],
243
- exampleCategories: ['permissions-rls'],
244
- nextStepTemplate: [
245
- 'Inspect route and permission profile before changing access.',
246
- 'Decide anonymous publicMethods versus authenticated route permission.',
247
- 'Use route operation tools instead of raw permission CRUD.',
248
- 'Audit and test the route after the change.',
249
- ],
250
- },
251
- {
252
- key: 'guards-permissions-rules',
253
- title: 'Guards, field permissions, and column validation rules',
254
- useWhen: [
255
- 'Adding route guards, guard rules, field permissions, or column rules.',
256
- 'Restricting field read/write behavior.',
257
- 'Adding body validation rules at metadata level.',
258
- ],
259
- keywords: ['guard', 'field permission', 'column rule', 'validation rule', 'rule', 'rls'],
260
- firstTools: ['get_enfyra_required_knowledge', 'inspect_table', 'inspect_route'],
261
- inspectTools: ['inspect_table', 'inspect_route', 'discover_query_capabilities'],
262
- knowledgeTools: ['get_enfyra_required_knowledge'],
263
- writeTools: ['ensure_guard', 'ensure_field_permission', 'ensure_column_rule'],
264
- verifyTools: ['test_rest_endpoint', 'query_table', 'run_admin_test'],
265
- avoidTools: [
266
- {
267
- tool: 'raw create_record on guard/rule tables',
268
- when: 'a dedicated ensure_* operation exists',
269
- useInstead: 'ensure_guard, ensure_field_permission, or ensure_column_rule',
270
- reason: 'Ensure tools resolve ids and preserve the current rule contract.',
271
- },
272
- ],
273
- requiredAck: ['globalRulesAckKey'],
274
- exampleCategories: ['permissions-rls', 'schema-relations'],
275
- nextStepTemplate: [
276
- 'Inspect the target table/route and decide the security boundary first.',
277
- 'Use the specific ensure_* operation for the rule surface.',
278
- 'Verify with the route/query behavior the rule is meant to protect.',
279
- ],
280
- },
281
- {
282
- key: 'flow',
283
- title: 'Flow, scheduled/manual flow, or flow step',
284
- useWhen: [
285
- 'Creating or changing manual/scheduled flows.',
286
- 'Choosing or writing a flow step.',
287
- 'Testing or triggering a flow.',
288
- ],
289
- keywords: ['flow', 'scheduled', 'manual flow', 'flow step', 'trigger flow', 'workflow'],
290
- firstTools: ['get_enfyra_required_knowledge', 'choose_flow_step_tool', 'discover_script_contexts'],
291
- inspectTools: ['inspect_feature', 'query_table'],
292
- knowledgeTools: ['get_enfyra_required_knowledge', 'discover_script_contexts'],
293
- writeTools: ['ensure_manual_flow', 'ensure_scheduled_flow', 'ensure_query_flow_step', 'ensure_create_flow_step', 'ensure_update_flow_step', 'ensure_delete_flow_step', 'ensure_http_flow_step', 'ensure_sleep_flow_step', 'ensure_trigger_flow_step', 'ensure_log_flow_step', 'ensure_condition_flow_step', 'ensure_script_flow_step'],
294
- verifyTools: ['test_flow_step', 'run_admin_test', 'trigger_flow'],
295
- avoidTools: [
296
- {
297
- tool: 'ensure_script_flow_step',
298
- when: 'a fixed query/create/update/delete/http/sleep/trigger/log step can express the operation',
299
- useInstead: 'choose_flow_step_tool then the fixed-type ensure_*_flow_step',
300
- reason: 'Atomic step types are easier to inspect, test, and maintain than oversized scripts.',
301
- },
302
- ],
303
- requiredAck: ['globalRulesAckKey', 'dynamicCodeAckKey for script or condition source'],
304
- exampleCategories: ['flows'],
305
- nextStepTemplate: [
306
- 'Use choose_flow_step_tool before mutating when step type is unclear.',
307
- 'Prefer fixed-type flow step tools over script steps.',
308
- 'Validate/test script or condition steps before relying on the flow.',
309
- 'Trigger manually only after the saved steps are verified.',
310
- ],
311
- },
312
- {
313
- key: 'websocket',
314
- title: 'Socket.IO gateway or websocket event',
315
- useWhen: [
316
- 'Creating or changing websocket gateways/events.',
317
- 'Writing websocket handler source.',
318
- 'Testing websocket event logic.',
319
- ],
320
- keywords: ['websocket', 'socket', 'socket.io', 'gateway', 'realtime', 'room'],
321
- firstTools: ['get_enfyra_required_knowledge', 'discover_script_contexts', 'inspect_feature'],
322
- inspectTools: ['inspect_feature', 'discover_script_contexts'],
323
- knowledgeTools: ['get_enfyra_required_knowledge', 'discover_script_contexts'],
324
- writeTools: ['ensure_websocket_gateway', 'ensure_websocket_event'],
325
- verifyTools: ['run_admin_test'],
326
- avoidTools: [
327
- {
328
- tool: 'raw CRUD on enfyra_websocket_event',
329
- when: 'saving websocket event source',
330
- useInstead: 'ensure_websocket_event',
331
- reason: 'The operation tool validates script source and preserves gateway/event linkage.',
332
- },
333
- ],
334
- requiredAck: ['globalRulesAckKey', 'dynamicCodeAckKey when saving event source'],
335
- exampleCategories: ['websocket'],
336
- nextStepTemplate: [
337
- 'Discover websocket script context before writing source.',
338
- 'Ensure gateway first, then event.',
339
- 'Use run_admin_test for event/connection scripts where possible.',
340
- ],
341
- },
342
- {
343
- key: 'graphql',
344
- title: 'GraphQL enablement and query surface',
345
- useWhen: [
346
- 'Enabling/disabling GraphQL for a table.',
347
- 'Checking GraphQL endpoint/schema behavior.',
348
- 'Clarifying REST route methods versus GraphQL table exposure.',
349
- ],
350
- keywords: ['graphql', 'gql', 'schema endpoint'],
351
- firstTools: ['discover_enfyra_system', 'inspect_table'],
352
- inspectTools: ['discover_enfyra_system', 'inspect_table'],
353
- knowledgeTools: ['get_enfyra_required_knowledge for writes'],
354
- writeTools: ['set_table_graphql', 'update_table'],
355
- verifyTools: ['reload_graphql', 'discover_enfyra_system'],
356
- avoidTools: [
357
- {
358
- tool: 'public_route_methods',
359
- when: 'trying to expose GraphQL table data',
360
- useInstead: 'set_table_graphql plus GraphQL auth planning',
361
- reason: 'REST publicMethods do not make GraphQL table data anonymous.',
362
- },
363
- ],
364
- requiredAck: ['globalRulesAckKey for writes'],
365
- exampleCategories: ['queries-deep'],
366
- nextStepTemplate: [
367
- 'Inspect the table and GraphQL enablement state.',
368
- 'Use set_table_graphql for enablement changes.',
369
- 'Remember GraphQL table data requires Bearer auth even when REST is public.',
370
- ],
371
- },
372
- {
373
- key: 'package',
374
- title: 'Runtime package install or package-backed extension/script support',
375
- useWhen: [
376
- 'Installing npm packages for dynamic code or extension runtime use.',
377
- 'Checking package availability before using getPackages in extensions.',
378
- ],
379
- keywords: ['package', 'npm', 'install', 'dependency', 'getpackages'],
380
- firstTools: ['search_npm', 'get_enfyra_required_knowledge'],
381
- inspectTools: ['search_npm', 'query_table'],
382
- knowledgeTools: ['get_enfyra_required_knowledge'],
383
- writeTools: ['install_package'],
384
- verifyTools: ['query_table'],
385
- avoidTools: [
386
- {
387
- tool: 'raw create_record on enfyra_package',
388
- when: 'installing packages',
389
- useInstead: 'install_package',
390
- reason: 'The package tool resolves package metadata and avoids duplicate package records.',
391
- },
392
- ],
393
- requiredAck: ['globalRulesAckKey'],
394
- exampleCategories: ['extensions', 'handlers-hooks'],
395
- nextStepTemplate: [
396
- 'Search package metadata first.',
397
- 'Install with install_package and globalRulesAckKey.',
398
- 'Use getPackages inside extension runtime code rather than static imports.',
399
- ],
400
- },
401
- {
402
- key: 'cache',
403
- title: 'Cache reload or stale metadata/runtime diagnosis',
404
- useWhen: [
405
- 'Diagnosing stale routes, metadata, GraphQL, or full runtime cache.',
406
- 'Manually reloading only after natural partial reload appears stale.',
407
- ],
408
- keywords: ['cache', 'reload', 'stale', 'refresh metadata', 'reload routes'],
409
- firstTools: ['inspect_table', 'inspect_route', 'get_enfyra_api_context'],
410
- inspectTools: ['inspect_table', 'inspect_route', 'get_enfyra_api_context'],
411
- knowledgeTools: ['get_enfyra_required_knowledge for manual reloads'],
412
- writeTools: ['reload_metadata', 'reload_routes', 'reload_graphql', 'reload_all'],
413
- verifyTools: ['inspect_table', 'inspect_route', 'discover_enfyra_system'],
414
- avoidTools: [
415
- {
416
- tool: 'manual reload tools',
417
- when: 'a successful metadata mutation already triggered natural partial reload and no stale evidence exists',
418
- useInstead: 'verify behavior first with inspect/test tools',
419
- reason: 'Manual reloads should be evidence-driven, not reflexive.',
420
- },
421
- ],
422
- requiredAck: ['globalRulesAckKey for manual reload tools'],
423
- exampleCategories: [],
424
- nextStepTemplate: [
425
- 'Verify stale behavior with narrow inspect/test tools.',
426
- 'Choose the narrowest reload surface if stale evidence exists.',
427
- 'Re-verify the same narrow behavior after reload.',
428
- ],
429
- },
430
- {
431
- key: 'logs-debug',
432
- title: 'Logs, runtime diagnostics, and test runner debugging',
433
- useWhen: [
434
- 'Reading app/error logs.',
435
- 'Searching logs for route, flow, websocket, or dynamic script failures.',
436
- 'Running admin tests for supported runtime surfaces.',
437
- ],
438
- keywords: ['log', 'debug', 'error', 'trace', 'tail', 'diagnostic', 'test runner'],
439
- firstTools: ['get_log_files', 'search_logs'],
440
- inspectTools: ['get_log_files', 'get_log_content', 'tail_log', 'search_logs'],
441
- knowledgeTools: [],
442
- writeTools: [],
443
- verifyTools: ['run_admin_test', 'test_flow_step', 'test_rest_endpoint'],
444
- avoidTools: [
445
- {
446
- tool: 'broad metadata discovery',
447
- when: 'the problem is a concrete runtime error with known log text',
448
- useInstead: 'search_logs or tail_log',
449
- reason: 'Log tools are narrower and cheaper for runtime debugging.',
450
- },
451
- ],
452
- requiredAck: [],
453
- exampleCategories: [],
454
- nextStepTemplate: [
455
- 'Search or tail the narrowest log first.',
456
- 'Use the matching test tool to reproduce once the failing surface is known.',
457
- 'Patch only after the failing step is identified.',
458
- ],
459
- },
460
- {
461
- key: 'auth-context',
462
- title: 'MCP target, auth token, current user, and permission profile',
463
- useWhen: [
464
- 'Confirming which Enfyra instance MCP is connected to.',
465
- 'Debugging API-token exchange or current MCP user permissions.',
466
- 'Checking whether non-root tokens can call admin helper routes.',
467
- ],
468
- keywords: ['auth', 'token', 'current user', 'permission profile', 'target', 'api base', '403', 'exchange'],
469
- firstTools: ['get_enfyra_api_context', 'get_current_user', 'get_permission_profile'],
470
- inspectTools: ['get_enfyra_api_context', 'get_current_user', 'get_permission_profile'],
471
- knowledgeTools: [],
472
- writeTools: ['login'],
473
- verifyTools: ['get_current_user', 'get_permission_profile'],
474
- avoidTools: [
475
- {
476
- tool: 'discover_enfyra_system',
477
- when: 'only confirming the connected API base',
478
- useInstead: 'get_enfyra_api_context',
479
- reason: 'Target sanity checks should be cheap and should not load broad metadata.',
480
- },
481
- ],
482
- requiredAck: [],
483
- exampleCategories: ['ssr-app-auth', 'oauth-setup'],
484
- nextStepTemplate: [
485
- 'Use get_enfyra_api_context for target sanity checks.',
486
- 'Use get_permission_profile before assuming admin helper route access with non-root tokens.',
487
- 'Use login only when an interactive credential login is explicitly needed.',
488
- ],
489
- },
490
- ];
491
-
492
- function compactWorkflow(workflow) {
493
- return {
494
- key: workflow.key,
495
- title: workflow.title,
496
- useWhen: workflow.useWhen,
497
- };
498
- }
499
-
500
- function planWorkflow(workflow) {
501
- return {
502
- ...compactWorkflow(workflow),
503
- firstTools: workflow.firstTools,
504
- inspectTools: workflow.inspectTools,
505
- knowledgeTools: workflow.knowledgeTools,
506
- writeTools: workflow.writeTools,
507
- verifyTools: workflow.verifyTools,
508
- requiredAck: workflow.requiredAck,
509
- exampleCategories: workflow.exampleCategories,
510
- nextSteps: workflow.nextStepTemplate,
511
- avoidTools: workflow.avoidTools,
512
- };
513
- }
514
-
515
- function fullWorkflow(workflow) {
516
- return {
517
- ...planWorkflow(workflow),
518
- keywords: workflow.keywords,
519
- };
520
- }
521
-
522
- function normalize(value) {
523
- return String(value || '').trim().toLowerCase();
524
- }
525
-
526
- function scoreWorkflow(workflow, { intent, surface, risk }) {
527
- let score = 0;
528
- const text = normalize(intent);
529
- if (surface && workflow.key === surface) score += 20;
530
- if (surface && workflow.key.includes(surface)) score += 8;
531
- if (text) {
532
- for (const keyword of workflow.keywords || []) {
533
- if (text.includes(keyword)) score += 4;
534
- }
535
- for (const phrase of workflow.useWhen || []) {
536
- const words = normalize(phrase).split(/\W+/).filter((word) => word.length > 3);
537
- if (words.some((word) => text.includes(word))) score += 1;
538
- }
539
- }
540
- if (risk === 'debug' && workflow.key === 'logs-debug') score += 6;
541
- if (risk === 'write' && workflow.writeTools.length) score += 2;
542
- if (risk === 'destructive' && workflow.avoidTools.some((item) => normalize(item.when).includes('delete'))) score += 2;
543
- return score;
544
- }
545
-
546
- export function listWorkflowSurfaces() {
547
- return TOOL_WORKFLOWS.map(compactWorkflow);
548
- }
549
-
550
- export function discoverWorkflowRoutes({
551
- intent = '',
552
- surface,
553
- risk = 'unknown',
554
- detail = 'summary',
555
- limit = 5,
556
- } = {}) {
557
- const normalizedSurface = surface ? normalize(surface) : undefined;
558
- const normalizedDetail = ALL_DETAILS.includes(detail) ? detail : 'summary';
559
- const normalizedRisk = normalize(risk) || 'unknown';
560
- const formatter = normalizedDetail === 'full'
561
- ? fullWorkflow
562
- : normalizedDetail === 'plan'
563
- ? planWorkflow
564
- : compactWorkflow;
565
- const scored = TOOL_WORKFLOWS
566
- .map((workflow) => ({ workflow, score: scoreWorkflow(workflow, { intent, surface: normalizedSurface, risk: normalizedRisk }) }))
567
- .filter((item) => !normalizedSurface || item.workflow.key === normalizedSurface || item.score > 0)
568
- .sort((a, b) => b.score - a.score || a.workflow.key.localeCompare(b.workflow.key));
569
- const selected = (scored.length ? scored : TOOL_WORKFLOWS.map((workflow) => ({ workflow, score: 0 })))
570
- .slice(0, Math.max(1, Math.min(Number(limit) || 5, 10)));
571
- return {
572
- action: 'enfyra_workflows_discovered',
573
- intent: intent || null,
574
- requestedSurface: surface || null,
575
- risk: normalizedRisk,
576
- detail: normalizedDetail,
577
- matchedWorkflowCount: scored.length,
578
- workflows: selected.map((item) => ({
579
- score: item.score,
580
- ...formatter(item.workflow),
581
- })),
582
- surfaces: normalizedDetail === 'summary' ? WORKFLOW_SURFACES : undefined,
583
- guidance: [
584
- 'Use this as progressive disclosure: pick the closest workflow, then call its firstTools instead of loading every Enfyra tool/example.',
585
- 'For writes, call get_enfyra_required_knowledge and pass the returned acknowledgement keys into write tools.',
586
- 'Treat avoidTools as negative routing boundaries; they prevent near-correct tool choices from crossing the wrong platform contract.',
587
- ],
588
- };
589
- }