@lexq/cli 0.1.59 → 0.3.0
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 +5 -0
- package/README.md +1 -1
- package/dist/index.js +402 -379
- package/dist/mcp/register.d.ts +1 -1
- package/dist/mcp/register.js +366 -365
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -13,11 +13,19 @@ import { stdin, stdout } from "process";
|
|
|
13
13
|
import dedent from "dedent";
|
|
14
14
|
|
|
15
15
|
// src/lib/config.ts
|
|
16
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
16
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
17
17
|
import { homedir } from "os";
|
|
18
18
|
import { join } from "path";
|
|
19
19
|
var CONFIG_DIR = join(homedir(), ".lexq");
|
|
20
20
|
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
21
|
+
var DIR_MODE = 448;
|
|
22
|
+
var FILE_MODE = 384;
|
|
23
|
+
function restrict(target, mode) {
|
|
24
|
+
try {
|
|
25
|
+
chmodSync(target, mode);
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
21
29
|
var DEFAULT_CONFIG = {
|
|
22
30
|
baseUrl: "https://api.lexq.io/api/v1/partners",
|
|
23
31
|
format: "json"
|
|
@@ -33,11 +41,16 @@ function loadConfig() {
|
|
|
33
41
|
}
|
|
34
42
|
function saveConfig(config) {
|
|
35
43
|
if (!existsSync(CONFIG_DIR)) {
|
|
36
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
44
|
+
mkdirSync(CONFIG_DIR, { recursive: true, mode: DIR_MODE });
|
|
37
45
|
}
|
|
46
|
+
restrict(CONFIG_DIR, DIR_MODE);
|
|
38
47
|
const current = loadConfig();
|
|
39
48
|
const merged = { ...current, ...config };
|
|
40
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2),
|
|
49
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), {
|
|
50
|
+
encoding: "utf-8",
|
|
51
|
+
mode: FILE_MODE
|
|
52
|
+
});
|
|
53
|
+
restrict(CONFIG_FILE, FILE_MODE);
|
|
41
54
|
}
|
|
42
55
|
function deleteConfig() {
|
|
43
56
|
if (existsSync(CONFIG_FILE)) {
|
|
@@ -3317,8 +3330,8 @@ import dedent17 from "dedent";
|
|
|
3317
3330
|
import { readFileSync as readFileSync3 } from "fs";
|
|
3318
3331
|
import { dirname, join as join2 } from "path";
|
|
3319
3332
|
import { fileURLToPath } from "url";
|
|
3320
|
-
import {
|
|
3321
|
-
import {
|
|
3333
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
3334
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
3322
3335
|
|
|
3323
3336
|
// src/mcp/tools/_shared.ts
|
|
3324
3337
|
function createCallApiFromConfig() {
|
|
@@ -3402,27 +3415,28 @@ function formatUnregisteredFactWarning(meta) {
|
|
|
3402
3415
|
}
|
|
3403
3416
|
|
|
3404
3417
|
// src/mcp/tools/status.ts
|
|
3418
|
+
import { z } from "zod";
|
|
3405
3419
|
function registerStatusTools(server, callApi) {
|
|
3406
3420
|
server.registerTool(
|
|
3407
3421
|
"lexq_whoami",
|
|
3408
3422
|
{
|
|
3409
3423
|
title: "Who Am I",
|
|
3410
3424
|
description: "Show current authentication info (tenant ID, user ID, role).",
|
|
3411
|
-
inputSchema: {}
|
|
3425
|
+
inputSchema: z.object({})
|
|
3412
3426
|
},
|
|
3413
3427
|
async () => callApi("GET", "whoami")
|
|
3414
3428
|
);
|
|
3415
3429
|
}
|
|
3416
3430
|
|
|
3417
3431
|
// src/mcp/tools/groups.ts
|
|
3418
|
-
import { z } from "zod";
|
|
3432
|
+
import { z as z2 } from "zod";
|
|
3419
3433
|
function registerGroupTools(server, callApi) {
|
|
3420
3434
|
server.registerTool(
|
|
3421
3435
|
"lexq_groups_list",
|
|
3422
3436
|
{
|
|
3423
3437
|
title: "List Policy Groups",
|
|
3424
3438
|
description: "List all policy groups (tenant-wide, priority ASC).",
|
|
3425
|
-
inputSchema: {}
|
|
3439
|
+
inputSchema: z2.object({})
|
|
3426
3440
|
},
|
|
3427
3441
|
async () => callApi("GET", "policy-groups")
|
|
3428
3442
|
);
|
|
@@ -3431,9 +3445,9 @@ function registerGroupTools(server, callApi) {
|
|
|
3431
3445
|
{
|
|
3432
3446
|
title: "Get Policy Group",
|
|
3433
3447
|
description: "Get a single policy group by ID.",
|
|
3434
|
-
inputSchema: {
|
|
3435
|
-
groupId:
|
|
3436
|
-
}
|
|
3448
|
+
inputSchema: z2.object({
|
|
3449
|
+
groupId: z2.string().uuid().describe("Policy group ID")
|
|
3450
|
+
})
|
|
3437
3451
|
},
|
|
3438
3452
|
async ({ groupId }) => callApi("GET", `policy-groups/${groupId}`)
|
|
3439
3453
|
);
|
|
@@ -3442,18 +3456,18 @@ function registerGroupTools(server, callApi) {
|
|
|
3442
3456
|
{
|
|
3443
3457
|
title: "Create Policy Group",
|
|
3444
3458
|
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. Policy groups that share an activationGroup form a cluster and must share the same activationMode / activationStrategy / executionLimit; executionLimit is how many of those groups run, not how many rules.",
|
|
3445
|
-
inputSchema: {
|
|
3446
|
-
name:
|
|
3447
|
-
description:
|
|
3448
|
-
activationMode:
|
|
3449
|
-
activationStrategy:
|
|
3450
|
-
executionLimit:
|
|
3459
|
+
inputSchema: z2.object({
|
|
3460
|
+
name: z2.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
3461
|
+
description: z2.string().optional().describe("Group description"),
|
|
3462
|
+
activationMode: z2.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
|
|
3463
|
+
activationStrategy: z2.enum(ConflictResolutionStrategy).optional().describe("Ranking used to pick the winning groups when mode is EXCLUSIVE or MAX_N"),
|
|
3464
|
+
executionLimit: z2.number().int().min(1).optional().describe(
|
|
3451
3465
|
"How many policy groups sharing this activationGroup may run (required when mode is MAX_N). Counts groups, not rules: every rule of a winning group runs."
|
|
3452
3466
|
),
|
|
3453
|
-
activationGroup:
|
|
3467
|
+
activationGroup: z2.string().optional().describe(
|
|
3454
3468
|
"Activation group (Execution Group) cluster key. Policy groups sharing this key compete, and group priority picks the winners."
|
|
3455
3469
|
)
|
|
3456
|
-
}
|
|
3470
|
+
})
|
|
3457
3471
|
},
|
|
3458
3472
|
async (args) => {
|
|
3459
3473
|
const body = {
|
|
@@ -3472,20 +3486,20 @@ function registerGroupTools(server, callApi) {
|
|
|
3472
3486
|
{
|
|
3473
3487
|
title: "Update Policy Group",
|
|
3474
3488
|
description: "Update a policy group. Only provided fields are updated; omitted fields remain unchanged.",
|
|
3475
|
-
inputSchema: {
|
|
3476
|
-
groupId:
|
|
3477
|
-
name:
|
|
3478
|
-
description:
|
|
3479
|
-
status:
|
|
3480
|
-
activationGroup:
|
|
3489
|
+
inputSchema: z2.object({
|
|
3490
|
+
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
3491
|
+
name: z2.string().optional().describe("New name"),
|
|
3492
|
+
description: z2.string().optional().describe("New description"),
|
|
3493
|
+
status: z2.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
|
|
3494
|
+
activationGroup: z2.string().optional().describe(
|
|
3481
3495
|
"Activation group (Execution Group) cluster key. Policy groups sharing this key compete, and group priority picks the winners."
|
|
3482
3496
|
),
|
|
3483
|
-
activationMode:
|
|
3484
|
-
activationStrategy:
|
|
3485
|
-
executionLimit:
|
|
3497
|
+
activationMode: z2.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
|
|
3498
|
+
activationStrategy: z2.enum(ConflictResolutionStrategy).optional().describe("Ranking used to pick the winning groups"),
|
|
3499
|
+
executionLimit: z2.number().int().min(1).optional().describe(
|
|
3486
3500
|
"How many policy groups sharing this activationGroup may run. Counts groups, not rules."
|
|
3487
3501
|
)
|
|
3488
|
-
}
|
|
3502
|
+
})
|
|
3489
3503
|
},
|
|
3490
3504
|
async ({ groupId, ...body }) => callApi("PUT", `policy-groups/${groupId}`, { body })
|
|
3491
3505
|
);
|
|
@@ -3494,9 +3508,9 @@ function registerGroupTools(server, callApi) {
|
|
|
3494
3508
|
{
|
|
3495
3509
|
title: "Delete Policy Group",
|
|
3496
3510
|
description: "Archive a policy group. Only non-live groups can be deleted. This is irreversible.",
|
|
3497
|
-
inputSchema: {
|
|
3498
|
-
groupId:
|
|
3499
|
-
}
|
|
3511
|
+
inputSchema: z2.object({
|
|
3512
|
+
groupId: z2.string().uuid().describe("Policy group ID")
|
|
3513
|
+
})
|
|
3500
3514
|
},
|
|
3501
3515
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
|
|
3502
3516
|
);
|
|
@@ -3505,9 +3519,9 @@ function registerGroupTools(server, callApi) {
|
|
|
3505
3519
|
{
|
|
3506
3520
|
title: "Reorder Policy Groups",
|
|
3507
3521
|
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.",
|
|
3508
|
-
inputSchema: {
|
|
3509
|
-
groupIds:
|
|
3510
|
-
}
|
|
3522
|
+
inputSchema: z2.object({
|
|
3523
|
+
groupIds: z2.array(z2.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
|
|
3524
|
+
})
|
|
3511
3525
|
},
|
|
3512
3526
|
async ({ groupIds }) => {
|
|
3513
3527
|
const groups = groupIds.map((groupId, index) => ({
|
|
@@ -3522,11 +3536,11 @@ function registerGroupTools(server, callApi) {
|
|
|
3522
3536
|
{
|
|
3523
3537
|
title: "Start A/B Test",
|
|
3524
3538
|
description: "Start an A/B test on a policy group. Requires a challenger version ID and traffic rate. The split is computed from context.trafficKey on each execution request; requests that omit it never reach the challenger and the test stays at 0%.",
|
|
3525
|
-
inputSchema: {
|
|
3526
|
-
groupId:
|
|
3527
|
-
testVersionId:
|
|
3528
|
-
trafficRate:
|
|
3529
|
-
}
|
|
3539
|
+
inputSchema: z2.object({
|
|
3540
|
+
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
3541
|
+
testVersionId: z2.string().uuid().describe("Challenger version ID to test"),
|
|
3542
|
+
trafficRate: z2.number().int().min(1).max(99).describe("Traffic percentage routed to challenger (1-99)")
|
|
3543
|
+
})
|
|
3530
3544
|
},
|
|
3531
3545
|
async ({ groupId, ...body }) => callApi("POST", `policy-groups/${groupId}/ab-test`, { body })
|
|
3532
3546
|
);
|
|
@@ -3535,9 +3549,9 @@ function registerGroupTools(server, callApi) {
|
|
|
3535
3549
|
{
|
|
3536
3550
|
title: "Stop A/B Test",
|
|
3537
3551
|
description: "Stop a running A/B test. All traffic is restored to the control (current) version.",
|
|
3538
|
-
inputSchema: {
|
|
3539
|
-
groupId:
|
|
3540
|
-
}
|
|
3552
|
+
inputSchema: z2.object({
|
|
3553
|
+
groupId: z2.string().uuid().describe("Policy group ID")
|
|
3554
|
+
})
|
|
3541
3555
|
},
|
|
3542
3556
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}/ab-test`)
|
|
3543
3557
|
);
|
|
@@ -3546,28 +3560,28 @@ function registerGroupTools(server, callApi) {
|
|
|
3546
3560
|
{
|
|
3547
3561
|
title: "Adjust A/B Test",
|
|
3548
3562
|
description: "Adjust traffic rate of a running A/B test.",
|
|
3549
|
-
inputSchema: {
|
|
3550
|
-
groupId:
|
|
3551
|
-
trafficRate:
|
|
3552
|
-
}
|
|
3563
|
+
inputSchema: z2.object({
|
|
3564
|
+
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
3565
|
+
trafficRate: z2.number().int().min(1).max(99).describe("New traffic percentage for challenger (1-99)")
|
|
3566
|
+
})
|
|
3553
3567
|
},
|
|
3554
3568
|
async ({ groupId, ...body }) => callApi("PATCH", `policy-groups/${groupId}/ab-test/traffic-rate`, { body })
|
|
3555
3569
|
);
|
|
3556
3570
|
}
|
|
3557
3571
|
|
|
3558
3572
|
// src/mcp/tools/versions.ts
|
|
3559
|
-
import { z as
|
|
3573
|
+
import { z as z3 } from "zod";
|
|
3560
3574
|
function registerVersionTools(server, callApi) {
|
|
3561
3575
|
server.registerTool(
|
|
3562
3576
|
"lexq_versions_list",
|
|
3563
3577
|
{
|
|
3564
3578
|
title: "List Policy Versions",
|
|
3565
3579
|
description: "List all versions of a policy group.",
|
|
3566
|
-
inputSchema: {
|
|
3567
|
-
groupId:
|
|
3568
|
-
page:
|
|
3569
|
-
size:
|
|
3570
|
-
}
|
|
3580
|
+
inputSchema: z3.object({
|
|
3581
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3582
|
+
page: z3.number().int().min(0).default(0).describe("Page number"),
|
|
3583
|
+
size: z3.number().int().min(1).max(100).default(20).describe("Page size")
|
|
3584
|
+
})
|
|
3571
3585
|
},
|
|
3572
3586
|
async ({ groupId, page, size }) => callApi("GET", `policy-groups/${groupId}/versions`, {
|
|
3573
3587
|
params: paginationParams(page, size)
|
|
@@ -3578,10 +3592,10 @@ function registerVersionTools(server, callApi) {
|
|
|
3578
3592
|
{
|
|
3579
3593
|
title: "Get Policy Version",
|
|
3580
3594
|
description: "Get a single version by ID, including its rules and fact requirements.",
|
|
3581
|
-
inputSchema: {
|
|
3582
|
-
groupId:
|
|
3583
|
-
versionId:
|
|
3584
|
-
}
|
|
3595
|
+
inputSchema: z3.object({
|
|
3596
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3597
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
3598
|
+
})
|
|
3585
3599
|
},
|
|
3586
3600
|
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}`)
|
|
3587
3601
|
);
|
|
@@ -3590,12 +3604,12 @@ function registerVersionTools(server, callApi) {
|
|
|
3590
3604
|
{
|
|
3591
3605
|
title: "Create Policy Version",
|
|
3592
3606
|
description: "Create a new DRAFT version in a policy group. Optionally provide a commit message and effective date range.",
|
|
3593
|
-
inputSchema: {
|
|
3594
|
-
groupId:
|
|
3595
|
-
commitMessage:
|
|
3596
|
-
effectiveFrom:
|
|
3597
|
-
effectiveTo:
|
|
3598
|
-
}
|
|
3607
|
+
inputSchema: z3.object({
|
|
3608
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3609
|
+
commitMessage: z3.string().optional().describe("Commit message describing this version"),
|
|
3610
|
+
effectiveFrom: z3.string().optional().describe("Effective start date (ISO 8601)"),
|
|
3611
|
+
effectiveTo: z3.string().optional().describe("Effective end date (ISO 8601)")
|
|
3612
|
+
})
|
|
3599
3613
|
},
|
|
3600
3614
|
async ({ groupId, ...body }) => callApi("POST", `policy-groups/${groupId}/versions`, { body })
|
|
3601
3615
|
);
|
|
@@ -3604,13 +3618,13 @@ function registerVersionTools(server, callApi) {
|
|
|
3604
3618
|
{
|
|
3605
3619
|
title: "Update Policy Version",
|
|
3606
3620
|
description: "Update a DRAFT version. Only DRAFT versions can be modified. Only provided fields are changed.",
|
|
3607
|
-
inputSchema: {
|
|
3608
|
-
groupId:
|
|
3609
|
-
versionId:
|
|
3610
|
-
commitMessage:
|
|
3611
|
-
effectiveFrom:
|
|
3612
|
-
effectiveTo:
|
|
3613
|
-
}
|
|
3621
|
+
inputSchema: z3.object({
|
|
3622
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3623
|
+
versionId: z3.string().uuid().describe("Version ID"),
|
|
3624
|
+
commitMessage: z3.string().optional().describe("New commit message"),
|
|
3625
|
+
effectiveFrom: z3.string().optional().describe("New effective start date"),
|
|
3626
|
+
effectiveTo: z3.string().optional().describe("New effective end date")
|
|
3627
|
+
})
|
|
3614
3628
|
},
|
|
3615
3629
|
async ({ groupId, versionId, ...body }) => callApi("PUT", `policy-groups/${groupId}/versions/${versionId}`, { body })
|
|
3616
3630
|
);
|
|
@@ -3619,10 +3633,10 @@ function registerVersionTools(server, callApi) {
|
|
|
3619
3633
|
{
|
|
3620
3634
|
title: "Delete Policy Version",
|
|
3621
3635
|
description: "Delete a DRAFT version. Only DRAFT versions can be deleted.",
|
|
3622
|
-
inputSchema: {
|
|
3623
|
-
groupId:
|
|
3624
|
-
versionId:
|
|
3625
|
-
}
|
|
3636
|
+
inputSchema: z3.object({
|
|
3637
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3638
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
3639
|
+
})
|
|
3626
3640
|
},
|
|
3627
3641
|
async ({ groupId, versionId }) => callApi("DELETE", `policy-groups/${groupId}/versions/${versionId}`)
|
|
3628
3642
|
);
|
|
@@ -3631,10 +3645,10 @@ function registerVersionTools(server, callApi) {
|
|
|
3631
3645
|
{
|
|
3632
3646
|
title: "Clone Policy Version",
|
|
3633
3647
|
description: "Clone an existing version to create a new DRAFT. Useful when the source version is already published.",
|
|
3634
|
-
inputSchema: {
|
|
3635
|
-
groupId:
|
|
3636
|
-
versionId:
|
|
3637
|
-
}
|
|
3648
|
+
inputSchema: z3.object({
|
|
3649
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
3650
|
+
versionId: z3.string().uuid().describe("Source version ID to clone")
|
|
3651
|
+
})
|
|
3638
3652
|
},
|
|
3639
3653
|
async ({ groupId, versionId }) => {
|
|
3640
3654
|
return callApi("POST", `policy-groups/${groupId}/versions/${versionId}/clone`, {
|
|
@@ -3645,7 +3659,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3645
3659
|
}
|
|
3646
3660
|
|
|
3647
3661
|
// src/mcp/tools/rules.ts
|
|
3648
|
-
import { z as
|
|
3662
|
+
import { z as z4 } from "zod";
|
|
3649
3663
|
import dedent15 from "dedent";
|
|
3650
3664
|
function registerRuleTools(server, callApi) {
|
|
3651
3665
|
server.registerTool(
|
|
@@ -3653,10 +3667,10 @@ function registerRuleTools(server, callApi) {
|
|
|
3653
3667
|
{
|
|
3654
3668
|
title: "List Rules",
|
|
3655
3669
|
description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
|
|
3656
|
-
inputSchema: {
|
|
3657
|
-
groupId:
|
|
3658
|
-
versionId:
|
|
3659
|
-
}
|
|
3670
|
+
inputSchema: z4.object({
|
|
3671
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
3672
|
+
versionId: z4.string().uuid().describe("Version ID")
|
|
3673
|
+
})
|
|
3660
3674
|
},
|
|
3661
3675
|
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
|
|
3662
3676
|
);
|
|
@@ -3665,11 +3679,11 @@ function registerRuleTools(server, callApi) {
|
|
|
3665
3679
|
{
|
|
3666
3680
|
title: "Get Rule Detail",
|
|
3667
3681
|
description: "Get full rule detail including condition tree and action definitions.",
|
|
3668
|
-
inputSchema: {
|
|
3669
|
-
groupId:
|
|
3670
|
-
versionId:
|
|
3671
|
-
ruleId:
|
|
3672
|
-
}
|
|
3682
|
+
inputSchema: z4.object({
|
|
3683
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
3684
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
3685
|
+
ruleId: z4.string().uuid().describe("Rule ID")
|
|
3686
|
+
})
|
|
3673
3687
|
},
|
|
3674
3688
|
async ({ groupId, versionId, ruleId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules/${ruleId}`)
|
|
3675
3689
|
);
|
|
@@ -3738,13 +3752,13 @@ function registerRuleTools(server, callApi) {
|
|
|
3738
3752
|
|
|
3739
3753
|
RoundingOption (optional, MUTATE_FACT only): { scale: integer (0..${MAX_ROUNDING_SCALE}), mode?: "HALF_UP"|"HALF_DOWN"|"HALF_EVEN"|"FLOOR"|"CEILING"|"DOWN"|"UP" } mode defaults to HALF_UP. When omitted, calculator output is preserved at full precision (lossless).
|
|
3740
3754
|
`,
|
|
3741
|
-
inputSchema: {
|
|
3742
|
-
groupId:
|
|
3743
|
-
versionId:
|
|
3744
|
-
rule:
|
|
3755
|
+
inputSchema: z4.object({
|
|
3756
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
3757
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
3758
|
+
rule: z4.string().describe(
|
|
3745
3759
|
"JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
3746
3760
|
)
|
|
3747
|
-
}
|
|
3761
|
+
})
|
|
3748
3762
|
},
|
|
3749
3763
|
async ({ groupId, versionId, rule }) => {
|
|
3750
3764
|
const body = parseJson(rule);
|
|
@@ -3756,14 +3770,14 @@ function registerRuleTools(server, callApi) {
|
|
|
3756
3770
|
{
|
|
3757
3771
|
title: "Update Rule",
|
|
3758
3772
|
description: "Update an existing rule in a DRAFT version. Only provided fields are changed.",
|
|
3759
|
-
inputSchema: {
|
|
3760
|
-
groupId:
|
|
3761
|
-
versionId:
|
|
3762
|
-
ruleId:
|
|
3763
|
-
rule:
|
|
3773
|
+
inputSchema: z4.object({
|
|
3774
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
3775
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
3776
|
+
ruleId: z4.string().uuid().describe("Rule ID"),
|
|
3777
|
+
rule: z4.string().describe(
|
|
3764
3778
|
"JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
3765
3779
|
)
|
|
3766
|
-
}
|
|
3780
|
+
})
|
|
3767
3781
|
},
|
|
3768
3782
|
async ({ groupId, versionId, ruleId, rule }) => {
|
|
3769
3783
|
const body = parseJson(rule);
|
|
@@ -3777,12 +3791,12 @@ function registerRuleTools(server, callApi) {
|
|
|
3777
3791
|
{
|
|
3778
3792
|
title: "Delete Rule",
|
|
3779
3793
|
description: "Delete a rule from a DRAFT version.",
|
|
3780
|
-
inputSchema: {
|
|
3781
|
-
groupId:
|
|
3782
|
-
versionId:
|
|
3783
|
-
ruleId:
|
|
3784
|
-
force:
|
|
3785
|
-
}
|
|
3794
|
+
inputSchema: z4.object({
|
|
3795
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
3796
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
3797
|
+
ruleId: z4.string().uuid().describe("Rule ID"),
|
|
3798
|
+
force: z4.boolean().default(false).describe("Skip confirmation (for the last rule in a version)")
|
|
3799
|
+
})
|
|
3786
3800
|
},
|
|
3787
3801
|
async ({ groupId, versionId, ruleId, force }) => {
|
|
3788
3802
|
const params = {};
|
|
@@ -3797,11 +3811,11 @@ function registerRuleTools(server, callApi) {
|
|
|
3797
3811
|
{
|
|
3798
3812
|
title: "Reorder Rules",
|
|
3799
3813
|
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).",
|
|
3800
|
-
inputSchema: {
|
|
3801
|
-
groupId:
|
|
3802
|
-
versionId:
|
|
3803
|
-
ruleIds:
|
|
3804
|
-
}
|
|
3814
|
+
inputSchema: z4.object({
|
|
3815
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
3816
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
3817
|
+
ruleIds: z4.array(z4.string().uuid()).describe("Rule IDs in desired priority order")
|
|
3818
|
+
})
|
|
3805
3819
|
},
|
|
3806
3820
|
async ({ groupId, versionId, ruleIds }) => {
|
|
3807
3821
|
const rules = ruleIds.map((ruleId, index) => ({
|
|
@@ -3818,12 +3832,12 @@ function registerRuleTools(server, callApi) {
|
|
|
3818
3832
|
{
|
|
3819
3833
|
title: "Toggle Rule",
|
|
3820
3834
|
description: "Enable or disable a rule without deleting it.",
|
|
3821
|
-
inputSchema: {
|
|
3822
|
-
groupId:
|
|
3823
|
-
versionId:
|
|
3824
|
-
ruleId:
|
|
3825
|
-
isEnabled:
|
|
3826
|
-
}
|
|
3835
|
+
inputSchema: z4.object({
|
|
3836
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
3837
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
3838
|
+
ruleId: z4.string().uuid().describe("Rule ID"),
|
|
3839
|
+
isEnabled: z4.boolean().describe("true to enable, false to disable")
|
|
3840
|
+
})
|
|
3827
3841
|
},
|
|
3828
3842
|
async ({ groupId, versionId, ruleId, isEnabled }) => callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/${ruleId}/enabled`, {
|
|
3829
3843
|
body: { isEnabled }
|
|
@@ -3832,7 +3846,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3832
3846
|
}
|
|
3833
3847
|
|
|
3834
3848
|
// src/mcp/tools/facts.ts
|
|
3835
|
-
import { z as
|
|
3849
|
+
import { z as z5 } from "zod";
|
|
3836
3850
|
|
|
3837
3851
|
// src/types/facts.ts
|
|
3838
3852
|
var FACT_KEY_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
@@ -3845,11 +3859,11 @@ function registerFactTools(server, callApi) {
|
|
|
3845
3859
|
{
|
|
3846
3860
|
title: "List Fact Definitions",
|
|
3847
3861
|
description: "List all fact definitions (input variable schema). Shows key, type, required, and PII status. Always check this before creating rules.",
|
|
3848
|
-
inputSchema: {
|
|
3849
|
-
page:
|
|
3850
|
-
size:
|
|
3851
|
-
keyword:
|
|
3852
|
-
}
|
|
3862
|
+
inputSchema: z5.object({
|
|
3863
|
+
page: z5.number().int().min(0).default(0).describe("Page number"),
|
|
3864
|
+
size: z5.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
3865
|
+
keyword: z5.string().optional().describe("Search keyword")
|
|
3866
|
+
})
|
|
3853
3867
|
},
|
|
3854
3868
|
async ({ page, size, keyword }) => {
|
|
3855
3869
|
const params = paginationParams(page, size);
|
|
@@ -3862,16 +3876,16 @@ function registerFactTools(server, callApi) {
|
|
|
3862
3876
|
{
|
|
3863
3877
|
title: "Create Fact Definition",
|
|
3864
3878
|
description: "Register a new input variable. Key starts with a letter, then letters, numbers, and underscores (e.g. paymentAmount). Casing is not enforced. Types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER.",
|
|
3865
|
-
inputSchema: {
|
|
3866
|
-
key:
|
|
3867
|
-
name:
|
|
3868
|
-
type:
|
|
3869
|
-
description:
|
|
3870
|
-
isRequired:
|
|
3871
|
-
isPii:
|
|
3879
|
+
inputSchema: z5.object({
|
|
3880
|
+
key: z5.string().regex(FACT_KEY_PATTERN).describe("Variable key. Any casing; must start with a letter."),
|
|
3881
|
+
name: z5.string().describe("Display name"),
|
|
3882
|
+
type: z5.enum(VALUE_TYPES).describe("Value type"),
|
|
3883
|
+
description: z5.string().optional().describe("Description"),
|
|
3884
|
+
isRequired: z5.boolean().default(false).describe("Whether this fact is required for rule evaluation"),
|
|
3885
|
+
isPii: z5.boolean().default(false).describe(
|
|
3872
3886
|
"Mark as PII \u2014 masked on every read surface, revealable only in the console (audited)"
|
|
3873
3887
|
)
|
|
3874
|
-
}
|
|
3888
|
+
})
|
|
3875
3889
|
},
|
|
3876
3890
|
async (args) => callApi("POST", "schema/facts", { body: args })
|
|
3877
3891
|
);
|
|
@@ -3880,16 +3894,16 @@ function registerFactTools(server, callApi) {
|
|
|
3880
3894
|
{
|
|
3881
3895
|
title: "Update Fact Definition",
|
|
3882
3896
|
description: "Update a fact definition. The key is immutable. The type can change only while no rule references the fact; if any does, the call fails with FD-007 and reports the count. Only the fields you send are changed. System facts accept name, description, and PII only.",
|
|
3883
|
-
inputSchema: {
|
|
3884
|
-
factId:
|
|
3885
|
-
name:
|
|
3886
|
-
description:
|
|
3887
|
-
type:
|
|
3897
|
+
inputSchema: z5.object({
|
|
3898
|
+
factId: z5.string().uuid().describe("Fact definition ID"),
|
|
3899
|
+
name: z5.string().optional().describe("Display name"),
|
|
3900
|
+
description: z5.string().optional().describe("Description"),
|
|
3901
|
+
type: z5.enum(VALUE_TYPES).optional().describe(
|
|
3888
3902
|
"Value type. Omit to leave it unchanged. Changing it fails with FD-007 while any rule references the fact."
|
|
3889
3903
|
),
|
|
3890
|
-
isRequired:
|
|
3891
|
-
isPii:
|
|
3892
|
-
}
|
|
3904
|
+
isRequired: z5.boolean().optional().describe("Required flag"),
|
|
3905
|
+
isPii: z5.boolean().optional().describe("PII flag \u2014 enables/disables masking (changeable even on system facts)")
|
|
3906
|
+
})
|
|
3893
3907
|
},
|
|
3894
3908
|
async ({ factId, ...body }) => callApi("PUT", `schema/facts/${factId}`, { body })
|
|
3895
3909
|
);
|
|
@@ -3898,9 +3912,9 @@ function registerFactTools(server, callApi) {
|
|
|
3898
3912
|
{
|
|
3899
3913
|
title: "Delete Fact Definition",
|
|
3900
3914
|
description: "Delete a fact definition. System facts cannot be deleted. Neither can a fact that any rule references: that call fails with FD-006 and reports the count. Remove the references first.",
|
|
3901
|
-
inputSchema: {
|
|
3902
|
-
factId:
|
|
3903
|
-
}
|
|
3915
|
+
inputSchema: z5.object({
|
|
3916
|
+
factId: z5.string().uuid().describe("Fact definition ID")
|
|
3917
|
+
})
|
|
3904
3918
|
},
|
|
3905
3919
|
async ({ factId }) => callApi("DELETE", `schema/facts/${factId}`)
|
|
3906
3920
|
);
|
|
@@ -3909,7 +3923,7 @@ function registerFactTools(server, callApi) {
|
|
|
3909
3923
|
{
|
|
3910
3924
|
title: "Get Action Runtime Fact Metadata",
|
|
3911
3925
|
description: "Retrieve runtime fact requirements per Action type. For each Action, shows which input facts must be present in the execution payload \u2014 e.g. MUTATE_FACT always requires its targetVar fact, plus refVar when one is specified. The factRequired flag describes the FACT, not the parameter: refVar is an optional parameter, but if you specify it the named fact must exist. 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; only SET_FACT creates a fact from nothing. Static data, safe to cache in-session.",
|
|
3912
|
-
inputSchema: {}
|
|
3926
|
+
inputSchema: z5.object({})
|
|
3913
3927
|
},
|
|
3914
3928
|
async () => callApi("GET", "schema/action-metadata")
|
|
3915
3929
|
);
|
|
@@ -3918,10 +3932,10 @@ function registerFactTools(server, callApi) {
|
|
|
3918
3932
|
{
|
|
3919
3933
|
title: "List Unregistered Facts",
|
|
3920
3934
|
description: "List facts referenced by a version's rules but not yet defined (read-only \u2014 does not block publish/deploy, INV-4). Version-wide: covers every rule in the version. Each entry carries the inferred type, suggested name, and where it is referenced (condition/action). Register them with lexq_facts_create to enable type validation and the dry-run requirements analyzer.",
|
|
3921
|
-
inputSchema: {
|
|
3922
|
-
groupId:
|
|
3923
|
-
versionId:
|
|
3924
|
-
}
|
|
3935
|
+
inputSchema: z5.object({
|
|
3936
|
+
groupId: z5.string().uuid().describe("Policy group ID"),
|
|
3937
|
+
versionId: z5.string().uuid().describe("Version ID")
|
|
3938
|
+
})
|
|
3925
3939
|
},
|
|
3926
3940
|
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/unregistered-facts`)
|
|
3927
3941
|
);
|
|
@@ -3930,10 +3944,10 @@ function registerFactTools(server, callApi) {
|
|
|
3930
3944
|
{
|
|
3931
3945
|
title: "Export Fact Catalog",
|
|
3932
3946
|
description: "Export the fact catalog. The two formats carry different things: CSV is the catalog as it stands, system facts included, for reading in a spreadsheet; JSON matches the shape that batch create accepts, so it can be fed straight back in, which is why it leaves out the fields that endpoint does not take. Returns the file contents as text.",
|
|
3933
|
-
inputSchema: {
|
|
3934
|
-
format:
|
|
3935
|
-
keyword:
|
|
3936
|
-
}
|
|
3947
|
+
inputSchema: z5.object({
|
|
3948
|
+
format: z5.enum(EXPORT_FORMATS).default("csv").describe("Export format"),
|
|
3949
|
+
keyword: z5.string().optional().describe("Filter by key or name")
|
|
3950
|
+
})
|
|
3937
3951
|
},
|
|
3938
3952
|
async ({ format, keyword }) => callApi("GET", "schema/facts/export", {
|
|
3939
3953
|
params: keyword ? { format, keyword } : { format }
|
|
@@ -3942,18 +3956,18 @@ function registerFactTools(server, callApi) {
|
|
|
3942
3956
|
}
|
|
3943
3957
|
|
|
3944
3958
|
// src/mcp/tools/deploy.ts
|
|
3945
|
-
import { z as
|
|
3959
|
+
import { z as z6 } from "zod";
|
|
3946
3960
|
function registerDeployTools(server, callApi) {
|
|
3947
3961
|
server.registerTool(
|
|
3948
3962
|
"lexq_deploy_publish",
|
|
3949
3963
|
{
|
|
3950
3964
|
title: "Publish Version",
|
|
3951
3965
|
description: "Publish a DRAFT version (DRAFT \u2192 ACTIVE). Locks the version from further edits. Must have at least one rule. Undefined facts referenced by rules do not block publishing (INV-4); call lexq_facts_unregistered first to review them.",
|
|
3952
|
-
inputSchema: {
|
|
3953
|
-
groupId:
|
|
3954
|
-
versionId:
|
|
3955
|
-
memo:
|
|
3956
|
-
}
|
|
3966
|
+
inputSchema: z6.object({
|
|
3967
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
3968
|
+
versionId: z6.string().uuid().describe("Version ID to publish"),
|
|
3969
|
+
memo: z6.string().min(1).describe("Publish memo (required)")
|
|
3970
|
+
})
|
|
3957
3971
|
},
|
|
3958
3972
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/versions/${versionId}/publish`, { body: { memo } })
|
|
3959
3973
|
);
|
|
@@ -3962,11 +3976,11 @@ function registerDeployTools(server, callApi) {
|
|
|
3962
3976
|
{
|
|
3963
3977
|
title: "Deploy to Live",
|
|
3964
3978
|
description: "Deploy an ACTIVE (published) version to live traffic. Takes effect immediately. Versions whose effective start date has not arrived are rejected (P-037) \u2014 use lexq_deploy_schedule for those. Undefined facts do not block deployment (INV-4); use lexq_facts_unregistered to review what the version references but has not defined.",
|
|
3965
|
-
inputSchema: {
|
|
3966
|
-
groupId:
|
|
3967
|
-
versionId:
|
|
3968
|
-
memo:
|
|
3969
|
-
}
|
|
3979
|
+
inputSchema: z6.object({
|
|
3980
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
3981
|
+
versionId: z6.string().uuid().describe("Version ID to deploy"),
|
|
3982
|
+
memo: z6.string().min(1).describe("Deployment memo (required)")
|
|
3983
|
+
})
|
|
3970
3984
|
},
|
|
3971
3985
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/deploy`, {
|
|
3972
3986
|
body: { versionId, memo }
|
|
@@ -3977,10 +3991,10 @@ function registerDeployTools(server, callApi) {
|
|
|
3977
3991
|
{
|
|
3978
3992
|
title: "Rollback Deployment",
|
|
3979
3993
|
description: "Rollback to the previous deployed version. Only available if there is a previous version.",
|
|
3980
|
-
inputSchema: {
|
|
3981
|
-
groupId:
|
|
3982
|
-
memo:
|
|
3983
|
-
}
|
|
3994
|
+
inputSchema: z6.object({
|
|
3995
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
3996
|
+
memo: z6.string().min(1).describe("Rollback reason (required)")
|
|
3997
|
+
})
|
|
3984
3998
|
},
|
|
3985
3999
|
async ({ groupId, memo }) => callApi("POST", `policy-groups/${groupId}/rollback`, {
|
|
3986
4000
|
body: { memo }
|
|
@@ -3991,10 +4005,10 @@ function registerDeployTools(server, callApi) {
|
|
|
3991
4005
|
{
|
|
3992
4006
|
title: "Undeploy",
|
|
3993
4007
|
description: "Remove the live version from traffic. The version stays ACTIVE but no longer serves requests.",
|
|
3994
|
-
inputSchema: {
|
|
3995
|
-
groupId:
|
|
3996
|
-
memo:
|
|
3997
|
-
}
|
|
4008
|
+
inputSchema: z6.object({
|
|
4009
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
4010
|
+
memo: z6.string().min(1).describe("Undeploy reason (required)")
|
|
4011
|
+
})
|
|
3998
4012
|
},
|
|
3999
4013
|
async ({ groupId, memo }) => callApi("POST", `policy-groups/${groupId}/undeploy`, {
|
|
4000
4014
|
body: { memo }
|
|
@@ -4005,11 +4019,11 @@ function registerDeployTools(server, callApi) {
|
|
|
4005
4019
|
{
|
|
4006
4020
|
title: "Schedule Deployment",
|
|
4007
4021
|
description: "Schedule an ACTIVE version with a future effective start date to auto-deploy at that time (Scheduled Deployment). One pending schedule per group; manual deploy/rollback/undeploy, starting an A/B test, or archiving the group cancels it. The snapshot hash is sealed at scheduling and re-verified at execution (fail-closed).",
|
|
4008
|
-
inputSchema: {
|
|
4009
|
-
groupId:
|
|
4010
|
-
versionId:
|
|
4011
|
-
memo:
|
|
4012
|
-
}
|
|
4022
|
+
inputSchema: z6.object({
|
|
4023
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
4024
|
+
versionId: z6.string().uuid().describe("ACTIVE version ID with a future effective start date"),
|
|
4025
|
+
memo: z6.string().min(1).describe("Schedule memo (required)")
|
|
4026
|
+
})
|
|
4013
4027
|
},
|
|
4014
4028
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/schedule`, {
|
|
4015
4029
|
body: { versionId, memo }
|
|
@@ -4020,9 +4034,9 @@ function registerDeployTools(server, callApi) {
|
|
|
4020
4034
|
{
|
|
4021
4035
|
title: "Cancel Scheduled Deployment",
|
|
4022
4036
|
description: "Cancel the pending scheduled deployment for a group. The version itself is not affected. Fails with P-039 if no pending schedule exists.",
|
|
4023
|
-
inputSchema: {
|
|
4024
|
-
groupId:
|
|
4025
|
-
}
|
|
4037
|
+
inputSchema: z6.object({
|
|
4038
|
+
groupId: z6.string().uuid().describe("Policy group ID")
|
|
4039
|
+
})
|
|
4026
4040
|
},
|
|
4027
4041
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}/schedule`)
|
|
4028
4042
|
);
|
|
@@ -4031,10 +4045,10 @@ function registerDeployTools(server, callApi) {
|
|
|
4031
4045
|
{
|
|
4032
4046
|
title: "List Scheduled Deployments",
|
|
4033
4047
|
description: "List scheduled deployments across all groups (all statuses: PENDING, EXECUTED, CANCELED, FAILED), newest first.",
|
|
4034
|
-
inputSchema: {
|
|
4035
|
-
page:
|
|
4036
|
-
size:
|
|
4037
|
-
}
|
|
4048
|
+
inputSchema: z6.object({
|
|
4049
|
+
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
4050
|
+
size: z6.number().int().min(1).max(100).default(20).describe("Page size")
|
|
4051
|
+
})
|
|
4038
4052
|
},
|
|
4039
4053
|
async ({ page, size }) => callApi("GET", "policy-groups/schedules", { params: paginationParams(page, size) })
|
|
4040
4054
|
);
|
|
@@ -4043,14 +4057,14 @@ function registerDeployTools(server, callApi) {
|
|
|
4043
4057
|
{
|
|
4044
4058
|
title: "Deployment History",
|
|
4045
4059
|
description: "List deployment history across all groups.",
|
|
4046
|
-
inputSchema: {
|
|
4047
|
-
page:
|
|
4048
|
-
size:
|
|
4049
|
-
groupId:
|
|
4050
|
-
types:
|
|
4051
|
-
startDate:
|
|
4052
|
-
endDate:
|
|
4053
|
-
}
|
|
4060
|
+
inputSchema: z6.object({
|
|
4061
|
+
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
4062
|
+
size: z6.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
4063
|
+
groupId: z6.string().uuid().optional().describe("Filter by group ID"),
|
|
4064
|
+
types: z6.string().optional().describe("Filter by deployment types (comma-separated: DEPLOY,ROLLBACK,UNDEPLOY)"),
|
|
4065
|
+
startDate: z6.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
4066
|
+
endDate: z6.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4067
|
+
})
|
|
4054
4068
|
},
|
|
4055
4069
|
async ({ page, size, groupId, types, startDate, endDate }) => {
|
|
4056
4070
|
const params = paginationParams(page, size);
|
|
@@ -4066,9 +4080,9 @@ function registerDeployTools(server, callApi) {
|
|
|
4066
4080
|
{
|
|
4067
4081
|
title: "Deployment Detail",
|
|
4068
4082
|
description: "Get detailed info about a specific deployment including snapshot hash and integrity check.",
|
|
4069
|
-
inputSchema: {
|
|
4070
|
-
deploymentId:
|
|
4071
|
-
}
|
|
4083
|
+
inputSchema: z6.object({
|
|
4084
|
+
deploymentId: z6.string().uuid().describe("Deployment ID")
|
|
4085
|
+
})
|
|
4072
4086
|
},
|
|
4073
4087
|
async ({ deploymentId }) => callApi("GET", `deployments/${deploymentId}`)
|
|
4074
4088
|
);
|
|
@@ -4077,7 +4091,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4077
4091
|
{
|
|
4078
4092
|
title: "Deployment Overview",
|
|
4079
4093
|
description: "Show current deployment status of all groups \u2014 which version is live, last deployment type, and deployer.",
|
|
4080
|
-
inputSchema: {}
|
|
4094
|
+
inputSchema: z6.object({})
|
|
4081
4095
|
},
|
|
4082
4096
|
async () => callApi("GET", "deployments/overview")
|
|
4083
4097
|
);
|
|
@@ -4086,9 +4100,9 @@ function registerDeployTools(server, callApi) {
|
|
|
4086
4100
|
{
|
|
4087
4101
|
title: "List Deployable Versions",
|
|
4088
4102
|
description: "List ACTIVE (published) versions that can be deployed for a group. Use this to find which versions are available before calling deploy live.",
|
|
4089
|
-
inputSchema: {
|
|
4090
|
-
groupId:
|
|
4091
|
-
}
|
|
4103
|
+
inputSchema: z6.object({
|
|
4104
|
+
groupId: z6.string().uuid().describe("Policy group ID")
|
|
4105
|
+
})
|
|
4092
4106
|
},
|
|
4093
4107
|
async ({ groupId }) => callApi("GET", `deployments/groups/${groupId}/deployable-versions`)
|
|
4094
4108
|
);
|
|
@@ -4097,10 +4111,10 @@ function registerDeployTools(server, callApi) {
|
|
|
4097
4111
|
{
|
|
4098
4112
|
title: "Deployment Diff",
|
|
4099
4113
|
description: "Compare rule snapshots between two versions. Shows added, removed, and modified rules. Useful for reviewing changes before deploying a new version.",
|
|
4100
|
-
inputSchema: {
|
|
4101
|
-
baseVersionId:
|
|
4102
|
-
targetVersionId:
|
|
4103
|
-
}
|
|
4114
|
+
inputSchema: z6.object({
|
|
4115
|
+
baseVersionId: z6.string().uuid().describe("Base version ID (typically the current live)"),
|
|
4116
|
+
targetVersionId: z6.string().uuid().describe("Target version ID (the one you want to deploy)")
|
|
4117
|
+
})
|
|
4104
4118
|
},
|
|
4105
4119
|
async ({ baseVersionId, targetVersionId }) => callApi("GET", "deployments/diff", {
|
|
4106
4120
|
params: { baseVersionId, targetVersionId }
|
|
@@ -4109,7 +4123,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4109
4123
|
}
|
|
4110
4124
|
|
|
4111
4125
|
// src/mcp/tools/analytics.ts
|
|
4112
|
-
import { z as
|
|
4126
|
+
import { z as z7 } from "zod";
|
|
4113
4127
|
import dedent16 from "dedent";
|
|
4114
4128
|
function registerAnalyticsTools(server, callApi) {
|
|
4115
4129
|
server.registerTool(
|
|
@@ -4128,11 +4142,11 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4128
4142
|
|
|
4129
4143
|
Example input: { "facts": { "paymentAmount": 100000, "customerTier": "VIP" } }
|
|
4130
4144
|
Always dry-run before publishing to validate rule behavior.`,
|
|
4131
|
-
inputSchema: {
|
|
4132
|
-
versionId:
|
|
4133
|
-
facts:
|
|
4134
|
-
includeDebugInfo:
|
|
4135
|
-
}
|
|
4145
|
+
inputSchema: z7.object({
|
|
4146
|
+
versionId: z7.string().uuid().describe("Policy version ID to test against"),
|
|
4147
|
+
facts: z7.string().describe('JSON string of facts object, e.g. {"paymentAmount":100000}'),
|
|
4148
|
+
includeDebugInfo: z7.boolean().default(true).describe("Include execution and decision traces")
|
|
4149
|
+
})
|
|
4136
4150
|
},
|
|
4137
4151
|
async ({ versionId, facts, includeDebugInfo }) => {
|
|
4138
4152
|
const parsedFacts = parseJson(facts);
|
|
@@ -4152,11 +4166,11 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4152
4166
|
resultA / resultB — full DryRunResponse for each version
|
|
4153
4167
|
diff.mutatedDiff — changes in mutatedFacts between A and B (key → {before, after})
|
|
4154
4168
|
diff.generatedDiff — changes in generatedVariables between A and B`,
|
|
4155
|
-
inputSchema: {
|
|
4156
|
-
versionIdA:
|
|
4157
|
-
versionIdB:
|
|
4158
|
-
facts:
|
|
4159
|
-
}
|
|
4169
|
+
inputSchema: z7.object({
|
|
4170
|
+
versionIdA: z7.string().uuid().describe("Baseline version ID"),
|
|
4171
|
+
versionIdB: z7.string().uuid().describe("Candidate version ID"),
|
|
4172
|
+
facts: z7.string().describe("JSON string of facts object")
|
|
4173
|
+
})
|
|
4160
4174
|
},
|
|
4161
4175
|
async ({ versionIdA, versionIdB, facts }) => {
|
|
4162
4176
|
const parsedFacts = parseJson(facts);
|
|
@@ -4170,10 +4184,10 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4170
4184
|
{
|
|
4171
4185
|
title: "Analyze Requirements",
|
|
4172
4186
|
description: "Analyze which input facts a version requires. Returns required keys, types, and an example request body.",
|
|
4173
|
-
inputSchema: {
|
|
4174
|
-
groupId:
|
|
4175
|
-
versionId:
|
|
4176
|
-
}
|
|
4187
|
+
inputSchema: z7.object({
|
|
4188
|
+
groupId: z7.string().uuid().describe("Policy group ID"),
|
|
4189
|
+
versionId: z7.string().uuid().describe("Version ID")
|
|
4190
|
+
})
|
|
4177
4191
|
},
|
|
4178
4192
|
async ({ groupId, versionId }) => callApi("GET", `analytics/groups/${groupId}/versions/${versionId}/requirements`)
|
|
4179
4193
|
);
|
|
@@ -4209,9 +4223,9 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4209
4223
|
"options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true }
|
|
4210
4224
|
}
|
|
4211
4225
|
`,
|
|
4212
|
-
inputSchema: {
|
|
4213
|
-
body:
|
|
4214
|
-
}
|
|
4226
|
+
inputSchema: z7.object({
|
|
4227
|
+
body: z7.string().describe("JSON string of SimulationRequest")
|
|
4228
|
+
})
|
|
4215
4229
|
},
|
|
4216
4230
|
async ({ body }) => {
|
|
4217
4231
|
const parsed = parseJson(body);
|
|
@@ -4223,9 +4237,9 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4223
4237
|
{
|
|
4224
4238
|
title: "Simulation Status",
|
|
4225
4239
|
description: "Get simulation status and results. Poll until status is COMPLETED or FAILED.",
|
|
4226
|
-
inputSchema: {
|
|
4227
|
-
simulationId:
|
|
4228
|
-
}
|
|
4240
|
+
inputSchema: z7.object({
|
|
4241
|
+
simulationId: z7.string().uuid().describe("Simulation ID")
|
|
4242
|
+
})
|
|
4229
4243
|
},
|
|
4230
4244
|
async ({ simulationId }) => callApi("GET", `analytics/simulations/${simulationId}`)
|
|
4231
4245
|
);
|
|
@@ -4234,13 +4248,13 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4234
4248
|
{
|
|
4235
4249
|
title: "List Simulations",
|
|
4236
4250
|
description: "List simulation history with optional filters.",
|
|
4237
|
-
inputSchema: {
|
|
4238
|
-
page:
|
|
4239
|
-
size:
|
|
4240
|
-
status:
|
|
4241
|
-
from:
|
|
4242
|
-
to:
|
|
4243
|
-
}
|
|
4251
|
+
inputSchema: z7.object({
|
|
4252
|
+
page: z7.number().int().min(0).default(0).describe("Page number"),
|
|
4253
|
+
size: z7.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
4254
|
+
status: z7.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELED"]).optional().describe("Filter by status"),
|
|
4255
|
+
from: z7.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
4256
|
+
to: z7.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4257
|
+
})
|
|
4244
4258
|
},
|
|
4245
4259
|
async ({ page, size, status, from, to }) => {
|
|
4246
4260
|
const params = paginationParams(page, size);
|
|
@@ -4255,9 +4269,9 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4255
4269
|
{
|
|
4256
4270
|
title: "Cancel Simulation",
|
|
4257
4271
|
description: "Cancel a running or pending simulation.",
|
|
4258
|
-
inputSchema: {
|
|
4259
|
-
simulationId:
|
|
4260
|
-
}
|
|
4272
|
+
inputSchema: z7.object({
|
|
4273
|
+
simulationId: z7.string().uuid().describe("Simulation ID")
|
|
4274
|
+
})
|
|
4261
4275
|
},
|
|
4262
4276
|
async ({ simulationId }) => callApi("DELETE", `analytics/simulations/${simulationId}`)
|
|
4263
4277
|
);
|
|
@@ -4266,10 +4280,10 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4266
4280
|
{
|
|
4267
4281
|
title: "Export Simulation",
|
|
4268
4282
|
description: "Export simulation results as JSON or CSV. Returns the raw data.",
|
|
4269
|
-
inputSchema: {
|
|
4270
|
-
simulationId:
|
|
4271
|
-
format:
|
|
4272
|
-
}
|
|
4283
|
+
inputSchema: z7.object({
|
|
4284
|
+
simulationId: z7.string().uuid().describe("Simulation ID"),
|
|
4285
|
+
format: z7.enum(EXPORT_FORMATS).default("json").describe("Export format")
|
|
4286
|
+
})
|
|
4273
4287
|
},
|
|
4274
4288
|
async ({ simulationId, format }) => callApi("GET", `analytics/simulations/${simulationId}/export`, {
|
|
4275
4289
|
params: { format }
|
|
@@ -4293,10 +4307,10 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4293
4307
|
|
|
4294
4308
|
JSON example: [{"userId":"user_001","paymentAmount":150000}, {"userId":"user_002","paymentAmount":50000}]
|
|
4295
4309
|
`,
|
|
4296
|
-
inputSchema: {
|
|
4297
|
-
content:
|
|
4298
|
-
filename:
|
|
4299
|
-
}
|
|
4310
|
+
inputSchema: z7.object({
|
|
4311
|
+
content: z7.string().describe("CSV or JSON content as string"),
|
|
4312
|
+
filename: z7.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
|
|
4313
|
+
})
|
|
4300
4314
|
},
|
|
4301
4315
|
async ({ content, filename }) => {
|
|
4302
4316
|
const result = await callApi("POST", "analytics/datasets/upload", {
|
|
@@ -4323,11 +4337,11 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4323
4337
|
{
|
|
4324
4338
|
title: "Download Dataset Template",
|
|
4325
4339
|
description: "Generate a sample CSV or JSON template based on the required facts of a version. Use this to understand the expected data format before uploading a dataset.",
|
|
4326
|
-
inputSchema: {
|
|
4327
|
-
groupId:
|
|
4328
|
-
versionId:
|
|
4329
|
-
format:
|
|
4330
|
-
}
|
|
4340
|
+
inputSchema: z7.object({
|
|
4341
|
+
groupId: z7.string().uuid().describe("Policy group ID"),
|
|
4342
|
+
versionId: z7.string().uuid().describe("Version ID"),
|
|
4343
|
+
format: z7.enum(EXPORT_FORMATS).default("csv").describe("Template format")
|
|
4344
|
+
})
|
|
4331
4345
|
},
|
|
4332
4346
|
async ({ groupId, versionId, format }) => callApi("GET", `analytics/groups/${groupId}/versions/${versionId}/dataset-template`, {
|
|
4333
4347
|
params: { format }
|
|
@@ -4336,7 +4350,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4336
4350
|
}
|
|
4337
4351
|
|
|
4338
4352
|
// src/mcp/tools/profile.ts
|
|
4339
|
-
import { z as
|
|
4353
|
+
import { z as z8 } from "zod";
|
|
4340
4354
|
var RELATIVE_THRESHOLD = `flagged = p50 \u2265 ${SLOW_MULTIPLIER}\xD7 median of per-rule p50s within the group; absolute thresholds are intentionally not supported.`;
|
|
4341
4355
|
function profileParams(opts) {
|
|
4342
4356
|
const params = {};
|
|
@@ -4352,15 +4366,15 @@ function registerProfileTools(server, callApi) {
|
|
|
4352
4366
|
{
|
|
4353
4367
|
title: "Group Latency Profile",
|
|
4354
4368
|
description: "Per-rule latency profile of a policy group over a time window: group TOTAL distribution split by cache state (HIT = compiled ruleset cache hit, MISS = deep-load + compile), a per-rule CONDITION/ACTION percentile table, and slow-rule flags. " + RELATIVE_THRESHOLD + ` Every percentile is accompanied by its sample count n; a percentile is withheld (null) unless n\xD7(1\u2212q) \u2265 ${TAIL_MIN_OBS} (p50 needs n \u2265 6, p95 n \u2265 60, p99 n \u2265 300 \u2014 display gate, separate from the n \u2265 ${MIN_SAMPLES} judgment gate). Baselines report INSUFFICIENT_COHORT when fewer than ${MIN_COHORT_SIZE} rules qualify. Rule detail comes from a deterministic ${PROFILE_SAMPLE_PERMILLE / 10}% sample of calls; TOTAL is recorded for every call. Defaults: last ${PROFILE_DEFAULT_WINDOW_HOURS}h, live version, cacheState HIT.`,
|
|
4355
|
-
inputSchema: {
|
|
4356
|
-
groupId:
|
|
4357
|
-
versionId:
|
|
4358
|
-
from:
|
|
4369
|
+
inputSchema: z8.object({
|
|
4370
|
+
groupId: z8.string().uuid().describe("Policy group ID"),
|
|
4371
|
+
versionId: z8.string().uuid().optional().describe("Version to inspect (default: live version)"),
|
|
4372
|
+
from: z8.string().optional().describe(
|
|
4359
4373
|
`Window start, ISO-8601 instant (e.g. 2026-07-01T00:00:00Z). Default: ${PROFILE_DEFAULT_WINDOW_HOURS}h ago`
|
|
4360
4374
|
),
|
|
4361
|
-
to:
|
|
4362
|
-
cacheState:
|
|
4363
|
-
}
|
|
4375
|
+
to: z8.string().optional().describe("Window end, ISO-8601 instant. Default: now"),
|
|
4376
|
+
cacheState: z8.enum(["HIT", "MISS"]).optional().describe("Cache dimension for the rule table and judgment (default: HIT)")
|
|
4377
|
+
})
|
|
4364
4378
|
},
|
|
4365
4379
|
async ({ groupId, versionId, from, to, cacheState }) => callApi("GET", `policy-groups/${groupId}/profile`, {
|
|
4366
4380
|
params: profileParams({ versionId, from, to, cacheState })
|
|
@@ -4371,15 +4385,15 @@ function registerProfileTools(server, callApi) {
|
|
|
4371
4385
|
{
|
|
4372
4386
|
title: "Rule Latency Detail",
|
|
4373
4387
|
description: `Single-rule latency detail: merged phase \xD7 cacheState distributions plus a per-window time series (${LATENCY_WINDOW_MILLIS / 1e3}s windows). Missing windows are genuine gaps \u2014 never interpolated. Series points carry each window's own values; percentiles in merged distributions are withheld (null) unless n\xD7(1\u2212q) \u2265 ${TAIL_MIN_OBS} (p50 n \u2265 6, p95 n \u2265 60, p99 n \u2265 300). ` + RELATIVE_THRESHOLD,
|
|
4374
|
-
inputSchema: {
|
|
4375
|
-
groupId:
|
|
4376
|
-
ruleId:
|
|
4377
|
-
versionId:
|
|
4378
|
-
from:
|
|
4388
|
+
inputSchema: z8.object({
|
|
4389
|
+
groupId: z8.string().uuid().describe("Policy group ID"),
|
|
4390
|
+
ruleId: z8.string().uuid().describe("Rule ID (from lexq_profile_overview)"),
|
|
4391
|
+
versionId: z8.string().uuid().optional().describe("Version to inspect (default: live version)"),
|
|
4392
|
+
from: z8.string().optional().describe(
|
|
4379
4393
|
`Window start, ISO-8601 instant. Default: ${PROFILE_DEFAULT_WINDOW_HOURS}h ago`
|
|
4380
4394
|
),
|
|
4381
|
-
to:
|
|
4382
|
-
}
|
|
4395
|
+
to: z8.string().optional().describe("Window end, ISO-8601 instant. Default: now")
|
|
4396
|
+
})
|
|
4383
4397
|
},
|
|
4384
4398
|
async ({ groupId, ruleId, versionId, from, to }) => callApi("GET", `policy-groups/${groupId}/profile/rules/${ruleId}`, {
|
|
4385
4399
|
params: profileParams({ versionId, from, to })
|
|
@@ -4388,17 +4402,17 @@ function registerProfileTools(server, callApi) {
|
|
|
4388
4402
|
}
|
|
4389
4403
|
|
|
4390
4404
|
// src/mcp/tools/replay.ts
|
|
4391
|
-
import { z as
|
|
4405
|
+
import { z as z9 } from "zod";
|
|
4392
4406
|
function registerReplayTools(server, callApi) {
|
|
4393
4407
|
server.registerTool(
|
|
4394
4408
|
"lexq_replay_decision",
|
|
4395
4409
|
{
|
|
4396
4410
|
title: "Replay a Decision",
|
|
4397
4411
|
description: "Re-evaluate a past execution (traceId) against a candidate version and return the decision diff (decisionChanged, effect changes, fired rules) plus a determinism verdict. Synchronous and free of charge (TPS throttle only). A replay sends no webhook, notification, or event: rule actions produce no outward effects.",
|
|
4398
|
-
inputSchema: {
|
|
4399
|
-
traceId:
|
|
4400
|
-
candidateVersionId:
|
|
4401
|
-
}
|
|
4412
|
+
inputSchema: z9.object({
|
|
4413
|
+
traceId: z9.string().describe("Trace ID of the past execution to replay"),
|
|
4414
|
+
candidateVersionId: z9.string().uuid().describe("Version to re-evaluate against")
|
|
4415
|
+
})
|
|
4402
4416
|
},
|
|
4403
4417
|
async ({ traceId, candidateVersionId }) => callApi("POST", "replay/decisions", { body: { traceId, candidateVersionId } })
|
|
4404
4418
|
);
|
|
@@ -4407,14 +4421,14 @@ function registerReplayTools(server, callApi) {
|
|
|
4407
4421
|
{
|
|
4408
4422
|
title: "Start Window Replay (Blast Radius)",
|
|
4409
4423
|
description: "Submit an async job that replays a date window of past executions against a candidate version and measures the blast radius (how many decisions change). Billed per replayed record (REPLAY metric); VIEWER role cannot submit. Poll with lexq_replay_status.",
|
|
4410
|
-
inputSchema: {
|
|
4411
|
-
candidateVersionId:
|
|
4412
|
-
from:
|
|
4413
|
-
to:
|
|
4414
|
-
maxRecords:
|
|
4424
|
+
inputSchema: z9.object({
|
|
4425
|
+
candidateVersionId: z9.string().uuid().describe("Version to re-evaluate against"),
|
|
4426
|
+
from: z9.string().describe("Window start date (yyyy-MM-dd)"),
|
|
4427
|
+
to: z9.string().describe("Window end date (yyyy-MM-dd)"),
|
|
4428
|
+
maxRecords: z9.number().int().min(1).optional().describe(
|
|
4415
4429
|
`Sample cap (server default applies; hard cap ${REPLAY_WINDOW_MAX_RECORDS / 1e3}k)`
|
|
4416
4430
|
)
|
|
4417
|
-
}
|
|
4431
|
+
})
|
|
4418
4432
|
},
|
|
4419
4433
|
async ({ candidateVersionId, from, to, maxRecords }) => callApi("POST", "replay/jobs", { body: { candidateVersionId, from, to, maxRecords } })
|
|
4420
4434
|
);
|
|
@@ -4423,9 +4437,9 @@ function registerReplayTools(server, callApi) {
|
|
|
4423
4437
|
{
|
|
4424
4438
|
title: "Get Replay Job Status",
|
|
4425
4439
|
description: "Poll a window replay job. RUNNING shows progress 0\u2013100; COMPLETED fills summary and changedSamples; FAILED carries errorMessage. capped=true means the window exceeded the sample cap and only part was replayed.",
|
|
4426
|
-
inputSchema: {
|
|
4427
|
-
jobId:
|
|
4428
|
-
}
|
|
4440
|
+
inputSchema: z9.object({
|
|
4441
|
+
jobId: z9.string().describe("Replay job ID from lexq_replay_start")
|
|
4442
|
+
})
|
|
4429
4443
|
},
|
|
4430
4444
|
async ({ jobId }) => callApi("GET", `replay/jobs/${jobId}`)
|
|
4431
4445
|
);
|
|
@@ -4434,10 +4448,10 @@ function registerReplayTools(server, callApi) {
|
|
|
4434
4448
|
{
|
|
4435
4449
|
title: "List Replay Jobs",
|
|
4436
4450
|
description: "List window replay job history (reverse-chronological). Lightweight items \u2014 use lexq_replay_status for summary and changed samples.",
|
|
4437
|
-
inputSchema: {
|
|
4438
|
-
page:
|
|
4439
|
-
size:
|
|
4440
|
-
}
|
|
4451
|
+
inputSchema: z9.object({
|
|
4452
|
+
page: z9.number().int().min(0).default(0).describe("Page number"),
|
|
4453
|
+
size: z9.number().int().min(1).max(100).default(20).describe("Page size")
|
|
4454
|
+
})
|
|
4441
4455
|
},
|
|
4442
4456
|
async ({ page, size }) => callApi("GET", "replay/jobs", { params: paginationParams(page, size) })
|
|
4443
4457
|
);
|
|
@@ -4446,9 +4460,9 @@ function registerReplayTools(server, callApi) {
|
|
|
4446
4460
|
{
|
|
4447
4461
|
title: "Cancel Replay Job",
|
|
4448
4462
|
description: "Cooperatively cancel a PENDING or RUNNING window replay job. Other states are rejected. VIEWER role cannot cancel.",
|
|
4449
|
-
inputSchema: {
|
|
4450
|
-
jobId:
|
|
4451
|
-
}
|
|
4463
|
+
inputSchema: z9.object({
|
|
4464
|
+
jobId: z9.string().describe("Replay job ID")
|
|
4465
|
+
})
|
|
4452
4466
|
},
|
|
4453
4467
|
async ({ jobId }) => callApi("POST", `replay/jobs/${jobId}/cancel`)
|
|
4454
4468
|
);
|
|
@@ -4457,33 +4471,33 @@ function registerReplayTools(server, callApi) {
|
|
|
4457
4471
|
{
|
|
4458
4472
|
title: "Export Replay Result",
|
|
4459
4473
|
description: "Export a COMPLETED window replay job. A running job is rejected \u2014 a partial result reads as the whole thing on the receiving end. The two formats carry different things: CSV holds the effect blast radius, whose columns are fixed; JSON holds the changed samples and action parameters, which nest and whose keys differ per tenant. JSON is not a superset of CSV. Returns the file contents as text.",
|
|
4460
|
-
inputSchema: {
|
|
4461
|
-
jobId:
|
|
4462
|
-
format:
|
|
4463
|
-
}
|
|
4474
|
+
inputSchema: z9.object({
|
|
4475
|
+
jobId: z9.string().describe("Replay job ID"),
|
|
4476
|
+
format: z9.enum(EXPORT_FORMATS).default("json").describe("Export format")
|
|
4477
|
+
})
|
|
4464
4478
|
},
|
|
4465
4479
|
async ({ jobId, format }) => callApi("GET", `replay/jobs/${jobId}/export`, { params: { format } })
|
|
4466
4480
|
);
|
|
4467
4481
|
}
|
|
4468
4482
|
|
|
4469
4483
|
// src/mcp/tools/history.ts
|
|
4470
|
-
import { z as
|
|
4484
|
+
import { z as z10 } from "zod";
|
|
4471
4485
|
function registerHistoryTools(server, callApi) {
|
|
4472
4486
|
server.registerTool(
|
|
4473
4487
|
"lexq_history_list",
|
|
4474
4488
|
{
|
|
4475
4489
|
title: "List Execution History",
|
|
4476
4490
|
description: "List policy execution history. Shows trace ID, group, version, status, match result, and latency.",
|
|
4477
|
-
inputSchema: {
|
|
4478
|
-
page:
|
|
4479
|
-
size:
|
|
4480
|
-
traceId:
|
|
4481
|
-
groupId:
|
|
4482
|
-
versionId:
|
|
4483
|
-
status:
|
|
4484
|
-
startDate:
|
|
4485
|
-
endDate:
|
|
4486
|
-
}
|
|
4491
|
+
inputSchema: z10.object({
|
|
4492
|
+
page: z10.number().int().min(0).default(0).describe("Page number"),
|
|
4493
|
+
size: z10.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
4494
|
+
traceId: z10.string().optional().describe("Filter by trace ID"),
|
|
4495
|
+
groupId: z10.string().uuid().optional().describe("Filter by policy group"),
|
|
4496
|
+
versionId: z10.string().uuid().optional().describe("Filter by version"),
|
|
4497
|
+
status: z10.enum(["SUCCESS", "NO_MATCH", "ERROR", "TIMEOUT"]).optional().describe("Filter by execution status"),
|
|
4498
|
+
startDate: z10.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
4499
|
+
endDate: z10.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4500
|
+
})
|
|
4487
4501
|
},
|
|
4488
4502
|
async ({ page, size, traceId, groupId, versionId, status, startDate, endDate }) => {
|
|
4489
4503
|
const params = paginationParams(page, size);
|
|
@@ -4501,9 +4515,9 @@ function registerHistoryTools(server, callApi) {
|
|
|
4501
4515
|
{
|
|
4502
4516
|
title: "Get Execution Detail",
|
|
4503
4517
|
description: "Get full execution detail including inputFacts, mutatedFacts, generatedVariables, executionTraces, and decisionTraces.",
|
|
4504
|
-
inputSchema: {
|
|
4505
|
-
traceId:
|
|
4506
|
-
}
|
|
4518
|
+
inputSchema: z10.object({
|
|
4519
|
+
traceId: z10.string().describe("Trace ID from execution history")
|
|
4520
|
+
})
|
|
4507
4521
|
},
|
|
4508
4522
|
async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
|
|
4509
4523
|
);
|
|
@@ -4512,11 +4526,11 @@ function registerHistoryTools(server, callApi) {
|
|
|
4512
4526
|
{
|
|
4513
4527
|
title: "Execution Statistics",
|
|
4514
4528
|
description: "Get execution KPIs: total executions, success/failure counts, success rate, and average latency.",
|
|
4515
|
-
inputSchema: {
|
|
4516
|
-
groupId:
|
|
4517
|
-
startDate:
|
|
4518
|
-
endDate:
|
|
4519
|
-
}
|
|
4529
|
+
inputSchema: z10.object({
|
|
4530
|
+
groupId: z10.string().uuid().optional().describe("Filter by policy group"),
|
|
4531
|
+
startDate: z10.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
4532
|
+
endDate: z10.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4533
|
+
})
|
|
4520
4534
|
},
|
|
4521
4535
|
async ({ groupId, startDate, endDate }) => {
|
|
4522
4536
|
const params = {};
|
|
@@ -4529,16 +4543,16 @@ function registerHistoryTools(server, callApi) {
|
|
|
4529
4543
|
}
|
|
4530
4544
|
|
|
4531
4545
|
// src/mcp/tools/provenance.ts
|
|
4532
|
-
import { z as
|
|
4546
|
+
import { z as z11 } from "zod";
|
|
4533
4547
|
function registerProvenanceTools(server, callApi) {
|
|
4534
4548
|
server.registerTool(
|
|
4535
4549
|
"lexq_provenance_get",
|
|
4536
4550
|
{
|
|
4537
4551
|
title: "Get Decision Provenance",
|
|
4538
4552
|
description: "Get the lineage of a single decision: what was decided, deterministic why per rule, input facts (PII facts are masked as \u2022\u2022\u2022\u2022\u2022\u2022 with maskedKeys listing them \u2014 values are revealable only in the console, audited), the authored/published/deployed responsibility chain, and the rule snapshot fingerprint.",
|
|
4539
|
-
inputSchema: {
|
|
4540
|
-
traceId:
|
|
4541
|
-
}
|
|
4553
|
+
inputSchema: z11.object({
|
|
4554
|
+
traceId: z11.string().describe("Trace ID of the execution")
|
|
4555
|
+
})
|
|
4542
4556
|
},
|
|
4543
4557
|
async ({ traceId }) => callApi("GET", `provenance/${traceId}`)
|
|
4544
4558
|
);
|
|
@@ -4547,15 +4561,15 @@ function registerProvenanceTools(server, callApi) {
|
|
|
4547
4561
|
{
|
|
4548
4562
|
title: "List PII Reveal Audits",
|
|
4549
4563
|
description: "List the PII reveal audit ledger \u2014 who revealed which fact of which trace, and when. Metadata only; revealed values are never stored or returned. Use for monthly access-log inspection and SIEM collection.",
|
|
4550
|
-
inputSchema: {
|
|
4551
|
-
page:
|
|
4552
|
-
size:
|
|
4553
|
-
traceId:
|
|
4554
|
-
revealedBy:
|
|
4555
|
-
factKey:
|
|
4556
|
-
startDate:
|
|
4557
|
-
endDate:
|
|
4558
|
-
}
|
|
4564
|
+
inputSchema: z11.object({
|
|
4565
|
+
page: z11.number().int().min(0).default(0).describe("Page number"),
|
|
4566
|
+
size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
4567
|
+
traceId: z11.string().optional().describe("Filter by trace ID (exact match)"),
|
|
4568
|
+
revealedBy: z11.string().optional().describe("Filter by operator ID (exact match)"),
|
|
4569
|
+
factKey: z11.string().optional().describe("Filter by fact key (partial match, case-insensitive)"),
|
|
4570
|
+
startDate: z11.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
4571
|
+
endDate: z11.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4572
|
+
})
|
|
4559
4573
|
},
|
|
4560
4574
|
async ({ page, size, traceId, revealedBy, factKey, startDate, endDate }) => {
|
|
4561
4575
|
const params = paginationParams(page, size);
|
|
@@ -4570,22 +4584,22 @@ function registerProvenanceTools(server, callApi) {
|
|
|
4570
4584
|
}
|
|
4571
4585
|
|
|
4572
4586
|
// src/mcp/tools/logs.ts
|
|
4573
|
-
import { z as
|
|
4587
|
+
import { z as z12 } from "zod";
|
|
4574
4588
|
function registerLogTools(server, callApi) {
|
|
4575
4589
|
server.registerTool(
|
|
4576
4590
|
"lexq_logs_list",
|
|
4577
4591
|
{
|
|
4578
4592
|
title: "List Failure Logs",
|
|
4579
4593
|
description: "List system failure logs from background tasks (platform event webhooks, scheduled deployments).",
|
|
4580
|
-
inputSchema: {
|
|
4581
|
-
page:
|
|
4582
|
-
size:
|
|
4583
|
-
taskType:
|
|
4584
|
-
status:
|
|
4585
|
-
keyword:
|
|
4586
|
-
startDate:
|
|
4587
|
-
endDate:
|
|
4588
|
-
}
|
|
4594
|
+
inputSchema: z12.object({
|
|
4595
|
+
page: z12.number().int().min(0).default(0).describe("Page number"),
|
|
4596
|
+
size: z12.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
4597
|
+
taskType: z12.enum(TaskType).optional().describe("Task type"),
|
|
4598
|
+
status: z12.enum(FailureStatus).optional().describe("Log status"),
|
|
4599
|
+
keyword: z12.string().optional().describe("Search in refId, refSubId, errorMessage"),
|
|
4600
|
+
startDate: z12.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
4601
|
+
endDate: z12.string().optional().describe("End date (yyyy-MM-dd)")
|
|
4602
|
+
})
|
|
4589
4603
|
},
|
|
4590
4604
|
async ({ page, size, taskType, status, keyword, startDate, endDate }) => {
|
|
4591
4605
|
const params = paginationParams(page, size);
|
|
@@ -4602,9 +4616,9 @@ function registerLogTools(server, callApi) {
|
|
|
4602
4616
|
{
|
|
4603
4617
|
title: "Get Failure Log",
|
|
4604
4618
|
description: "Get failure log detail by ID.",
|
|
4605
|
-
inputSchema: {
|
|
4606
|
-
logId:
|
|
4607
|
-
}
|
|
4619
|
+
inputSchema: z12.object({
|
|
4620
|
+
logId: z12.string().uuid().describe("Failure log ID")
|
|
4621
|
+
})
|
|
4608
4622
|
},
|
|
4609
4623
|
async ({ logId }) => callApi("GET", `failure-logs/${logId}`)
|
|
4610
4624
|
);
|
|
@@ -4613,10 +4627,10 @@ function registerLogTools(server, callApi) {
|
|
|
4613
4627
|
{
|
|
4614
4628
|
title: "Process Failure Log",
|
|
4615
4629
|
description: "Process a single failure log: RESOLVE (mark as manually fixed) or IGNORE (skip intentionally).",
|
|
4616
|
-
inputSchema: {
|
|
4617
|
-
logId:
|
|
4618
|
-
action:
|
|
4619
|
-
}
|
|
4630
|
+
inputSchema: z12.object({
|
|
4631
|
+
logId: z12.string().uuid().describe("Failure log ID"),
|
|
4632
|
+
action: z12.enum(FailureAction).describe("Action to take")
|
|
4633
|
+
})
|
|
4620
4634
|
},
|
|
4621
4635
|
async ({ logId, action }) => callApi("POST", `failure-logs/${logId}/actions`, {
|
|
4622
4636
|
params: { action }
|
|
@@ -4627,10 +4641,10 @@ function registerLogTools(server, callApi) {
|
|
|
4627
4641
|
{
|
|
4628
4642
|
title: "Bulk Process Failure Logs",
|
|
4629
4643
|
description: "Process multiple failure logs at once. Provide an array of log IDs and the action.",
|
|
4630
|
-
inputSchema: {
|
|
4631
|
-
logIds:
|
|
4632
|
-
action:
|
|
4633
|
-
}
|
|
4644
|
+
inputSchema: z12.object({
|
|
4645
|
+
logIds: z12.array(z12.string().uuid()).describe("Array of failure log IDs"),
|
|
4646
|
+
action: z12.enum(FailureAction).describe("Action to apply to all logs")
|
|
4647
|
+
})
|
|
4634
4648
|
},
|
|
4635
4649
|
async ({ logIds, action }) => callApi("POST", "failure-logs/bulk-actions", {
|
|
4636
4650
|
body: { logIds, action }
|
|
@@ -4639,17 +4653,17 @@ function registerLogTools(server, callApi) {
|
|
|
4639
4653
|
}
|
|
4640
4654
|
|
|
4641
4655
|
// src/mcp/tools/webhook-subscriptions.ts
|
|
4642
|
-
import { z as
|
|
4656
|
+
import { z as z13 } from "zod";
|
|
4643
4657
|
function registerWebhookSubscriptionTools(server, callApi) {
|
|
4644
4658
|
server.registerTool(
|
|
4645
4659
|
"lexq_webhook_subscriptions_list",
|
|
4646
4660
|
{
|
|
4647
4661
|
title: "List Webhook Subscriptions",
|
|
4648
4662
|
description: "List platform event webhook subscriptions. These receive deployment lifecycle notifications (publish, deploy, rollback, undeploy).",
|
|
4649
|
-
inputSchema: {
|
|
4650
|
-
page:
|
|
4651
|
-
size:
|
|
4652
|
-
}
|
|
4663
|
+
inputSchema: z13.object({
|
|
4664
|
+
page: z13.number().int().min(0).default(0).describe("Page number"),
|
|
4665
|
+
size: z13.number().int().min(1).max(100).default(20).describe("Page size")
|
|
4666
|
+
})
|
|
4653
4667
|
},
|
|
4654
4668
|
async ({ page, size }) => {
|
|
4655
4669
|
const params = paginationParams(page, size);
|
|
@@ -4661,9 +4675,9 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4661
4675
|
{
|
|
4662
4676
|
title: "Get Webhook Subscription",
|
|
4663
4677
|
description: "Get webhook subscription detail by ID.",
|
|
4664
|
-
inputSchema: {
|
|
4665
|
-
id:
|
|
4666
|
-
}
|
|
4678
|
+
inputSchema: z13.object({
|
|
4679
|
+
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
4680
|
+
})
|
|
4667
4681
|
},
|
|
4668
4682
|
async ({ id }) => callApi("GET", `webhook-subscriptions/${id}`)
|
|
4669
4683
|
);
|
|
@@ -4672,15 +4686,15 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4672
4686
|
{
|
|
4673
4687
|
title: "Save Webhook Subscription",
|
|
4674
4688
|
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": "..."}).',
|
|
4675
|
-
inputSchema: {
|
|
4676
|
-
id:
|
|
4677
|
-
name:
|
|
4678
|
-
webhookUrl:
|
|
4679
|
-
subscribedEvents:
|
|
4680
|
-
payloadFormat:
|
|
4681
|
-
secret:
|
|
4682
|
-
isActive:
|
|
4683
|
-
}
|
|
4689
|
+
inputSchema: z13.object({
|
|
4690
|
+
id: z13.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
|
|
4691
|
+
name: z13.string().min(1).describe("Subscription name (unique per tenant)"),
|
|
4692
|
+
webhookUrl: z13.string().url().describe("Webhook endpoint URL"),
|
|
4693
|
+
subscribedEvents: z13.array(z13.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
|
|
4694
|
+
payloadFormat: z13.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
|
|
4695
|
+
secret: z13.string().optional().describe("HMAC-SHA256 signing secret"),
|
|
4696
|
+
isActive: z13.boolean().optional().default(true).describe("Whether the subscription is active")
|
|
4697
|
+
})
|
|
4684
4698
|
},
|
|
4685
4699
|
async ({ ...body }) => callApi("POST", "webhook-subscriptions", { body })
|
|
4686
4700
|
);
|
|
@@ -4689,9 +4703,9 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4689
4703
|
{
|
|
4690
4704
|
title: "Delete Webhook Subscription",
|
|
4691
4705
|
description: "Delete a webhook subscription by ID.",
|
|
4692
|
-
inputSchema: {
|
|
4693
|
-
id:
|
|
4694
|
-
}
|
|
4706
|
+
inputSchema: z13.object({
|
|
4707
|
+
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
4708
|
+
})
|
|
4695
4709
|
},
|
|
4696
4710
|
async ({ id }) => callApi("DELETE", `webhook-subscriptions/${id}`)
|
|
4697
4711
|
);
|
|
@@ -4700,23 +4714,23 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4700
4714
|
{
|
|
4701
4715
|
title: "Test Webhook Subscription",
|
|
4702
4716
|
description: "Send a test event to verify webhook connectivity. Returns the HTTP status code and success/failure message.",
|
|
4703
|
-
inputSchema: {
|
|
4704
|
-
id:
|
|
4705
|
-
}
|
|
4717
|
+
inputSchema: z13.object({
|
|
4718
|
+
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
4719
|
+
})
|
|
4706
4720
|
},
|
|
4707
4721
|
async ({ id }) => callApi("POST", `webhook-subscriptions/${id}/test`)
|
|
4708
4722
|
);
|
|
4709
4723
|
}
|
|
4710
4724
|
|
|
4711
4725
|
// src/mcp/tools/domain-templates.ts
|
|
4712
|
-
import { z as
|
|
4726
|
+
import { z as z14 } from "zod";
|
|
4713
4727
|
function registerDomainTemplateTools(server, callApi) {
|
|
4714
4728
|
server.registerTool(
|
|
4715
4729
|
"lexq_domain_templates_list",
|
|
4716
4730
|
{
|
|
4717
4731
|
title: "List Domain Templates",
|
|
4718
4732
|
description: "List all domain templates. A domain template is a curated, industry-specific starter pack of fact definitions and sample rules (e.g. ECOMMERCE). Each entry reports its key, status (ACTIVE or COMING_SOON), and a summary of what it provisions. Call this before preview or apply to discover which templates can currently be applied.",
|
|
4719
|
-
inputSchema: {}
|
|
4733
|
+
inputSchema: z14.object({})
|
|
4720
4734
|
},
|
|
4721
4735
|
async () => callApi("GET", "domain-templates")
|
|
4722
4736
|
);
|
|
@@ -4725,11 +4739,11 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
4725
4739
|
{
|
|
4726
4740
|
title: "Preview Domain Template",
|
|
4727
4741
|
description: "Preview exactly what a domain template will provision before applying it: the fact definitions it registers, the sample rules it creates, and an apply plan. This is a read-only dry run \u2014 nothing is created. Only ACTIVE templates can be previewed.",
|
|
4728
|
-
inputSchema: {
|
|
4729
|
-
template:
|
|
4742
|
+
inputSchema: z14.object({
|
|
4743
|
+
template: z14.string().describe(
|
|
4730
4744
|
"Domain template key (e.g. ECOMMERCE). Use lexq_domain_templates_list to see available keys \u2014 currently only ECOMMERCE is ACTIVE."
|
|
4731
4745
|
)
|
|
4732
|
-
}
|
|
4746
|
+
})
|
|
4733
4747
|
},
|
|
4734
4748
|
async ({ template }) => callApi("GET", `domain-templates/${template}/preview`)
|
|
4735
4749
|
);
|
|
@@ -4738,12 +4752,12 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
4738
4752
|
{
|
|
4739
4753
|
title: "Apply Domain Template",
|
|
4740
4754
|
description: "Apply a domain template to the current tenant. Creates the template's fact definitions and a new policy group pre-populated with its sample rules as a DRAFT version. Existing facts are skipped \u2014 apply is additive and never overwrites existing schema. Run lexq_domain_templates_preview first to review what will be created. Only ACTIVE templates can be applied.",
|
|
4741
|
-
inputSchema: {
|
|
4742
|
-
template:
|
|
4743
|
-
customName:
|
|
4755
|
+
inputSchema: z14.object({
|
|
4756
|
+
template: z14.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
|
|
4757
|
+
customName: z14.string().optional().describe(
|
|
4744
4758
|
"Optional custom name for the policy group that gets created. If omitted, the template's default name is used."
|
|
4745
4759
|
)
|
|
4746
|
-
}
|
|
4760
|
+
})
|
|
4747
4761
|
},
|
|
4748
4762
|
async ({ template, customName }) => {
|
|
4749
4763
|
const body = {};
|
|
@@ -4781,15 +4795,24 @@ function getVersion() {
|
|
|
4781
4795
|
return "0.1.0";
|
|
4782
4796
|
}
|
|
4783
4797
|
}
|
|
4784
|
-
|
|
4785
|
-
const
|
|
4786
|
-
name: "lexq",
|
|
4787
|
-
version: getVersion()
|
|
4788
|
-
});
|
|
4798
|
+
function startMcpServer() {
|
|
4799
|
+
const version = getVersion();
|
|
4789
4800
|
const callApi = createCallApiFromConfig();
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4801
|
+
serveStdio(
|
|
4802
|
+
() => {
|
|
4803
|
+
const server = new McpServer({ name: "lexq", version });
|
|
4804
|
+
registerAllTools(server, callApi);
|
|
4805
|
+
return server;
|
|
4806
|
+
},
|
|
4807
|
+
{
|
|
4808
|
+
/* stdout carries the protocol. Anything written there that is not a JSON-RPC message
|
|
4809
|
+
breaks the client's parser, so this goes to stderr — the only channel a stdio server
|
|
4810
|
+
can report on. Without it a transport that fails to start does so silently. */
|
|
4811
|
+
onerror: (error) => {
|
|
4812
|
+
console.error("[lexq mcp]", error.message);
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
);
|
|
4793
4816
|
}
|
|
4794
4817
|
|
|
4795
4818
|
// src/commands/serve.ts
|
|
@@ -4814,14 +4837,14 @@ function registerServeCommand(program) {
|
|
|
4814
4837
|
}
|
|
4815
4838
|
}
|
|
4816
4839
|
`
|
|
4817
|
-
).action(
|
|
4840
|
+
).action((opts) => {
|
|
4818
4841
|
if (!opts.mcp) {
|
|
4819
4842
|
console.error(
|
|
4820
4843
|
"Error: --mcp flag is required.\nUsage: lexq serve --mcp\n\nStarts a stdio MCP server for Claude Desktop, Claude.ai, Gemini, etc."
|
|
4821
4844
|
);
|
|
4822
4845
|
process.exit(1);
|
|
4823
4846
|
}
|
|
4824
|
-
|
|
4847
|
+
startMcpServer();
|
|
4825
4848
|
});
|
|
4826
4849
|
}
|
|
4827
4850
|
|