@kaminari-ad/mcp 0.24.2 → 0.25.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/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.25.0] - 2026-10-04
11
+
12
+ > Requires the API-side batch rule test deploy. Until it lands, the tool
13
+ > gets 404 from `POST /api/v1/custom-rules/test-batch`. Do not tag this
14
+ > release ahead of the API.
15
+
16
+ ### Added
17
+
18
+ - **`test_custom_rules_batch`.** Preview up to 20 unsaved rule definitions
19
+ against up to 50 stored scans in one call, instead of chaining
20
+ `test_custom_rule`. The reply is one cell per rule and scan plus counts.
21
+ A cell that could not run carries `error` and `error_code` without
22
+ failing the rest. `deadline_exceeded` means the call ran out of time
23
+ before that cell, and the agent re-sends just those cells. A
24
+ rate-limited error means the organization already has a batch running.
25
+ Request and response schemas are regenerated from the API. The scan cap
26
+ comes from the generated schema; the 20-rule cap is restated in the tool
27
+ because each rule carries its own agent-facing field descriptions.
28
+
10
29
  ## [0.24.2] - 2026-10-03
11
30
 
12
31
  ### Added
package/README.md CHANGED
@@ -115,7 +115,7 @@ which decides which credential type minted it.
115
115
 
116
116
  ## Tools
117
117
 
118
- 108 tools covering the public `/api/v1` surface of Kaminari Ad. Every tool carries MCP behaviour annotations (`title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) so MCP clients can warn before destructive actions. The complete list, by domain:
118
+ 109 tools covering the public `/api/v1` surface of Kaminari Ad. Every tool carries MCP behaviour annotations (`title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) so MCP clients can warn before destructive actions. The complete list, by domain:
119
119
 
120
120
  - **Account** (14) — `get_account`, `update_org`, `list_org_users`, `invite_user`, `update_user_role`, `remove_user`, `transfer_ownership`, `list_org_roles`, `create_custom_role`, `list_account_labels`, `update_account_labels`, `list_api_keys`, `create_api_key`, `revoke_api_key`
121
121
  - **Scans** (13) — `list_scans`, `get_scan`, `list_scan_children`, `create_scan`, `create_bulk_scans`, `recheck_scans`, `cancel_scan`, `get_scan_screenshot`, `get_scan_creative_screenshot`, `get_scan_landing_screenshot`, `get_scan_creative_html`, `get_scan_creative_video`, `get_scan_vast_xml`
@@ -123,7 +123,7 @@ which decides which credential type minted it.
123
123
  - **Campaign groups** (10) — list/get/create/update/run/cancel/archive/unarchive + `pause_campaign_group_schedule`, `resume_campaign_group_schedule`
124
124
  - **Runs** (3) — `get_run`, `list_run_scans`, `cancel_run` (use `list_campaign_runs` to enumerate runs of a campaign — the API has no standalone `/runs` index)
125
125
  - **Tags** (5) — `list_tags`, `get_tag_definition`, `update_tag_definition`, `delete_tag_definition`, `list_scan_tags`
126
- - **Custom rules** (6) — `list_custom_rules`, `get_custom_rule`, `create_custom_rule`, `update_custom_rule`, `delete_custom_rule`, `test_custom_rule`
126
+ - **Custom rules** (7) — `list_custom_rules`, `get_custom_rule`, `create_custom_rule`, `update_custom_rule`, `delete_custom_rule`, `test_custom_rule`, `test_custom_rules_batch`
127
127
  - **Custom taxonomies** (7) — `list_custom_taxonomies`, `get_custom_taxonomy`, `create_custom_taxonomy`, `update_custom_taxonomy`, `delete_custom_taxonomy`, `restore_custom_taxonomy`, `parse_custom_taxonomy_text`
128
128
  - **Policy sets** (11) — `list_policy_sets`, `get_policy_set`, `create_policy_set`, `update_policy_set`, `delete_policy_set`, `request_policy_set_approval`, `unpublish_policy_set`, `set_default_policy_set`, `list_policy_set_campaigns`, `attach_policy_set_campaigns`, `detach_policy_set_campaigns`
