@kaminari-ad/mcp 0.10.0 → 0.11.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 +49 -0
- package/dist/bin.js +3 -3
- package/dist/{chunk-5UVZYMC3.js → chunk-XCAKB2QW.js} +53 -15
- package/dist/chunk-XCAKB2QW.js.map +1 -0
- package/dist/{chunk-KFVVS55N.js → chunk-XSI6SZTX.js} +3 -3
- package/dist/{chunk-KFVVS55N.js.map → chunk-XSI6SZTX.js.map} +1 -1
- package/dist/{http-bootstrap-3MGAQHND.js → http-bootstrap-XJNTLL2C.js} +4 -4
- package/dist/{http-bootstrap-3MGAQHND.js.map → http-bootstrap-XJNTLL2C.js.map} +1 -1
- package/dist/{stdio-bootstrap-7EXISZ66.js → stdio-bootstrap-QK4ENRPP.js} +4 -4
- package/dist/{stdio-bootstrap-7EXISZ66.js.map → stdio-bootstrap-QK4ENRPP.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-5UVZYMC3.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.11.0] - 2026-07-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Combo-rule match scope.** `create_custom_rule`, `update_custom_rule`, and
|
|
15
|
+
`test_custom_rule` now document the rule-level `config.match_scope` key used
|
|
16
|
+
by `rule_type='combo'`: `"scan"` (the default, and the behaviour when the key
|
|
17
|
+
is absent) counts conditions across the union of all tags on the scan, while
|
|
18
|
+
`"url"` requires every condition to be satisfied by tags on the same link and
|
|
19
|
+
assigns the output tag to that link. The description warns about the traps a
|
|
20
|
+
per-link rule falls into: a combo built only from link-less tags (AI verdicts,
|
|
21
|
+
crawler behaviour) has no link to attach to and never matches, AI verdicts and
|
|
22
|
+
per-link detections are not evaluated together today, and a per-link rule needs
|
|
23
|
+
at least one positive condition. Any other `match_scope` value is rejected
|
|
24
|
+
before the request is sent, so a misspelled scope surfaces as a tool-input
|
|
25
|
+
error instead of a round trip; the API validates `match_scope` on create,
|
|
26
|
+
update, and the rule-test preview as well, so this is early feedback rather
|
|
27
|
+
than the durable guarantee. The rest of a combo config is still validated only
|
|
28
|
+
for admin-authored system rules, which is why the per-link traps are documented
|
|
29
|
+
rather than enforced. `get_custom_rule` and `list_custom_rules` now flag that
|
|
30
|
+
the returned `config` may carry `match_scope` and that it must be resent
|
|
31
|
+
verbatim, since `update_custom_rule` replaces `config` wholesale.
|
|
32
|
+
- **`referrer` on scan and campaign creation.** `create_scan`,
|
|
33
|
+
`create_bulk_scans`, `create_campaign`, and `update_campaign` accept an
|
|
34
|
+
optional http(s) page URL the check is performed from. With `ad_tag` or
|
|
35
|
+
`vast_tag` it is the publisher page the tag is embedded in: the browser
|
|
36
|
+
commits the page document on that URL without fetching the publisher, so the
|
|
37
|
+
creative renders as if embedded there. With `url` or `ad_discovery` it is
|
|
38
|
+
where the visitor came from, sent as the `Referer` of the page request.
|
|
39
|
+
`update_campaign` also accepts `referrer: null` to clear a stored one.
|
|
40
|
+
Scan and campaign responses surface `referrer` too, so an agent can confirm
|
|
41
|
+
what a scan actually ran with.
|
|
42
|
+
|
|
43
|
+
**Release ordering — this depends on an unreleased API.** The snapshots were
|
|
44
|
+
generated from api `feat/scan-referrer`
|
|
45
|
+
([api!341](https://gitlab.sdev.pw/adverif/api/-/merge_requests/341)). API
|
|
46
|
+
request DTOs are `extra="ignore"`, so an API without the field drops
|
|
47
|
+
`referrer` silently: no 422, the scan runs with no referrer, and the response
|
|
48
|
+
carries no `referrer` key — the agent gets a green result for a check it
|
|
49
|
+
believes ran from a publisher page. **Do not cut the npm tag or the
|
|
50
|
+
`kaminariad-mcp` image until api!341 is merged and deployed to
|
|
51
|
+
`kaminariadprod1`.** Nothing automated enforces this.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- Regenerated the OpenAPI type + zod-schema snapshots so the `/api/v1` surface
|
|
56
|
+
they describe carries `referrer`. That field is their only delta versus the
|
|
57
|
+
0.10.0 snapshots.
|
|
58
|
+
|
|
10
59
|
## [0.10.0] - 2026-07-28
|
|
11
60
|
|
|
12
61
|
### Added
|
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { NAME, VERSION, err, ok } from './chunk-
|
|
2
|
+
import { NAME, VERSION, err, ok } from './chunk-XSI6SZTX.js';
|
|
3
3
|
import process from 'process';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
@@ -160,10 +160,10 @@ async function main() {
|
|
|
160
160
|
}
|
|
161
161
|
const config = configResult.value;
|
|
162
162
|
if (config.transport === "stdio") {
|
|
163
|
-
const { bootstrapStdio } = await import('./stdio-bootstrap-
|
|
163
|
+
const { bootstrapStdio } = await import('./stdio-bootstrap-QK4ENRPP.js');
|
|
164
164
|
return bootstrapStdio(config);
|
|
165
165
|
}
|
|
166
|
-
const { bootstrapHttp } = await import('./http-bootstrap-
|
|
166
|
+
const { bootstrapHttp } = await import('./http-bootstrap-XJNTLL2C.js');
|
|
167
167
|
return bootstrapHttp(config);
|
|
168
168
|
}
|
|
169
169
|
main().then(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { err, ok } from './chunk-
|
|
2
|
+
import { err, ok } from './chunk-XSI6SZTX.js';
|
|
3
3
|
import { createHash, randomUUID } from 'crypto';
|
|
4
4
|
import createClient from 'openapi-fetch';
|
|
5
5
|
import { fetch } from 'undici';
|
|
@@ -245,6 +245,7 @@ z.object({
|
|
|
245
245
|
url: z.union([z.string(), z.null()]).optional(),
|
|
246
246
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
247
247
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
248
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
248
249
|
country_code: z.string().min(2).max(2),
|
|
249
250
|
emulator_id: z.string().min(1).max(100),
|
|
250
251
|
proxy: ProxyTargetRequest.optional(),
|
|
@@ -362,6 +363,7 @@ var ScanResponse = z.object({
|
|
|
362
363
|
public_report_url: z.string().optional().default(""),
|
|
363
364
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
364
365
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
366
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
365
367
|
creative_kind: z.enum(["banner", "video"]).optional().default("banner"),
|
|
366
368
|
creative_screenshot_url: z.string().optional().default(""),
|
|
367
369
|
creative_video_url: z.string().optional().default(""),
|
|
@@ -433,6 +435,7 @@ z.object({
|
|
|
433
435
|
url: z.union([z.string(), z.null()]).optional(),
|
|
434
436
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
435
437
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
438
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
436
439
|
country_codes: z.array(z.string()).min(1),
|
|
437
440
|
emulator_id: z.string().min(1).max(100),
|
|
438
441
|
proxy: ProxyTargetRequest.optional(),
|
|
@@ -490,6 +493,7 @@ z.object({
|
|
|
490
493
|
url: z.union([z.string(), z.null()]).optional(),
|
|
491
494
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
492
495
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
496
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
493
497
|
country_codes: z.array(z.string()).min(1),
|
|
494
498
|
group_id: z.union([z.string(), z.null()]).optional(),
|
|
495
499
|
emulator_categories: z.array(z.string()).optional(),
|
|
@@ -522,6 +526,7 @@ var CampaignResponse = z.object({
|
|
|
522
526
|
url: z.string(),
|
|
523
527
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
524
528
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
529
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
525
530
|
country_codes: z.array(z.string()),
|
|
526
531
|
group_id: z.string().uuid(),
|
|
527
532
|
emulator_selection: EmulatorSelectionResponse,
|
|
@@ -562,6 +567,7 @@ z.object({
|
|
|
562
567
|
url: z.union([z.string(), z.null()]),
|
|
563
568
|
ad_tag: z.union([z.string(), z.null()]),
|
|
564
569
|
vast_tag: z.union([z.string(), z.null()]),
|
|
570
|
+
referrer: z.union([z.string(), z.null()]),
|
|
565
571
|
country_codes: z.union([z.array(z.string()), z.null()]),
|
|
566
572
|
group_id: z.union([z.string(), z.null()]),
|
|
567
573
|
emulator_categories: z.union([z.array(z.string()), z.null()]),
|
|
@@ -1339,6 +1345,7 @@ var CampaignSchema = schemas.CampaignResponse.pick({
|
|
|
1339
1345
|
url: true,
|
|
1340
1346
|
ad_tag: true,
|
|
1341
1347
|
vast_tag: true,
|
|
1348
|
+
referrer: true,
|
|
1342
1349
|
country_codes: true,
|
|
1343
1350
|
group_id: true,
|
|
1344
1351
|
emulator_selection: true,
|
|
@@ -1772,6 +1779,7 @@ var ScanSchema = schemas.ScanResponse.pick({
|
|
|
1772
1779
|
public_report_url: true,
|
|
1773
1780
|
ad_tag: true,
|
|
1774
1781
|
vast_tag: true,
|
|
1782
|
+
referrer: true,
|
|
1775
1783
|
creative_kind: true,
|
|
1776
1784
|
video: true,
|
|
1777
1785
|
creative_screenshot_url: true,
|
|
@@ -3672,6 +3680,10 @@ function pickCampaignConfigBody(input) {
|
|
|
3672
3680
|
if (input.schedule_timezone !== void 0) body.schedule_timezone = input.schedule_timezone;
|
|
3673
3681
|
return body;
|
|
3674
3682
|
}
|
|
3683
|
+
var campaignReferrerUrl = z.string().url().regex(/^https?:\/\//i, "Must be an http(s) URL.").max(2048);
|
|
3684
|
+
var CAMPAIGN_REFERRER_DESCRIPTION = "Optional http(s) page URL every scan of this campaign is checked from. For `ad_tag` and `vast` campaigns this is the publisher page the tag is embedded in \u2014 NOT the creative and NOT its landing page; the browser commits the page document on this URL without fetching the publisher, so the creative renders as if embedded there, and its cross-origin subrequests receive the origin only (https://publisher.example/, no path). For `url` and `ad_discovery` campaigns this is where the visitor came from, sent in full as the Referer of the page request.";
|
|
3685
|
+
var campaignReferrerField = campaignReferrerUrl.optional().describe(CAMPAIGN_REFERRER_DESCRIPTION);
|
|
3686
|
+
var campaignReferrerUpdateField = campaignReferrerUrl.nullable().optional().describe(`${CAMPAIGN_REFERRER_DESCRIPTION} Pass null to clear it.`);
|
|
3675
3687
|
|
|
3676
3688
|
// src/application/tools/campaigns/create-campaign.tool.ts
|
|
3677
3689
|
var CreateCampaignInputShape = {
|
|
@@ -3686,6 +3698,7 @@ var CreateCampaignInputShape = {
|
|
|
3686
3698
|
vast_tag: z.string().optional().describe(
|
|
3687
3699
|
"VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (required if campaign_type=vast)."
|
|
3688
3700
|
),
|
|
3701
|
+
referrer: campaignReferrerField,
|
|
3689
3702
|
country_codes: z.array(z.string().length(2)).min(1).describe("ISO 3166-1 alpha-2 codes \u2014 one scan per country per run."),
|
|
3690
3703
|
group_id: z.string().uuid().optional().describe("Parent group UUID; defaults to the org's default group."),
|
|
3691
3704
|
...campaignConfigFields,
|
|
@@ -3712,6 +3725,7 @@ var createCampaignTool = {
|
|
|
3712
3725
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
3713
3726
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3714
3727
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
3728
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
3715
3729
|
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3716
3730
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3717
3731
|
...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
|
|
@@ -3882,6 +3896,7 @@ var UpdateCampaignInputShape = {
|
|
|
3882
3896
|
vast_tag: z.string().optional().describe(
|
|
3883
3897
|
"New VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (vast-type campaigns)."
|
|
3884
3898
|
),
|
|
3899
|
+
referrer: campaignReferrerUpdateField,
|
|
3885
3900
|
country_codes: z.array(z.string().length(2)).optional().describe("Replace the country list."),
|
|
3886
3901
|
group_id: z.string().uuid().optional().describe("Move the campaign to another group."),
|
|
3887
3902
|
...campaignConfigFields,
|
|
@@ -3891,7 +3906,7 @@ var UpdateCampaignInputShape = {
|
|
|
3891
3906
|
};
|
|
3892
3907
|
var updateCampaignTool = {
|
|
3893
3908
|
name: "update_campaign",
|
|
3894
|
-
description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding.",
|
|
3909
|
+
description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding, and `referrer` accepts null to clear the publisher page scans are checked from.",
|
|
3895
3910
|
annotations: {
|
|
3896
3911
|
title: "Update Campaign",
|
|
3897
3912
|
readOnlyHint: false,
|
|
@@ -3906,6 +3921,7 @@ var updateCampaignTool = {
|
|
|
3906
3921
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
3907
3922
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3908
3923
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
3924
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
3909
3925
|
...input.country_codes !== void 0 ? { country_codes: input.country_codes } : {},
|
|
3910
3926
|
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3911
3927
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
@@ -3918,6 +3934,21 @@ var updateCampaignTool = {
|
|
|
3918
3934
|
return ok(result.value);
|
|
3919
3935
|
}
|
|
3920
3936
|
};
|
|
3937
|
+
var COMBO_MATCH_SCOPE_DOC = 'For `rule_type=\'combo\'` the optional rule-level key `match_scope` decides where conditions are counted. `"scan"` (the default, and the behaviour when the key is absent) checks them against the union of all tags on the scan. With `"url"`, every condition must be satisfied by tags on the same link, and the tag is assigned to that link \u2014 so a rule built only from link-less tags (AI verdicts, crawler behaviour) has no link to attach to and will never match; keep those on the whole scan. Mixing the two kinds does not help either: AI verdicts and per-link detections reach the engine in different scan parts and are not evaluated together today, so such a combo fires under neither scope. Give a `"url"` rule at least one positive condition (`all_of` / `any_of` / `tag_category` + `count_gte`) \u2014 a config carrying only `none_of` matches every link that merely lacks those tags, and the API does not reject that on this path. Thresholds stay "N or more" (`count_gte` / `any_of_min`) in both scopes; no other `match_scope` value is accepted.';
|
|
3938
|
+
var COMBO_MATCH_SCOPE_READ_DOC = 'For `rule_type=\'combo\'` the returned `config` may carry the rule-level key `match_scope` ("scan" = count conditions across the whole scan, "url" = every condition must be satisfied by tags on the same link). Resend it verbatim when updating: `update_custom_rule` replaces `config` wholesale, so dropping the key reverts the rule to whole-scan matching.';
|
|
3939
|
+
var ruleConfigField = z.record(z.unknown()).superRefine((config, ctx) => {
|
|
3940
|
+
const scope = config["match_scope"];
|
|
3941
|
+
if (scope === void 0 || scope === "scan" || scope === "url") {
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
ctx.addIssue({
|
|
3945
|
+
code: z.ZodIssueCode.custom,
|
|
3946
|
+
path: ["match_scope"],
|
|
3947
|
+
message: 'config.match_scope must be "scan" or "url". Omit the key for scan-wide matching (the default).'
|
|
3948
|
+
});
|
|
3949
|
+
});
|
|
3950
|
+
|
|
3951
|
+
// src/application/tools/custom-rules/create-custom-rule.tool.ts
|
|
3921
3952
|
var CreateCustomRuleInputShape = {
|
|
3922
3953
|
name: z.string().min(1).max(200).describe(
|
|
3923
3954
|
"Display name. For non-LLM rules this also becomes the `display_name` of the auto-registered tag definition (see `tag_slug`)."
|
|
@@ -3928,8 +3959,8 @@ var CreateCustomRuleInputShape = {
|
|
|
3928
3959
|
rule_type: z.string().max(50).describe(
|
|
3929
3960
|
"Rule engine. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`. The API validates."
|
|
3930
3961
|
),
|
|
3931
|
-
config:
|
|
3932
|
-
"Rule-type-specific configuration object. Shape depends on `rule_type`. For `rule_type='llm'` the shape is `{ prompt: string, tags: { <tag_slug>: <description>, ... } }`; each key in `config.tags` is auto-registered as a custom tag definition AND must not collide with a system slug (same 422 contract as `tag_slug`)."
|
|
3962
|
+
config: ruleConfigField.describe(
|
|
3963
|
+
"Rule-type-specific configuration object. Shape depends on `rule_type`. For `rule_type='llm'` the shape is `{ prompt: string, tags: { <tag_slug>: <description>, ... } }`; each key in `config.tags` is auto-registered as a custom tag definition AND must not collide with a system slug (same 422 contract as `tag_slug`). " + COMBO_MATCH_SCOPE_DOC
|
|
3933
3964
|
),
|
|
3934
3965
|
target: z.string().max(30).optional().describe(
|
|
3935
3966
|
"Where to apply the rule (e.g. 'page' for landing HTML). Default: page. See API docs for the full set of valid values."
|
|
@@ -3937,7 +3968,7 @@ var CreateCustomRuleInputShape = {
|
|
|
3937
3968
|
};
|
|
3938
3969
|
var createCustomRuleTool = {
|
|
3939
3970
|
name: "create_custom_rule",
|
|
3940
|
-
description: "Define a custom tag-detection rule. The API auto-registers a tag definition for each slug the rule emits (`tag_slug` for non-LLM rules; `config.tags` keys for `rule_type='llm'`); slugs that collide with a built-in system tag are rejected with HTTP 422 / code `checking.system_slug_reserved`. Matches tag every future scan; existing scans are untouched until you call `recheck_scans`.",
|
|
3971
|
+
description: "Define a custom tag-detection rule. The API auto-registers a tag definition for each slug the rule emits (`tag_slug` for non-LLM rules; `config.tags` keys for `rule_type='llm'`); slugs that collide with a built-in system tag are rejected with HTTP 422 / code `checking.system_slug_reserved`. For `rule_type='combo'` set `config.match_scope='url'` when the thresholds must be met inside one link instead of anywhere on the scan (default `'scan'`). Matches tag every future scan; existing scans are untouched until you call `recheck_scans`.",
|
|
3941
3972
|
annotations: {
|
|
3942
3973
|
title: "Create Custom Rule",
|
|
3943
3974
|
readOnlyHint: false,
|
|
@@ -3982,7 +4013,7 @@ var deleteCustomRuleTool = {
|
|
|
3982
4013
|
var GetCustomRuleInputShape = { rule_id: z.string().uuid().describe("Rule UUID.") };
|
|
3983
4014
|
var getCustomRuleTool = {
|
|
3984
4015
|
name: "get_custom_rule",
|
|
3985
|
-
description: "Get one custom rule by UUID with name, tag-slug, type, config object, target, active flag.",
|
|
4016
|
+
description: "Get one custom rule by UUID with name, tag-slug, type, config object, target, active flag. " + COMBO_MATCH_SCOPE_READ_DOC,
|
|
3986
4017
|
annotations: {
|
|
3987
4018
|
title: "Get Custom Rule",
|
|
3988
4019
|
readOnlyHint: true,
|
|
@@ -4003,7 +4034,7 @@ var ListCustomRulesInputShape = {
|
|
|
4003
4034
|
};
|
|
4004
4035
|
var listCustomRulesTool = {
|
|
4005
4036
|
name: "list_custom_rules",
|
|
4006
|
-
description: "Paginated list of the organization's custom tag-detection rules (regex / heuristics) with their config, target, and active flag. Returns `{items, total, page, limit}`. To iterate: there is a next page iff `page * limit < total` (or equivalently `items.length === limit` until the last page). `page` is 1-indexed.",
|
|
4037
|
+
description: "Paginated list of the organization's custom tag-detection rules (regex / heuristics) with their config, target, and active flag. Returns `{items, total, page, limit}`. To iterate: there is a next page iff `page * limit < total` (or equivalently `items.length === limit` until the last page). `page` is 1-indexed. " + COMBO_MATCH_SCOPE_READ_DOC,
|
|
4007
4038
|
annotations: {
|
|
4008
4039
|
title: "List Custom Rules",
|
|
4009
4040
|
readOnlyHint: true,
|
|
@@ -4022,8 +4053,8 @@ var TestCustomRuleInputShape = {
|
|
|
4022
4053
|
rule_type: z.string().max(50).describe(
|
|
4023
4054
|
"Rule engine type. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`."
|
|
4024
4055
|
),
|
|
4025
|
-
config:
|
|
4026
|
-
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. NOTE: `test_custom_rule` evaluates the rule against a scan WITHOUT persisting it, so slug-collision validation does NOT run here \u2014 verify slugs against `list_tags` (`scope=system`) before promoting to `create_custom_rule`."
|
|
4056
|
+
config: ruleConfigField.describe(
|
|
4057
|
+
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. NOTE: `test_custom_rule` evaluates the rule against a scan WITHOUT persisting it, so slug-collision validation does NOT run here \u2014 verify slugs against `list_tags` (`scope=system`) before promoting to `create_custom_rule`. " + COMBO_MATCH_SCOPE_DOC
|
|
4027
4058
|
),
|
|
4028
4059
|
target: z.string().max(30).describe(
|
|
4029
4060
|
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
@@ -4032,7 +4063,7 @@ var TestCustomRuleInputShape = {
|
|
|
4032
4063
|
};
|
|
4033
4064
|
var testCustomRuleTool = {
|
|
4034
4065
|
name: "test_custom_rule",
|
|
4035
|
-
description: "Preview-test a rule definition against an existing scan WITHOUT persisting the rule. Returns `matched: bool`, evaluation time, and per-tag-slug details. Use to validate config before `create_custom_rule`. NOTE: slug-collision validation does NOT run in preview mode \u2014 verify any `tag_slug` (or LLM `config.tags` keys) against `list_tags` (`scope=system`) before promoting to `create_custom_rule`, which would otherwise reject the same payload with HTTP 422 / code `checking.system_slug_reserved`.",
|
|
4066
|
+
description: "Preview-test a rule definition against an existing scan WITHOUT persisting the rule. Returns `matched: bool`, evaluation time, and per-tag-slug details. Use to validate config before `create_custom_rule`. Combo rules are evaluated against the scan's existing tags, so `config.match_scope` is honoured \u2014 but per-URL hits are collapsed to one row per tag slug, so the result says whether the rule fires, not which URLs it would tag. NOTE: slug-collision validation does NOT run in preview mode \u2014 verify any `tag_slug` (or LLM `config.tags` keys) against `list_tags` (`scope=system`) before promoting to `create_custom_rule`, which would otherwise reject the same payload with HTTP 422 / code `checking.system_slug_reserved`.",
|
|
4036
4067
|
annotations: {
|
|
4037
4068
|
title: "Test Custom Rule",
|
|
4038
4069
|
readOnlyHint: true,
|
|
@@ -4060,8 +4091,8 @@ var UpdateCustomRuleInputShape = {
|
|
|
4060
4091
|
tag_slug: z.string().max(100).optional().describe(
|
|
4061
4092
|
"New tag slug to assign on match. **MUST NOT collide with a built-in system tag slug** (see `list_tags` where `scope=system`); colliding requests return 422 with code `checking.system_slug_reserved`. Re-registering an existing custom slug refreshes its tag definition's `display_name` / `description`."
|
|
4062
4093
|
),
|
|
4063
|
-
config:
|
|
4064
|
-
"New rule-type-specific config object. For `rule_type='llm'` the keys of `config.tags` are auto-registered as tag definitions; any key that collides with a system slug returns the same 422 contract."
|
|
4094
|
+
config: ruleConfigField.optional().describe(
|
|
4095
|
+
"New rule-type-specific config object. Replaces the stored config wholesale \u2014 resend every key you want to keep, including a combo rule's `match_scope`. For `rule_type='llm'` the keys of `config.tags` are auto-registered as tag definitions; any key that collides with a system slug returns the same 422 contract. " + COMBO_MATCH_SCOPE_DOC
|
|
4065
4096
|
),
|
|
4066
4097
|
target: z.string().max(30).optional().describe(
|
|
4067
4098
|
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
@@ -4070,7 +4101,7 @@ var UpdateCustomRuleInputShape = {
|
|
|
4070
4101
|
};
|
|
4071
4102
|
var updateCustomRuleTool = {
|
|
4072
4103
|
name: "update_custom_rule",
|
|
4073
|
-
description: "Update a custom tag-detection rule. Only supplied fields are sent. Changing `tag_slug` (or `config.tags` keys for `rule_type='llm'`) onto a built-in system slug returns HTTP 422 / code `checking.system_slug_reserved`. To toggle activation, pass `is_active`. Existing tagged scans are NOT re-evaluated \u2014 call `recheck_scans` for that. (Rule engine `rule_type` cannot be changed after creation; create a new rule instead.)",
|
|
4104
|
+
description: "Update a custom tag-detection rule. Only supplied fields are sent, but a supplied `config` replaces the stored one wholesale (omitting a combo rule's `match_scope` reverts it to the scan-wide default). Changing `tag_slug` (or `config.tags` keys for `rule_type='llm'`) onto a built-in system slug returns HTTP 422 / code `checking.system_slug_reserved`. To toggle activation, pass `is_active`. Existing tagged scans are NOT re-evaluated \u2014 call `recheck_scans` for that. (Rule engine `rule_type` cannot be changed after creation; create a new rule instead.)",
|
|
4074
4105
|
annotations: {
|
|
4075
4106
|
title: "Update Custom Rule",
|
|
4076
4107
|
readOnlyHint: false,
|
|
@@ -4733,6 +4764,9 @@ var scanProxyField = z.object({
|
|
|
4733
4764
|
city: z.string().optional().describe("Proxy city targeting."),
|
|
4734
4765
|
isp: z.string().optional().describe("Proxy ISP targeting.")
|
|
4735
4766
|
}).optional().describe("Optional proxy geo targeting for the crawl. Omit to use the org default.");
|
|
4767
|
+
var scanReferrerField = z.string().url().regex(/^https?:\/\//i, "Must be an http(s) URL.").max(2048).optional().describe(
|
|
4768
|
+
"Optional http(s) page URL the check is performed from. With `ad_tag` or `vast_tag` this is the publisher page the tag is embedded in \u2014 NOT the creative and NOT its landing page; the browser commits the page document on this URL without fetching the publisher, so the creative renders as if embedded there, and its cross-origin subrequests receive the origin only (https://publisher.example/, no path). With a `url` target this is where the visitor came from, sent in full as the Referer of the page request."
|
|
4769
|
+
);
|
|
4736
4770
|
|
|
4737
4771
|
// src/application/tools/scans/create-bulk-scans.tool.ts
|
|
4738
4772
|
var CreateBulkScansInputShape = {
|
|
@@ -4743,6 +4777,7 @@ var CreateBulkScansInputShape = {
|
|
|
4743
4777
|
vast_tag: z.string().optional().describe(
|
|
4744
4778
|
"VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML. Provide exactly one of `url`, `ad_tag`, or `vast_tag`."
|
|
4745
4779
|
),
|
|
4780
|
+
referrer: scanReferrerField,
|
|
4746
4781
|
country_codes: z.array(z.string().length(2)).min(1).max(50).describe("List of ISO 3166-1 alpha-2 country codes; one scan per country is created."),
|
|
4747
4782
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; same for every country in the batch."),
|
|
4748
4783
|
proxy: scanProxyField,
|
|
@@ -4767,6 +4802,7 @@ var createBulkScansTool = {
|
|
|
4767
4802
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
4768
4803
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4769
4804
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
4805
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
4770
4806
|
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
4771
4807
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
4772
4808
|
...pickRepeatRetryBody(input)
|
|
@@ -4786,6 +4822,7 @@ var CreateScanInputShape = {
|
|
|
4786
4822
|
vast_tag: z.string().optional().describe(
|
|
4787
4823
|
"VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (a document containing a <VAST> element). Fetched and played in a real browser. Provide exactly one of `url`, `ad_tag`, or `vast_tag`."
|
|
4788
4824
|
),
|
|
4825
|
+
referrer: scanReferrerField,
|
|
4789
4826
|
country_code: z.string().length(2).describe("ISO 3166-1 alpha-2 country code, e.g. US, DE, JP. Determines proxy geo."),
|
|
4790
4827
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; use `list_emulators` to discover valid values."),
|
|
4791
4828
|
proxy: scanProxyField,
|
|
@@ -4815,6 +4852,7 @@ var createScanTool = {
|
|
|
4815
4852
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
4816
4853
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4817
4854
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
4855
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
4818
4856
|
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
4819
4857
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
4820
4858
|
...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
|
|
@@ -5611,5 +5649,5 @@ function formatToolError(error) {
|
|
|
5611
5649
|
}
|
|
5612
5650
|
|
|
5613
5651
|
export { BearerToken, SERVER_INSTRUCTIONS, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
|
|
5614
|
-
//# sourceMappingURL=chunk-
|
|
5615
|
-
//# sourceMappingURL=chunk-
|
|
5652
|
+
//# sourceMappingURL=chunk-XCAKB2QW.js.map
|
|
5653
|
+
//# sourceMappingURL=chunk-XCAKB2QW.js.map
|