@lexq/cli 0.1.19 → 0.1.21
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/README.md +123 -105
- package/dist/index.js +36 -16
- package/dist/mcp/register.js +23 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
# LexQ CLI
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
3
|
+
> **The decision platform for engineering teams.**
|
|
4
|
+
> CLI and MCP server for [LexQ](https://lexq.io) — move business rules out of
|
|
5
|
+
> your deploy pipeline, prove every change with simulation, and ship without
|
|
6
|
+
> a git push.
|
|
5
7
|
|
|
6
8
|
**[Website](https://lexq.io)** · **[Docs](https://docs.lexq.io)** · **[Console](https://console.lexq.io)**
|
|
7
9
|
|
|
8
10
|
[](https://www.npmjs.com/package/@lexq/cli)
|
|
9
11
|
[](LICENSE)
|
|
10
12
|
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why LexQ?
|
|
16
|
+
|
|
17
|
+
Your business rules — pricing, promotions, fee logic, eligibility checks —
|
|
18
|
+
change often. But every change ships through the same PR → review → staging
|
|
19
|
+
→ deploy cycle as your core application code. A one-line discount rule
|
|
20
|
+
takes two weeks.
|
|
21
|
+
|
|
22
|
+
This is an architectural problem, not a process problem. Business logic
|
|
23
|
+
that changes weekly shouldn't live in code that ships quarterly.
|
|
24
|
+
|
|
25
|
+
**LexQ separates the two.** Rules live outside your application. You change
|
|
26
|
+
them in a visual console or through this CLI. You prove every change with
|
|
27
|
+
batch simulation against real execution data. You deploy without touching
|
|
28
|
+
your codebase.
|
|
29
|
+
|
|
30
|
+
Every decision leaves a full audit trace.
|
|
31
|
+
|
|
11
32
|
## Install
|
|
12
33
|
|
|
13
34
|
```bash
|
|
@@ -55,7 +76,7 @@ lexq rules create --group-id <GROUP_ID> --version-id <VERSION_ID> --json '{
|
|
|
55
76
|
}]
|
|
56
77
|
}'
|
|
57
78
|
|
|
58
|
-
# 6. Test
|
|
79
|
+
# 6. Test against your data before shipping
|
|
59
80
|
lexq analytics dry-run --version-id <VERSION_ID> --debug --mock \
|
|
60
81
|
--json '{"facts":{"customer_tier":"VIP","payment_amount":100000}}'
|
|
61
82
|
|
|
@@ -64,106 +85,16 @@ lexq deploy publish --group-id <GROUP_ID> --version-id <VERSION_ID> --memo "v1"
|
|
|
64
85
|
lexq deploy live --group-id <GROUP_ID> --version-id <VERSION_ID> --memo "Initial deploy"
|
|
65
86
|
```
|
|
66
87
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
lexq auth login | logout | whoami
|
|
71
|
-
lexq status API health check
|
|
72
|
-
lexq serve Run as MCP stdio server (--mcp)
|
|
73
|
-
lexq groups list | get | create | update | delete
|
|
74
|
-
lexq groups ab-test start | stop | adjust
|
|
75
|
-
lexq versions list | get | create | update | delete | clone
|
|
76
|
-
lexq rules list | get | create | update | delete | reorder | toggle
|
|
77
|
-
lexq facts list | create | update | delete | action-metadata
|
|
78
|
-
lexq deploy publish | live | rollback | undeploy | history | detail | overview | deployable | diff
|
|
79
|
-
lexq analytics dry-run | dry-run-compare | requirements
|
|
80
|
-
lexq analytics simulation start | status | list | cancel | export
|
|
81
|
-
lexq analytics dataset upload | template
|
|
82
|
-
lexq history list | get | stats
|
|
83
|
-
lexq integrations list | get | save | delete | config-spec
|
|
84
|
-
lexq logs list | get | action | bulk-action
|
|
85
|
-
lexq webhook-subscriptions list | get | save | delete | test
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Global Options
|
|
89
|
-
|
|
90
|
-
| Flag | Description |
|
|
91
|
-
|--------------------------|--------------------------------------------|
|
|
92
|
-
| `--format <json\|table>` | Output format (default: `json`) |
|
|
93
|
-
| `--api-key <key>` | Override stored API key |
|
|
94
|
-
| `--base-url <url>` | Override API base URL |
|
|
95
|
-
| `--dry-run` | Preview the HTTP request without executing |
|
|
96
|
-
| `--verbose` | Show request/response details |
|
|
97
|
-
| `--no-color` | Disable colored output |
|
|
98
|
-
|
|
99
|
-
## AI Agent Skills
|
|
100
|
-
|
|
101
|
-
LexQ CLI ships with **AI Agent Skills** — structured documentation that AI coding agents can read to autonomously manage
|
|
102
|
-
policies.
|
|
103
|
-
|
|
104
|
-
```
|
|
105
|
-
skills/
|
|
106
|
-
├── lexq-shared/SKILL.md Core concepts, auth, workflow
|
|
107
|
-
├── lexq-groups/SKILL.md Policy groups, conflict resolution, A/B testing
|
|
108
|
-
├── lexq-rules/SKILL.md Condition syntax, action types, mutex
|
|
109
|
-
├── lexq-simulation/SKILL.md Dry run, batch simulation, compare
|
|
110
|
-
├── lexq-execution/SKILL.md Execution history, stats, failure logs,
|
|
111
|
-
│ integrations, webhook subscriptions
|
|
112
|
-
└── lexq-recipes/SKILL.md End-to-end recipes
|
|
113
|
-
|
|
114
|
-
.claude/CLAUDE.md Claude Code project context
|
|
115
|
-
AGENTS.md Universal agent guide (Cursor, Windsurf, Gemini CLI, Cline)
|
|
116
|
-
CONTEXT.md Platform architecture & glossary
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### For AI Agent Developers
|
|
120
|
-
|
|
121
|
-
Skills are included in the npm package. After installing `@lexq/cli`, agents can read skills from:
|
|
122
|
-
|
|
123
|
-
```
|
|
124
|
-
node_modules/@lexq/cli/skills/
|
|
125
|
-
node_modules/@lexq/cli/AGENTS.md
|
|
126
|
-
node_modules/@lexq/cli/CONTEXT.md
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Or reference them directly in your project by copying the `skills/` directory.
|
|
130
|
-
|
|
131
|
-
## Configuration
|
|
132
|
-
|
|
133
|
-
Config is stored at `~/.lexq/config.json`:
|
|
134
|
-
|
|
135
|
-
```json
|
|
136
|
-
{
|
|
137
|
-
"apiKey": "sk_live_xxxxxxxxxxxxx",
|
|
138
|
-
"baseUrl": "https://api.lexq.io/api/v1/partners",
|
|
139
|
-
"format": "json"
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## Development
|
|
88
|
+
## For AI Agents — 63 MCP Tools
|
|
144
89
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
pnpm build
|
|
150
|
-
pnpm start -- groups list
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
pnpm typecheck # Type check
|
|
155
|
-
pnpm lint # ESLint
|
|
156
|
-
bash tests/e2e.sh # CLI E2E tests (requires API key)
|
|
157
|
-
bash tests/test-engine-api.sh # Engine API integration tests
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## MCP Server Mode
|
|
161
|
-
|
|
162
|
-
LexQ exposes **63 policy engine tools** via MCP. Two connection methods:
|
|
90
|
+
LexQ is designed to be AI-native. The entire policy engine API — all 63 tools
|
|
91
|
+
— is exposed via Model Context Protocol. Claude, Cursor, and other MCP-compatible
|
|
92
|
+
agents can create, simulate, and deploy rules autonomously, with human approval
|
|
93
|
+
before production.
|
|
163
94
|
|
|
164
95
|
### Claude.ai (Cloud — no install)
|
|
165
96
|
|
|
166
|
-
1.
|
|
97
|
+
1. **Settings → Connectors → Add Custom Integration**
|
|
167
98
|
2. Enter: `https://mcp.lexq.io`
|
|
168
99
|
3. Sign in with your LexQ account and select an API key
|
|
169
100
|
4. Done — 63 tools available in every conversation
|
|
@@ -182,7 +113,7 @@ For any MCP client that supports remote servers:
|
|
|
182
113
|
}
|
|
183
114
|
```
|
|
184
115
|
|
|
185
|
-
OAuth 2.1 authentication is
|
|
116
|
+
OAuth 2.1 authentication is handled automatically by your client.
|
|
186
117
|
|
|
187
118
|
### Local (stdio)
|
|
188
119
|
|
|
@@ -192,9 +123,7 @@ Run LexQ CLI as a local MCP server:
|
|
|
192
123
|
lexq serve --mcp
|
|
193
124
|
```
|
|
194
125
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
### Claude Desktop
|
|
126
|
+
#### Claude Desktop
|
|
198
127
|
|
|
199
128
|
Add to `claude_desktop_config.json`:
|
|
200
129
|
|
|
@@ -214,7 +143,7 @@ Add to `claude_desktop_config.json`:
|
|
|
214
143
|
}
|
|
215
144
|
```
|
|
216
145
|
|
|
217
|
-
|
|
146
|
+
#### VS Code / Cursor
|
|
218
147
|
|
|
219
148
|
`.vscode/mcp.json`:
|
|
220
149
|
|
|
@@ -234,7 +163,96 @@ Add to `claude_desktop_config.json`:
|
|
|
234
163
|
}
|
|
235
164
|
```
|
|
236
165
|
|
|
237
|
-
> **Prerequisite:** `lexq auth login` must have been run once to store an
|
|
166
|
+
> **Prerequisite:** `lexq auth login` must have been run once to store an
|
|
167
|
+
> API key in `~/.lexq/config.json`.
|
|
168
|
+
|
|
169
|
+
## AI Agent Skills
|
|
170
|
+
|
|
171
|
+
LexQ CLI ships with **AI Agent Skills** — structured documentation agents
|
|
172
|
+
read to understand *how* to use the tools, not just *what* they are.
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
skills/
|
|
176
|
+
├── lexq-shared/SKILL.md Core concepts, auth, workflow
|
|
177
|
+
├── lexq-groups/SKILL.md Policy groups, conflict resolution, A/B testing
|
|
178
|
+
├── lexq-rules/SKILL.md Condition syntax, action types, mutex
|
|
179
|
+
├── lexq-simulation/SKILL.md Dry run, batch simulation, compare
|
|
180
|
+
├── lexq-execution/SKILL.md Execution history, stats, failure logs
|
|
181
|
+
└── lexq-recipes/SKILL.md End-to-end recipes
|
|
182
|
+
|
|
183
|
+
.claude/CLAUDE.md Claude Code project context
|
|
184
|
+
AGENTS.md Universal agent guide (Cursor, Windsurf, Gemini CLI, Cline)
|
|
185
|
+
CONTEXT.md Platform architecture & glossary
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
After installing `@lexq/cli`, agents can read skills from:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
node_modules/@lexq/cli/skills/
|
|
192
|
+
node_modules/@lexq/cli/AGENTS.md
|
|
193
|
+
node_modules/@lexq/cli/CONTEXT.md
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Commands
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
lexq auth login | logout | whoami
|
|
200
|
+
lexq status API health check
|
|
201
|
+
lexq serve Run as MCP stdio server (--mcp)
|
|
202
|
+
lexq groups list | get | create | update | delete
|
|
203
|
+
lexq groups ab-test start | stop | adjust
|
|
204
|
+
lexq versions list | get | create | update | delete | clone
|
|
205
|
+
lexq rules list | get | create | update | delete | reorder | toggle
|
|
206
|
+
lexq facts list | create | update | delete | action-metadata
|
|
207
|
+
lexq deploy publish | live | rollback | undeploy | history | detail | overview | deployable | diff
|
|
208
|
+
lexq analytics dry-run | dry-run-compare | requirements
|
|
209
|
+
lexq analytics simulation start | status | list | cancel | export
|
|
210
|
+
lexq analytics dataset upload | template
|
|
211
|
+
lexq history list | get | stats
|
|
212
|
+
lexq integrations list | get | save | delete | config-spec
|
|
213
|
+
lexq logs list | get | action | bulk-action
|
|
214
|
+
lexq webhook-subscriptions list | get | save | delete | test
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Global Options
|
|
218
|
+
|
|
219
|
+
| Flag | Description |
|
|
220
|
+
|--------------------------|--------------------------------------------|
|
|
221
|
+
| `--format <json\|table>` | Output format (default: `json`) |
|
|
222
|
+
| `--api-key <key>` | Override stored API key |
|
|
223
|
+
| `--base-url <url>` | Override API base URL |
|
|
224
|
+
| `--dry-run` | Preview the HTTP request without executing |
|
|
225
|
+
| `--verbose` | Show request/response details |
|
|
226
|
+
| `--no-color` | Disable colored output |
|
|
227
|
+
|
|
228
|
+
## Configuration
|
|
229
|
+
|
|
230
|
+
Config is stored at `~/.lexq/config.json`:
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"apiKey": "YOUR_API_KEY",
|
|
235
|
+
"baseUrl": "https://api.lexq.io/api/v1/partners",
|
|
236
|
+
"format": "json"
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
git clone https://github.com/lexq-io/lexq-cli.git
|
|
244
|
+
cd lexq-cli
|
|
245
|
+
pnpm install
|
|
246
|
+
pnpm build
|
|
247
|
+
pnpm start -- groups list
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
pnpm typecheck # Type check
|
|
252
|
+
pnpm lint # ESLint
|
|
253
|
+
bash tests/e2e.sh # CLI E2E tests (requires API key)
|
|
254
|
+
bash tests/test-engine-api.sh # Engine API integration tests
|
|
255
|
+
```
|
|
238
256
|
|
|
239
257
|
## License
|
|
240
258
|
|
package/dist/index.js
CHANGED
|
@@ -222,20 +222,25 @@ function registerAuthCommands(program) {
|
|
|
222
222
|
`
|
|
223
223
|
).action(async () => {
|
|
224
224
|
try {
|
|
225
|
+
const globalOpts = program.opts();
|
|
225
226
|
const config = loadConfig();
|
|
226
|
-
|
|
227
|
+
const apiKey = globalOpts.apiKey ?? config.apiKey;
|
|
228
|
+
const baseUrl = globalOpts.baseUrl ?? config.baseUrl;
|
|
229
|
+
if (!apiKey) {
|
|
227
230
|
console.error('Not authenticated. Run "lexq auth login" first.');
|
|
228
231
|
process.exit(1);
|
|
229
232
|
}
|
|
230
233
|
const info = await apiRequest("GET", "whoami", {
|
|
231
|
-
apiKey
|
|
232
|
-
baseUrl
|
|
234
|
+
apiKey,
|
|
235
|
+
baseUrl,
|
|
236
|
+
dryRun: globalOpts.dryRun,
|
|
237
|
+
verbose: globalOpts.verbose
|
|
233
238
|
});
|
|
234
|
-
const masked =
|
|
239
|
+
const masked = apiKey.length > 8 ? apiKey.substring(0, 4) + "****" + apiKey.substring(apiKey.length - 4) : "****";
|
|
235
240
|
printJson({
|
|
236
241
|
...info,
|
|
237
242
|
apiKey: masked,
|
|
238
|
-
baseUrl
|
|
243
|
+
baseUrl
|
|
239
244
|
});
|
|
240
245
|
} catch (error) {
|
|
241
246
|
printError(error);
|
|
@@ -871,7 +876,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
871
876
|
"valueType": "STRING"
|
|
872
877
|
},
|
|
873
878
|
"actions": [
|
|
874
|
-
{ "type": "
|
|
879
|
+
{ "type": "MUTATE_FACT", "parameters": { "refVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "rate": 20 } }
|
|
875
880
|
]
|
|
876
881
|
}'
|
|
877
882
|
|
|
@@ -880,7 +885,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
880
885
|
LESS_THAN, LESS_THAN_OR_EQUAL, CONTAINS, IN, NOT_IN
|
|
881
886
|
|
|
882
887
|
Action Types:
|
|
883
|
-
|
|
888
|
+
MUTATE_FACT, INCREMENT_FACT, EMIT_EVENT, BLOCK, EMIT_NOTIFICATION, EMIT_WEBHOOK, SET_FACT, ADD_TAG
|
|
884
889
|
|
|
885
890
|
Value Types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
|
|
886
891
|
|
|
@@ -920,7 +925,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
|
|
|
920
925
|
$ lexq rules update --group-id <gid> --version-id <vid> --id <rid> --json '{
|
|
921
926
|
"name": "VIP 25% Discount",
|
|
922
927
|
"actions": [
|
|
923
|
-
{ "type": "
|
|
928
|
+
{ "type": "MUTATE_FACT", "parameters": { "refVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "rate": 25 } }
|
|
924
929
|
]
|
|
925
930
|
}'
|
|
926
931
|
`
|
|
@@ -3119,18 +3124,20 @@ function registerRuleTools(server, callApi) {
|
|
|
3119
3124
|
Actions: [{ type, parameters }]
|
|
3120
3125
|
|
|
3121
3126
|
Action parameter schemas:
|
|
3122
|
-
-
|
|
3123
|
-
-
|
|
3124
|
-
-
|
|
3127
|
+
- MUTATE_FACT: { refVar: string, operator: "ASSIGN"|"ADD"|"SUB"|"MUL"|"DIV", method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Constraints: DIV + PERCENTAGE is invalid (use MUL with rate/100 inverse). DIV + AMOUNT requires value !== 0.
|
|
3128
|
+
- INCREMENT_FACT: { targetVar: string, method: "PERCENTAGE"|"AMOUNT", refVar?: string (required when PERCENTAGE), rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Note: external system call (e.g. point system sync) is NOT a primitive responsibility. Compose [INCREMENT_FACT, EMIT_EVENT] chain instead.
|
|
3129
|
+
- EMIT_EVENT: { integrationId: uuid, eventPayload: object (Map<string,unknown>, ≥1 entry) } eventPayload is passed through to the integration provider as-is. Domain-specific keys (couponId, ticketId, etc.) are routed by the provider, not validated by the engine.
|
|
3125
3130
|
- BLOCK: { reason: string }
|
|
3126
|
-
-
|
|
3127
|
-
-
|
|
3131
|
+
- EMIT_NOTIFICATION: { integrationId: uuid, targetVar: string, notificationPayload: object (Map<string,unknown>, ≥1 entry) } targetVar identifies the recipient fact (e.g. phone_number / email / device_token). notificationPayload (channel, templateId, body, variables, etc.) is passed through to the provider.
|
|
3132
|
+
- EMIT_WEBHOOK: { url: string, method: "POST", payloadTemplate?: object } payloadTemplate is optional. Without it, all facts are sent as-is. With it, the object is sent as the HTTP body with {{variables}} replaced at execution time. Variables: {{fact.xxx}}, {{output.xxx}}, {{timestamp}}, {{ruleName}}, {{groupName}}, {{versionNo}}, {{xxx}} (shorthand).
|
|
3128
3133
|
Platform examples:
|
|
3129
3134
|
Slack: { "text": "Rule {{ruleName}} fired — {{fact.customer_tier}}" }
|
|
3130
3135
|
Discord: { "content": "Rule {{ruleName}} fired — {{fact.customer_tier}}" }
|
|
3131
3136
|
Generic: { "event": "rule_matched", "rule": "{{ruleName}}", "amount": "{{output.payment_amount}}" }
|
|
3132
3137
|
- SET_FACT: { key: string, value: string|number|boolean }
|
|
3133
3138
|
- ADD_TAG: { tag: string, targetVar: string }
|
|
3139
|
+
|
|
3140
|
+
RoundingOption (optional, MUTATE_FACT / INCREMENT_FACT only): { scale: integer (0..16), 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).
|
|
3134
3141
|
`,
|
|
3135
3142
|
inputSchema: {
|
|
3136
3143
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3437,9 +3444,16 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
3437
3444
|
title: "Dry Run",
|
|
3438
3445
|
description: dedent14`
|
|
3439
3446
|
Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
|
|
3447
|
+
|
|
3448
|
+
Returns:
|
|
3449
|
+
inputFacts — normalized input facts
|
|
3450
|
+
mutatedFacts — input facts changed by rule actions (e.g. DISCOUNT mutates payment_amount)
|
|
3451
|
+
generatedVariables — new variables created by rules (e.g. last_discount_amount)
|
|
3452
|
+
executionTraces — per-rule match status
|
|
3453
|
+
decisionTraces — per-rule decision (SELECTED / BLOCKED_MUTEX / etc.)
|
|
3454
|
+
|
|
3440
3455
|
Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
|
|
3441
|
-
Always dry-run before publishing to validate rule behavior
|
|
3442
|
-
`,
|
|
3456
|
+
Always dry-run before publishing to validate rule behavior.`,
|
|
3443
3457
|
inputSchema: {
|
|
3444
3458
|
versionId: z6.string().uuid().describe("Policy version ID to test against"),
|
|
3445
3459
|
facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
|
|
@@ -3458,7 +3472,13 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
3458
3472
|
"lexq_dry_run_compare",
|
|
3459
3473
|
{
|
|
3460
3474
|
title: "Dry Run Compare",
|
|
3461
|
-
description:
|
|
3475
|
+
description: dedent14`
|
|
3476
|
+
Compare dry run results between two versions using the same input facts. Useful for validating changes.
|
|
3477
|
+
|
|
3478
|
+
Returns:
|
|
3479
|
+
resultA / resultB — full DryRunResponse for each version
|
|
3480
|
+
diff.mutatedDiff — changes in mutatedFacts between A and B (key → {before, after})
|
|
3481
|
+
diff.generatedDiff — changes in generatedVariables between A and B`,
|
|
3462
3482
|
inputSchema: {
|
|
3463
3483
|
versionIdA: z6.string().uuid().describe("Baseline version ID"),
|
|
3464
3484
|
versionIdB: z6.string().uuid().describe("Candidate version ID"),
|
package/dist/mcp/register.js
CHANGED
|
@@ -293,18 +293,20 @@ function registerRuleTools(server, callApi) {
|
|
|
293
293
|
Actions: [{ type, parameters }]
|
|
294
294
|
|
|
295
295
|
Action parameter schemas:
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
296
|
+
- MUTATE_FACT: { refVar: string, operator: "ASSIGN"|"ADD"|"SUB"|"MUL"|"DIV", method: "PERCENTAGE"|"AMOUNT", rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Constraints: DIV + PERCENTAGE is invalid (use MUL with rate/100 inverse). DIV + AMOUNT requires value !== 0.
|
|
297
|
+
- INCREMENT_FACT: { targetVar: string, method: "PERCENTAGE"|"AMOUNT", refVar?: string (required when PERCENTAGE), rate?: number (when PERCENTAGE), value?: number (when AMOUNT), rounding?: RoundingOption } Note: external system call (e.g. point system sync) is NOT a primitive responsibility. Compose [INCREMENT_FACT, EMIT_EVENT] chain instead.
|
|
298
|
+
- EMIT_EVENT: { integrationId: uuid, eventPayload: object (Map<string,unknown>, ≥1 entry) } eventPayload is passed through to the integration provider as-is. Domain-specific keys (couponId, ticketId, etc.) are routed by the provider, not validated by the engine.
|
|
299
299
|
- BLOCK: { reason: string }
|
|
300
|
-
-
|
|
301
|
-
-
|
|
300
|
+
- EMIT_NOTIFICATION: { integrationId: uuid, targetVar: string, notificationPayload: object (Map<string,unknown>, ≥1 entry) } targetVar identifies the recipient fact (e.g. phone_number / email / device_token). notificationPayload (channel, templateId, body, variables, etc.) is passed through to the provider.
|
|
301
|
+
- EMIT_WEBHOOK: { url: string, method: "POST", payloadTemplate?: object } payloadTemplate is optional. Without it, all facts are sent as-is. With it, the object is sent as the HTTP body with {{variables}} replaced at execution time. Variables: {{fact.xxx}}, {{output.xxx}}, {{timestamp}}, {{ruleName}}, {{groupName}}, {{versionNo}}, {{xxx}} (shorthand).
|
|
302
302
|
Platform examples:
|
|
303
303
|
Slack: { "text": "Rule {{ruleName}} fired — {{fact.customer_tier}}" }
|
|
304
304
|
Discord: { "content": "Rule {{ruleName}} fired — {{fact.customer_tier}}" }
|
|
305
305
|
Generic: { "event": "rule_matched", "rule": "{{ruleName}}", "amount": "{{output.payment_amount}}" }
|
|
306
306
|
- SET_FACT: { key: string, value: string|number|boolean }
|
|
307
307
|
- ADD_TAG: { tag: string, targetVar: string }
|
|
308
|
+
|
|
309
|
+
RoundingOption (optional, MUTATE_FACT / INCREMENT_FACT only): { scale: integer (0..16), 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).
|
|
308
310
|
`,
|
|
309
311
|
inputSchema: {
|
|
310
312
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -611,9 +613,16 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
611
613
|
title: "Dry Run",
|
|
612
614
|
description: dedent2`
|
|
613
615
|
Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
|
|
616
|
+
|
|
617
|
+
Returns:
|
|
618
|
+
inputFacts — normalized input facts
|
|
619
|
+
mutatedFacts — input facts changed by rule actions (e.g. DISCOUNT mutates payment_amount)
|
|
620
|
+
generatedVariables — new variables created by rules (e.g. last_discount_amount)
|
|
621
|
+
executionTraces — per-rule match status
|
|
622
|
+
decisionTraces — per-rule decision (SELECTED / BLOCKED_MUTEX / etc.)
|
|
623
|
+
|
|
614
624
|
Example input: { "facts": { "payment_amount": 100000, "customer_tier": "VIP" } }
|
|
615
|
-
Always dry-run before publishing to validate rule behavior
|
|
616
|
-
`,
|
|
625
|
+
Always dry-run before publishing to validate rule behavior.`,
|
|
617
626
|
inputSchema: {
|
|
618
627
|
versionId: z6.string().uuid().describe("Policy version ID to test against"),
|
|
619
628
|
facts: z6.string().describe('JSON string of facts object, e.g. {"payment_amount":100000}'),
|
|
@@ -632,7 +641,13 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
632
641
|
"lexq_dry_run_compare",
|
|
633
642
|
{
|
|
634
643
|
title: "Dry Run Compare",
|
|
635
|
-
description:
|
|
644
|
+
description: dedent2`
|
|
645
|
+
Compare dry run results between two versions using the same input facts. Useful for validating changes.
|
|
646
|
+
|
|
647
|
+
Returns:
|
|
648
|
+
resultA / resultB — full DryRunResponse for each version
|
|
649
|
+
diff.mutatedDiff — changes in mutatedFacts between A and B (key → {before, after})
|
|
650
|
+
diff.generatedDiff — changes in generatedVariables between A and B`,
|
|
636
651
|
inputSchema: {
|
|
637
652
|
versionIdA: z6.string().uuid().describe("Baseline version ID"),
|
|
638
653
|
versionIdB: z6.string().uuid().describe("Candidate version ID"),
|