129
129
  - **Alerts** (4) — `list_alerts`, `update_alert_status`, `bulk_update_alert_status`, `get_alert_stats`
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { NAME, VERSION, err, ok } from './chunk-4P6CNASK.js';
2
+ import { NAME, VERSION, err, ok } from './chunk-IG36AHO3.js';
3
3
  import process from 'process';
4
4
  import { z } from 'zod';
5
5
 
@@ -162,10 +162,10 @@ async function main() {
162
162
  }
163
163
  const config = configResult.value;
164
164
  if (config.transport === "stdio") {
165
- const { bootstrapStdio } = await import('./stdio-bootstrap-M4TOIBTL.js');
165
+ const { bootstrapStdio } = await import('./stdio-bootstrap-3IOUWYG3.js');
166
166
  return bootstrapStdio(config);
167
167
  }
168
- const { bootstrapHttp } = await import('./http-bootstrap-KBFALUIO.js');
168
+ const { bootstrapHttp } = await import('./http-bootstrap-6L4VQJRM.js');
169
169
  return bootstrapHttp(config);
170
170
  }
171
171
  main().then(
@@ -3,8 +3,8 @@ export { err, ok } from 'neverthrow';
3
3
 
4
4
  // src/shared/version.ts
5
5
  var NAME = "@kaminari-ad/mcp";
6
- var VERSION = "0.24.2";
6
+ var VERSION = "0.25.0";
7
7
 
8
8
  export { NAME, VERSION };
9
- //# sourceMappingURL=chunk-4P6CNASK.js.map
10
- //# sourceMappingURL=chunk-4P6CNASK.js.map
9
+ //# sourceMappingURL=chunk-IG36AHO3.js.map
10
+ //# sourceMappingURL=chunk-IG36AHO3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;;AASO,IAAM,IAAA,GAAO;AACb,IAAM,OAAA,GAAU","file":"chunk-4P6CNASK.js","sourcesContent":["/**\n * Package version and name. Hard-coded as constants here, asserted to\n * match `package.json` by a unit test.\n *\n * Why not import `package.json`: it would force JSON-module support at\n * runtime and tsup-bundling would inline the entire manifest. Two\n * constants + one assertion test is simpler and gives the same safety.\n */\n\nexport const NAME = \"@kaminari-ad/mcp\";\nexport const VERSION = \"0.24.2\";\n"]}
1
+ {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;;AASO,IAAM,IAAA,GAAO;AACb,IAAM,OAAA,GAAU","file":"chunk-IG36AHO3.js","sourcesContent":["/**\n * Package version and name. Hard-coded as constants here, asserted to\n * match `package.json` by a unit test.\n *\n * Why not import `package.json`: it would force JSON-module support at\n * runtime and tsup-bundling would inline the entire manifest. Two\n * constants + one assertion test is simpler and gives the same safety.\n */\n\nexport const NAME = \"@kaminari-ad/mcp\";\nexport const VERSION = \"0.25.0\";\n"]}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { err, ok } from './chunk-4P6CNASK.js';
2
+ import { err, ok } from './chunk-IG36AHO3.js';
3
3
  import { createHash, randomUUID } from 'crypto';
4
4
  import createClient from 'openapi-fetch';
5
5
  import { fetch } from 'undici';
@@ -789,6 +789,37 @@ var RuleTestResponse = z.object({
789
789
  llm_prompt_url: z.string().optional().default(""),
790
790
  llm_response_url: z.string().optional().default("")
791
791
  }).passthrough();
