@lexq/cli 0.1.18 → 0.1.20
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/AGENTS.md +60 -21
- package/CONTEXT.md +110 -49
- package/README.md +134 -109
- package/dist/index.js +1248 -169
- package/dist/mcp/register.d.ts +1 -1
- package/dist/mcp/register.js +210 -68
- package/package.json +3 -1
- package/skills/lexq-shared/SKILL.md +48 -42
package/dist/mcp/register.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ type CallApi = (method: string, path: string, opts?: {
|
|
|
27
27
|
declare function paginationParams(page?: number, size?: number): Record<string, string>;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Registers all
|
|
30
|
+
* Registers all 62 MCP tools on the given server.
|
|
31
31
|
*
|
|
32
32
|
* @param server - McpServer instance
|
|
33
33
|
* @param callApi - API caller function (config-based for CLI, Bearer-based for HTTP)
|
package/dist/mcp/register.js
CHANGED
|
@@ -89,7 +89,7 @@ function registerGroupTools(server, callApi) {
|
|
|
89
89
|
"lexq_groups_update",
|
|
90
90
|
{
|
|
91
91
|
title: "Update Policy Group",
|
|
92
|
-
description: "Update a policy group.
|
|
92
|
+
description: "Update a policy group. Only provided fields are updated; omitted fields remain unchanged.",
|
|
93
93
|
inputSchema: {
|
|
94
94
|
groupId: z.string().uuid().describe("Policy group ID"),
|
|
95
95
|
name: z.string().optional().describe("New name"),
|
|
@@ -186,10 +186,10 @@ function registerVersionTools(server, callApi) {
|
|
|
186
186
|
"lexq_versions_create",
|
|
187
187
|
{
|
|
188
188
|
title: "Create Policy Version",
|
|
189
|
-
description: "Create a new DRAFT version in a policy group.
|
|
189
|
+
description: "Create a new DRAFT version in a policy group. Optionally provide a commit message and effective date range.",
|
|
190
190
|
inputSchema: {
|
|
191
191
|
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
192
|
-
commitMessage: z2.string().describe("Commit message describing this version"),
|
|
192
|
+
commitMessage: z2.string().optional().describe("Commit message describing this version"),
|
|
193
193
|
effectiveFrom: z2.string().optional().describe("Effective start date (ISO 8601)"),
|
|
194
194
|
effectiveTo: z2.string().optional().describe("Effective end date (ISO 8601)")
|
|
195
195
|
}
|
|
@@ -200,7 +200,7 @@ function registerVersionTools(server, callApi) {
|
|
|
200
200
|
"lexq_versions_update",
|
|
201
201
|
{
|
|
202
202
|
title: "Update Policy Version",
|
|
203
|
-
description: "Update a DRAFT version. Only DRAFT versions can be modified.",
|
|
203
|
+
description: "Update a DRAFT version. Only DRAFT versions can be modified. Only provided fields are changed.",
|
|
204
204
|
inputSchema: {
|
|
205
205
|
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
206
206
|
versionId: z2.string().uuid().describe("Version ID"),
|
|
@@ -243,6 +243,7 @@ function registerVersionTools(server, callApi) {
|
|
|
243
243
|
|
|
244
244
|
// src/mcp/tools/rules.ts
|
|
245
245
|
import { z as z3 } from "zod";
|
|
246
|
+
import dedent from "dedent";
|
|
246
247
|
function registerRuleTools(server, callApi) {
|
|
247
248
|
server.registerTool(
|
|
248
249
|
"lexq_rules_list",
|
|
@@ -277,32 +278,34 @@ function registerRuleTools(server, callApi) {
|
|
|
277
278
|
"lexq_rules_create",
|
|
278
279
|
{
|
|
279
280
|
title: "Create Rule",
|
|
280
|
-
description: `
|
|
281
|
+
description: dedent`
|
|
282
|
+
Create a rule in a DRAFT version. Requires name, priority, condition tree, and actions array.
|
|
281
283
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
284
|
+
Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
|
|
285
|
+
If a required key is missing, ask the user to confirm the type, isRequired, and description
|
|
286
|
+
before calling lexq_facts_create — registering facts enables type validation, Console UI
|
|
287
|
+
autocomplete, and the dry-run requirements analyzer.
|
|
286
288
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
289
|
+
Condition: { type: "SINGLE", field, operator, value, valueType } or { type: "GROUP", operator: "AND"|"OR", children: [...] }
|
|
290
|
+
Operators: EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
|
|
291
|
+
Value types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
|
|
290
292
|
|
|
291
|
-
|
|
293
|
+
Actions: [{ type, parameters }]
|
|
292
294
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
295
|
+
Action parameter schemas:
|
|
296
|
+
- DISCOUNT: { refVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT) }
|
|
297
|
+
- POINT: { refVar: string, targetVar: string, method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), integrationId: uuid }
|
|
298
|
+
- COUPON_ISSUE: { couponId: string, integrationId: uuid }
|
|
299
|
+
- BLOCK: { reason: string }
|
|
300
|
+
- NOTIFICATION: { channel: "SMS"|"EMAIL"|"PUSH", targetVar: string, templateId: string, integrationId: uuid }
|
|
301
|
+
- WEBHOOK: { url: string, method: "POST", payloadTemplate?: object } payloadTemplate is optional. Without it, all facts are sent as-is. With it, the object is sent as the HTTP body with {{variables}} replaced at execution time. Variables: {{fact.xxx}}, {{output.xxx}}, {{timestamp}}, {{ruleName}}, {{groupName}}, {{versionNo}}, {{xxx}} (shorthand).
|
|
302
|
+
Platform examples:
|
|
303
|
+
Slack: { "text": "Rule {{ruleName}} fired — {{fact.customer_tier}}" }
|
|
304
|
+
Discord: { "content": "Rule {{ruleName}} fired — {{fact.customer_tier}}" }
|
|
305
|
+
Generic: { "event": "rule_matched", "rule": "{{ruleName}}", "amount": "{{output.payment_amount}}" }
|
|
306
|
+
- SET_FACT: { key: string, value: string|number|boolean }
|
|
307
|
+
- ADD_TAG: { tag: string, targetVar: string }
|
|
308
|
+
`,
|
|
306
309
|
inputSchema: {
|
|
307
310
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
308
311
|
versionId: z3.string().uuid().describe("Version ID"),
|
|
@@ -435,12 +438,12 @@ function registerFactTools(server, callApi) {
|
|
|
435
438
|
"lexq_facts_update",
|
|
436
439
|
{
|
|
437
440
|
title: "Update Fact Definition",
|
|
438
|
-
description: "Update a fact definition. Key and type cannot be changed. System facts only allow name and description changes.",
|
|
441
|
+
description: "Update a fact definition. Key and type cannot be changed. Only provided fields are updated. System facts only allow name and description changes.",
|
|
439
442
|
inputSchema: {
|
|
440
443
|
factId: z4.string().uuid().describe("Fact definition ID"),
|
|
441
|
-
name: z4.string().describe("Display name"),
|
|
444
|
+
name: z4.string().optional().describe("Display name"),
|
|
442
445
|
description: z4.string().optional().describe("Description"),
|
|
443
|
-
isRequired: z4.boolean().describe("Required flag")
|
|
446
|
+
isRequired: z4.boolean().optional().describe("Required flag")
|
|
444
447
|
}
|
|
445
448
|
},
|
|
446
449
|
async ({ factId, ...body }) => callApi("PUT", `schema/facts/${factId}`, { body })
|
|
@@ -456,6 +459,15 @@ function registerFactTools(server, callApi) {
|
|
|
456
459
|
},
|
|
457
460
|
async ({ factId }) => callApi("DELETE", `schema/facts/${factId}`)
|
|
458
461
|
);
|
|
462
|
+
server.registerTool(
|
|
463
|
+
"lexq_facts_action_metadata",
|
|
464
|
+
{
|
|
465
|
+
title: "Get Action Runtime Fact Metadata",
|
|
466
|
+
description: "Retrieve runtime Facts metadata for each Action type. Shows which Facts are required as input, produced as output, or consumed at runtime by each Action (DISCOUNT, SEND_SMS, ADD_TAG, SET_FACT, etc.). Use this BEFORE designing rules to understand which Action produces which output variables (e.g., DISCOUNT produces last_discount_amount). Data is static and changes only on engine deployment \u2014 safe to cache in-session.",
|
|
467
|
+
inputSchema: {}
|
|
468
|
+
},
|
|
469
|
+
async () => callApi("GET", "schema/action-metadata")
|
|
470
|
+
);
|
|
459
471
|
}
|
|
460
472
|
|
|
461
473
|
// src/mcp/tools/deploy.ts
|
|
@@ -469,7 +481,7 @@ function registerDeployTools(server, callApi) {
|
|
|
469
481
|
inputSchema: {
|
|
470
482
|
groupId: z5.string().uuid().describe("Policy group ID"),
|
|
471
483
|
versionId: z5.string().uuid().describe("Version ID to publish"),
|
|
472
|
-
memo: z5.string().min(1).describe("Publish
|
|
484
|
+
memo: z5.string().min(1).describe("Publish memo (required)")
|
|
473
485
|
}
|
|
474
486
|
},
|
|
475
487
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/versions/${versionId}/publish`, { body: { memo } })
|
|
@@ -482,7 +494,7 @@ function registerDeployTools(server, callApi) {
|
|
|
482
494
|
inputSchema: {
|
|
483
495
|
groupId: z5.string().uuid().describe("Policy group ID"),
|
|
484
496
|
versionId: z5.string().uuid().describe("Version ID to deploy"),
|
|
485
|
-
memo: z5.string().min(1).describe("
|
|
497
|
+
memo: z5.string().min(1).describe("Deployment memo (required)")
|
|
486
498
|
}
|
|
487
499
|
},
|
|
488
500
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/deploy`, {
|
|
@@ -496,7 +508,7 @@ function registerDeployTools(server, callApi) {
|
|
|
496
508
|
description: "Rollback to the previous deployed version. Only available if there is a previous version.",
|
|
497
509
|
inputSchema: {
|
|
498
510
|
groupId: z5.string().uuid().describe("Policy group ID"),
|
|
499
|
-
memo: z5.string().
|
|
511
|
+
memo: z5.string().min(1).describe("Rollback reason (required)")
|
|
500
512
|
}
|
|
501
513
|
},
|
|
502
514
|
async ({ groupId, memo }) => callApi("POST", `policy-groups/${groupId}/rollback`, {
|
|
@@ -510,7 +522,7 @@ function registerDeployTools(server, callApi) {
|
|
|
510
522
|
description: "Remove the live version from traffic. The version stays ACTIVE but no longer serves requests.",
|
|
511
523
|
inputSchema: {
|
|
512
524
|
groupId: z5.string().uuid().describe("Policy group ID"),
|
|
513
|
-
memo: z5.string().
|
|
525
|
+
memo: z5.string().min(1).describe("Undeploy reason (required)")
|
|
514
526
|
}
|
|
515
527
|
},
|
|
516
528
|
async ({ groupId, memo }) => callApi("POST", `policy-groups/${groupId}/undeploy`, {
|
|
@@ -526,13 +538,19 @@ function registerDeployTools(server, callApi) {
|
|
|
526
538
|
page: z5.number().int().min(0).default(0).describe("Page number"),
|
|
527
539
|
size: z5.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
528
540
|
groupId: z5.string().uuid().optional().describe("Filter by group ID"),
|
|
529
|
-
|
|
541
|
+
types: z5.string().optional().describe(
|
|
542
|
+
"Filter by deployment types (comma-separated: PUBLISH,DEPLOY,ROLLBACK,UNDEPLOY)"
|
|
543
|
+
),
|
|
544
|
+
startDate: z5.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
545
|
+
endDate: z5.string().optional().describe("End date (yyyy-MM-dd)")
|
|
530
546
|
}
|
|
531
547
|
},
|
|
532
|
-
async ({ page, size, groupId,
|
|
548
|
+
async ({ page, size, groupId, types, startDate, endDate }) => {
|
|
533
549
|
const params = paginationParams(page, size);
|
|
534
550
|
if (groupId) params.groupId = groupId;
|
|
535
|
-
if (
|
|
551
|
+
if (types) params.types = types;
|
|
552
|
+
if (startDate) params.startDate = startDate;
|
|
553
|
+
if (endDate) params.endDate = endDate;
|
|
536
554
|
return callApi("GET", "deployments", { params });
|
|
537
555
|
}
|
|
538
556
|
);
|
|
@@ -556,18 +574,53 @@ function registerDeployTools(server, callApi) {
|
|
|
556
574
|
},
|
|
557
575
|
async () => callApi("GET", "deployments/overview")
|
|
558
576
|
);
|
|
577
|
+
server.registerTool(
|
|
578
|
+
"lexq_deploy_deployable",
|
|
579
|
+
{
|
|
580
|
+
title: "List Deployable Versions",
|
|
581
|
+
description: "List ACTIVE (published) versions that can be deployed for a group. Use this to find which versions are available before calling deploy live.",
|
|
582
|
+
inputSchema: {
|
|
583
|
+
groupId: z5.string().uuid().describe("Policy group ID")
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
async ({ groupId }) => callApi("GET", `deployments/groups/${groupId}/deployable-versions`)
|
|
587
|
+
);
|
|
588
|
+
server.registerTool(
|
|
589
|
+
"lexq_deploy_diff",
|
|
590
|
+
{
|
|
591
|
+
title: "Deployment Diff",
|
|
592
|
+
description: "Compare rule snapshots between two versions. Shows added, removed, and modified rules. Useful for reviewing changes before deploying a new version.",
|
|
593
|
+
inputSchema: {
|
|
594
|
+
baseVersionId: z5.string().uuid().describe("Base version ID (typically the current live)"),
|
|
595
|
+
targetVersionId: z5.string().uuid().describe("Target version ID (the one you want to deploy)")
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
async ({ baseVersionId, targetVersionId }) => callApi("GET", "deployments/diff", {
|
|
599
|
+
params: { baseVersionId, targetVersionId }
|
|
600
|
+
})
|
|
601
|
+
);
|
|
559
602
|
}
|
|
560
603
|
|
|
561
604
|
// src/mcp/tools/analytics.ts
|
|
562
605
|
import { z as z6 } from "zod";
|
|
606
|
+
import dedent2 from "dedent";
|
|
563
607
|
function registerAnalyticsTools(server, callApi) {
|
|
564
608
|
server.registerTool(
|
|
565
609
|
"lexq_dry_run",
|
|
566
610
|
{
|
|
567
611
|
title: "Dry Run",
|
|
568
|
-
description: `
|
|
569
|
-
|
|
570
|
-
|
|
612
|
+
description: dedent2`
|
|
613
|
+
Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
inputFacts — normalized input facts
|
|
617
|
+
mutatedFacts — input facts changed by rule actions (e.g. DISCOUNT mutates payment_amount)
|
|
618
|
+
generatedVariables — new variables created by rules (e.g. last_discount_amount)
|
|
619
|
+
executionTraces — per-rule match status
|
|
620
|
+
decisionTraces — per-rule decision (SELECTED / BLOCKED_MUTEX / etc.)
|
|
621
|
+
|
|
622
|
+
Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
|
|
623
|
+
Always dry-run before publishing to validate rule behavior.`,
|
|
571
624
|
inputSchema: {
|
|
572
625
|
versionId: z6.string().uuid().describe("Policy version ID to test against"),
|
|
573
626
|
facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
|
|
@@ -586,7 +639,13 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
586
639
|
"lexq_dry_run_compare",
|
|
587
640
|
{
|
|
588
641
|
title: "Dry Run Compare",
|
|
589
|
-
description:
|
|
642
|
+
description: dedent2`
|
|
643
|
+
Compare dry run results between two versions using the same input facts. Useful for validating changes.
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
resultA / resultB — full DryRunResponse for each version
|
|
647
|
+
diff.mutatedDiff — changes in mutatedFacts between A and B (key → {before, after})
|
|
648
|
+
diff.generatedDiff — changes in generatedVariables between A and B`,
|
|
590
649
|
inputSchema: {
|
|
591
650
|
versionIdA: z6.string().uuid().describe("Baseline version ID"),
|
|
592
651
|
versionIdB: z6.string().uuid().describe("Candidate version ID"),
|
|
@@ -616,21 +675,23 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
616
675
|
"lexq_simulation_start",
|
|
617
676
|
{
|
|
618
677
|
title: "Start Simulation",
|
|
619
|
-
description: `
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
678
|
+
description: dedent2`
|
|
679
|
+
Start a batch simulation against historical or uploaded data.
|
|
680
|
+
|
|
681
|
+
dataset.type: "HISTORICAL" or "UPLOADED"
|
|
682
|
+
dataset.source (when HISTORICAL): "EXECUTION_LOGS"
|
|
683
|
+
dataset.from / dataset.to: date range (yyyy-MM-dd, when HISTORICAL)
|
|
684
|
+
options.maxRecords: number (max 100000, default 10000)
|
|
685
|
+
options.baselinePolicyVersionId: uuid (optional, for comparison)
|
|
686
|
+
options.includeRuleStats: boolean
|
|
687
|
+
|
|
688
|
+
Example body:
|
|
689
|
+
{
|
|
690
|
+
"policyVersionId": "<uuid>",
|
|
691
|
+
"dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
|
|
692
|
+
"options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
|
|
693
|
+
}
|
|
694
|
+
`,
|
|
634
695
|
inputSchema: {
|
|
635
696
|
body: z6.string().describe("JSON string of SimulationRequest")
|
|
636
697
|
}
|
|
@@ -701,16 +762,18 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
701
762
|
"lexq_dataset_upload",
|
|
702
763
|
{
|
|
703
764
|
title: "Upload Dataset",
|
|
704
|
-
description: `
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
765
|
+
description: dedent2`
|
|
766
|
+
Upload inline CSV or JSON content as a simulation dataset.
|
|
767
|
+
The content is uploaded to S3 and a path is returned.
|
|
768
|
+
Use this path in simulation start with dataset type UPLOADED.
|
|
769
|
+
|
|
770
|
+
CSV example:
|
|
771
|
+
user_id,payment_amount
|
|
772
|
+
user_001,150000
|
|
773
|
+
user_002,50000
|
|
774
|
+
|
|
775
|
+
JSON example: [{"user_id":"user_001","payment_amount":150000}, {"user_id":"user_002","payment_amount":50000}]
|
|
776
|
+
`,
|
|
714
777
|
inputSchema: {
|
|
715
778
|
content: z6.string().describe("CSV or JSON content as string"),
|
|
716
779
|
filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
|
|
@@ -893,8 +956,16 @@ var TaskType = [
|
|
|
893
956
|
"WEBHOOK_EXECUTE",
|
|
894
957
|
// Internal
|
|
895
958
|
"IMAGE_PROCESSING",
|
|
896
|
-
"DAILY_SETTLEMENT"
|
|
959
|
+
"DAILY_SETTLEMENT",
|
|
960
|
+
"PLATFORM_WEBHOOK"
|
|
897
961
|
];
|
|
962
|
+
var PlatformEventType = [
|
|
963
|
+
"VERSION_PUBLISHED",
|
|
964
|
+
"DEPLOYED",
|
|
965
|
+
"ROLLED_BACK",
|
|
966
|
+
"UNDEPLOYED"
|
|
967
|
+
];
|
|
968
|
+
var WebhookPayloadFormat = ["GENERIC", "SLACK"];
|
|
898
969
|
|
|
899
970
|
// src/mcp/tools/logs.ts
|
|
900
971
|
function registerLogTools(server, callApi) {
|
|
@@ -940,14 +1011,14 @@ function registerLogTools(server, callApi) {
|
|
|
940
1011
|
"lexq_logs_action",
|
|
941
1012
|
{
|
|
942
1013
|
title: "Process Failure Log",
|
|
943
|
-
description: "Process a single failure log: RETRY, RESOLVE, or IGNORE.",
|
|
1014
|
+
description: "Process a single failure log: RETRY (re-execute with original payload), RESOLVE (mark as manually fixed), or IGNORE (skip intentionally).",
|
|
944
1015
|
inputSchema: {
|
|
945
1016
|
logId: z9.string().uuid().describe("Failure log ID"),
|
|
946
1017
|
action: z9.enum(FailureAction).describe("Action to take")
|
|
947
1018
|
}
|
|
948
1019
|
},
|
|
949
1020
|
async ({ logId, action }) => callApi("POST", `failure-logs/${logId}/actions`, {
|
|
950
|
-
|
|
1021
|
+
params: { action }
|
|
951
1022
|
})
|
|
952
1023
|
);
|
|
953
1024
|
server.registerTool(
|
|
@@ -966,6 +1037,76 @@ function registerLogTools(server, callApi) {
|
|
|
966
1037
|
);
|
|
967
1038
|
}
|
|
968
1039
|
|
|
1040
|
+
// src/mcp/tools/webhook-subscriptions.ts
|
|
1041
|
+
import { z as z10 } from "zod";
|
|
1042
|
+
function registerWebhookSubscriptionTools(server, callApi) {
|
|
1043
|
+
server.registerTool(
|
|
1044
|
+
"lexq_webhook_subscriptions_list",
|
|
1045
|
+
{
|
|
1046
|
+
title: "List Webhook Subscriptions",
|
|
1047
|
+
description: "List platform event webhook subscriptions. These receive deployment lifecycle notifications (publish, deploy, rollback, undeploy).",
|
|
1048
|
+
inputSchema: {
|
|
1049
|
+
page: z10.number().int().min(0).default(0).describe("Page number"),
|
|
1050
|
+
size: z10.number().int().min(1).max(100).default(20).describe("Page size")
|
|
1051
|
+
}
|
|
1052
|
+
},
|
|
1053
|
+
async ({ page, size }) => {
|
|
1054
|
+
const params = paginationParams(page, size);
|
|
1055
|
+
return callApi("GET", "webhook-subscriptions", { params });
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
1058
|
+
server.registerTool(
|
|
1059
|
+
"lexq_webhook_subscriptions_get",
|
|
1060
|
+
{
|
|
1061
|
+
title: "Get Webhook Subscription",
|
|
1062
|
+
description: "Get webhook subscription detail by ID.",
|
|
1063
|
+
inputSchema: {
|
|
1064
|
+
id: z10.string().uuid().describe("Webhook subscription ID")
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
async ({ id }) => callApi("GET", `webhook-subscriptions/${id}`)
|
|
1068
|
+
);
|
|
1069
|
+
server.registerTool(
|
|
1070
|
+
"lexq_webhook_subscriptions_save",
|
|
1071
|
+
{
|
|
1072
|
+
title: "Save Webhook Subscription",
|
|
1073
|
+
description: 'Create or update a webhook subscription. Omit id to create, provide id to update. Events: VERSION_PUBLISHED, DEPLOYED, ROLLED_BACK, UNDEPLOYED. Formats: GENERIC (full JSON), SLACK ({"text": "..."}).',
|
|
1074
|
+
inputSchema: {
|
|
1075
|
+
id: z10.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
|
|
1076
|
+
name: z10.string().min(1).describe("Subscription name (unique per tenant)"),
|
|
1077
|
+
webhookUrl: z10.string().url().describe("Webhook endpoint URL"),
|
|
1078
|
+
subscribedEvents: z10.array(z10.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
|
|
1079
|
+
payloadFormat: z10.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
|
|
1080
|
+
secret: z10.string().optional().describe("HMAC-SHA256 signing secret"),
|
|
1081
|
+
isActive: z10.boolean().optional().default(true).describe("Whether the subscription is active")
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
async ({ ...body }) => callApi("POST", "webhook-subscriptions", { body })
|
|
1085
|
+
);
|
|
1086
|
+
server.registerTool(
|
|
1087
|
+
"lexq_webhook_subscriptions_delete",
|
|
1088
|
+
{
|
|
1089
|
+
title: "Delete Webhook Subscription",
|
|
1090
|
+
description: "Delete a webhook subscription by ID.",
|
|
1091
|
+
inputSchema: {
|
|
1092
|
+
id: z10.string().uuid().describe("Webhook subscription ID")
|
|
1093
|
+
}
|
|
1094
|
+
},
|
|
1095
|
+
async ({ id }) => callApi("DELETE", `webhook-subscriptions/${id}`)
|
|
1096
|
+
);
|
|
1097
|
+
server.registerTool(
|
|
1098
|
+
"lexq_webhook_subscriptions_test",
|
|
1099
|
+
{
|
|
1100
|
+
title: "Test Webhook Subscription",
|
|
1101
|
+
description: "Send a test event to verify webhook connectivity. Returns the HTTP status code and success/failure message.",
|
|
1102
|
+
inputSchema: {
|
|
1103
|
+
id: z10.string().uuid().describe("Webhook subscription ID")
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
async ({ id }) => callApi("POST", `webhook-subscriptions/${id}/test`)
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
969
1110
|
// src/mcp/register.ts
|
|
970
1111
|
function registerAllTools(server, callApi) {
|
|
971
1112
|
registerStatusTools(server, callApi);
|
|
@@ -978,6 +1119,7 @@ function registerAllTools(server, callApi) {
|
|
|
978
1119
|
registerHistoryTools(server, callApi);
|
|
979
1120
|
registerIntegrationTools(server, callApi);
|
|
980
1121
|
registerLogTools(server, callApi);
|
|
1122
|
+
registerWebhookSubscriptionTools(server, callApi);
|
|
981
1123
|
}
|
|
982
1124
|
export {
|
|
983
1125
|
paginationParams,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexq/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "LexQ CLI — manage policies, simulate rules, and deploy from the terminal. Built for humans and AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -47,12 +47,14 @@
|
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
48
48
|
"cli-table3": "^0.6.5",
|
|
49
49
|
"commander": "^13.1.0",
|
|
50
|
+
"dedent": "^1.7.2",
|
|
50
51
|
"ora": "^8.2.0",
|
|
51
52
|
"prettier": "^3.8.1",
|
|
52
53
|
"zod": "^3.25.76"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@eslint/js": "^9.20.0",
|
|
57
|
+
"@types/dedent": "^0.7.2",
|
|
56
58
|
"@types/node": "^22.12.0",
|
|
57
59
|
"eslint": "^10.2.0",
|
|
58
60
|
"tsup": "^8.5.1",
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## What is LexQ?
|
|
6
6
|
|
|
7
|
-
LexQ is a **policy execution engine**. Customers define business rules (conditions → actions), deploy them to
|
|
7
|
+
LexQ is a **policy execution engine**. Customers define business rules (conditions → actions), deploy them to
|
|
8
|
+
production, and execute them via API — all without touching application code. Core differentiators are **pre-deploy
|
|
9
|
+
simulation** and **A/B testing** for rule versions.
|
|
8
10
|
|
|
9
11
|
## Authentication
|
|
10
12
|
|
|
@@ -20,22 +22,22 @@ lexq auth whoami
|
|
|
20
22
|
The API key is passed as `X-API-KEY` header on every request. You can also override per-command:
|
|
21
23
|
|
|
22
24
|
```bash
|
|
23
|
-
lexq groups list --api-key
|
|
25
|
+
lexq groups list --api-key sk_live_override_key
|
|
24
26
|
```
|
|
25
27
|
|
|
26
28
|
**Base URL:** `https://api.lexq.io/api/v1/partners` (default). Override with `--base-url`.
|
|
27
29
|
|
|
28
30
|
## Core Concepts
|
|
29
31
|
|
|
30
|
-
| Concept
|
|
31
|
-
|
|
32
|
-
| **Policy Group**
|
|
33
|
-
| **Policy Version**
|
|
34
|
-
| **Policy Rule**
|
|
35
|
-
| **Fact Definition** | Input schema — declares available variables and their types (STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER). | Function parameter
|
|
36
|
-
| **Deployment**
|
|
37
|
-
| **Dry Run**
|
|
38
|
-
| **Simulation**
|
|
32
|
+
| Concept | Description | Analogy |
|
|
33
|
+
|---------------------|------------------------------------------------------------------------------------------------------------------|------------------------|
|
|
34
|
+
| **Policy Group** | A container for rule versions. Has a lifecycle status (ACTIVE / DISABLED / ARCHIVED). | Git repository |
|
|
35
|
+
| **Policy Version** | A snapshot of rules within a group. Follows DRAFT → ACTIVE → ARCHIVED lifecycle. | Git branch / commit |
|
|
36
|
+
| **Policy Rule** | A condition + actions pair within a version. Evaluated in priority order. | if-then statement |
|
|
37
|
+
| **Fact Definition** | Input schema — declares available variables and their types (STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER). | Function parameter |
|
|
38
|
+
| **Deployment** | Promotes a PUBLISHED version to live traffic. Supports rollback. | Production release |
|
|
39
|
+
| **Dry Run** | Tests a single input against a DRAFT or ACTIVE version without side effects. | Unit test |
|
|
40
|
+
| **Simulation** | Batch-tests a version against historical execution data. Compares with a baseline. | Integration test suite |
|
|
39
41
|
|
|
40
42
|
## Standard Workflow
|
|
41
43
|
|
|
@@ -64,32 +66,33 @@ This is the typical lifecycle. **Always follow this order:**
|
|
|
64
66
|
|
|
65
67
|
Every command accepts these flags:
|
|
66
68
|
|
|
67
|
-
| Flag
|
|
68
|
-
|
|
69
|
-
| `--format <json\|table>` | Output format
|
|
70
|
-
| `--api-key <key>`
|
|
71
|
-
| `--base-url <url>`
|
|
72
|
-
| `--dry-run`
|
|
73
|
-
| `--verbose`
|
|
74
|
-
| `--no-color`
|
|
69
|
+
| Flag | Description | Default |
|
|
70
|
+
|--------------------------|--------------------------------------------|---------------------------------------|
|
|
71
|
+
| `--format <json\|table>` | Output format | `json` |
|
|
72
|
+
| `--api-key <key>` | Override stored API key | from config |
|
|
73
|
+
| `--base-url <url>` | Override API base URL | `https://api.lexq.io/api/v1/partners` |
|
|
74
|
+
| `--dry-run` | Preview the HTTP request without executing | off |
|
|
75
|
+
| `--verbose` | Show request/response details | off |
|
|
76
|
+
| `--no-color` | Disable colored output | off |
|
|
75
77
|
|
|
76
|
-
**Agent best practice:** Always use `--format json` (the default). Parse JSON output programmatically. Use
|
|
78
|
+
**Agent best practice:** Always use `--format json` (the default). Parse JSON output programmatically. Use
|
|
79
|
+
`--format table` only when displaying to humans.
|
|
77
80
|
|
|
78
81
|
## Command Groups
|
|
79
82
|
|
|
80
|
-
| Group
|
|
81
|
-
|
|
82
|
-
| `auth`
|
|
83
|
-
| `status`
|
|
84
|
-
| `groups`
|
|
85
|
-
| `versions`
|
|
86
|
-
| `rules`
|
|
87
|
-
| `facts`
|
|
88
|
-
| `deploy`
|
|
89
|
-
| `analytics`
|
|
90
|
-
| `history`
|
|
91
|
-
| `integrations` | `list`, `get`, `save`, `delete`, `config-spec`
|
|
92
|
-
| `logs`
|
|
83
|
+
| Group | Commands | Description |
|
|
84
|
+
|----------------|--------------------------------------------------------------------------------------------|---------------------------------|
|
|
85
|
+
| `auth` | `login`, `logout`, `whoami` | Authentication |
|
|
86
|
+
| `status` | (root) | API health check |
|
|
87
|
+
| `groups` | `list`, `get`, `create`, `update`, `delete` + `ab-test start\|stop\|adjust` | Policy group CRUD + A/B testing |
|
|
88
|
+
| `versions` | `list`, `get`, `create`, `update`, `delete`, `clone` | Version CRUD |
|
|
89
|
+
| `rules` | `list`, `get`, `create`, `update`, `delete`, `reorder`, `toggle` | Rule CRUD |
|
|
90
|
+
| `facts` | `list`, `create`, `update`, `delete` | Fact definition CRUD |
|
|
91
|
+
| `deploy` | `publish`, `live`, `rollback`, `undeploy`, `history`, `detail`, `overview` | Deployment lifecycle |
|
|
92
|
+
| `analytics` | `dry-run`, `dry-run-compare`, `requirements`, `simulation start/status/list/cancel/export` | Testing & analysis |
|
|
93
|
+
| `history` | `list`, `get`, `stats` | Execution history |
|
|
94
|
+
| `integrations` | `list`, `get`, `save`, `delete`, `config-spec` | External integrations |
|
|
95
|
+
| `logs` | `list`, `get`, `action`, `bulk-action` | Failure log management |
|
|
93
96
|
|
|
94
97
|
## Pagination
|
|
95
98
|
|
|
@@ -97,7 +100,9 @@ All list endpoints return a `PageResponse`:
|
|
|
97
100
|
|
|
98
101
|
```json
|
|
99
102
|
{
|
|
100
|
-
"content": [
|
|
103
|
+
"content": [
|
|
104
|
+
...
|
|
105
|
+
],
|
|
101
106
|
"totalElements": 42,
|
|
102
107
|
"totalPages": 3,
|
|
103
108
|
"pageNo": 0,
|
|
@@ -121,13 +126,13 @@ API errors return:
|
|
|
121
126
|
|
|
122
127
|
**Common error codes and what to do:**
|
|
123
128
|
|
|
124
|
-
| Code
|
|
125
|
-
|
|
126
|
-
| `ENTITY_NOT_FOUND` | Resource doesn't exist
|
|
127
|
-
| `INVALID_INPUT`
|
|
128
|
-
| `CANNOT_MODIFY`
|
|
129
|
-
| `EMPTY_RULES`
|
|
130
|
-
| `UNAUTHORIZED`
|
|
129
|
+
| Code | Meaning | Action |
|
|
130
|
+
|--------------------|--------------------------------|---------------------------------------------------------------------------------|
|
|
131
|
+
| `ENTITY_NOT_FOUND` | Resource doesn't exist | Verify the ID. Run the corresponding `list` command. |
|
|
132
|
+
| `INVALID_INPUT` | Validation failed | Check required fields. Run `lexq analytics requirements` for fact requirements. |
|
|
133
|
+
| `CANNOT_MODIFY` | Version is not DRAFT | Clone the version to create a new DRAFT: `lexq versions clone` |
|
|
134
|
+
| `EMPTY_RULES` | Publish attempted with 0 rules | Add at least one rule before publishing. |
|
|
135
|
+
| `UNAUTHORIZED` | Invalid or missing API key | Run `lexq auth login` with a valid key. |
|
|
131
136
|
|
|
132
137
|
## Important Conventions
|
|
133
138
|
|
|
@@ -136,4 +141,5 @@ API errors return:
|
|
|
136
141
|
3. **Dates use ISO 8601.** Example: `2025-01-01T00:00:00Z`. Time zone is UTC.
|
|
137
142
|
4. **JSON bodies via `--json`.** Most create/update commands accept `--json '<body>'` for the request body.
|
|
138
143
|
5. **File input via `--file`.** Analytics commands accept `--file path/to/body.json` as an alternative to `--json`.
|
|
139
|
-
6. **Confirmation prompts.** Destructive operations (delete, cancel, undeploy) prompt for confirmation. Use `--force` to
|
|
144
|
+
6. **Confirmation prompts.** Destructive operations (delete, cancel, undeploy) prompt for confirmation. Use `--force` to
|
|
145
|
+
skip in automation.
|