@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/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), "utf-8");
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 { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3321
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
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: z.string().uuid().describe("Policy group ID")
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: z.string().describe("Group name (unique among non-ARCHIVED)"),
3447
- description: z.string().optional().describe("Group description"),
3448
- activationMode: z.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
3449
- activationStrategy: z.enum(ConflictResolutionStrategy).optional().describe("Ranking used to pick the winning groups when mode is EXCLUSIVE or MAX_N"),
3450
- executionLimit: z.number().int().min(1).optional().describe(
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: z.string().optional().describe(
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: z.string().uuid().describe("Policy group ID"),
3477
- name: z.string().optional().describe("New name"),
3478
- description: z.string().optional().describe("New description"),
3479
- status: z.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
3480
- activationGroup: z.string().optional().describe(
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: z.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
3484
- activationStrategy: z.enum(ConflictResolutionStrategy).optional().describe("Ranking used to pick the winning groups"),
3485
- executionLimit: z.number().int().min(1).optional().describe(
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: z.string().uuid().describe("Policy group ID")
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: z.array(z.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
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: z.string().uuid().describe("Policy group ID"),
3527
- testVersionId: z.string().uuid().describe("Challenger version ID to test"),
3528
- trafficRate: z.number().int().min(1).max(99).describe("Traffic percentage routed to challenger (1-99)")
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: z.string().uuid().describe("Policy group ID")
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: z.string().uuid().describe("Policy group ID"),
3551
- trafficRate: z.number().int().min(1).max(99).describe("New traffic percentage for challenger (1-99)")
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 z2 } from "zod";
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: z2.string().uuid().describe("Policy group ID"),
3568
- page: z2.number().int().min(0).default(0).describe("Page number"),
3569
- size: z2.number().int().min(1).max(100).default(20).describe("Page 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: z2.string().uuid().describe("Policy group ID"),
3583
- versionId: z2.string().uuid().describe("Version ID")
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: z2.string().uuid().describe("Policy group ID"),
3595
- commitMessage: z2.string().optional().describe("Commit message describing this version"),
3596
- effectiveFrom: z2.string().optional().describe("Effective start date (ISO 8601)"),
3597
- effectiveTo: z2.string().optional().describe("Effective end date (ISO 8601)")
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: z2.string().uuid().describe("Policy group ID"),
3609
- versionId: z2.string().uuid().describe("Version ID"),
3610
- commitMessage: z2.string().optional().describe("New commit message"),
3611
- effectiveFrom: z2.string().optional().describe("New effective start date"),
3612
- effectiveTo: z2.string().optional().describe("New effective end date")
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: z2.string().uuid().describe("Policy group ID"),
3624
- versionId: z2.string().uuid().describe("Version ID")
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: z2.string().uuid().describe("Policy group ID"),
3636
- versionId: z2.string().uuid().describe("Source version ID to clone")
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 z3 } from "zod";
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: z3.string().uuid().describe("Policy group ID"),
3658
- versionId: z3.string().uuid().describe("Version ID")
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: z3.string().uuid().describe("Policy group ID"),
3670
- versionId: z3.string().uuid().describe("Version ID"),
3671
- ruleId: z3.string().uuid().describe("Rule ID")
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: z3.string().uuid().describe("Policy group ID"),
3743
- versionId: z3.string().uuid().describe("Version ID"),
3744
- rule: z3.string().describe(
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: z3.string().uuid().describe("Policy group ID"),
3761
- versionId: z3.string().uuid().describe("Version ID"),
3762
- ruleId: z3.string().uuid().describe("Rule ID"),
3763
- rule: z3.string().describe(
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: z3.string().uuid().describe("Policy group ID"),
3782
- versionId: z3.string().uuid().describe("Version ID"),
3783
- ruleId: z3.string().uuid().describe("Rule ID"),
3784
- force: z3.boolean().default(false).describe("Skip confirmation (for the last rule in a version)")
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: z3.string().uuid().describe("Policy group ID"),
3802
- versionId: z3.string().uuid().describe("Version ID"),
3803
- ruleIds: z3.array(z3.string().uuid()).describe("Rule IDs in desired priority order")
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: z3.string().uuid().describe("Policy group ID"),
3823
- versionId: z3.string().uuid().describe("Version ID"),
3824
- ruleId: z3.string().uuid().describe("Rule ID"),
3825
- isEnabled: z3.boolean().describe("true to enable, false to disable")
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 z4 } from "zod";
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: z4.number().int().min(0).default(0).describe("Page number"),
3850
- size: z4.number().int().min(1).max(100).default(20).describe("Page size"),
3851
- keyword: z4.string().optional().describe("Search 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: z4.string().regex(FACT_KEY_PATTERN).describe("Variable key. Any casing; must start with a letter."),
3867
- name: z4.string().describe("Display name"),
3868
- type: z4.enum(VALUE_TYPES).describe("Value type"),
3869
- description: z4.string().optional().describe("Description"),
3870
- isRequired: z4.boolean().default(false).describe("Whether this fact is required for rule evaluation"),
3871
- isPii: z4.boolean().default(false).describe(
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: z4.string().uuid().describe("Fact definition ID"),
3885
- name: z4.string().optional().describe("Display name"),
3886
- description: z4.string().optional().describe("Description"),
3887
- type: z4.enum(VALUE_TYPES).optional().describe(
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: z4.boolean().optional().describe("Required flag"),
3891
- isPii: z4.boolean().optional().describe("PII flag \u2014 enables/disables masking (changeable even on system facts)")
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: z4.string().uuid().describe("Fact definition ID")
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: z4.string().uuid().describe("Policy group ID"),
3923
- versionId: z4.string().uuid().describe("Version ID")
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: z4.enum(EXPORT_FORMATS).default("csv").describe("Export format"),
3935
- keyword: z4.string().optional().describe("Filter by key or name")
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 z5 } from "zod";
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: z5.string().uuid().describe("Policy group ID"),
3954
- versionId: z5.string().uuid().describe("Version ID to publish"),
3955
- memo: z5.string().min(1).describe("Publish memo (required)")
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: z5.string().uuid().describe("Policy group ID"),
3967
- versionId: z5.string().uuid().describe("Version ID to deploy"),
3968
- memo: z5.string().min(1).describe("Deployment memo (required)")
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: z5.string().uuid().describe("Policy group ID"),
3982
- memo: z5.string().min(1).describe("Rollback reason (required)")
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: z5.string().uuid().describe("Policy group ID"),
3996
- memo: z5.string().min(1).describe("Undeploy reason (required)")
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: z5.string().uuid().describe("Policy group ID"),
4010
- versionId: z5.string().uuid().describe("ACTIVE version ID with a future effective start date"),
4011
- memo: z5.string().min(1).describe("Schedule memo (required)")
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: z5.string().uuid().describe("Policy group ID")
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: z5.number().int().min(0).default(0).describe("Page number"),
4036
- size: z5.number().int().min(1).max(100).default(20).describe("Page 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: z5.number().int().min(0).default(0).describe("Page number"),
4048
- size: z5.number().int().min(1).max(100).default(20).describe("Page size"),
4049
- groupId: z5.string().uuid().optional().describe("Filter by group ID"),
4050
- types: z5.string().optional().describe("Filter by deployment types (comma-separated: DEPLOY,ROLLBACK,UNDEPLOY)"),
4051
- startDate: z5.string().optional().describe("Start date (yyyy-MM-dd)"),
4052
- endDate: z5.string().optional().describe("End date (yyyy-MM-dd)")
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: z5.string().uuid().describe("Deployment ID")
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: z5.string().uuid().describe("Policy group ID")
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: z5.string().uuid().describe("Base version ID (typically the current live)"),
4102
- targetVersionId: z5.string().uuid().describe("Target version ID (the one you want to deploy)")
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 z6 } from "zod";
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: z6.string().uuid().describe("Policy version ID to test against"),
4133
- facts: z6.string().describe('JSON string of facts object, e.g. {"paymentAmount":100000}'),
4134
- includeDebugInfo: z6.boolean().default(true).describe("Include execution and decision traces")
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: z6.string().uuid().describe("Baseline version ID"),
4157
- versionIdB: z6.string().uuid().describe("Candidate version ID"),
4158
- facts: z6.string().describe("JSON string of facts object")
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: z6.string().uuid().describe("Policy group ID"),
4175
- versionId: z6.string().uuid().describe("Version ID")
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: z6.string().describe("JSON string of SimulationRequest")
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: z6.string().uuid().describe("Simulation ID")
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: z6.number().int().min(0).default(0).describe("Page number"),
4239
- size: z6.number().int().min(1).max(100).default(20).describe("Page size"),
4240
- status: z6.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELED"]).optional().describe("Filter by status"),
4241
- from: z6.string().optional().describe("Start date (yyyy-MM-dd)"),
4242
- to: z6.string().optional().describe("End date (yyyy-MM-dd)")
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: z6.string().uuid().describe("Simulation ID")
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: z6.string().uuid().describe("Simulation ID"),
4271
- format: z6.enum(EXPORT_FORMATS).default("json").describe("Export 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: z6.string().describe("CSV or JSON content as string"),
4298
- filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
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: z6.string().uuid().describe("Policy group ID"),
4328
- versionId: z6.string().uuid().describe("Version ID"),
4329
- format: z6.enum(EXPORT_FORMATS).default("csv").describe("Template 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 z7 } from "zod";
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: z7.string().uuid().describe("Policy group ID"),
4357
- versionId: z7.string().uuid().optional().describe("Version to inspect (default: live version)"),
4358
- from: z7.string().optional().describe(
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: z7.string().optional().describe("Window end, ISO-8601 instant. Default: now"),
4362
- cacheState: z7.enum(["HIT", "MISS"]).optional().describe("Cache dimension for the rule table and judgment (default: HIT)")
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: z7.string().uuid().describe("Policy group ID"),
4376
- ruleId: z7.string().uuid().describe("Rule ID (from lexq_profile_overview)"),
4377
- versionId: z7.string().uuid().optional().describe("Version to inspect (default: live version)"),
4378
- from: z7.string().optional().describe(
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: z7.string().optional().describe("Window end, ISO-8601 instant. Default: now")
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 z8 } from "zod";
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: z8.string().describe("Trace ID of the past execution to replay"),
4400
- candidateVersionId: z8.string().uuid().describe("Version to re-evaluate against")
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: z8.string().uuid().describe("Version to re-evaluate against"),
4412
- from: z8.string().describe("Window start date (yyyy-MM-dd)"),
4413
- to: z8.string().describe("Window end date (yyyy-MM-dd)"),
4414
- maxRecords: z8.number().int().min(1).optional().describe(
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: z8.string().describe("Replay job ID from lexq_replay_start")
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: z8.number().int().min(0).default(0).describe("Page number"),
4439
- size: z8.number().int().min(1).max(100).default(20).describe("Page 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: z8.string().describe("Replay job ID")
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: z8.string().describe("Replay job ID"),
4462
- format: z8.enum(EXPORT_FORMATS).default("json").describe("Export 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 z9 } from "zod";
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: z9.number().int().min(0).default(0).describe("Page number"),
4479
- size: z9.number().int().min(1).max(100).default(20).describe("Page size"),
4480
- traceId: z9.string().optional().describe("Filter by trace ID"),
4481
- groupId: z9.string().uuid().optional().describe("Filter by policy group"),
4482
- versionId: z9.string().uuid().optional().describe("Filter by version"),
4483
- status: z9.enum(["SUCCESS", "NO_MATCH", "ERROR", "TIMEOUT"]).optional().describe("Filter by execution status"),
4484
- startDate: z9.string().optional().describe("Start date (yyyy-MM-dd)"),
4485
- endDate: z9.string().optional().describe("End date (yyyy-MM-dd)")
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: z9.string().describe("Trace ID from execution history")
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: z9.string().uuid().optional().describe("Filter by policy group"),
4517
- startDate: z9.string().optional().describe("Start date (yyyy-MM-dd)"),
4518
- endDate: z9.string().optional().describe("End date (yyyy-MM-dd)")
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 z10 } from "zod";
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: z10.string().describe("Trace ID of the execution")
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: z10.number().int().min(0).default(0).describe("Page number"),
4552
- size: z10.number().int().min(1).max(100).default(20).describe("Page size"),
4553
- traceId: z10.string().optional().describe("Filter by trace ID (exact match)"),
4554
- revealedBy: z10.string().optional().describe("Filter by operator ID (exact match)"),
4555
- factKey: z10.string().optional().describe("Filter by fact key (partial match, case-insensitive)"),
4556
- startDate: z10.string().optional().describe("Start date (yyyy-MM-dd)"),
4557
- endDate: z10.string().optional().describe("End date (yyyy-MM-dd)")
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 z11 } from "zod";
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: z11.number().int().min(0).default(0).describe("Page number"),
4582
- size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
4583
- taskType: z11.enum(TaskType).optional().describe("Task type"),
4584
- status: z11.enum(FailureStatus).optional().describe("Log status"),
4585
- keyword: z11.string().optional().describe("Search in refId, refSubId, errorMessage"),
4586
- startDate: z11.string().optional().describe("Start date (yyyy-MM-dd)"),
4587
- endDate: z11.string().optional().describe("End date (yyyy-MM-dd)")
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: z11.string().uuid().describe("Failure log ID")
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: z11.string().uuid().describe("Failure log ID"),
4618
- action: z11.enum(FailureAction).describe("Action to take")
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: z11.array(z11.string().uuid()).describe("Array of failure log IDs"),
4632
- action: z11.enum(FailureAction).describe("Action to apply to all logs")
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 z12 } from "zod";
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: z12.number().int().min(0).default(0).describe("Page number"),
4651
- size: z12.number().int().min(1).max(100).default(20).describe("Page 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: z12.string().uuid().describe("Webhook subscription 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: z12.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
4677
- name: z12.string().min(1).describe("Subscription name (unique per tenant)"),
4678
- webhookUrl: z12.string().url().describe("Webhook endpoint URL"),
4679
- subscribedEvents: z12.array(z12.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
4680
- payloadFormat: z12.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
4681
- secret: z12.string().optional().describe("HMAC-SHA256 signing secret"),
4682
- isActive: z12.boolean().optional().default(true).describe("Whether the subscription is active")
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: z12.string().uuid().describe("Webhook subscription 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: z12.string().uuid().describe("Webhook subscription 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 z13 } from "zod";
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: z13.string().describe(
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: z13.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
4743
- customName: z13.string().optional().describe(
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
- async function startMcpServer() {
4785
- const server = new McpServer({
4786
- name: "lexq",
4787
- version: getVersion()
4788
- });
4798
+ function startMcpServer() {
4799
+ const version = getVersion();
4789
4800
  const callApi = createCallApiFromConfig();
4790
- registerAllTools(server, callApi);
4791
- const transport = new StdioServerTransport();
4792
- await server.connect(transport);
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(async (opts) => {
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
- await startMcpServer();
4847
+ startMcpServer();
4825
4848
  });
4826
4849
  }
4827
4850