@lexq/cli 0.1.58 → 0.2.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 +376 -367
- package/dist/mcp/register.d.ts +1 -1
- package/dist/mcp/register.js +374 -365
- package/package.json +7 -4
package/dist/mcp/register.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
// src/mcp/tools/status.ts
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
function registerStatusTools(server, callApi) {
|
|
3
4
|
server.registerTool(
|
|
4
5
|
"lexq_whoami",
|
|
5
6
|
{
|
|
6
7
|
title: "Who Am I",
|
|
7
8
|
description: "Show current authentication info (tenant ID, user ID, role).",
|
|
8
|
-
inputSchema: {}
|
|
9
|
+
inputSchema: z.object({})
|
|
9
10
|
},
|
|
10
11
|
async () => callApi("GET", "whoami")
|
|
11
12
|
);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
// src/mcp/tools/groups.ts
|
|
15
|
-
import { z } from "zod";
|
|
16
|
+
import { z as z2 } from "zod";
|
|
16
17
|
|
|
17
18
|
// src/types/enums.ts
|
|
18
19
|
var ValueType = ["STRING", "NUMBER", "BOOLEAN", "LIST_STRING", "LIST_NUMBER"];
|
|
@@ -39,7 +40,7 @@ function registerGroupTools(server, callApi) {
|
|
|
39
40
|
{
|
|
40
41
|
title: "List Policy Groups",
|
|
41
42
|
description: "List all policy groups (tenant-wide, priority ASC).",
|
|
42
|
-
inputSchema: {}
|
|
43
|
+
inputSchema: z2.object({})
|
|
43
44
|
},
|
|
44
45
|
async () => callApi("GET", "policy-groups")
|
|
45
46
|
);
|
|
@@ -48,9 +49,9 @@ function registerGroupTools(server, callApi) {
|
|
|
48
49
|
{
|
|
49
50
|
title: "Get Policy Group",
|
|
50
51
|
description: "Get a single policy group by ID.",
|
|
51
|
-
inputSchema: {
|
|
52
|
-
groupId:
|
|
53
|
-
}
|
|
52
|
+
inputSchema: z2.object({
|
|
53
|
+
groupId: z2.string().uuid().describe("Policy group ID")
|
|
54
|
+
})
|
|
54
55
|
},
|
|
55
56
|
async ({ groupId }) => callApi("GET", `policy-groups/${groupId}`)
|
|
56
57
|
);
|
|
@@ -58,15 +59,19 @@ function registerGroupTools(server, callApi) {
|
|
|
58
59
|
"lexq_groups_create",
|
|
59
60
|
{
|
|
60
61
|
title: "Create Policy Group",
|
|
61
|
-
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.
|
|
62
|
-
inputSchema: {
|
|
63
|
-
name:
|
|
64
|
-
description:
|
|
65
|
-
activationMode:
|
|
66
|
-
activationStrategy:
|
|
67
|
-
executionLimit:
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
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.",
|
|
63
|
+
inputSchema: z2.object({
|
|
64
|
+
name: z2.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
65
|
+
description: z2.string().optional().describe("Group description"),
|
|
66
|
+
activationMode: z2.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
|
|
67
|
+
activationStrategy: z2.enum(ConflictResolutionStrategy).optional().describe("Ranking used to pick the winning groups when mode is EXCLUSIVE or MAX_N"),
|
|
68
|
+
executionLimit: z2.number().int().min(1).optional().describe(
|
|
69
|
+
"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."
|
|
70
|
+
),
|
|
71
|
+
activationGroup: z2.string().optional().describe(
|
|
72
|
+
"Activation group (Execution Group) cluster key. Policy groups sharing this key compete, and group priority picks the winners."
|
|
73
|
+
)
|
|
74
|
+
})
|
|
70
75
|
},
|
|
71
76
|
async (args) => {
|
|
72
77
|
const body = {
|
|
@@ -85,16 +90,20 @@ function registerGroupTools(server, callApi) {
|
|
|
85
90
|
{
|
|
86
91
|
title: "Update Policy Group",
|
|
87
92
|
description: "Update a policy group. Only provided fields are updated; omitted fields remain unchanged.",
|
|
88
|
-
inputSchema: {
|
|
89
|
-
groupId:
|
|
90
|
-
name:
|
|
91
|
-
description:
|
|
92
|
-
status:
|
|
93
|
-
activationGroup:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
inputSchema: z2.object({
|
|
94
|
+
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
95
|
+
name: z2.string().optional().describe("New name"),
|
|
96
|
+
description: z2.string().optional().describe("New description"),
|
|
97
|
+
status: z2.enum(["ACTIVE", "DISABLED"]).optional().describe("Status (DISABLED = emergency stop)"),
|
|
98
|
+
activationGroup: z2.string().optional().describe(
|
|
99
|
+
"Activation group (Execution Group) cluster key. Policy groups sharing this key compete, and group priority picks the winners."
|
|
100
|
+
),
|
|
101
|
+
activationMode: z2.enum(ConflictResolutionMode).optional().describe("Conflict resolution mode"),
|
|
102
|
+
activationStrategy: z2.enum(ConflictResolutionStrategy).optional().describe("Ranking used to pick the winning groups"),
|
|
103
|
+
executionLimit: z2.number().int().min(1).optional().describe(
|
|
104
|
+
"How many policy groups sharing this activationGroup may run. Counts groups, not rules."
|
|
105
|
+
)
|
|
106
|
+
})
|
|
98
107
|
},
|
|
99
108
|
async ({ groupId, ...body }) => callApi("PUT", `policy-groups/${groupId}`, { body })
|
|
100
109
|
);
|
|
@@ -103,9 +112,9 @@ function registerGroupTools(server, callApi) {
|
|
|
103
112
|
{
|
|
104
113
|
title: "Delete Policy Group",
|
|
105
114
|
description: "Archive a policy group. Only non-live groups can be deleted. This is irreversible.",
|
|
106
|
-
inputSchema: {
|
|
107
|
-
groupId:
|
|
108
|
-
}
|
|
115
|
+
inputSchema: z2.object({
|
|
116
|
+
groupId: z2.string().uuid().describe("Policy group ID")
|
|
117
|
+
})
|
|
109
118
|
},
|
|
110
119
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}`)
|
|
111
120
|
);
|
|
@@ -114,9 +123,9 @@ function registerGroupTools(server, callApi) {
|
|
|
114
123
|
{
|
|
115
124
|
title: "Reorder Policy Groups",
|
|
116
125
|
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.",
|
|
117
|
-
inputSchema: {
|
|
118
|
-
groupIds:
|
|
119
|
-
}
|
|
126
|
+
inputSchema: z2.object({
|
|
127
|
+
groupIds: z2.array(z2.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
|
|
128
|
+
})
|
|
120
129
|
},
|
|
121
130
|
async ({ groupIds }) => {
|
|
122
131
|
const groups = groupIds.map((groupId, index) => ({
|
|
@@ -131,11 +140,11 @@ function registerGroupTools(server, callApi) {
|
|
|
131
140
|
{
|
|
132
141
|
title: "Start A/B Test",
|
|
133
142
|
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%.",
|
|
134
|
-
inputSchema: {
|
|
135
|
-
groupId:
|
|
136
|
-
testVersionId:
|
|
137
|
-
trafficRate:
|
|
138
|
-
}
|
|
143
|
+
inputSchema: z2.object({
|
|
144
|
+
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
145
|
+
testVersionId: z2.string().uuid().describe("Challenger version ID to test"),
|
|
146
|
+
trafficRate: z2.number().int().min(1).max(99).describe("Traffic percentage routed to challenger (1-99)")
|
|
147
|
+
})
|
|
139
148
|
},
|
|
140
149
|
async ({ groupId, ...body }) => callApi("POST", `policy-groups/${groupId}/ab-test`, { body })
|
|
141
150
|
);
|
|
@@ -144,9 +153,9 @@ function registerGroupTools(server, callApi) {
|
|
|
144
153
|
{
|
|
145
154
|
title: "Stop A/B Test",
|
|
146
155
|
description: "Stop a running A/B test. All traffic is restored to the control (current) version.",
|
|
147
|
-
inputSchema: {
|
|
148
|
-
groupId:
|
|
149
|
-
}
|
|
156
|
+
inputSchema: z2.object({
|
|
157
|
+
groupId: z2.string().uuid().describe("Policy group ID")
|
|
158
|
+
})
|
|
150
159
|
},
|
|
151
160
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}/ab-test`)
|
|
152
161
|
);
|
|
@@ -155,17 +164,17 @@ function registerGroupTools(server, callApi) {
|
|
|
155
164
|
{
|
|
156
165
|
title: "Adjust A/B Test",
|
|
157
166
|
description: "Adjust traffic rate of a running A/B test.",
|
|
158
|
-
inputSchema: {
|
|
159
|
-
groupId:
|
|
160
|
-
trafficRate:
|
|
161
|
-
}
|
|
167
|
+
inputSchema: z2.object({
|
|
168
|
+
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
169
|
+
trafficRate: z2.number().int().min(1).max(99).describe("New traffic percentage for challenger (1-99)")
|
|
170
|
+
})
|
|
162
171
|
},
|
|
163
172
|
async ({ groupId, ...body }) => callApi("PATCH", `policy-groups/${groupId}/ab-test/traffic-rate`, { body })
|
|
164
173
|
);
|
|
165
174
|
}
|
|
166
175
|
|
|
167
176
|
// src/mcp/tools/versions.ts
|
|
168
|
-
import { z as
|
|
177
|
+
import { z as z3 } from "zod";
|
|
169
178
|
|
|
170
179
|
// src/lib/config.ts
|
|
171
180
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
@@ -230,11 +239,11 @@ function registerVersionTools(server, callApi) {
|
|
|
230
239
|
{
|
|
231
240
|
title: "List Policy Versions",
|
|
232
241
|
description: "List all versions of a policy group.",
|
|
233
|
-
inputSchema: {
|
|
234
|
-
groupId:
|
|
235
|
-
page:
|
|
236
|
-
size:
|
|
237
|
-
}
|
|
242
|
+
inputSchema: z3.object({
|
|
243
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
244
|
+
page: z3.number().int().min(0).default(0).describe("Page number"),
|
|
245
|
+
size: z3.number().int().min(1).max(100).default(20).describe("Page size")
|
|
246
|
+
})
|
|
238
247
|
},
|
|
239
248
|
async ({ groupId, page, size }) => callApi("GET", `policy-groups/${groupId}/versions`, {
|
|
240
249
|
params: paginationParams(page, size)
|
|
@@ -245,10 +254,10 @@ function registerVersionTools(server, callApi) {
|
|
|
245
254
|
{
|
|
246
255
|
title: "Get Policy Version",
|
|
247
256
|
description: "Get a single version by ID, including its rules and fact requirements.",
|
|
248
|
-
inputSchema: {
|
|
249
|
-
groupId:
|
|
250
|
-
versionId:
|
|
251
|
-
}
|
|
257
|
+
inputSchema: z3.object({
|
|
258
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
259
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
260
|
+
})
|
|
252
261
|
},
|
|
253
262
|
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}`)
|
|
254
263
|
);
|
|
@@ -257,12 +266,12 @@ function registerVersionTools(server, callApi) {
|
|
|
257
266
|
{
|
|
258
267
|
title: "Create Policy Version",
|
|
259
268
|
description: "Create a new DRAFT version in a policy group. Optionally provide a commit message and effective date range.",
|
|
260
|
-
inputSchema: {
|
|
261
|
-
groupId:
|
|
262
|
-
commitMessage:
|
|
263
|
-
effectiveFrom:
|
|
264
|
-
effectiveTo:
|
|
265
|
-
}
|
|
269
|
+
inputSchema: z3.object({
|
|
270
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
271
|
+
commitMessage: z3.string().optional().describe("Commit message describing this version"),
|
|
272
|
+
effectiveFrom: z3.string().optional().describe("Effective start date (ISO 8601)"),
|
|
273
|
+
effectiveTo: z3.string().optional().describe("Effective end date (ISO 8601)")
|
|
274
|
+
})
|
|
266
275
|
},
|
|
267
276
|
async ({ groupId, ...body }) => callApi("POST", `policy-groups/${groupId}/versions`, { body })
|
|
268
277
|
);
|
|
@@ -271,13 +280,13 @@ function registerVersionTools(server, callApi) {
|
|
|
271
280
|
{
|
|
272
281
|
title: "Update Policy Version",
|
|
273
282
|
description: "Update a DRAFT version. Only DRAFT versions can be modified. Only provided fields are changed.",
|
|
274
|
-
inputSchema: {
|
|
275
|
-
groupId:
|
|
276
|
-
versionId:
|
|
277
|
-
commitMessage:
|
|
278
|
-
effectiveFrom:
|
|
279
|
-
effectiveTo:
|
|
280
|
-
}
|
|
283
|
+
inputSchema: z3.object({
|
|
284
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
285
|
+
versionId: z3.string().uuid().describe("Version ID"),
|
|
286
|
+
commitMessage: z3.string().optional().describe("New commit message"),
|
|
287
|
+
effectiveFrom: z3.string().optional().describe("New effective start date"),
|
|
288
|
+
effectiveTo: z3.string().optional().describe("New effective end date")
|
|
289
|
+
})
|
|
281
290
|
},
|
|
282
291
|
async ({ groupId, versionId, ...body }) => callApi("PUT", `policy-groups/${groupId}/versions/${versionId}`, { body })
|
|
283
292
|
);
|
|
@@ -286,10 +295,10 @@ function registerVersionTools(server, callApi) {
|
|
|
286
295
|
{
|
|
287
296
|
title: "Delete Policy Version",
|
|
288
297
|
description: "Delete a DRAFT version. Only DRAFT versions can be deleted.",
|
|
289
|
-
inputSchema: {
|
|
290
|
-
groupId:
|
|
291
|
-
versionId:
|
|
292
|
-
}
|
|
298
|
+
inputSchema: z3.object({
|
|
299
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
300
|
+
versionId: z3.string().uuid().describe("Version ID")
|
|
301
|
+
})
|
|
293
302
|
},
|
|
294
303
|
async ({ groupId, versionId }) => callApi("DELETE", `policy-groups/${groupId}/versions/${versionId}`)
|
|
295
304
|
);
|
|
@@ -298,10 +307,10 @@ function registerVersionTools(server, callApi) {
|
|
|
298
307
|
{
|
|
299
308
|
title: "Clone Policy Version",
|
|
300
309
|
description: "Clone an existing version to create a new DRAFT. Useful when the source version is already published.",
|
|
301
|
-
inputSchema: {
|
|
302
|
-
groupId:
|
|
303
|
-
versionId:
|
|
304
|
-
}
|
|
310
|
+
inputSchema: z3.object({
|
|
311
|
+
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
312
|
+
versionId: z3.string().uuid().describe("Source version ID to clone")
|
|
313
|
+
})
|
|
305
314
|
},
|
|
306
315
|
async ({ groupId, versionId }) => {
|
|
307
316
|
return callApi("POST", `policy-groups/${groupId}/versions/${versionId}/clone`, {
|
|
@@ -323,7 +332,7 @@ var MIN_COHORT_SIZE = 3;
|
|
|
323
332
|
var PROFILE_DEFAULT_WINDOW_HOURS = 24;
|
|
324
333
|
|
|
325
334
|
// src/mcp/tools/rules.ts
|
|
326
|
-
import { z as
|
|
335
|
+
import { z as z4 } from "zod";
|
|
327
336
|
import dedent from "dedent";
|
|
328
337
|
function registerRuleTools(server, callApi) {
|
|
329
338
|
server.registerTool(
|
|
@@ -331,10 +340,10 @@ function registerRuleTools(server, callApi) {
|
|
|
331
340
|
{
|
|
332
341
|
title: "List Rules",
|
|
333
342
|
description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
|
|
334
|
-
inputSchema: {
|
|
335
|
-
groupId:
|
|
336
|
-
versionId:
|
|
337
|
-
}
|
|
343
|
+
inputSchema: z4.object({
|
|
344
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
345
|
+
versionId: z4.string().uuid().describe("Version ID")
|
|
346
|
+
})
|
|
338
347
|
},
|
|
339
348
|
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules`)
|
|
340
349
|
);
|
|
@@ -343,11 +352,11 @@ function registerRuleTools(server, callApi) {
|
|
|
343
352
|
{
|
|
344
353
|
title: "Get Rule Detail",
|
|
345
354
|
description: "Get full rule detail including condition tree and action definitions.",
|
|
346
|
-
inputSchema: {
|
|
347
|
-
groupId:
|
|
348
|
-
versionId:
|
|
349
|
-
ruleId:
|
|
350
|
-
}
|
|
355
|
+
inputSchema: z4.object({
|
|
356
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
357
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
358
|
+
ruleId: z4.string().uuid().describe("Rule ID")
|
|
359
|
+
})
|
|
351
360
|
},
|
|
352
361
|
async ({ groupId, versionId, ruleId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/rules/${ruleId}`)
|
|
353
362
|
);
|
|
@@ -416,13 +425,13 @@ function registerRuleTools(server, callApi) {
|
|
|
416
425
|
|
|
417
426
|
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).
|
|
418
427
|
`,
|
|
419
|
-
inputSchema: {
|
|
420
|
-
groupId:
|
|
421
|
-
versionId:
|
|
422
|
-
rule:
|
|
428
|
+
inputSchema: z4.object({
|
|
429
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
430
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
431
|
+
rule: z4.string().describe(
|
|
423
432
|
"JSON string of CreateRuleRequest: { name, condition, actions, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
424
433
|
)
|
|
425
|
-
}
|
|
434
|
+
})
|
|
426
435
|
},
|
|
427
436
|
async ({ groupId, versionId, rule }) => {
|
|
428
437
|
const body = parseJson(rule);
|
|
@@ -434,14 +443,14 @@ function registerRuleTools(server, callApi) {
|
|
|
434
443
|
{
|
|
435
444
|
title: "Update Rule",
|
|
436
445
|
description: "Update an existing rule in a DRAFT version. Only provided fields are changed.",
|
|
437
|
-
inputSchema: {
|
|
438
|
-
groupId:
|
|
439
|
-
versionId:
|
|
440
|
-
ruleId:
|
|
441
|
-
rule:
|
|
446
|
+
inputSchema: z4.object({
|
|
447
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
448
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
449
|
+
ruleId: z4.string().uuid().describe("Rule ID"),
|
|
450
|
+
rule: z4.string().describe(
|
|
442
451
|
"JSON string of UpdateRuleRequest: { name?, condition?, actions?, mutexGroup?, mutexMode?, mutexStrategy?, mutexLimit?, isEnabled? }"
|
|
443
452
|
)
|
|
444
|
-
}
|
|
453
|
+
})
|
|
445
454
|
},
|
|
446
455
|
async ({ groupId, versionId, ruleId, rule }) => {
|
|
447
456
|
const body = parseJson(rule);
|
|
@@ -455,12 +464,12 @@ function registerRuleTools(server, callApi) {
|
|
|
455
464
|
{
|
|
456
465
|
title: "Delete Rule",
|
|
457
466
|
description: "Delete a rule from a DRAFT version.",
|
|
458
|
-
inputSchema: {
|
|
459
|
-
groupId:
|
|
460
|
-
versionId:
|
|
461
|
-
ruleId:
|
|
462
|
-
force:
|
|
463
|
-
}
|
|
467
|
+
inputSchema: z4.object({
|
|
468
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
469
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
470
|
+
ruleId: z4.string().uuid().describe("Rule ID"),
|
|
471
|
+
force: z4.boolean().default(false).describe("Skip confirmation (for the last rule in a version)")
|
|
472
|
+
})
|
|
464
473
|
},
|
|
465
474
|
async ({ groupId, versionId, ruleId, force }) => {
|
|
466
475
|
const params = {};
|
|
@@ -475,11 +484,11 @@ function registerRuleTools(server, callApi) {
|
|
|
475
484
|
{
|
|
476
485
|
title: "Reorder Rules",
|
|
477
486
|
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).",
|
|
478
|
-
inputSchema: {
|
|
479
|
-
groupId:
|
|
480
|
-
versionId:
|
|
481
|
-
ruleIds:
|
|
482
|
-
}
|
|
487
|
+
inputSchema: z4.object({
|
|
488
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
489
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
490
|
+
ruleIds: z4.array(z4.string().uuid()).describe("Rule IDs in desired priority order")
|
|
491
|
+
})
|
|
483
492
|
},
|
|
484
493
|
async ({ groupId, versionId, ruleIds }) => {
|
|
485
494
|
const rules = ruleIds.map((ruleId, index) => ({
|
|
@@ -496,12 +505,12 @@ function registerRuleTools(server, callApi) {
|
|
|
496
505
|
{
|
|
497
506
|
title: "Toggle Rule",
|
|
498
507
|
description: "Enable or disable a rule without deleting it.",
|
|
499
|
-
inputSchema: {
|
|
500
|
-
groupId:
|
|
501
|
-
versionId:
|
|
502
|
-
ruleId:
|
|
503
|
-
isEnabled:
|
|
504
|
-
}
|
|
508
|
+
inputSchema: z4.object({
|
|
509
|
+
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
510
|
+
versionId: z4.string().uuid().describe("Version ID"),
|
|
511
|
+
ruleId: z4.string().uuid().describe("Rule ID"),
|
|
512
|
+
isEnabled: z4.boolean().describe("true to enable, false to disable")
|
|
513
|
+
})
|
|
505
514
|
},
|
|
506
515
|
async ({ groupId, versionId, ruleId, isEnabled }) => callApi("PATCH", `policy-groups/${groupId}/versions/${versionId}/rules/${ruleId}/enabled`, {
|
|
507
516
|
body: { isEnabled }
|
|
@@ -510,7 +519,7 @@ function registerRuleTools(server, callApi) {
|
|
|
510
519
|
}
|
|
511
520
|
|
|
512
521
|
// src/mcp/tools/facts.ts
|
|
513
|
-
import { z as
|
|
522
|
+
import { z as z5 } from "zod";
|
|
514
523
|
|
|
515
524
|
// src/types/facts.ts
|
|
516
525
|
var FACT_KEY_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
@@ -527,11 +536,11 @@ function registerFactTools(server, callApi) {
|
|
|
527
536
|
{
|
|
528
537
|
title: "List Fact Definitions",
|
|
529
538
|
description: "List all fact definitions (input variable schema). Shows key, type, required, and PII status. Always check this before creating rules.",
|
|
530
|
-
inputSchema: {
|
|
531
|
-
page:
|
|
532
|
-
size:
|
|
533
|
-
keyword:
|
|
534
|
-
}
|
|
539
|
+
inputSchema: z5.object({
|
|
540
|
+
page: z5.number().int().min(0).default(0).describe("Page number"),
|
|
541
|
+
size: z5.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
542
|
+
keyword: z5.string().optional().describe("Search keyword")
|
|
543
|
+
})
|
|
535
544
|
},
|
|
536
545
|
async ({ page, size, keyword }) => {
|
|
537
546
|
const params = paginationParams(page, size);
|
|
@@ -544,16 +553,16 @@ function registerFactTools(server, callApi) {
|
|
|
544
553
|
{
|
|
545
554
|
title: "Create Fact Definition",
|
|
546
555
|
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.",
|
|
547
|
-
inputSchema: {
|
|
548
|
-
key:
|
|
549
|
-
name:
|
|
550
|
-
type:
|
|
551
|
-
description:
|
|
552
|
-
isRequired:
|
|
553
|
-
isPii:
|
|
556
|
+
inputSchema: z5.object({
|
|
557
|
+
key: z5.string().regex(FACT_KEY_PATTERN).describe("Variable key. Any casing; must start with a letter."),
|
|
558
|
+
name: z5.string().describe("Display name"),
|
|
559
|
+
type: z5.enum(VALUE_TYPES).describe("Value type"),
|
|
560
|
+
description: z5.string().optional().describe("Description"),
|
|
561
|
+
isRequired: z5.boolean().default(false).describe("Whether this fact is required for rule evaluation"),
|
|
562
|
+
isPii: z5.boolean().default(false).describe(
|
|
554
563
|
"Mark as PII \u2014 masked on every read surface, revealable only in the console (audited)"
|
|
555
564
|
)
|
|
556
|
-
}
|
|
565
|
+
})
|
|
557
566
|
},
|
|
558
567
|
async (args) => callApi("POST", "schema/facts", { body: args })
|
|
559
568
|
);
|
|
@@ -562,16 +571,16 @@ function registerFactTools(server, callApi) {
|
|
|
562
571
|
{
|
|
563
572
|
title: "Update Fact Definition",
|
|
564
573
|
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.",
|
|
565
|
-
inputSchema: {
|
|
566
|
-
factId:
|
|
567
|
-
name:
|
|
568
|
-
description:
|
|
569
|
-
type:
|
|
574
|
+
inputSchema: z5.object({
|
|
575
|
+
factId: z5.string().uuid().describe("Fact definition ID"),
|
|
576
|
+
name: z5.string().optional().describe("Display name"),
|
|
577
|
+
description: z5.string().optional().describe("Description"),
|
|
578
|
+
type: z5.enum(VALUE_TYPES).optional().describe(
|
|
570
579
|
"Value type. Omit to leave it unchanged. Changing it fails with FD-007 while any rule references the fact."
|
|
571
580
|
),
|
|
572
|
-
isRequired:
|
|
573
|
-
isPii:
|
|
574
|
-
}
|
|
581
|
+
isRequired: z5.boolean().optional().describe("Required flag"),
|
|
582
|
+
isPii: z5.boolean().optional().describe("PII flag \u2014 enables/disables masking (changeable even on system facts)")
|
|
583
|
+
})
|
|
575
584
|
},
|
|
576
585
|
async ({ factId, ...body }) => callApi("PUT", `schema/facts/${factId}`, { body })
|
|
577
586
|
);
|
|
@@ -580,9 +589,9 @@ function registerFactTools(server, callApi) {
|
|
|
580
589
|
{
|
|
581
590
|
title: "Delete Fact Definition",
|
|
582
591
|
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.",
|
|
583
|
-
inputSchema: {
|
|
584
|
-
factId:
|
|
585
|
-
}
|
|
592
|
+
inputSchema: z5.object({
|
|
593
|
+
factId: z5.string().uuid().describe("Fact definition ID")
|
|
594
|
+
})
|
|
586
595
|
},
|
|
587
596
|
async ({ factId }) => callApi("DELETE", `schema/facts/${factId}`)
|
|
588
597
|
);
|
|
@@ -591,7 +600,7 @@ function registerFactTools(server, callApi) {
|
|
|
591
600
|
{
|
|
592
601
|
title: "Get Action Runtime Fact Metadata",
|
|
593
602
|
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.",
|
|
594
|
-
inputSchema: {}
|
|
603
|
+
inputSchema: z5.object({})
|
|
595
604
|
},
|
|
596
605
|
async () => callApi("GET", "schema/action-metadata")
|
|
597
606
|
);
|
|
@@ -600,10 +609,10 @@ function registerFactTools(server, callApi) {
|
|
|
600
609
|
{
|
|
601
610
|
title: "List Unregistered Facts",
|
|
602
611
|
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.",
|
|
603
|
-
inputSchema: {
|
|
604
|
-
groupId:
|
|
605
|
-
versionId:
|
|
606
|
-
}
|
|
612
|
+
inputSchema: z5.object({
|
|
613
|
+
groupId: z5.string().uuid().describe("Policy group ID"),
|
|
614
|
+
versionId: z5.string().uuid().describe("Version ID")
|
|
615
|
+
})
|
|
607
616
|
},
|
|
608
617
|
async ({ groupId, versionId }) => callApi("GET", `policy-groups/${groupId}/versions/${versionId}/unregistered-facts`)
|
|
609
618
|
);
|
|
@@ -612,10 +621,10 @@ function registerFactTools(server, callApi) {
|
|
|
612
621
|
{
|
|
613
622
|
title: "Export Fact Catalog",
|
|
614
623
|
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.",
|
|
615
|
-
inputSchema: {
|
|
616
|
-
format:
|
|
617
|
-
keyword:
|
|
618
|
-
}
|
|
624
|
+
inputSchema: z5.object({
|
|
625
|
+
format: z5.enum(EXPORT_FORMATS).default("csv").describe("Export format"),
|
|
626
|
+
keyword: z5.string().optional().describe("Filter by key or name")
|
|
627
|
+
})
|
|
619
628
|
},
|
|
620
629
|
async ({ format, keyword }) => callApi("GET", "schema/facts/export", {
|
|
621
630
|
params: keyword ? { format, keyword } : { format }
|
|
@@ -624,18 +633,18 @@ function registerFactTools(server, callApi) {
|
|
|
624
633
|
}
|
|
625
634
|
|
|
626
635
|
// src/mcp/tools/deploy.ts
|
|
627
|
-
import { z as
|
|
636
|
+
import { z as z6 } from "zod";
|
|
628
637
|
function registerDeployTools(server, callApi) {
|
|
629
638
|
server.registerTool(
|
|
630
639
|
"lexq_deploy_publish",
|
|
631
640
|
{
|
|
632
641
|
title: "Publish Version",
|
|
633
642
|
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.",
|
|
634
|
-
inputSchema: {
|
|
635
|
-
groupId:
|
|
636
|
-
versionId:
|
|
637
|
-
memo:
|
|
638
|
-
}
|
|
643
|
+
inputSchema: z6.object({
|
|
644
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
645
|
+
versionId: z6.string().uuid().describe("Version ID to publish"),
|
|
646
|
+
memo: z6.string().min(1).describe("Publish memo (required)")
|
|
647
|
+
})
|
|
639
648
|
},
|
|
640
649
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/versions/${versionId}/publish`, { body: { memo } })
|
|
641
650
|
);
|
|
@@ -644,11 +653,11 @@ function registerDeployTools(server, callApi) {
|
|
|
644
653
|
{
|
|
645
654
|
title: "Deploy to Live",
|
|
646
655
|
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.",
|
|
647
|
-
inputSchema: {
|
|
648
|
-
groupId:
|
|
649
|
-
versionId:
|
|
650
|
-
memo:
|
|
651
|
-
}
|
|
656
|
+
inputSchema: z6.object({
|
|
657
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
658
|
+
versionId: z6.string().uuid().describe("Version ID to deploy"),
|
|
659
|
+
memo: z6.string().min(1).describe("Deployment memo (required)")
|
|
660
|
+
})
|
|
652
661
|
},
|
|
653
662
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/deploy`, {
|
|
654
663
|
body: { versionId, memo }
|
|
@@ -659,10 +668,10 @@ function registerDeployTools(server, callApi) {
|
|
|
659
668
|
{
|
|
660
669
|
title: "Rollback Deployment",
|
|
661
670
|
description: "Rollback to the previous deployed version. Only available if there is a previous version.",
|
|
662
|
-
inputSchema: {
|
|
663
|
-
groupId:
|
|
664
|
-
memo:
|
|
665
|
-
}
|
|
671
|
+
inputSchema: z6.object({
|
|
672
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
673
|
+
memo: z6.string().min(1).describe("Rollback reason (required)")
|
|
674
|
+
})
|
|
666
675
|
},
|
|
667
676
|
async ({ groupId, memo }) => callApi("POST", `policy-groups/${groupId}/rollback`, {
|
|
668
677
|
body: { memo }
|
|
@@ -673,10 +682,10 @@ function registerDeployTools(server, callApi) {
|
|
|
673
682
|
{
|
|
674
683
|
title: "Undeploy",
|
|
675
684
|
description: "Remove the live version from traffic. The version stays ACTIVE but no longer serves requests.",
|
|
676
|
-
inputSchema: {
|
|
677
|
-
groupId:
|
|
678
|
-
memo:
|
|
679
|
-
}
|
|
685
|
+
inputSchema: z6.object({
|
|
686
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
687
|
+
memo: z6.string().min(1).describe("Undeploy reason (required)")
|
|
688
|
+
})
|
|
680
689
|
},
|
|
681
690
|
async ({ groupId, memo }) => callApi("POST", `policy-groups/${groupId}/undeploy`, {
|
|
682
691
|
body: { memo }
|
|
@@ -687,11 +696,11 @@ function registerDeployTools(server, callApi) {
|
|
|
687
696
|
{
|
|
688
697
|
title: "Schedule Deployment",
|
|
689
698
|
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).",
|
|
690
|
-
inputSchema: {
|
|
691
|
-
groupId:
|
|
692
|
-
versionId:
|
|
693
|
-
memo:
|
|
694
|
-
}
|
|
699
|
+
inputSchema: z6.object({
|
|
700
|
+
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
701
|
+
versionId: z6.string().uuid().describe("ACTIVE version ID with a future effective start date"),
|
|
702
|
+
memo: z6.string().min(1).describe("Schedule memo (required)")
|
|
703
|
+
})
|
|
695
704
|
},
|
|
696
705
|
async ({ groupId, versionId, memo }) => callApi("POST", `policy-groups/${groupId}/schedule`, {
|
|
697
706
|
body: { versionId, memo }
|
|
@@ -702,9 +711,9 @@ function registerDeployTools(server, callApi) {
|
|
|
702
711
|
{
|
|
703
712
|
title: "Cancel Scheduled Deployment",
|
|
704
713
|
description: "Cancel the pending scheduled deployment for a group. The version itself is not affected. Fails with P-039 if no pending schedule exists.",
|
|
705
|
-
inputSchema: {
|
|
706
|
-
groupId:
|
|
707
|
-
}
|
|
714
|
+
inputSchema: z6.object({
|
|
715
|
+
groupId: z6.string().uuid().describe("Policy group ID")
|
|
716
|
+
})
|
|
708
717
|
},
|
|
709
718
|
async ({ groupId }) => callApi("DELETE", `policy-groups/${groupId}/schedule`)
|
|
710
719
|
);
|
|
@@ -713,10 +722,10 @@ function registerDeployTools(server, callApi) {
|
|
|
713
722
|
{
|
|
714
723
|
title: "List Scheduled Deployments",
|
|
715
724
|
description: "List scheduled deployments across all groups (all statuses: PENDING, EXECUTED, CANCELED, FAILED), newest first.",
|
|
716
|
-
inputSchema: {
|
|
717
|
-
page:
|
|
718
|
-
size:
|
|
719
|
-
}
|
|
725
|
+
inputSchema: z6.object({
|
|
726
|
+
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
727
|
+
size: z6.number().int().min(1).max(100).default(20).describe("Page size")
|
|
728
|
+
})
|
|
720
729
|
},
|
|
721
730
|
async ({ page, size }) => callApi("GET", "policy-groups/schedules", { params: paginationParams(page, size) })
|
|
722
731
|
);
|
|
@@ -725,14 +734,14 @@ function registerDeployTools(server, callApi) {
|
|
|
725
734
|
{
|
|
726
735
|
title: "Deployment History",
|
|
727
736
|
description: "List deployment history across all groups.",
|
|
728
|
-
inputSchema: {
|
|
729
|
-
page:
|
|
730
|
-
size:
|
|
731
|
-
groupId:
|
|
732
|
-
types:
|
|
733
|
-
startDate:
|
|
734
|
-
endDate:
|
|
735
|
-
}
|
|
737
|
+
inputSchema: z6.object({
|
|
738
|
+
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
739
|
+
size: z6.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
740
|
+
groupId: z6.string().uuid().optional().describe("Filter by group ID"),
|
|
741
|
+
types: z6.string().optional().describe("Filter by deployment types (comma-separated: DEPLOY,ROLLBACK,UNDEPLOY)"),
|
|
742
|
+
startDate: z6.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
743
|
+
endDate: z6.string().optional().describe("End date (yyyy-MM-dd)")
|
|
744
|
+
})
|
|
736
745
|
},
|
|
737
746
|
async ({ page, size, groupId, types, startDate, endDate }) => {
|
|
738
747
|
const params = paginationParams(page, size);
|
|
@@ -748,9 +757,9 @@ function registerDeployTools(server, callApi) {
|
|
|
748
757
|
{
|
|
749
758
|
title: "Deployment Detail",
|
|
750
759
|
description: "Get detailed info about a specific deployment including snapshot hash and integrity check.",
|
|
751
|
-
inputSchema: {
|
|
752
|
-
deploymentId:
|
|
753
|
-
}
|
|
760
|
+
inputSchema: z6.object({
|
|
761
|
+
deploymentId: z6.string().uuid().describe("Deployment ID")
|
|
762
|
+
})
|
|
754
763
|
},
|
|
755
764
|
async ({ deploymentId }) => callApi("GET", `deployments/${deploymentId}`)
|
|
756
765
|
);
|
|
@@ -759,7 +768,7 @@ function registerDeployTools(server, callApi) {
|
|
|
759
768
|
{
|
|
760
769
|
title: "Deployment Overview",
|
|
761
770
|
description: "Show current deployment status of all groups \u2014 which version is live, last deployment type, and deployer.",
|
|
762
|
-
inputSchema: {}
|
|
771
|
+
inputSchema: z6.object({})
|
|
763
772
|
},
|
|
764
773
|
async () => callApi("GET", "deployments/overview")
|
|
765
774
|
);
|
|
@@ -768,9 +777,9 @@ function registerDeployTools(server, callApi) {
|
|
|
768
777
|
{
|
|
769
778
|
title: "List Deployable Versions",
|
|
770
779
|
description: "List ACTIVE (published) versions that can be deployed for a group. Use this to find which versions are available before calling deploy live.",
|
|
771
|
-
inputSchema: {
|
|
772
|
-
groupId:
|
|
773
|
-
}
|
|
780
|
+
inputSchema: z6.object({
|
|
781
|
+
groupId: z6.string().uuid().describe("Policy group ID")
|
|
782
|
+
})
|
|
774
783
|
},
|
|
775
784
|
async ({ groupId }) => callApi("GET", `deployments/groups/${groupId}/deployable-versions`)
|
|
776
785
|
);
|
|
@@ -779,10 +788,10 @@ function registerDeployTools(server, callApi) {
|
|
|
779
788
|
{
|
|
780
789
|
title: "Deployment Diff",
|
|
781
790
|
description: "Compare rule snapshots between two versions. Shows added, removed, and modified rules. Useful for reviewing changes before deploying a new version.",
|
|
782
|
-
inputSchema: {
|
|
783
|
-
baseVersionId:
|
|
784
|
-
targetVersionId:
|
|
785
|
-
}
|
|
791
|
+
inputSchema: z6.object({
|
|
792
|
+
baseVersionId: z6.string().uuid().describe("Base version ID (typically the current live)"),
|
|
793
|
+
targetVersionId: z6.string().uuid().describe("Target version ID (the one you want to deploy)")
|
|
794
|
+
})
|
|
786
795
|
},
|
|
787
796
|
async ({ baseVersionId, targetVersionId }) => callApi("GET", "deployments/diff", {
|
|
788
797
|
params: { baseVersionId, targetVersionId }
|
|
@@ -791,7 +800,7 @@ function registerDeployTools(server, callApi) {
|
|
|
791
800
|
}
|
|
792
801
|
|
|
793
802
|
// src/mcp/tools/analytics.ts
|
|
794
|
-
import { z as
|
|
803
|
+
import { z as z7 } from "zod";
|
|
795
804
|
import dedent2 from "dedent";
|
|
796
805
|
function registerAnalyticsTools(server, callApi) {
|
|
797
806
|
server.registerTool(
|
|
@@ -810,11 +819,11 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
810
819
|
|
|
811
820
|
Example input: { "facts": { "paymentAmount": 100000, "customerTier": "VIP" } }
|
|
812
821
|
Always dry-run before publishing to validate rule behavior.`,
|
|
813
|
-
inputSchema: {
|
|
814
|
-
versionId:
|
|
815
|
-
facts:
|
|
816
|
-
includeDebugInfo:
|
|
817
|
-
}
|
|
822
|
+
inputSchema: z7.object({
|
|
823
|
+
versionId: z7.string().uuid().describe("Policy version ID to test against"),
|
|
824
|
+
facts: z7.string().describe('JSON string of facts object, e.g. {"paymentAmount":100000}'),
|
|
825
|
+
includeDebugInfo: z7.boolean().default(true).describe("Include execution and decision traces")
|
|
826
|
+
})
|
|
818
827
|
},
|
|
819
828
|
async ({ versionId, facts, includeDebugInfo }) => {
|
|
820
829
|
const parsedFacts = parseJson(facts);
|
|
@@ -834,11 +843,11 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
834
843
|
resultA / resultB — full DryRunResponse for each version
|
|
835
844
|
diff.mutatedDiff — changes in mutatedFacts between A and B (key → {before, after})
|
|
836
845
|
diff.generatedDiff — changes in generatedVariables between A and B`,
|
|
837
|
-
inputSchema: {
|
|
838
|
-
versionIdA:
|
|
839
|
-
versionIdB:
|
|
840
|
-
facts:
|
|
841
|
-
}
|
|
846
|
+
inputSchema: z7.object({
|
|
847
|
+
versionIdA: z7.string().uuid().describe("Baseline version ID"),
|
|
848
|
+
versionIdB: z7.string().uuid().describe("Candidate version ID"),
|
|
849
|
+
facts: z7.string().describe("JSON string of facts object")
|
|
850
|
+
})
|
|
842
851
|
},
|
|
843
852
|
async ({ versionIdA, versionIdB, facts }) => {
|
|
844
853
|
const parsedFacts = parseJson(facts);
|
|
@@ -852,10 +861,10 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
852
861
|
{
|
|
853
862
|
title: "Analyze Requirements",
|
|
854
863
|
description: "Analyze which input facts a version requires. Returns required keys, types, and an example request body.",
|
|
855
|
-
inputSchema: {
|
|
856
|
-
groupId:
|
|
857
|
-
versionId:
|
|
858
|
-
}
|
|
864
|
+
inputSchema: z7.object({
|
|
865
|
+
groupId: z7.string().uuid().describe("Policy group ID"),
|
|
866
|
+
versionId: z7.string().uuid().describe("Version ID")
|
|
867
|
+
})
|
|
859
868
|
},
|
|
860
869
|
async ({ groupId, versionId }) => callApi("GET", `analytics/groups/${groupId}/versions/${versionId}/requirements`)
|
|
861
870
|
);
|
|
@@ -891,9 +900,9 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
891
900
|
"options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true }
|
|
892
901
|
}
|
|
893
902
|
`,
|
|
894
|
-
inputSchema: {
|
|
895
|
-
body:
|
|
896
|
-
}
|
|
903
|
+
inputSchema: z7.object({
|
|
904
|
+
body: z7.string().describe("JSON string of SimulationRequest")
|
|
905
|
+
})
|
|
897
906
|
},
|
|
898
907
|
async ({ body }) => {
|
|
899
908
|
const parsed = parseJson(body);
|
|
@@ -905,9 +914,9 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
905
914
|
{
|
|
906
915
|
title: "Simulation Status",
|
|
907
916
|
description: "Get simulation status and results. Poll until status is COMPLETED or FAILED.",
|
|
908
|
-
inputSchema: {
|
|
909
|
-
simulationId:
|
|
910
|
-
}
|
|
917
|
+
inputSchema: z7.object({
|
|
918
|
+
simulationId: z7.string().uuid().describe("Simulation ID")
|
|
919
|
+
})
|
|
911
920
|
},
|
|
912
921
|
async ({ simulationId }) => callApi("GET", `analytics/simulations/${simulationId}`)
|
|
913
922
|
);
|
|
@@ -916,13 +925,13 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
916
925
|
{
|
|
917
926
|
title: "List Simulations",
|
|
918
927
|
description: "List simulation history with optional filters.",
|
|
919
|
-
inputSchema: {
|
|
920
|
-
page:
|
|
921
|
-
size:
|
|
922
|
-
status:
|
|
923
|
-
from:
|
|
924
|
-
to:
|
|
925
|
-
}
|
|
928
|
+
inputSchema: z7.object({
|
|
929
|
+
page: z7.number().int().min(0).default(0).describe("Page number"),
|
|
930
|
+
size: z7.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
931
|
+
status: z7.enum(["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELED"]).optional().describe("Filter by status"),
|
|
932
|
+
from: z7.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
933
|
+
to: z7.string().optional().describe("End date (yyyy-MM-dd)")
|
|
934
|
+
})
|
|
926
935
|
},
|
|
927
936
|
async ({ page, size, status, from, to }) => {
|
|
928
937
|
const params = paginationParams(page, size);
|
|
@@ -937,9 +946,9 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
937
946
|
{
|
|
938
947
|
title: "Cancel Simulation",
|
|
939
948
|
description: "Cancel a running or pending simulation.",
|
|
940
|
-
inputSchema: {
|
|
941
|
-
simulationId:
|
|
942
|
-
}
|
|
949
|
+
inputSchema: z7.object({
|
|
950
|
+
simulationId: z7.string().uuid().describe("Simulation ID")
|
|
951
|
+
})
|
|
943
952
|
},
|
|
944
953
|
async ({ simulationId }) => callApi("DELETE", `analytics/simulations/${simulationId}`)
|
|
945
954
|
);
|
|
@@ -948,10 +957,10 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
948
957
|
{
|
|
949
958
|
title: "Export Simulation",
|
|
950
959
|
description: "Export simulation results as JSON or CSV. Returns the raw data.",
|
|
951
|
-
inputSchema: {
|
|
952
|
-
simulationId:
|
|
953
|
-
format:
|
|
954
|
-
}
|
|
960
|
+
inputSchema: z7.object({
|
|
961
|
+
simulationId: z7.string().uuid().describe("Simulation ID"),
|
|
962
|
+
format: z7.enum(EXPORT_FORMATS).default("json").describe("Export format")
|
|
963
|
+
})
|
|
955
964
|
},
|
|
956
965
|
async ({ simulationId, format }) => callApi("GET", `analytics/simulations/${simulationId}/export`, {
|
|
957
966
|
params: { format }
|
|
@@ -975,10 +984,10 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
975
984
|
|
|
976
985
|
JSON example: [{"userId":"user_001","paymentAmount":150000}, {"userId":"user_002","paymentAmount":50000}]
|
|
977
986
|
`,
|
|
978
|
-
inputSchema: {
|
|
979
|
-
content:
|
|
980
|
-
filename:
|
|
981
|
-
}
|
|
987
|
+
inputSchema: z7.object({
|
|
988
|
+
content: z7.string().describe("CSV or JSON content as string"),
|
|
989
|
+
filename: z7.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
|
|
990
|
+
})
|
|
982
991
|
},
|
|
983
992
|
async ({ content, filename }) => {
|
|
984
993
|
const result = await callApi("POST", "analytics/datasets/upload", {
|
|
@@ -1005,11 +1014,11 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
1005
1014
|
{
|
|
1006
1015
|
title: "Download Dataset Template",
|
|
1007
1016
|
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.",
|
|
1008
|
-
inputSchema: {
|
|
1009
|
-
groupId:
|
|
1010
|
-
versionId:
|
|
1011
|
-
format:
|
|
1012
|
-
}
|
|
1017
|
+
inputSchema: z7.object({
|
|
1018
|
+
groupId: z7.string().uuid().describe("Policy group ID"),
|
|
1019
|
+
versionId: z7.string().uuid().describe("Version ID"),
|
|
1020
|
+
format: z7.enum(EXPORT_FORMATS).default("csv").describe("Template format")
|
|
1021
|
+
})
|
|
1013
1022
|
},
|
|
1014
1023
|
async ({ groupId, versionId, format }) => callApi("GET", `analytics/groups/${groupId}/versions/${versionId}/dataset-template`, {
|
|
1015
1024
|
params: { format }
|
|
@@ -1018,7 +1027,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
1018
1027
|
}
|
|
1019
1028
|
|
|
1020
1029
|
// src/mcp/tools/profile.ts
|
|
1021
|
-
import { z as
|
|
1030
|
+
import { z as z8 } from "zod";
|
|
1022
1031
|
var RELATIVE_THRESHOLD = `flagged = p50 \u2265 ${SLOW_MULTIPLIER}\xD7 median of per-rule p50s within the group; absolute thresholds are intentionally not supported.`;
|
|
1023
1032
|
function profileParams(opts) {
|
|
1024
1033
|
const params = {};
|
|
@@ -1034,15 +1043,15 @@ function registerProfileTools(server, callApi) {
|
|
|
1034
1043
|
{
|
|
1035
1044
|
title: "Group Latency Profile",
|
|
1036
1045
|
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.`,
|
|
1037
|
-
inputSchema: {
|
|
1038
|
-
groupId:
|
|
1039
|
-
versionId:
|
|
1040
|
-
from:
|
|
1046
|
+
inputSchema: z8.object({
|
|
1047
|
+
groupId: z8.string().uuid().describe("Policy group ID"),
|
|
1048
|
+
versionId: z8.string().uuid().optional().describe("Version to inspect (default: live version)"),
|
|
1049
|
+
from: z8.string().optional().describe(
|
|
1041
1050
|
`Window start, ISO-8601 instant (e.g. 2026-07-01T00:00:00Z). Default: ${PROFILE_DEFAULT_WINDOW_HOURS}h ago`
|
|
1042
1051
|
),
|
|
1043
|
-
to:
|
|
1044
|
-
cacheState:
|
|
1045
|
-
}
|
|
1052
|
+
to: z8.string().optional().describe("Window end, ISO-8601 instant. Default: now"),
|
|
1053
|
+
cacheState: z8.enum(["HIT", "MISS"]).optional().describe("Cache dimension for the rule table and judgment (default: HIT)")
|
|
1054
|
+
})
|
|
1046
1055
|
},
|
|
1047
1056
|
async ({ groupId, versionId, from, to, cacheState }) => callApi("GET", `policy-groups/${groupId}/profile`, {
|
|
1048
1057
|
params: profileParams({ versionId, from, to, cacheState })
|
|
@@ -1053,15 +1062,15 @@ function registerProfileTools(server, callApi) {
|
|
|
1053
1062
|
{
|
|
1054
1063
|
title: "Rule Latency Detail",
|
|
1055
1064
|
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,
|
|
1056
|
-
inputSchema: {
|
|
1057
|
-
groupId:
|
|
1058
|
-
ruleId:
|
|
1059
|
-
versionId:
|
|
1060
|
-
from:
|
|
1065
|
+
inputSchema: z8.object({
|
|
1066
|
+
groupId: z8.string().uuid().describe("Policy group ID"),
|
|
1067
|
+
ruleId: z8.string().uuid().describe("Rule ID (from lexq_profile_overview)"),
|
|
1068
|
+
versionId: z8.string().uuid().optional().describe("Version to inspect (default: live version)"),
|
|
1069
|
+
from: z8.string().optional().describe(
|
|
1061
1070
|
`Window start, ISO-8601 instant. Default: ${PROFILE_DEFAULT_WINDOW_HOURS}h ago`
|
|
1062
1071
|
),
|
|
1063
|
-
to:
|
|
1064
|
-
}
|
|
1072
|
+
to: z8.string().optional().describe("Window end, ISO-8601 instant. Default: now")
|
|
1073
|
+
})
|
|
1065
1074
|
},
|
|
1066
1075
|
async ({ groupId, ruleId, versionId, from, to }) => callApi("GET", `policy-groups/${groupId}/profile/rules/${ruleId}`, {
|
|
1067
1076
|
params: profileParams({ versionId, from, to })
|
|
@@ -1070,17 +1079,17 @@ function registerProfileTools(server, callApi) {
|
|
|
1070
1079
|
}
|
|
1071
1080
|
|
|
1072
1081
|
// src/mcp/tools/replay.ts
|
|
1073
|
-
import { z as
|
|
1082
|
+
import { z as z9 } from "zod";
|
|
1074
1083
|
function registerReplayTools(server, callApi) {
|
|
1075
1084
|
server.registerTool(
|
|
1076
1085
|
"lexq_replay_decision",
|
|
1077
1086
|
{
|
|
1078
1087
|
title: "Replay a Decision",
|
|
1079
1088
|
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.",
|
|
1080
|
-
inputSchema: {
|
|
1081
|
-
traceId:
|
|
1082
|
-
candidateVersionId:
|
|
1083
|
-
}
|
|
1089
|
+
inputSchema: z9.object({
|
|
1090
|
+
traceId: z9.string().describe("Trace ID of the past execution to replay"),
|
|
1091
|
+
candidateVersionId: z9.string().uuid().describe("Version to re-evaluate against")
|
|
1092
|
+
})
|
|
1084
1093
|
},
|
|
1085
1094
|
async ({ traceId, candidateVersionId }) => callApi("POST", "replay/decisions", { body: { traceId, candidateVersionId } })
|
|
1086
1095
|
);
|
|
@@ -1089,14 +1098,14 @@ function registerReplayTools(server, callApi) {
|
|
|
1089
1098
|
{
|
|
1090
1099
|
title: "Start Window Replay (Blast Radius)",
|
|
1091
1100
|
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.",
|
|
1092
|
-
inputSchema: {
|
|
1093
|
-
candidateVersionId:
|
|
1094
|
-
from:
|
|
1095
|
-
to:
|
|
1096
|
-
maxRecords:
|
|
1101
|
+
inputSchema: z9.object({
|
|
1102
|
+
candidateVersionId: z9.string().uuid().describe("Version to re-evaluate against"),
|
|
1103
|
+
from: z9.string().describe("Window start date (yyyy-MM-dd)"),
|
|
1104
|
+
to: z9.string().describe("Window end date (yyyy-MM-dd)"),
|
|
1105
|
+
maxRecords: z9.number().int().min(1).optional().describe(
|
|
1097
1106
|
`Sample cap (server default applies; hard cap ${REPLAY_WINDOW_MAX_RECORDS / 1e3}k)`
|
|
1098
1107
|
)
|
|
1099
|
-
}
|
|
1108
|
+
})
|
|
1100
1109
|
},
|
|
1101
1110
|
async ({ candidateVersionId, from, to, maxRecords }) => callApi("POST", "replay/jobs", { body: { candidateVersionId, from, to, maxRecords } })
|
|
1102
1111
|
);
|
|
@@ -1105,9 +1114,9 @@ function registerReplayTools(server, callApi) {
|
|
|
1105
1114
|
{
|
|
1106
1115
|
title: "Get Replay Job Status",
|
|
1107
1116
|
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.",
|
|
1108
|
-
inputSchema: {
|
|
1109
|
-
jobId:
|
|
1110
|
-
}
|
|
1117
|
+
inputSchema: z9.object({
|
|
1118
|
+
jobId: z9.string().describe("Replay job ID from lexq_replay_start")
|
|
1119
|
+
})
|
|
1111
1120
|
},
|
|
1112
1121
|
async ({ jobId }) => callApi("GET", `replay/jobs/${jobId}`)
|
|
1113
1122
|
);
|
|
@@ -1116,10 +1125,10 @@ function registerReplayTools(server, callApi) {
|
|
|
1116
1125
|
{
|
|
1117
1126
|
title: "List Replay Jobs",
|
|
1118
1127
|
description: "List window replay job history (reverse-chronological). Lightweight items \u2014 use lexq_replay_status for summary and changed samples.",
|
|
1119
|
-
inputSchema: {
|
|
1120
|
-
page:
|
|
1121
|
-
size:
|
|
1122
|
-
}
|
|
1128
|
+
inputSchema: z9.object({
|
|
1129
|
+
page: z9.number().int().min(0).default(0).describe("Page number"),
|
|
1130
|
+
size: z9.number().int().min(1).max(100).default(20).describe("Page size")
|
|
1131
|
+
})
|
|
1123
1132
|
},
|
|
1124
1133
|
async ({ page, size }) => callApi("GET", "replay/jobs", { params: paginationParams(page, size) })
|
|
1125
1134
|
);
|
|
@@ -1128,9 +1137,9 @@ function registerReplayTools(server, callApi) {
|
|
|
1128
1137
|
{
|
|
1129
1138
|
title: "Cancel Replay Job",
|
|
1130
1139
|
description: "Cooperatively cancel a PENDING or RUNNING window replay job. Other states are rejected. VIEWER role cannot cancel.",
|
|
1131
|
-
inputSchema: {
|
|
1132
|
-
jobId:
|
|
1133
|
-
}
|
|
1140
|
+
inputSchema: z9.object({
|
|
1141
|
+
jobId: z9.string().describe("Replay job ID")
|
|
1142
|
+
})
|
|
1134
1143
|
},
|
|
1135
1144
|
async ({ jobId }) => callApi("POST", `replay/jobs/${jobId}/cancel`)
|
|
1136
1145
|
);
|
|
@@ -1139,33 +1148,33 @@ function registerReplayTools(server, callApi) {
|
|
|
1139
1148
|
{
|
|
1140
1149
|
title: "Export Replay Result",
|
|
1141
1150
|
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.",
|
|
1142
|
-
inputSchema: {
|
|
1143
|
-
jobId:
|
|
1144
|
-
format:
|
|
1145
|
-
}
|
|
1151
|
+
inputSchema: z9.object({
|
|
1152
|
+
jobId: z9.string().describe("Replay job ID"),
|
|
1153
|
+
format: z9.enum(EXPORT_FORMATS).default("json").describe("Export format")
|
|
1154
|
+
})
|
|
1146
1155
|
},
|
|
1147
1156
|
async ({ jobId, format }) => callApi("GET", `replay/jobs/${jobId}/export`, { params: { format } })
|
|
1148
1157
|
);
|
|
1149
1158
|
}
|
|
1150
1159
|
|
|
1151
1160
|
// src/mcp/tools/history.ts
|
|
1152
|
-
import { z as
|
|
1161
|
+
import { z as z10 } from "zod";
|
|
1153
1162
|
function registerHistoryTools(server, callApi) {
|
|
1154
1163
|
server.registerTool(
|
|
1155
1164
|
"lexq_history_list",
|
|
1156
1165
|
{
|
|
1157
1166
|
title: "List Execution History",
|
|
1158
1167
|
description: "List policy execution history. Shows trace ID, group, version, status, match result, and latency.",
|
|
1159
|
-
inputSchema: {
|
|
1160
|
-
page:
|
|
1161
|
-
size:
|
|
1162
|
-
traceId:
|
|
1163
|
-
groupId:
|
|
1164
|
-
versionId:
|
|
1165
|
-
status:
|
|
1166
|
-
startDate:
|
|
1167
|
-
endDate:
|
|
1168
|
-
}
|
|
1168
|
+
inputSchema: z10.object({
|
|
1169
|
+
page: z10.number().int().min(0).default(0).describe("Page number"),
|
|
1170
|
+
size: z10.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
1171
|
+
traceId: z10.string().optional().describe("Filter by trace ID"),
|
|
1172
|
+
groupId: z10.string().uuid().optional().describe("Filter by policy group"),
|
|
1173
|
+
versionId: z10.string().uuid().optional().describe("Filter by version"),
|
|
1174
|
+
status: z10.enum(["SUCCESS", "NO_MATCH", "ERROR", "TIMEOUT"]).optional().describe("Filter by execution status"),
|
|
1175
|
+
startDate: z10.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
1176
|
+
endDate: z10.string().optional().describe("End date (yyyy-MM-dd)")
|
|
1177
|
+
})
|
|
1169
1178
|
},
|
|
1170
1179
|
async ({ page, size, traceId, groupId, versionId, status, startDate, endDate }) => {
|
|
1171
1180
|
const params = paginationParams(page, size);
|
|
@@ -1183,9 +1192,9 @@ function registerHistoryTools(server, callApi) {
|
|
|
1183
1192
|
{
|
|
1184
1193
|
title: "Get Execution Detail",
|
|
1185
1194
|
description: "Get full execution detail including inputFacts, mutatedFacts, generatedVariables, executionTraces, and decisionTraces.",
|
|
1186
|
-
inputSchema: {
|
|
1187
|
-
traceId:
|
|
1188
|
-
}
|
|
1195
|
+
inputSchema: z10.object({
|
|
1196
|
+
traceId: z10.string().describe("Trace ID from execution history")
|
|
1197
|
+
})
|
|
1189
1198
|
},
|
|
1190
1199
|
async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
|
|
1191
1200
|
);
|
|
@@ -1194,11 +1203,11 @@ function registerHistoryTools(server, callApi) {
|
|
|
1194
1203
|
{
|
|
1195
1204
|
title: "Execution Statistics",
|
|
1196
1205
|
description: "Get execution KPIs: total executions, success/failure counts, success rate, and average latency.",
|
|
1197
|
-
inputSchema: {
|
|
1198
|
-
groupId:
|
|
1199
|
-
startDate:
|
|
1200
|
-
endDate:
|
|
1201
|
-
}
|
|
1206
|
+
inputSchema: z10.object({
|
|
1207
|
+
groupId: z10.string().uuid().optional().describe("Filter by policy group"),
|
|
1208
|
+
startDate: z10.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
1209
|
+
endDate: z10.string().optional().describe("End date (yyyy-MM-dd)")
|
|
1210
|
+
})
|
|
1202
1211
|
},
|
|
1203
1212
|
async ({ groupId, startDate, endDate }) => {
|
|
1204
1213
|
const params = {};
|
|
@@ -1211,16 +1220,16 @@ function registerHistoryTools(server, callApi) {
|
|
|
1211
1220
|
}
|
|
1212
1221
|
|
|
1213
1222
|
// src/mcp/tools/provenance.ts
|
|
1214
|
-
import { z as
|
|
1223
|
+
import { z as z11 } from "zod";
|
|
1215
1224
|
function registerProvenanceTools(server, callApi) {
|
|
1216
1225
|
server.registerTool(
|
|
1217
1226
|
"lexq_provenance_get",
|
|
1218
1227
|
{
|
|
1219
1228
|
title: "Get Decision Provenance",
|
|
1220
1229
|
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.",
|
|
1221
|
-
inputSchema: {
|
|
1222
|
-
traceId:
|
|
1223
|
-
}
|
|
1230
|
+
inputSchema: z11.object({
|
|
1231
|
+
traceId: z11.string().describe("Trace ID of the execution")
|
|
1232
|
+
})
|
|
1224
1233
|
},
|
|
1225
1234
|
async ({ traceId }) => callApi("GET", `provenance/${traceId}`)
|
|
1226
1235
|
);
|
|
@@ -1229,15 +1238,15 @@ function registerProvenanceTools(server, callApi) {
|
|
|
1229
1238
|
{
|
|
1230
1239
|
title: "List PII Reveal Audits",
|
|
1231
1240
|
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.",
|
|
1232
|
-
inputSchema: {
|
|
1233
|
-
page:
|
|
1234
|
-
size:
|
|
1235
|
-
traceId:
|
|
1236
|
-
revealedBy:
|
|
1237
|
-
factKey:
|
|
1238
|
-
startDate:
|
|
1239
|
-
endDate:
|
|
1240
|
-
}
|
|
1241
|
+
inputSchema: z11.object({
|
|
1242
|
+
page: z11.number().int().min(0).default(0).describe("Page number"),
|
|
1243
|
+
size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
1244
|
+
traceId: z11.string().optional().describe("Filter by trace ID (exact match)"),
|
|
1245
|
+
revealedBy: z11.string().optional().describe("Filter by operator ID (exact match)"),
|
|
1246
|
+
factKey: z11.string().optional().describe("Filter by fact key (partial match, case-insensitive)"),
|
|
1247
|
+
startDate: z11.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
1248
|
+
endDate: z11.string().optional().describe("End date (yyyy-MM-dd)")
|
|
1249
|
+
})
|
|
1241
1250
|
},
|
|
1242
1251
|
async ({ page, size, traceId, revealedBy, factKey, startDate, endDate }) => {
|
|
1243
1252
|
const params = paginationParams(page, size);
|
|
@@ -1252,22 +1261,22 @@ function registerProvenanceTools(server, callApi) {
|
|
|
1252
1261
|
}
|
|
1253
1262
|
|
|
1254
1263
|
// src/mcp/tools/logs.ts
|
|
1255
|
-
import { z as
|
|
1264
|
+
import { z as z12 } from "zod";
|
|
1256
1265
|
function registerLogTools(server, callApi) {
|
|
1257
1266
|
server.registerTool(
|
|
1258
1267
|
"lexq_logs_list",
|
|
1259
1268
|
{
|
|
1260
1269
|
title: "List Failure Logs",
|
|
1261
1270
|
description: "List system failure logs from background tasks (platform event webhooks, scheduled deployments).",
|
|
1262
|
-
inputSchema: {
|
|
1263
|
-
page:
|
|
1264
|
-
size:
|
|
1265
|
-
taskType:
|
|
1266
|
-
status:
|
|
1267
|
-
keyword:
|
|
1268
|
-
startDate:
|
|
1269
|
-
endDate:
|
|
1270
|
-
}
|
|
1271
|
+
inputSchema: z12.object({
|
|
1272
|
+
page: z12.number().int().min(0).default(0).describe("Page number"),
|
|
1273
|
+
size: z12.number().int().min(1).max(100).default(20).describe("Page size"),
|
|
1274
|
+
taskType: z12.enum(TaskType).optional().describe("Task type"),
|
|
1275
|
+
status: z12.enum(FailureStatus).optional().describe("Log status"),
|
|
1276
|
+
keyword: z12.string().optional().describe("Search in refId, refSubId, errorMessage"),
|
|
1277
|
+
startDate: z12.string().optional().describe("Start date (yyyy-MM-dd)"),
|
|
1278
|
+
endDate: z12.string().optional().describe("End date (yyyy-MM-dd)")
|
|
1279
|
+
})
|
|
1271
1280
|
},
|
|
1272
1281
|
async ({ page, size, taskType, status, keyword, startDate, endDate }) => {
|
|
1273
1282
|
const params = paginationParams(page, size);
|
|
@@ -1284,9 +1293,9 @@ function registerLogTools(server, callApi) {
|
|
|
1284
1293
|
{
|
|
1285
1294
|
title: "Get Failure Log",
|
|
1286
1295
|
description: "Get failure log detail by ID.",
|
|
1287
|
-
inputSchema: {
|
|
1288
|
-
logId:
|
|
1289
|
-
}
|
|
1296
|
+
inputSchema: z12.object({
|
|
1297
|
+
logId: z12.string().uuid().describe("Failure log ID")
|
|
1298
|
+
})
|
|
1290
1299
|
},
|
|
1291
1300
|
async ({ logId }) => callApi("GET", `failure-logs/${logId}`)
|
|
1292
1301
|
);
|
|
@@ -1295,10 +1304,10 @@ function registerLogTools(server, callApi) {
|
|
|
1295
1304
|
{
|
|
1296
1305
|
title: "Process Failure Log",
|
|
1297
1306
|
description: "Process a single failure log: RESOLVE (mark as manually fixed) or IGNORE (skip intentionally).",
|
|
1298
|
-
inputSchema: {
|
|
1299
|
-
logId:
|
|
1300
|
-
action:
|
|
1301
|
-
}
|
|
1307
|
+
inputSchema: z12.object({
|
|
1308
|
+
logId: z12.string().uuid().describe("Failure log ID"),
|
|
1309
|
+
action: z12.enum(FailureAction).describe("Action to take")
|
|
1310
|
+
})
|
|
1302
1311
|
},
|
|
1303
1312
|
async ({ logId, action }) => callApi("POST", `failure-logs/${logId}/actions`, {
|
|
1304
1313
|
params: { action }
|
|
@@ -1309,10 +1318,10 @@ function registerLogTools(server, callApi) {
|
|
|
1309
1318
|
{
|
|
1310
1319
|
title: "Bulk Process Failure Logs",
|
|
1311
1320
|
description: "Process multiple failure logs at once. Provide an array of log IDs and the action.",
|
|
1312
|
-
inputSchema: {
|
|
1313
|
-
logIds:
|
|
1314
|
-
action:
|
|
1315
|
-
}
|
|
1321
|
+
inputSchema: z12.object({
|
|
1322
|
+
logIds: z12.array(z12.string().uuid()).describe("Array of failure log IDs"),
|
|
1323
|
+
action: z12.enum(FailureAction).describe("Action to apply to all logs")
|
|
1324
|
+
})
|
|
1316
1325
|
},
|
|
1317
1326
|
async ({ logIds, action }) => callApi("POST", "failure-logs/bulk-actions", {
|
|
1318
1327
|
body: { logIds, action }
|
|
@@ -1321,17 +1330,17 @@ function registerLogTools(server, callApi) {
|
|
|
1321
1330
|
}
|
|
1322
1331
|
|
|
1323
1332
|
// src/mcp/tools/webhook-subscriptions.ts
|
|
1324
|
-
import { z as
|
|
1333
|
+
import { z as z13 } from "zod";
|
|
1325
1334
|
function registerWebhookSubscriptionTools(server, callApi) {
|
|
1326
1335
|
server.registerTool(
|
|
1327
1336
|
"lexq_webhook_subscriptions_list",
|
|
1328
1337
|
{
|
|
1329
1338
|
title: "List Webhook Subscriptions",
|
|
1330
1339
|
description: "List platform event webhook subscriptions. These receive deployment lifecycle notifications (publish, deploy, rollback, undeploy).",
|
|
1331
|
-
inputSchema: {
|
|
1332
|
-
page:
|
|
1333
|
-
size:
|
|
1334
|
-
}
|
|
1340
|
+
inputSchema: z13.object({
|
|
1341
|
+
page: z13.number().int().min(0).default(0).describe("Page number"),
|
|
1342
|
+
size: z13.number().int().min(1).max(100).default(20).describe("Page size")
|
|
1343
|
+
})
|
|
1335
1344
|
},
|
|
1336
1345
|
async ({ page, size }) => {
|
|
1337
1346
|
const params = paginationParams(page, size);
|
|
@@ -1343,9 +1352,9 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
1343
1352
|
{
|
|
1344
1353
|
title: "Get Webhook Subscription",
|
|
1345
1354
|
description: "Get webhook subscription detail by ID.",
|
|
1346
|
-
inputSchema: {
|
|
1347
|
-
id:
|
|
1348
|
-
}
|
|
1355
|
+
inputSchema: z13.object({
|
|
1356
|
+
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
1357
|
+
})
|
|
1349
1358
|
},
|
|
1350
1359
|
async ({ id }) => callApi("GET", `webhook-subscriptions/${id}`)
|
|
1351
1360
|
);
|
|
@@ -1354,15 +1363,15 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
1354
1363
|
{
|
|
1355
1364
|
title: "Save Webhook Subscription",
|
|
1356
1365
|
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": "..."}).',
|
|
1357
|
-
inputSchema: {
|
|
1358
|
-
id:
|
|
1359
|
-
name:
|
|
1360
|
-
webhookUrl:
|
|
1361
|
-
subscribedEvents:
|
|
1362
|
-
payloadFormat:
|
|
1363
|
-
secret:
|
|
1364
|
-
isActive:
|
|
1365
|
-
}
|
|
1366
|
+
inputSchema: z13.object({
|
|
1367
|
+
id: z13.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
|
|
1368
|
+
name: z13.string().min(1).describe("Subscription name (unique per tenant)"),
|
|
1369
|
+
webhookUrl: z13.string().url().describe("Webhook endpoint URL"),
|
|
1370
|
+
subscribedEvents: z13.array(z13.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
|
|
1371
|
+
payloadFormat: z13.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
|
|
1372
|
+
secret: z13.string().optional().describe("HMAC-SHA256 signing secret"),
|
|
1373
|
+
isActive: z13.boolean().optional().default(true).describe("Whether the subscription is active")
|
|
1374
|
+
})
|
|
1366
1375
|
},
|
|
1367
1376
|
async ({ ...body }) => callApi("POST", "webhook-subscriptions", { body })
|
|
1368
1377
|
);
|
|
@@ -1371,9 +1380,9 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
1371
1380
|
{
|
|
1372
1381
|
title: "Delete Webhook Subscription",
|
|
1373
1382
|
description: "Delete a webhook subscription by ID.",
|
|
1374
|
-
inputSchema: {
|
|
1375
|
-
id:
|
|
1376
|
-
}
|
|
1383
|
+
inputSchema: z13.object({
|
|
1384
|
+
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
1385
|
+
})
|
|
1377
1386
|
},
|
|
1378
1387
|
async ({ id }) => callApi("DELETE", `webhook-subscriptions/${id}`)
|
|
1379
1388
|
);
|
|
@@ -1382,23 +1391,23 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
1382
1391
|
{
|
|
1383
1392
|
title: "Test Webhook Subscription",
|
|
1384
1393
|
description: "Send a test event to verify webhook connectivity. Returns the HTTP status code and success/failure message.",
|
|
1385
|
-
inputSchema: {
|
|
1386
|
-
id:
|
|
1387
|
-
}
|
|
1394
|
+
inputSchema: z13.object({
|
|
1395
|
+
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
1396
|
+
})
|
|
1388
1397
|
},
|
|
1389
1398
|
async ({ id }) => callApi("POST", `webhook-subscriptions/${id}/test`)
|
|
1390
1399
|
);
|
|
1391
1400
|
}
|
|
1392
1401
|
|
|
1393
1402
|
// src/mcp/tools/domain-templates.ts
|
|
1394
|
-
import { z as
|
|
1403
|
+
import { z as z14 } from "zod";
|
|
1395
1404
|
function registerDomainTemplateTools(server, callApi) {
|
|
1396
1405
|
server.registerTool(
|
|
1397
1406
|
"lexq_domain_templates_list",
|
|
1398
1407
|
{
|
|
1399
1408
|
title: "List Domain Templates",
|
|
1400
1409
|
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.",
|
|
1401
|
-
inputSchema: {}
|
|
1410
|
+
inputSchema: z14.object({})
|
|
1402
1411
|
},
|
|
1403
1412
|
async () => callApi("GET", "domain-templates")
|
|
1404
1413
|
);
|
|
@@ -1407,11 +1416,11 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
1407
1416
|
{
|
|
1408
1417
|
title: "Preview Domain Template",
|
|
1409
1418
|
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.",
|
|
1410
|
-
inputSchema: {
|
|
1411
|
-
template:
|
|
1419
|
+
inputSchema: z14.object({
|
|
1420
|
+
template: z14.string().describe(
|
|
1412
1421
|
"Domain template key (e.g. ECOMMERCE). Use lexq_domain_templates_list to see available keys \u2014 currently only ECOMMERCE is ACTIVE."
|
|
1413
1422
|
)
|
|
1414
|
-
}
|
|
1423
|
+
})
|
|
1415
1424
|
},
|
|
1416
1425
|
async ({ template }) => callApi("GET", `domain-templates/${template}/preview`)
|
|
1417
1426
|
);
|
|
@@ -1420,12 +1429,12 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
1420
1429
|
{
|
|
1421
1430
|
title: "Apply Domain Template",
|
|
1422
1431
|
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.",
|
|
1423
|
-
inputSchema: {
|
|
1424
|
-
template:
|
|
1425
|
-
customName:
|
|
1432
|
+
inputSchema: z14.object({
|
|
1433
|
+
template: z14.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
|
|
1434
|
+
customName: z14.string().optional().describe(
|
|
1426
1435
|
"Optional custom name for the policy group that gets created. If omitted, the template's default name is used."
|
|
1427
1436
|
)
|
|
1428
|
-
}
|
|
1437
|
+
})
|
|
1429
1438
|
},
|
|
1430
1439
|
async ({ template, customName }) => {
|
|
1431
1440
|
const body = {};
|