792
+ var RuleTestBatchRule = z.object({
793
+ id: z.union([z.string(), z.null()]).optional(),
794
+ rule_type: z.string(),
795
+ config: z.object({}).partial().passthrough(),
796
+ target: z.string().optional().default("page"),
797
+ name: z.string().optional().default("Test Rule")
798
+ }).passthrough();
799
+ var RuleTestBatchRequest = z.object({
800
+ rules: z.array(RuleTestBatchRule).min(1).max(20),
801
+ scan_ids: z.array(z.string().uuid()).min(1).max(50)
802
+ }).passthrough();
803
+ var RuleTestBatchCell = z.object({
804
+ index: z.number().int(),
805
+ rule_id: z.union([z.string(), z.null()]).optional(),
806
+ scan_id: z.string().uuid(),
807
+ matched: z.boolean(),
808
+ tags: z.array(RuleTestTagResult),
809
+ elapsed_ms: z.number().int(),
810
+ error: z.union([z.string(), z.null()]).optional(),
811
+ error_code: z.union([z.string(), z.null()]).optional()
812
+ }).passthrough();
813
+ var RuleTestBatchSummary = z.object({
814
+ total: z.number().int(),
815
+ matched: z.number().int(),
816
+ failed: z.number().int(),
817
+ deadline_exceeded: z.number().int()
818
+ }).passthrough();
819
+ var RuleTestBatchResponse = z.object({
820
+ results: z.array(RuleTestBatchCell),
821
+ summary: RuleTestBatchSummary
822
+ }).passthrough();
792
823
  var IabV3PolicyCategoryRequest = z.object({
793
824
  tier1: z.string().min(1).max(200),
794
825
  tier2: z.union([z.string(), z.null()]).optional(),
@@ -1265,6 +1296,8 @@ var schemas = {
1265
1296
  TagDefinitionDetailResponse,
1266
1297
  CustomRuleResponse,
1267
1298
  RuleTestResponse,
1299
+ RuleTestBatchRequest,
1300
+ RuleTestBatchResponse,
1268
1301
  PolicyEntryResponse,
1269
1302
  LinkedCampaignResponse,
1270
1303
  PolicySetResponse,
@@ -1628,6 +1661,7 @@ var RuleTestSchema = schemas.RuleTestResponse.pick({
1628
1661
  tags: true
1629
1662
  }).strip();
1630
1663
  var parseRuleTest = (raw) => parseWithSchema(RuleTestSchema, raw, "rule-test");
1664
+ var parseRuleTestBatch = (raw) => parseWithSchema(schemas.RuleTestBatchResponse.strip(), raw, "rule-test-batch");
1631
1665
  var AlertStatsSchema = schemas.AlertStatsResponse.pick({
1632
1666
  open: true,
1633
1667
  escalated: true,
@@ -2529,6 +2563,9 @@ function createHttpApiGateway(config) {
2529
2563
  async testCustomRule(body) {
2530
2564
  return call("POST", "/api/v1/custom-rules/test", { body }, parseRuleTest);
2531
2565
  },
2566
+ async testCustomRulesBatch(body) {
2567
+ return call("POST", "/api/v1/custom-rules/test-batch", { body }, parseRuleTestBatch);
2568
+ },
2532
2569
  // ── Policy sets ───────────────────────────────────────────────
2533
2570
  async listPolicySets(filters) {
2534
2571
  return call("GET", "/api/v1/policy-sets", { params: { query: filters } }, parsePolicySetPage);
@@ -4533,6 +4570,65 @@ var testCustomRuleTool = {
4533
4570
  return ok(result.value);
4534
4571
  }
4535
4572
  };
4573
+ var TestCustomRulesBatchInputShape = {
4574
+ rules: z.array(
4575
+ z.object({
4576
+ id: z.string().uuid().optional().describe(
4577
+ "Optional UUID you choose to tell rules apart in the reply; echoed back as rule_id. The API accepts only a UUID here. It is not looked up and does not select a saved rule."
4578
+ ),
4579
+ rule_type: z.string().max(50).describe(
4580
+ "Rule engine type. Same set as `test_custom_rule`: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `regexp_request_url`, `regexp_request_body`, `blacklist_domain`, `combo`, `llm`."
4581
+ ),
4582
+ config: patternAwareRuleConfigField.describe(
4583
+ "Rule-type-specific config. Same shape as `test_custom_rule`'s `config`. " + PATTERN_RULE_CONFIG_DOC + " " + COMBO_MATCH_SCOPE_DOC
4584
+ ),
4585
+ target: z.string().max(30).default("page").describe(
4586
+ "Where to apply the rule. Defaults to 'page'. `regexp_request_url` and `regexp_request_body` also accept 'creative' and 'creative_and_page'."
4587
+ ),
4588
+ name: z.string().min(1).max(200).default("Test Rule").describe(
4589
+ "Label the LLM prompt quotes as [USER RULE: \"<name>\"]. Same field as the single test. Defaults to 'Test Rule', which is what that test sends when name is omitted. Capped at 200 like `create_custom_rule`'s name, so a draft that tests well can be saved as is."
4590
+ )
4591
+ })
4592
+ ).min(1).max(20).describe("Up to 20 rules. Every rule runs against every scan in `scan_ids`."),
4593
+ scan_ids: schemas.RuleTestBatchRequest.shape.scan_ids.describe(
4594
+ "Up to 50 stored scan UUIDs. Testing one rule against many scans is the usual case."
4595
+ )
4596
+ };
4597
+ var testCustomRulesBatchTool = {
4598
+ name: "test_custom_rules_batch",
4599
+ description: "Preview-test up to 20 rule definitions against up to 50 stored scans in one call, without saving them. Returns one cell per rule and scan; a cell's `index` is the rule's position in `rules`, so pair it with `scan_id` to identify the cell. A rule definition the API rejects fails the whole call as invalid input before anything runs. Otherwise a cell that could not run has `error` and `error_code` and never fails the rest: `deadline_exceeded` means the call ran out of time before that cell (about 110s per call) \u2014 call again with just those rules and scans; others are `timed_out`, `llm_failed`, `content_prohibited`, `scan_not_found`, `invalid_rule`, `failed`. `summary.failed` counts every cell with an error and `summary.deadline_exceeded` is the part of them the deadline cut off. A rate-limited error means this organization already has a batch running; wait for it. Many AI (`llm`) rules over many scans are better split across calls. Historical limits match `test_custom_rule`: `regexp_request_body` only has captured bodies for the last day, and `regexp_request_url` on a stored scan is best-effort.",
4600
+ annotations: {
4601
+ title: "Test Custom Rules Batch",
4602
+ readOnlyHint: true,
4603
+ destructiveHint: false,
4604
+ idempotentHint: true,
4605
+ openWorldHint: false
4606
+ },
4607
+ inputSchema: z.object(TestCustomRulesBatchInputShape),
4608
+ handler: async (input, ctx) => {
4609
+ for (const [index, rule] of input.rules.entries()) {
4610
+ const inputError = patternRuleInputError(rule);
4611
+ if (inputError?.kind === "invalid-input") {
4612
+ const message = "rules[" + String(index) + "]: " + inputError.message;
4613
+ return err(
4614
+ inputError.fieldErrors === void 0 ? { kind: "invalid-input", message } : { kind: "invalid-input", message, fieldErrors: inputError.fieldErrors }
4615
+ );
4616
+ }
4617
+ }
4618
+ const result = await ctx.api.testCustomRulesBatch({
4619
+ rules: input.rules.map((rule) => ({
4620
+ rule_type: rule.rule_type,
4621
+ config: rule.config,
4622
+ target: rule.target,
4623
+ name: rule.name,
4624
+ ...rule.id === void 0 ? {} : { id: rule.id }
4625
+ })),
4626
+ scan_ids: input.scan_ids
4627
+ });
4628
+ if (result.isErr()) return err(mapApiError(result.error));
4629
+ return ok(result.value);
4630
+ }
4631
+ };
4536
4632
  var UpdateCustomRuleInputShape = {
4537
4633
  rule_id: z.string().uuid().describe("Rule UUID to update."),
4538
4634
  name: z.string().min(1).max(200).optional().describe(
@@ -6263,6 +6359,7 @@ function registerAllTools(register) {
6263
6359
  register(updateCustomRuleTool);
6264
6360
  register(deleteCustomRuleTool);
6265
6361
  register(testCustomRuleTool);
6362
+ register(testCustomRulesBatchTool);
6266
6363
  register(listCustomTaxonomiesTool);
6267
6364
  register(getCustomTaxonomyTool);
6268
6365
  register(createCustomTaxonomyTool);
@@ -6387,5 +6484,5 @@ function formatToolError(error) {
6387
6484
  }
6388
6485
 
6389
6486
  export { BearerToken, SERVER_INSTRUCTIONS, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
6390
- //# sourceMappingURL=chunk-GPJ6BQNP.js.map
6391
- //# sourceMappingURL=chunk-GPJ6BQNP.js.map
6487
+ //# sourceMappingURL=chunk-MIDK3IA2.js.map
6488
+ //# sourceMappingURL=chunk-MIDK3IA2.js.map