@lexq/cli 0.1.29 → 0.1.31
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/dist/index.js +92 -50
- package/dist/mcp/register.js +50 -45
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -300,13 +300,14 @@ function registerGroupCommands(program) {
|
|
|
300
300
|
get Get group detail by ID
|
|
301
301
|
create Create a new group
|
|
302
302
|
update Update group settings
|
|
303
|
+
reorder Change group priorities (drag & drag equivalent)
|
|
303
304
|
delete Archive a group
|
|
304
305
|
ab-test Manage A/B tests (start, stop, adjust)
|
|
305
306
|
|
|
306
307
|
Statuses: ACTIVE, DISABLED (emergency stop), ARCHIVED (soft delete)
|
|
307
308
|
`
|
|
308
309
|
);
|
|
309
|
-
groups.command("list").description("List all policy groups").
|
|
310
|
+
groups.command("list").description("List all policy groups").action(async () => {
|
|
310
311
|
try {
|
|
311
312
|
const globalOpts = program.opts();
|
|
312
313
|
const format = globalOpts.format ?? "json";
|
|
@@ -314,13 +315,12 @@ function registerGroupCommands(program) {
|
|
|
314
315
|
apiKey: globalOpts.apiKey,
|
|
315
316
|
baseUrl: globalOpts.baseUrl,
|
|
316
317
|
dryRun: globalOpts.dryRun,
|
|
317
|
-
verbose: globalOpts.verbose
|
|
318
|
-
params: { page: opts.page, size: opts.size }
|
|
318
|
+
verbose: globalOpts.verbose
|
|
319
319
|
});
|
|
320
320
|
if (format === "table") {
|
|
321
321
|
printTable(
|
|
322
322
|
["ID", "Name", "Status", "Priority", "Version", "Updated"],
|
|
323
|
-
data.
|
|
323
|
+
data.map((g) => [
|
|
324
324
|
g.id,
|
|
325
325
|
g.name,
|
|
326
326
|
g.status,
|
|
@@ -331,7 +331,7 @@ function registerGroupCommands(program) {
|
|
|
331
331
|
{ truncate: 24 }
|
|
332
332
|
);
|
|
333
333
|
console.log(`
|
|
334
|
-
${data.
|
|
334
|
+
${data.length} total`);
|
|
335
335
|
} else {
|
|
336
336
|
printJson(data);
|
|
337
337
|
}
|
|
@@ -362,19 +362,20 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
362
362
|
Example:
|
|
363
363
|
$ lexq groups create --json '{
|
|
364
364
|
"name": "Payment Policy",
|
|
365
|
-
"description": "Rules for payment processing"
|
|
366
|
-
"priority": 0
|
|
365
|
+
"description": "Rules for payment processing"
|
|
367
366
|
}'
|
|
368
367
|
|
|
369
368
|
Fields:
|
|
370
369
|
name string Group name (required, unique per tenant)
|
|
371
370
|
description string Description (optional, max 255 chars)
|
|
372
|
-
|
|
373
|
-
activationGroup string Cross-group conflict resolution key (optional)
|
|
371
|
+
activationGroup string Execution Group — conflict-resolution cluster key (optional)
|
|
374
372
|
activationMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
|
|
375
373
|
activationStrategy string FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT [default: FIRST_MATCH]
|
|
376
374
|
executionLimit number Max rules to fire in MAX_N mode (optional)
|
|
377
375
|
status string ACTIVE | DISABLED [default: ACTIVE]
|
|
376
|
+
|
|
377
|
+
priority is auto-assigned (appended last, tenant-wide). Use 'lexq groups reorder' to change order.
|
|
378
|
+
Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.
|
|
378
379
|
`
|
|
379
380
|
).action(async (opts) => {
|
|
380
381
|
try {
|
|
@@ -398,9 +399,9 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
398
399
|
dedent3`
|
|
399
400
|
|
|
400
401
|
Example:
|
|
401
|
-
$ lexq groups update --id <groupId> --json '{"description": "Updated", "
|
|
402
|
+
$ lexq groups update --id <groupId> --json '{"description": "Updated", "status": "DISABLED"}'
|
|
402
403
|
|
|
403
|
-
All fields are optional — only provided fields are updated.
|
|
404
|
+
All fields are optional — only provided fields are updated. priority is not settable here — use 'lexq groups reorder'.
|
|
404
405
|
`
|
|
405
406
|
).action(async (opts) => {
|
|
406
407
|
try {
|
|
@@ -450,6 +451,41 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
450
451
|
process.exit(1);
|
|
451
452
|
}
|
|
452
453
|
});
|
|
454
|
+
groups.command("reorder").description("Reorder policy groups by priority (drag & drop equivalent)").requiredOption("--group-ids <ids>", "Comma-separated group IDs in desired order").addHelpText(
|
|
455
|
+
"after",
|
|
456
|
+
dedent3`
|
|
457
|
+
|
|
458
|
+
Assigns priority 1, 2, 3, ... to groups in the order given (tenant-wide, 1...N continuous).
|
|
459
|
+
activationGroup (Execution Group) is NOT affected — reorder only changes priority.
|
|
460
|
+
|
|
461
|
+
Example:
|
|
462
|
+
$ lexq groups reorder --group-ids "id3,id1,id2"
|
|
463
|
+
|
|
464
|
+
Result: id3 → priority 1, id1 → priority 2, id2 → priority 3
|
|
465
|
+
`
|
|
466
|
+
).action(async (opts) => {
|
|
467
|
+
try {
|
|
468
|
+
const globalOpts = program.opts();
|
|
469
|
+
const groupIds = opts.groupIds.split(",").map((id) => id.trim());
|
|
470
|
+
const body = {
|
|
471
|
+
groups: groupIds.map((groupId, index) => ({
|
|
472
|
+
groupId,
|
|
473
|
+
priority: index + 1
|
|
474
|
+
}))
|
|
475
|
+
};
|
|
476
|
+
await apiRequest("PATCH", "policy-groups/reorder", {
|
|
477
|
+
apiKey: globalOpts.apiKey,
|
|
478
|
+
baseUrl: globalOpts.baseUrl,
|
|
479
|
+
dryRun: globalOpts.dryRun,
|
|
480
|
+
verbose: globalOpts.verbose,
|
|
481
|
+
body
|
|
482
|
+
});
|
|
483
|
+
console.log(`\u2713 ${groupIds.length} groups reordered.`);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
printError(error);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
453
489
|
const abTest = groups.command("ab-test").description("A/B test management").addHelpText(
|
|
454
490
|
"after",
|
|
455
491
|
dedent3`
|
|
@@ -803,7 +839,7 @@ function registerRuleCommands(program) {
|
|
|
803
839
|
Only DRAFT versions allow rule modifications.
|
|
804
840
|
`
|
|
805
841
|
);
|
|
806
|
-
rules.command("list").description("List rules for a policy version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Policy version ID").
|
|
842
|
+
rules.command("list").description("List rules for a policy version").requiredOption("--group-id <groupId>", "Policy group ID").requiredOption("--version-id <versionId>", "Policy version ID").action(async (opts) => {
|
|
807
843
|
try {
|
|
808
844
|
const globalOpts = program.opts();
|
|
809
845
|
const format = globalOpts.format ?? "json";
|
|
@@ -814,14 +850,13 @@ function registerRuleCommands(program) {
|
|
|
814
850
|
apiKey: globalOpts.apiKey,
|
|
815
851
|
baseUrl: globalOpts.baseUrl,
|
|
816
852
|
dryRun: globalOpts.dryRun,
|
|
817
|
-
verbose: globalOpts.verbose
|
|
818
|
-
params: { page: opts.page, size: opts.size }
|
|
853
|
+
verbose: globalOpts.verbose
|
|
819
854
|
}
|
|
820
855
|
);
|
|
821
856
|
if (format === "table") {
|
|
822
857
|
printTable(
|
|
823
858
|
["ID", "Name", "Priority", "Conditions", "Actions", "Enabled"],
|
|
824
|
-
data.
|
|
859
|
+
data.map((r) => [
|
|
825
860
|
r.id,
|
|
826
861
|
r.name,
|
|
827
862
|
String(r.priority),
|
|
@@ -832,7 +867,7 @@ function registerRuleCommands(program) {
|
|
|
832
867
|
{ truncate: 24 }
|
|
833
868
|
);
|
|
834
869
|
console.log(`
|
|
835
|
-
${data.
|
|
870
|
+
${data.length} total`);
|
|
836
871
|
} else {
|
|
837
872
|
printJson(data);
|
|
838
873
|
}
|
|
@@ -867,7 +902,6 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
867
902
|
Example:
|
|
868
903
|
$ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
869
904
|
"name": "VIP 20% Discount",
|
|
870
|
-
"priority": 0,
|
|
871
905
|
"condition": {
|
|
872
906
|
"type": "SINGLE",
|
|
873
907
|
"field": "customer_tier",
|
|
@@ -891,8 +925,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
891
925
|
|
|
892
926
|
Mutex (optional — rule-level conflict resolution):
|
|
893
927
|
mutexGroup string Logical grouping key (e.g., "best-discount")
|
|
894
|
-
mutexMode string NONE | EXCLUSIVE [default: NONE]
|
|
928
|
+
mutexMode string NONE | EXCLUSIVE | MAX_N [default: NONE]
|
|
895
929
|
mutexStrategy string FIRST_MATCH | HIGHEST_PRIORITY | MAX_BENEFIT
|
|
930
|
+
mutexLimit number Max rules to fire in MAX_N mode (required when MAX_N)
|
|
931
|
+
|
|
932
|
+
priority is auto-assigned (appended last). Use 'lexq rules reorder' to change order.
|
|
896
933
|
`
|
|
897
934
|
).action(async (opts) => {
|
|
898
935
|
try {
|
|
@@ -983,12 +1020,12 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
983
1020
|
"after",
|
|
984
1021
|
dedent5`
|
|
985
1022
|
|
|
986
|
-
Assigns priority
|
|
1023
|
+
Assigns priority 1, 2, 3, ... to rules in the order given (1-based, 1...N continuous).
|
|
987
1024
|
|
|
988
1025
|
Example:
|
|
989
1026
|
$ lexq rules reorder --group-id <gid> --version-id <vid> --rule-ids "id3,id1,id2"
|
|
990
1027
|
|
|
991
|
-
Result: id3 → priority
|
|
1028
|
+
Result: id3 → priority 1, id1 → priority 2, id2 → priority 3
|
|
992
1029
|
`
|
|
993
1030
|
).action(async (opts) => {
|
|
994
1031
|
try {
|
|
@@ -997,7 +1034,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
997
1034
|
const body = {
|
|
998
1035
|
rules: ruleIds.map((ruleId, index) => ({
|
|
999
1036
|
ruleId,
|
|
1000
|
-
priority: index
|
|
1037
|
+
priority: index + 1
|
|
1001
1038
|
}))
|
|
1002
1039
|
};
|
|
1003
1040
|
await apiRequest(
|
|
@@ -1067,7 +1104,7 @@ function registerFactCommands(program) {
|
|
|
1067
1104
|
delete Remove a fact definition
|
|
1068
1105
|
action-metadata Show action runtime fact metadata
|
|
1069
1106
|
|
|
1070
|
-
System facts (
|
|
1107
|
+
System facts (user_id, user_tags) are auto-created.
|
|
1071
1108
|
`
|
|
1072
1109
|
);
|
|
1073
1110
|
facts.command("list").description("List fact definitions").option("--keyword <keyword>", "Filter by keyword").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
|
|
@@ -2868,16 +2905,10 @@ function registerGroupTools(server, callApi) {
|
|
|
2868
2905
|
"lexq_groups_list",
|
|
2869
2906
|
{
|
|
2870
2907
|
title: "List Policy Groups",
|
|
2871
|
-
description: "List all policy groups
|
|
2872
|
-
inputSchema: {
|
|
2873
|
-
page: z.number().int().min(0).default(0).describe("Page number (0-indexed)"),
|
|
2874
|
-
size: z.number().int().min(1).max(100).default(20).describe("Page size")
|
|
2875
|
-
}
|
|
2908
|
+
description: "List all policy groups (tenant-wide, priority ASC).",
|
|
2909
|
+
inputSchema: {}
|
|
2876
2910
|
},
|
|
2877
|
-
async (
|
|
2878
|
-
const params = paginationParams(page, size);
|
|
2879
|
-
return callApi("GET", "policy-groups", { params });
|
|
2880
|
-
}
|
|
2911
|
+
async () => callApi("GET", "policy-groups")
|
|
2881
2912
|
);
|
|
2882
2913
|
server.registerTool(
|
|
2883
2914
|
"lexq_groups_get",
|
|
@@ -2894,10 +2925,9 @@ function registerGroupTools(server, callApi) {
|
|
|
2894
2925
|
"lexq_groups_create",
|
|
2895
2926
|
{
|
|
2896
2927
|
title: "Create Policy Group",
|
|
2897
|
-
description: "Create a new policy group. Requires name
|
|
2928
|
+
description: "Create a new policy group. Requires name. Priority is auto-assigned (appended last, tenant-wide); use lexq_groups_reorder to change order. Optionally set conflict resolution, activation group, and description. Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.",
|
|
2898
2929
|
inputSchema: {
|
|
2899
2930
|
name: z.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
2900
|
-
priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
|
|
2901
2931
|
description: z.string().optional().describe("Group description"),
|
|
2902
2932
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
2903
2933
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
|
|
@@ -2907,8 +2937,7 @@ function registerGroupTools(server, callApi) {
|
|
|
2907
2937
|
},
|
|
2908
2938
|
async (args) => {
|
|
2909
2939
|
const body = {
|
|
2910
|
-
name: args.name
|
|
2911
|
-
priority: args.priority
|
|
2940
|
+
name: args.name
|
|
2912
2941
|
};
|
|
2913
2942
|
if (args.description !== void 0) body.description = args.description;
|
|
2914
2943
|
if (args.activationMode !== void 0) body.activationMode = args.activationMode;
|
|
@@ -2926,9 +2955,9 @@ function registerGroupTools(server, callApi) {
|
|
|
2926
2955
|
inputSchema: {
|
|
2927
2956
|
groupId: z.string().uuid().describe("Policy group ID"),
|
|
2928
2957
|
name: z.string().optional().describe("New name"),
|
|
2929
|
-
priority: z.number().int().min(0).optional().describe("New priority"),
|
|
2930
2958
|
description: z.string().optional().describe("New description"),
|
|
2931
2959
|
status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
|
|
2960
|
+
activationGroup: z.string().optional().describe("Activation group (Execution Group) cluster key"),
|
|
2932
2961
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
2933
2962
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
|
|
2934
2963
|
executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
|
|
@@ -2947,6 +2976,23 @@ function registerGroupTools(server, callApi) {
|
|
|
2947
2976
|
},
|
|
2948
2977
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
|
|
2949
2978
|
);
|
|
2979
|
+
server.registerTool(
|
|
2980
|
+
"lexq_groups_reorder",
|
|
2981
|
+
{
|
|
2982
|
+
title: "Reorder Policy Groups",
|
|
2983
|
+
description: "Reorder policy groups by priority. Priority is tenant-wide and flat (1...N continuous); array index 0 = priority 1 (highest precedence). activationGroup is not affected \u2014 this only changes priority.",
|
|
2984
|
+
inputSchema: {
|
|
2985
|
+
groupIds: z.array(z.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
|
|
2986
|
+
}
|
|
2987
|
+
},
|
|
2988
|
+
async ({ groupIds }) => {
|
|
2989
|
+
const groups = groupIds.map((groupId, index) => ({
|
|
2990
|
+
groupId,
|
|
2991
|
+
priority: index + 1
|
|
2992
|
+
}));
|
|
2993
|
+
return callApi("PATCH", "policy-groups/reorder", { body: { groups } });
|
|
2994
|
+
}
|
|
2995
|
+
);
|
|
2950
2996
|
server.registerTool(
|
|
2951
2997
|
"lexq_ab_test_start",
|
|
2952
2998
|
{
|
|
@@ -3082,17 +3128,13 @@ function registerRuleTools(server, callApi) {
|
|
|
3082
3128
|
"lexq_rules_list",
|
|
3083
3129
|
{
|
|
3084
3130
|
title: "List Rules",
|
|
3085
|
-
description: "List all rules in a version. Returns summary with conditionSummary and actionSummary.",
|
|
3131
|
+
description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
|
|
3086
3132
|
inputSchema: {
|
|
3087
3133
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3088
|
-
versionId: z3.string().uuid().describe("Version ID")
|
|
3089
|
-
page: z3.number().int().min(0).default(0).describe("Page number"),
|
|
3090
|
-
size: z3.number().int().min(1).max(100).default(20).describe("Page size")
|
|
3134
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
3091
3135
|
}
|
|
3092
3136
|
},
|
|
3093
|
-
async ({ groupId, versionId
|
|
3094
|
-
params: paginationParams(page, size)
|
|
3095
|
-
})
|
|
3137
|
+
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
|
|
3096
3138
|
);
|
|
3097
3139
|
server.registerTool(
|
|
3098
3140
|
"lexq_rules_get",
|
|
@@ -3112,7 +3154,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3112
3154
|
{
|
|
3113
3155
|
title: "Create Rule",
|
|
3114
3156
|
description: dedent13`
|
|
3115
|
-
Create a rule in a DRAFT version. Requires name,
|
|
3157
|
+
Create a rule in a DRAFT version. Requires name, condition tree, and actions array. priority is auto-assigned (appended last); use lexq_rules_reorder to change order.
|
|
3116
3158
|
|
|
3117
3159
|
Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
|
|
3118
3160
|
If a required key is missing, ask the user to confirm the type, isRequired, and description
|
|
@@ -3127,7 +3169,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3127
3169
|
|
|
3128
3170
|
Action parameter schemas:
|
|
3129
3171
|
- MUTATE_FACT: { refVar: string, operator: "ASSIGN"|"ADD"|"SUB"|"MUL"|"DIV", method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Constraints: DIV + PERCENTAGE is invalid (use MUL with rate/100 inverse). DIV + AMOUNT requires value !== 0.
|
|
3130
|
-
- INCREMENT_FACT: { targetVar: string, method: "PERCENTAGE"|"AMOUNT", refVar?: string (required when PERCENTAGE), rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Note: external system call (e.g. point system sync) is NOT a primitive responsibility. Compose [INCREMENT_FACT, EMIT_EVENT] chain instead.
|
|
3172
|
+
- INCREMENT_FACT: { targetVar: string, method: "PERCENTAGE"|"AMOUNT", refVar?: string (required when PERCENTAGE), rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } targetVar (accumulation target) must exist at execution; refVar (PERCENTAGE source) must exist when method is PERCENTAGE. Each is supplied as an input fact or written by a prior action in this rule — a missing required fact throws (no 0 default). Note: external system call (e.g. point system sync) is NOT a primitive responsibility. Compose [INCREMENT_FACT, EMIT_EVENT] chain instead.
|
|
3131
3173
|
- EMIT_EVENT: { integrationId: uuid, eventPayload: object (Map<string,unknown>, ≥1 entry) } eventPayload is passed through to the integration provider as-is. Domain-specific keys (couponId, ticketId, etc.) are routed by the provider, not validated by the engine.
|
|
3132
3174
|
- BLOCK: { reason: string }
|
|
3133
3175
|
- EMIT_NOTIFICATION: { integrationId: uuid, targetVar: string, notificationPayload: object (Map<string,unknown>, ≥1 entry) } targetVar identifies the recipient fact (e.g. phone_number / email / device_token). notificationPayload (channel, templateId, body, variables, etc.) is passed through to the provider.
|
|
@@ -3145,7 +3187,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3145
3187
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3146
3188
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
3147
3189
|
rule: z3.string().describe(
|
|
3148
|
-
"JSON string of CreateRuleRequest: { name,
|
|
3190
|
+
"JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
3149
3191
|
)
|
|
3150
3192
|
}
|
|
3151
3193
|
},
|
|
@@ -3164,7 +3206,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3164
3206
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
3165
3207
|
ruleId: z3.string().uuid().describe("Rule ID"),
|
|
3166
3208
|
rule: z3.string().describe(
|
|
3167
|
-
"JSON string of UpdateRuleRequest: { name?,
|
|
3209
|
+
"JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
3168
3210
|
)
|
|
3169
3211
|
}
|
|
3170
3212
|
},
|
|
@@ -3199,7 +3241,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3199
3241
|
"lexq_rules_reorder",
|
|
3200
3242
|
{
|
|
3201
3243
|
title: "Reorder Rules",
|
|
3202
|
-
description: "Reorder rules by specifying rule IDs
|
|
3244
|
+
description: "Reorder rules by specifying rule IDs in desired order. Priorities are assigned 1...N (1-based, continuous); array index 0 = priority 1 (highest precedence).",
|
|
3203
3245
|
inputSchema: {
|
|
3204
3246
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3205
3247
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
@@ -3209,7 +3251,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3209
3251
|
async ({ groupId, versionId, ruleIds }) => {
|
|
3210
3252
|
const rules = ruleIds.map((ruleId, index) => ({
|
|
3211
3253
|
ruleId,
|
|
3212
|
-
priority: index
|
|
3254
|
+
priority: index + 1
|
|
3213
3255
|
}));
|
|
3214
3256
|
return callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/reorder`, {
|
|
3215
3257
|
body: { rules }
|
|
@@ -3298,7 +3340,7 @@ function registerFactTools(server, callApi) {
|
|
|
3298
3340
|
"lexq_facts_action_metadata",
|
|
3299
3341
|
{
|
|
3300
3342
|
title: "Get Action Runtime Fact Metadata",
|
|
3301
|
-
description: "Retrieve runtime
|
|
3343
|
+
description: "Retrieve runtime fact requirements per Action type. For each Action, shows which input facts must be present in the execution payload (e.g. MUTATE_FACT requires its refVar fact; INCREMENT_FACT always requires targetVar, plus refVar when method is PERCENTAGE). A required fact absent at runtime throws \u2014 the engine never defaults to 0. Facts are supplied as input or written by a prior action in the same rule; Actions never create a fact from nothing. Static data, safe to cache in-session.",
|
|
3302
3344
|
inputSchema: {}
|
|
3303
3345
|
},
|
|
3304
3346
|
async () => callApi("GET", "schema/action-metadata")
|
package/dist/mcp/register.js
CHANGED
|
@@ -13,38 +13,15 @@ function registerStatusTools(server, callApi) {
|
|
|
13
13
|
|
|
14
14
|
// src/mcp/tools/groups.ts
|
|
15
15
|
import { z } from "zod";
|
|
16
|
-
|
|
17
|
-
// src/lib/config.ts
|
|
18
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
19
|
-
import { homedir } from "os";
|
|
20
|
-
import { join } from "path";
|
|
21
|
-
var CONFIG_DIR = join(homedir(), ".lexq");
|
|
22
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
23
|
-
|
|
24
|
-
// src/mcp/tools/_shared.ts
|
|
25
|
-
function paginationParams(page, size) {
|
|
26
|
-
const params = {};
|
|
27
|
-
if (page !== void 0) params.page = String(page);
|
|
28
|
-
if (size !== void 0) params.size = String(size);
|
|
29
|
-
return params;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// src/mcp/tools/groups.ts
|
|
33
16
|
function registerGroupTools(server, callApi) {
|
|
34
17
|
server.registerTool(
|
|
35
18
|
"lexq_groups_list",
|
|
36
19
|
{
|
|
37
20
|
title: "List Policy Groups",
|
|
38
|
-
description: "List all policy groups
|
|
39
|
-
inputSchema: {
|
|
40
|
-
page: z.number().int().min(0).default(0).describe("Page number (0-indexed)"),
|
|
41
|
-
size: z.number().int().min(1).max(100).default(20).describe("Page size")
|
|
42
|
-
}
|
|
21
|
+
description: "List all policy groups (tenant-wide, priority ASC).",
|
|
22
|
+
inputSchema: {}
|
|
43
23
|
},
|
|
44
|
-
async (
|
|
45
|
-
const params = paginationParams(page, size);
|
|
46
|
-
return callApi("GET", "policy-groups", { params });
|
|
47
|
-
}
|
|
24
|
+
async () => callApi("GET", "policy-groups")
|
|
48
25
|
);
|
|
49
26
|
server.registerTool(
|
|
50
27
|
"lexq_groups_get",
|
|
@@ -61,10 +38,9 @@ function registerGroupTools(server, callApi) {
|
|
|
61
38
|
"lexq_groups_create",
|
|
62
39
|
{
|
|
63
40
|
title: "Create Policy Group",
|
|
64
|
-
description: "Create a new policy group. Requires name
|
|
41
|
+
description: "Create a new policy group. Requires name. Priority is auto-assigned (appended last, tenant-wide); use lexq_groups_reorder to change order. Optionally set conflict resolution, activation group, and description. Groups sharing an activationGroup must share the same activationMode / activationStrategy / executionLimit.",
|
|
65
42
|
inputSchema: {
|
|
66
43
|
name: z.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
67
|
-
priority: z.number().int().min(0).describe("Execution priority (lower = higher)"),
|
|
68
44
|
description: z.string().optional().describe("Group description"),
|
|
69
45
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
70
46
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy when mode is EXCLUSIVE or MAX_N"),
|
|
@@ -74,8 +50,7 @@ function registerGroupTools(server, callApi) {
|
|
|
74
50
|
},
|
|
75
51
|
async (args) => {
|
|
76
52
|
const body = {
|
|
77
|
-
name: args.name
|
|
78
|
-
priority: args.priority
|
|
53
|
+
name: args.name
|
|
79
54
|
};
|
|
80
55
|
if (args.description !== void 0) body.description = args.description;
|
|
81
56
|
if (args.activationMode !== void 0) body.activationMode = args.activationMode;
|
|
@@ -93,9 +68,9 @@ function registerGroupTools(server, callApi) {
|
|
|
93
68
|
inputSchema: {
|
|
94
69
|
groupId: z.string().uuid().describe("Policy group ID"),
|
|
95
70
|
name: z.string().optional().describe("New name"),
|
|
96
|
-
priority: z.number().int().min(0).optional().describe("New priority"),
|
|
97
71
|
description: z.string().optional().describe("New description"),
|
|
98
72
|
status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
|
|
73
|
+
activationGroup: z.string().optional().describe("Activation group (Execution Group) cluster key"),
|
|
99
74
|
activationMode: z.enum(["NONE", "EXCLUSIVE", "MAX_N"]).optional().describe("Conflict resolution mode"),
|
|
100
75
|
activationStrategy: z.enum(["FIRST_MATCH", "HIGHEST_PRIORITY", "MAX_BENEFIT"]).optional().describe("Strategy"),
|
|
101
76
|
executionLimit: z.number().int().min(1).optional().describe("Max rule executions")
|
|
@@ -114,6 +89,23 @@ function registerGroupTools(server, callApi) {
|
|
|
114
89
|
},
|
|
115
90
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
|
|
116
91
|
);
|
|
92
|
+
server.registerTool(
|
|
93
|
+
"lexq_groups_reorder",
|
|
94
|
+
{
|
|
95
|
+
title: "Reorder Policy Groups",
|
|
96
|
+
description: "Reorder policy groups by priority. Priority is tenant-wide and flat (1...N continuous); array index 0 = priority 1 (highest precedence). activationGroup is not affected \u2014 this only changes priority.",
|
|
97
|
+
inputSchema: {
|
|
98
|
+
groupIds: z.array(z.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
async ({ groupIds }) => {
|
|
102
|
+
const groups = groupIds.map((groupId, index) => ({
|
|
103
|
+
groupId,
|
|
104
|
+
priority: index + 1
|
|
105
|
+
}));
|
|
106
|
+
return callApi("PATCH", "policy-groups/reorder", { body: { groups } });
|
|
107
|
+
}
|
|
108
|
+
);
|
|
117
109
|
server.registerTool(
|
|
118
110
|
"lexq_ab_test_start",
|
|
119
111
|
{
|
|
@@ -154,6 +146,23 @@ function registerGroupTools(server, callApi) {
|
|
|
154
146
|
|
|
155
147
|
// src/mcp/tools/versions.ts
|
|
156
148
|
import { z as z2 } from "zod";
|
|
149
|
+
|
|
150
|
+
// src/lib/config.ts
|
|
151
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
152
|
+
import { homedir } from "os";
|
|
153
|
+
import { join } from "path";
|
|
154
|
+
var CONFIG_DIR = join(homedir(), ".lexq");
|
|
155
|
+
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
156
|
+
|
|
157
|
+
// src/mcp/tools/_shared.ts
|
|
158
|
+
function paginationParams(page, size) {
|
|
159
|
+
const params = {};
|
|
160
|
+
if (page !== void 0) params.page = String(page);
|
|
161
|
+
if (size !== void 0) params.size = String(size);
|
|
162
|
+
return params;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/mcp/tools/versions.ts
|
|
157
166
|
function registerVersionTools(server, callApi) {
|
|
158
167
|
server.registerTool(
|
|
159
168
|
"lexq_versions_list",
|
|
@@ -249,17 +258,13 @@ function registerRuleTools(server, callApi) {
|
|
|
249
258
|
"lexq_rules_list",
|
|
250
259
|
{
|
|
251
260
|
title: "List Rules",
|
|
252
|
-
description: "List all rules in a version. Returns summary with conditionSummary and actionSummary.",
|
|
261
|
+
description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
|
|
253
262
|
inputSchema: {
|
|
254
263
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
255
|
-
versionId: z3.string().uuid().describe("Version ID")
|
|
256
|
-
page: z3.number().int().min(0).default(0).describe("Page number"),
|
|
257
|
-
size: z3.number().int().min(1).max(100).default(20).describe("Page size")
|
|
264
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
258
265
|
}
|
|
259
266
|
},
|
|
260
|
-
async ({ groupId, versionId
|
|
261
|
-
params: paginationParams(page, size)
|
|
262
|
-
})
|
|
267
|
+
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
|
|
263
268
|
);
|
|
264
269
|
server.registerTool(
|
|
265
270
|
"lexq_rules_get",
|
|
@@ -279,7 +284,7 @@ function registerRuleTools(server, callApi) {
|
|
|
279
284
|
{
|
|
280
285
|
title: "Create Rule",
|
|
281
286
|
description: dedent`
|
|
282
|
-
Create a rule in a DRAFT version. Requires name,
|
|
287
|
+
Create a rule in a DRAFT version. Requires name, condition tree, and actions array. priority is auto-assigned (appended last); use lexq_rules_reorder to change order.
|
|
283
288
|
|
|
284
289
|
Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
|
|
285
290
|
If a required key is missing, ask the user to confirm the type, isRequired, and description
|
|
@@ -294,7 +299,7 @@ function registerRuleTools(server, callApi) {
|
|
|
294
299
|
|
|
295
300
|
Action parameter schemas:
|
|
296
301
|
- MUTATE_FACT: { refVar: string, operator: "ASSIGN"|"ADD"|"SUB"|"MUL"|"DIV", method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Constraints: DIV + PERCENTAGE is invalid (use MUL with rate/100 inverse). DIV + AMOUNT requires value !== 0.
|
|
297
|
-
- INCREMENT_FACT: { targetVar: string, method: "PERCENTAGE"|"AMOUNT", refVar?: string (required when PERCENTAGE), rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Note: external system call (e.g. point system sync) is NOT a primitive responsibility. Compose [INCREMENT_FACT, EMIT_EVENT] chain instead.
|
|
302
|
+
- INCREMENT_FACT: { targetVar: string, method: "PERCENTAGE"|"AMOUNT", refVar?: string (required when PERCENTAGE), rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } targetVar (accumulation target) must exist at execution; refVar (PERCENTAGE source) must exist when method is PERCENTAGE. Each is supplied as an input fact or written by a prior action in this rule — a missing required fact throws (no 0 default). Note: external system call (e.g. point system sync) is NOT a primitive responsibility. Compose [INCREMENT_FACT, EMIT_EVENT] chain instead.
|
|
298
303
|
- EMIT_EVENT: { integrationId: uuid, eventPayload: object (Map<string,unknown>, ≥1 entry) } eventPayload is passed through to the integration provider as-is. Domain-specific keys (couponId, ticketId, etc.) are routed by the provider, not validated by the engine.
|
|
299
304
|
- BLOCK: { reason: string }
|
|
300
305
|
- EMIT_NOTIFICATION: { integrationId: uuid, targetVar: string, notificationPayload: object (Map<string,unknown>, ≥1 entry) } targetVar identifies the recipient fact (e.g. phone_number / email / device_token). notificationPayload (channel, templateId, body, variables, etc.) is passed through to the provider.
|
|
@@ -312,7 +317,7 @@ function registerRuleTools(server, callApi) {
|
|
|
312
317
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
313
318
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
314
319
|
rule: z3.string().describe(
|
|
315
|
-
"JSON string of CreateRuleRequest: { name,
|
|
320
|
+
"JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
316
321
|
)
|
|
317
322
|
}
|
|
318
323
|
},
|
|
@@ -331,7 +336,7 @@ function registerRuleTools(server, callApi) {
|
|
|
331
336
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
332
337
|
ruleId: z3.string().uuid().describe("Rule ID"),
|
|
333
338
|
rule: z3.string().describe(
|
|
334
|
-
"JSON string of UpdateRuleRequest: { name?,
|
|
339
|
+
"JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
335
340
|
)
|
|
336
341
|
}
|
|
337
342
|
},
|
|
@@ -366,7 +371,7 @@ function registerRuleTools(server, callApi) {
|
|
|
366
371
|
"lexq_rules_reorder",
|
|
367
372
|
{
|
|
368
373
|
title: "Reorder Rules",
|
|
369
|
-
description: "Reorder rules by specifying rule IDs
|
|
374
|
+
description: "Reorder rules by specifying rule IDs in desired order. Priorities are assigned 1...N (1-based, continuous); array index 0 = priority 1 (highest precedence).",
|
|
370
375
|
inputSchema: {
|
|
371
376
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
372
377
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
@@ -376,7 +381,7 @@ function registerRuleTools(server, callApi) {
|
|
|
376
381
|
async ({ groupId, versionId, ruleIds }) => {
|
|
377
382
|
const rules = ruleIds.map((ruleId, index) => ({
|
|
378
383
|
ruleId,
|
|
379
|
-
priority: index
|
|
384
|
+
priority: index + 1
|
|
380
385
|
}));
|
|
381
386
|
return callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/reorder`, {
|
|
382
387
|
body: { rules }
|
|
@@ -465,7 +470,7 @@ function registerFactTools(server, callApi) {
|
|
|
465
470
|
"lexq_facts_action_metadata",
|
|
466
471
|
{
|
|
467
472
|
title: "Get Action Runtime Fact Metadata",
|
|
468
|
-
description: "Retrieve runtime
|
|
473
|
+
description: "Retrieve runtime fact requirements per Action type. For each Action, shows which input facts must be present in the execution payload (e.g. MUTATE_FACT requires its refVar fact; INCREMENT_FACT always requires targetVar, plus refVar when method is PERCENTAGE). A required fact absent at runtime throws \u2014 the engine never defaults to 0. Facts are supplied as input or written by a prior action in the same rule; Actions never create a fact from nothing. Static data, safe to cache in-session.",
|
|
469
474
|
inputSchema: {}
|
|
470
475
|
},
|
|
471
476
|
async () => callApi("GET", "schema/action-metadata")
|