@kaminari-ad/mcp 0.19.1 → 0.22.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 +65 -0
- package/README.md +3 -3
- package/dist/bin.js +3 -3
- package/dist/{chunk-MKBTS4IH.js → chunk-J6SALJR4.js} +3 -3
- package/dist/{chunk-MKBTS4IH.js.map → chunk-J6SALJR4.js.map} +1 -1
- package/dist/{chunk-NPSDASQE.js → chunk-VEPJEI3S.js} +79 -18
- package/dist/chunk-VEPJEI3S.js.map +1 -0
- package/dist/{http-bootstrap-FN4DOOJT.js → http-bootstrap-JX5RQALF.js} +4 -4
- package/dist/{http-bootstrap-FN4DOOJT.js.map → http-bootstrap-JX5RQALF.js.map} +1 -1
- package/dist/{stdio-bootstrap-4PXPX6BD.js → stdio-bootstrap-6PXVYXHT.js} +4 -4
- package/dist/{stdio-bootstrap-4PXPX6BD.js.map → stdio-bootstrap-6PXVYXHT.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-NPSDASQE.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.22.0] - 2026-09-15
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **`regexp_request_url` and `regexp_request_body` accept the creative
|
|
15
|
+
surface.** Both were rejected locally with anything but `target='page'`,
|
|
16
|
+
which made the rule that catches an exploit kit embedded in a banner
|
|
17
|
+
unauthorable: that kit is a script inside the creative's own iframe and
|
|
18
|
+
appears on no landing page's request list. Both now take `'page'`,
|
|
19
|
+
`'creative'` or `'creative_and_page'` (evaluated on each surface, every
|
|
20
|
+
match reporting the one it came from), matching what the API validates.
|
|
21
|
+
Tool descriptions updated with it, since an agent picks the target from
|
|
22
|
+
them.
|
|
23
|
+
|
|
24
|
+
Requires the API-side deploy that widens the same validator. Until it
|
|
25
|
+
lands, production rejects the two new targets with HTTP 422.
|
|
26
|
+
|
|
27
|
+
## [0.21.0] - 2026-09-14
|
|
28
|
+
|
|
29
|
+
> Requires the API-side KAMIAD-177 deploy. Until it lands, production
|
|
30
|
+
> rejects `ignore_first_n_domains` as an unknown body field, so do not tag
|
|
31
|
+
> this release ahead of the API.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **`ignore_first_n_domains` on `create_campaign`, `update_campaign`,
|
|
36
|
+
`create_scan` and `create_bulk_scans`.** A check that enters through the
|
|
37
|
+
caller's own click or tracking domains used to have those domains rated
|
|
38
|
+
by every domain checker, so a reputation hit on the caller's own
|
|
39
|
+
infrastructure tagged the material and raised an alert that said nothing
|
|
40
|
+
about the offer. The new field (0-5, default 0) drops the first N unique
|
|
41
|
+
domains of the redirect chain, counted from the entry point, from
|
|
42
|
+
detection and tagging. Domains fold to their registrable form, so
|
|
43
|
+
`www.example.com` and `example.com` consume one slot between them, and
|
|
44
|
+
sub-requests made from a skipped domain are excluded too.
|
|
45
|
+
|
|
46
|
+
Deliberately NOT nullable, unlike `referrer` and `max_discovered_ads`
|
|
47
|
+
next to it: `0` is both the default and the reset, so a nullable variant
|
|
48
|
+
would only offer an agent a second spelling the API answers with a 422.
|
|
49
|
+
The shared `.describe()` text warns that a skipped domain is checked by
|
|
50
|
+
nothing, because the failure mode of guessing high is a hidden finding
|
|
51
|
+
rather than an error.
|
|
52
|
+
|
|
53
|
+
`CampaignResponse` and `ScanResponse` both echo the field, so an agent can
|
|
54
|
+
read back what a campaign will do and how much of a finished scan's chain
|
|
55
|
+
was excluded — the latter is the only way to answer that once the campaign
|
|
56
|
+
behind the scan has been edited. Parsed as optional rather than defaulted
|
|
57
|
+
to 0: an api that predates the field says nothing, and inventing a 0 would
|
|
58
|
+
claim the scan skipped nothing when the truth is unknown.
|
|
59
|
+
|
|
60
|
+
The generated schemas were left alone: the field is declared through the
|
|
61
|
+
same port-side intersection `max_discovered_ads` uses, because
|
|
62
|
+
`gen:api-types` reads the DEPLOYED spec and cannot see an unshipped
|
|
63
|
+
field. The next regen absorbs it and the intersections can go.
|
|
64
|
+
|
|
65
|
+
## [0.20.0] - 2026-09-13
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
|
|
69
|
+
- **`set_default_policy_set`** — mark or clear an owned policy set as
|
|
70
|
+
the organization's default for new campaigns (`POST /api/v1/policy-sets/{id}/set-default`).
|
|
71
|
+
`list_policy_sets` / `get_policy_set` now surface `is_default`. Omit
|
|
72
|
+
`policy_set_id` on `create_campaign` to bind that default; pass
|
|
73
|
+
`null` to create an unbound campaign.
|
|
74
|
+
|
|
10
75
|
## [0.19.1] - 2026-09-10
|
|
11
76
|
|
|
12
77
|
> Requires the API-side KAMIAD-104 deploy. Until it lands, production
|
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ Add to your MCP client config (Cursor: `~/.cursor/mcp.json`; Claude Desktop: `~/
|
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Restart your client. You should see `kaminari-ad` in the MCP servers list with
|
|
66
|
+
Restart your client. You should see `kaminari-ad` in the MCP servers list with 108 tools exposed.
|
|
67
67
|
|
|
68
68
|
### 2b. Hosted HTTP transport (no install)
|
|
69
69
|
|
|
@@ -113,7 +113,7 @@ which decides which credential type minted it.
|
|
|
113
113
|
|
|
114
114
|
## Tools
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
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:
|
|
117
117
|
|
|
118
118
|
- **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`
|
|
119
119
|
- **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
|
- **Tags** (5) — `list_tags`, `get_tag_definition`, `update_tag_definition`, `delete_tag_definition`, `list_scan_tags`
|
|
124
124
|
- **Custom rules** (6) — `list_custom_rules`, `get_custom_rule`, `create_custom_rule`, `update_custom_rule`, `delete_custom_rule`, `test_custom_rule`
|
|
125
125
|
- **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`
|
|
126
|
-
- **Policy sets** (
|
|
126
|
+
- **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`
|
|
127
127
|
- **Alerts** (4) — `list_alerts`, `update_alert_status`, `bulk_update_alert_status`, `get_alert_stats`
|
|
128
128
|
- **Webhooks** (11) — `list_webhooks`, `get_webhook`, `create_webhook`, `update_webhook`, `delete_webhook`, `list_webhook_event_types`, `list_webhook_deliveries`, `test_webhook`, `rotate_webhook_secret`, `replay_webhook_delivery`, `bulk_replay_webhook`
|
|
129
129
|
- **Billing** (4) — `get_billing_summary`, `list_usage`, `get_usage_summary`, `list_balance_history`
|
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-J6SALJR4.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-6PXVYXHT.js');
|
|
164
164
|
return bootstrapStdio(config);
|
|
165
165
|
}
|
|
166
|
-
const { bootstrapHttp } = await import('./http-bootstrap-
|
|
166
|
+
const { bootstrapHttp } = await import('./http-bootstrap-JX5RQALF.js');
|
|
167
167
|
return bootstrapHttp(config);
|
|
168
168
|
}
|
|
169
169
|
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.
|
|
6
|
+
var VERSION = "0.22.0";
|
|
7
7
|
|
|
8
8
|
export { NAME, VERSION };
|
|
9
|
-
//# sourceMappingURL=chunk-
|
|
10
|
-
//# sourceMappingURL=chunk-
|
|
9
|
+
//# sourceMappingURL=chunk-J6SALJR4.js.map
|
|
10
|
+
//# sourceMappingURL=chunk-J6SALJR4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;;AASO,IAAM,IAAA,GAAO;AACb,IAAM,OAAA,GAAU","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;;AASO,IAAM,IAAA,GAAO;AACb,IAAM,OAAA,GAAU","file":"chunk-J6SALJR4.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.22.0\";\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { err, ok } from './chunk-
|
|
2
|
+
import { err, ok } from './chunk-J6SALJR4.js';
|
|
3
3
|
import { createHash, randomUUID } from 'crypto';
|
|
4
4
|
import createClient from 'openapi-fetch';
|
|
5
5
|
import { fetch } from 'undici';
|
|
@@ -832,6 +832,7 @@ var PolicySetResponse = z.object({
|
|
|
832
832
|
organization_id: z.string().uuid(),
|
|
833
833
|
visibility: z.string(),
|
|
834
834
|
is_approved: z.boolean(),
|
|
835
|
+
is_default: z.boolean(),
|
|
835
836
|
entries: z.array(PolicyEntryResponse),
|
|
836
837
|
campaigns: z.array(LinkedCampaignResponse).optional(),
|
|
837
838
|
campaigns_total: z.number().int().optional().default(0),
|
|
@@ -846,6 +847,7 @@ var PolicySetListItem = z.object({
|
|
|
846
847
|
organization_id: z.string().uuid(),
|
|
847
848
|
visibility: z.string(),
|
|
848
849
|
is_approved: z.boolean(),
|
|
850
|
+
is_default: z.boolean(),
|
|
849
851
|
created_at: z.string().datetime({ offset: true })
|
|
850
852
|
}).passthrough();
|
|
851
853
|
z.object({
|
|
@@ -873,6 +875,7 @@ z.object({
|
|
|
873
875
|
campaign_ids: z.array(z.string().uuid()).max(500),
|
|
874
876
|
detach_all: z.boolean().default(false)
|
|
875
877
|
}).partial().passthrough();
|
|
878
|
+
z.object({ is_default: z.boolean() }).passthrough();
|
|
876
879
|
var AlertStatus = z.enum(["open", "escalated", "resolved", "dismissed"]);
|
|
877
880
|
z.union([AlertStatus, z.null()]).optional();
|
|
878
881
|
var AlertResponse = z.object({
|
|
@@ -1377,7 +1380,7 @@ var CampaignSchema = schemas.CampaignResponse.pick({
|
|
|
1377
1380
|
is_archived: true,
|
|
1378
1381
|
created_at: true,
|
|
1379
1382
|
last_run_at: true
|
|
1380
|
-
}).extend({ repeat_mode: schemas.RepeatMode.default("isolated") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).strip();
|
|
1383
|
+
}).extend({ repeat_mode: schemas.RepeatMode.default("isolated") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).extend({ ignore_first_n_domains: z.number().int().optional() }).strip();
|
|
1381
1384
|
var parseCampaign = (raw) => parseWithSchema(CampaignSchema, raw, "campaign");
|
|
1382
1385
|
var parseCampaignPage = (raw) => parsePagedWithItemSchema(CampaignSchema, raw, "campaigns");
|
|
1383
1386
|
|
|
@@ -1747,6 +1750,7 @@ var PolicySetSchema = schemas.PolicySetResponse.pick({
|
|
|
1747
1750
|
organization_id: true,
|
|
1748
1751
|
visibility: true,
|
|
1749
1752
|
is_approved: true,
|
|
1753
|
+
is_default: true,
|
|
1750
1754
|
entries: true,
|
|
1751
1755
|
created_at: true
|
|
1752
1756
|
}).strip();
|
|
@@ -1760,6 +1764,7 @@ var PolicySetListItemSchema = schemas.PolicySetListItem.pick({
|
|
|
1760
1764
|
organization_id: true,
|
|
1761
1765
|
visibility: true,
|
|
1762
1766
|
is_approved: true,
|
|
1767
|
+
is_default: true,
|
|
1763
1768
|
created_at: true
|
|
1764
1769
|
}).strip();
|
|
1765
1770
|
var parsePolicySetPage = (raw) => parsePagedWithItemSchema(PolicySetListItemSchema, raw, "policy-sets");
|
|
@@ -1839,7 +1844,7 @@ var ScanSchema = schemas.ScanResponse.pick({
|
|
|
1839
1844
|
repeat_scan_ids: true,
|
|
1840
1845
|
retry_attempt: true,
|
|
1841
1846
|
retry_max_attempts: true
|
|
1842
|
-
}).extend({ creative_kind: z.string().default("banner") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).extend({ network_host: z.string().optional() }).strip();
|
|
1847
|
+
}).extend({ creative_kind: z.string().default("banner") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).extend({ network_host: z.string().optional() }).extend({ ignore_first_n_domains: z.number().int().optional() }).strip();
|
|
1843
1848
|
var ScanArraySchema = z.array(ScanSchema);
|
|
1844
1849
|
var parseScan = (raw) => parseWithSchema(ScanSchema, raw, "scan");
|
|
1845
1850
|
var parseScanArray = (raw) => parseWithSchema(ScanArraySchema, raw, "scans");
|
|
@@ -2562,6 +2567,14 @@ function createHttpApiGateway(config) {
|
|
|
2562
2567
|
parseEmpty
|
|
2563
2568
|
);
|
|
2564
2569
|
},
|
|
2570
|
+
async setDefaultPolicySet(id, isDefault) {
|
|
2571
|
+
return call(
|
|
2572
|
+
"POST",
|
|
2573
|
+
"/api/v1/policy-sets/{policy_set_id}/set-default",
|
|
2574
|
+
{ params: { path: { policy_set_id: id } }, body: { is_default: isDefault } },
|
|
2575
|
+
parseEmpty
|
|
2576
|
+
);
|
|
2577
|
+
},
|
|
2565
2578
|
// ── Custom taxonomies ─────────────────────────────────────────
|
|
2566
2579
|
async listCustomTaxonomies(filters) {
|
|
2567
2580
|
return call(
|
|
@@ -3890,6 +3903,15 @@ var cancelCampaignTool = {
|
|
|
3890
3903
|
return ok(result.value);
|
|
3891
3904
|
}
|
|
3892
3905
|
};
|
|
3906
|
+
var IGNORE_FIRST_N_DOMAINS_DESCRIPTION = "How many leading domains of the redirect chain to exclude from detection and tagging (0-5). Use it when the check enters through the caller's OWN click or tracking domains: without it a reputation hit on one of those tags the material and raises an alert about the caller's infrastructure rather than about the offer. Counting starts at the entry point and follows chain order; domains fold to their registrable form, so www.example.com and example.com consume one slot between them, and any other request to one of those domains is excluded too. A skipped domain is checked by NOTHING, so never set this higher than the number of domains the caller actually owns at the head of the chain \u2014 ask rather than guess. The full redirect chain is still captured and returned either way.";
|
|
3907
|
+
var ignoreFirstNDomains = z.number().int().min(0).max(5);
|
|
3908
|
+
var ignoreFirstNDomainsField = ignoreFirstNDomains.optional().describe(`${IGNORE_FIRST_N_DOMAINS_DESCRIPTION} Omit it to skip nothing (0).`);
|
|
3909
|
+
var ignoreFirstNDomainsScanField = ignoreFirstNDomains.optional().describe(
|
|
3910
|
+
`${IGNORE_FIRST_N_DOMAINS_DESCRIPTION} Omit it to skip nothing (0). A scan created directly does NOT inherit this from the campaign named in \`campaign_id\` \u2014 only scans queued by a campaign run do \u2014 so send it explicitly on every direct submission that needs it.`
|
|
3911
|
+
);
|
|
3912
|
+
var ignoreFirstNDomainsUpdateField = ignoreFirstNDomains.optional().describe(
|
|
3913
|
+
`${IGNORE_FIRST_N_DOMAINS_DESCRIPTION} Omitting the field leaves the campaign's current setting unchanged; pass 0 to go back to skipping nothing. Not nullable \u2014 null is rejected with 422.`
|
|
3914
|
+
);
|
|
3893
3915
|
var MAX_DISCOVERED_ADS_DESCRIPTION = 'How many ad blocks to look for on each publisher page before stopping (1-25). Every ad found becomes its own scan with its own report and is BILLED AS A SEPARATE CHECK, so this multiplies the cost: a page scanned at 25 can cost 26 checks \u2014 one parent plus its children. Only valid on an ad-discovery target \u2014 `ad_discovery: true` on a scan, or `campaign_type: "ad_discovery"` on a campaign \u2014 and rejected with 422 anywhere else.';
|
|
3894
3916
|
var PLATFORM_DEFAULT_NOTE = "The platform default is 12 unless an operator retuned it.";
|
|
3895
3917
|
var maxDiscoveredAds = z.number().int().min(1).max(25);
|
|
@@ -3993,12 +4015,15 @@ var CreateCampaignInputShape = {
|
|
|
3993
4015
|
"VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (required if campaign_type=vast)."
|
|
3994
4016
|
),
|
|
3995
4017
|
referrer: campaignReferrerField,
|
|
4018
|
+
ignore_first_n_domains: ignoreFirstNDomainsField,
|
|
3996
4019
|
max_discovered_ads: maxDiscoveredAdsField,
|
|
3997
4020
|
country_codes: z.array(z.string().length(2)).min(1).describe("ISO 3166-1 alpha-2 codes \u2014 one scan per country per run."),
|
|
3998
4021
|
group_id: z.string().uuid().optional().describe("Parent group UUID; defaults to the org's default group."),
|
|
3999
4022
|
...campaignConfigFields,
|
|
4000
4023
|
labels: z.record(z.string()).optional().describe("Arbitrary metadata applied to every queued scan."),
|
|
4001
|
-
policy_set_id: z.string().uuid().optional().describe(
|
|
4024
|
+
policy_set_id: z.union([z.string().uuid(), z.null()]).optional().describe(
|
|
4025
|
+
"Policy set to evaluate every scan against. Omit to bind the organization's default (if any). Pass null for no policy set and no policy alerts."
|
|
4026
|
+
),
|
|
4002
4027
|
schedule_enabled: z.boolean().optional().describe("If true, the scheduler runs immediately. Default: false (manual run).")
|
|
4003
4028
|
};
|
|
4004
4029
|
var createCampaignTool = {
|
|
@@ -4021,6 +4046,7 @@ var createCampaignTool = {
|
|
|
4021
4046
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4022
4047
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
4023
4048
|
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
4049
|
+
...input.ignore_first_n_domains !== void 0 ? { ignore_first_n_domains: input.ignore_first_n_domains } : {},
|
|
4024
4050
|
...input.max_discovered_ads !== void 0 ? { max_discovered_ads: input.max_discovered_ads } : {},
|
|
4025
4051
|
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
4026
4052
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
@@ -4195,6 +4221,7 @@ var UpdateCampaignInputShape = {
|
|
|
4195
4221
|
"New VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (vast-type campaigns)."
|
|
4196
4222
|
),
|
|
4197
4223
|
referrer: campaignReferrerUpdateField,
|
|
4224
|
+
ignore_first_n_domains: ignoreFirstNDomainsUpdateField,
|
|
4198
4225
|
max_discovered_ads: maxDiscoveredAdsUpdateField,
|
|
4199
4226
|
country_codes: z.array(z.string().length(2)).optional().describe("Replace the country list."),
|
|
4200
4227
|
group_id: z.string().uuid().optional().describe("Move the campaign to another group."),
|
|
@@ -4205,7 +4232,7 @@ var UpdateCampaignInputShape = {
|
|
|
4205
4232
|
};
|
|
4206
4233
|
var updateCampaignTool = {
|
|
4207
4234
|
name: "update_campaign",
|
|
4208
|
-
description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding, `referrer` accepts null to clear the publisher page scans are checked from, and `max_discovered_ads` accepts null to go back to the platform ad cap.",
|
|
4235
|
+
description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding, `referrer` accepts null to clear the publisher page scans are checked from, and `max_discovered_ads` accepts null to go back to the platform ad cap. `ignore_first_n_domains` is the exception: it is not nullable, so pass 0 to go back to skipping nothing.",
|
|
4209
4236
|
annotations: {
|
|
4210
4237
|
title: "Update Campaign",
|
|
4211
4238
|
readOnlyHint: false,
|
|
@@ -4221,6 +4248,7 @@ var updateCampaignTool = {
|
|
|
4221
4248
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4222
4249
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
4223
4250
|
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
4251
|
+
...input.ignore_first_n_domains !== void 0 ? { ignore_first_n_domains: input.ignore_first_n_domains } : {},
|
|
4224
4252
|
...input.max_discovered_ads !== void 0 ? { max_discovered_ads: input.max_discovered_ads } : {},
|
|
4225
4253
|
...input.country_codes !== void 0 ? { country_codes: input.country_codes } : {},
|
|
4226
4254
|
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
@@ -4235,21 +4263,26 @@ var updateCampaignTool = {
|
|
|
4235
4263
|
}
|
|
4236
4264
|
};
|
|
4237
4265
|
var PATTERN_RULE_TYPES = ["regexp_request_url", "regexp_request_body"];
|
|
4266
|
+
var PATTERN_RULE_TARGETS = ["page", "creative", "creative_and_page"];
|
|
4238
4267
|
var patternRuleConfigSchema = z.object({
|
|
4239
4268
|
pattern: z.string().min(1).max(4096).describe("Non-empty regular expression, at most 4,096 characters."),
|
|
4240
4269
|
flags: z.enum(["", "i"]).optional().describe("Omit or use '' for case-sensitive matching; use 'i' to ignore case.")
|
|
4241
4270
|
}).strict();
|
|
4242
|
-
var PATTERN_RULE_CONFIG_DOC = "For `rule_type='regexp_request_url'` and `rule_type='regexp_request_body'`, `config` must be exactly `{ pattern: string, flags?: '' | 'i' }`: pattern is non-empty and at most 4,096 characters; omit flags or use `''` for case-sensitive matching, or `'i'` for case-insensitive matching. `target`
|
|
4271
|
+
var PATTERN_RULE_CONFIG_DOC = "For `rule_type='regexp_request_url'` and `rule_type='regexp_request_body'`, `config` must be exactly `{ pattern: string, flags?: '' | 'i' }`: pattern is non-empty and at most 4,096 characters; omit flags or use `''` for case-sensitive matching, or `'i'` for case-insensitive matching. `target` may be `'page'` (the landing tab), `'creative'` (the ad-tag creative's own traffic) or `'creative_and_page'` (both, reported separately) \u2014 a script inside a banner's iframe is on no landing's request list, so a page-only rule cannot see it. Default `page`. `regexp_request_url` inspects up to 5,000 captured request URLs on a fresh scan; tests and rechecks of stored scans reconstruct main-frame hops plus up to 200 persisted subrequests with selected resource types omitted, so historical matching is best-effort. `regexp_request_body` inspects the CONTENTS of the page's scripts, fetch/XHR responses and iframe documents \u2014 never images, video, fonts or stylesheets \u2014 capped at 400 resources, 128 KB each and 8 MB per scan. Those contents are kept for ONE DAY, so a test or recheck against an older scan reports no match because there is nothing left to read.";
|
|
4243
4272
|
function isPatternRuleType(ruleType) {
|
|
4244
4273
|
return PATTERN_RULE_TYPES.some((known) => known === ruleType);
|
|
4245
4274
|
}
|
|
4275
|
+
function isPatternRuleTarget(target) {
|
|
4276
|
+
return PATTERN_RULE_TARGETS.some((known) => known === target);
|
|
4277
|
+
}
|
|
4246
4278
|
function patternRuleInputError(input) {
|
|
4247
4279
|
if (!isPatternRuleType(input.rule_type)) return null;
|
|
4248
|
-
if (input.target !== void 0 && input.target
|
|
4280
|
+
if (input.target !== void 0 && !isPatternRuleTarget(input.target)) {
|
|
4281
|
+
const allowed = PATTERN_RULE_TARGETS.join(", ");
|
|
4249
4282
|
return {
|
|
4250
4283
|
kind: "invalid-input",
|
|
4251
|
-
message: `${input.rule_type}
|
|
4252
|
-
fieldErrors: { target: [`Must be
|
|
4284
|
+
message: `${input.rule_type} accepts target: ${allowed}.`,
|
|
4285
|
+
fieldErrors: { target: [`Must be one of: ${allowed}.`] }
|
|
4253
4286
|
};
|
|
4254
4287
|
}
|
|
4255
4288
|
const parsed = patternRuleConfigSchema.safeParse(input.config);
|
|
@@ -4292,12 +4325,12 @@ var CreateCustomRuleInputShape = {
|
|
|
4292
4325
|
"Rule-type-specific configuration object. Shape depends on `rule_type`. " + PATTERN_RULE_CONFIG_DOC + " `regexp_url` remains redirect-chain-only. 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
|
|
4293
4326
|
),
|
|
4294
4327
|
target: z.string().max(30).optional().describe(
|
|
4295
|
-
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` and `regexp_request_body`
|
|
4328
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` and `regexp_request_body` also accept `'creative'` (the ad-tag creative's own traffic) and `'creative_and_page'` (both, reported separately). Default: page. See API docs for the full set of valid values."
|
|
4296
4329
|
)
|
|
4297
4330
|
};
|
|
4298
4331
|
var createCustomRuleTool = {
|
|
4299
4332
|
name: "create_custom_rule",
|
|
4300
|
-
description: "Define a custom tag-detection rule. Use `rule_type='regexp_request_url'` to match captured network and subresource URLs
|
|
4333
|
+
description: "Define a custom tag-detection rule. Use `rule_type='regexp_request_url'` to match captured network and subresource URLs; fresh scans carry up to 5,000 URLs, while later tests/rechecks use a reduced persisted request tree and are best-effort. Use `rule_type='regexp_request_body'` to match the CONTENTS of those sub-resources instead \u2014 the right choice when the code you want to catch keeps changing its filename; those contents are kept for one day. `rule_type='regexp_url'` remains redirect-chain-only. The API auto-registers a tag definition for each emitted slug and rejects built-in system-slug collisions with HTTP 422 / `checking.system_slug_reserved`. Matches tag future scans; existing scans are untouched until `recheck_scans`.",
|
|
4301
4334
|
annotations: {
|
|
4302
4335
|
title: "Create Custom Rule",
|
|
4303
4336
|
readOnlyHint: false,
|
|
@@ -4388,7 +4421,7 @@ var TestCustomRuleInputShape = {
|
|
|
4388
4421
|
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. " + PATTERN_RULE_CONFIG_DOC + " `regexp_url` remains redirect-chain-only. 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
|
|
4389
4422
|
),
|
|
4390
4423
|
target: z.string().max(30).describe(
|
|
4391
|
-
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` and `regexp_request_body`
|
|
4424
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` and `regexp_request_body` also accept `'creative'` and `'creative_and_page'`. See API docs for the full set of valid values."
|
|
4392
4425
|
),
|
|
4393
4426
|
scan_id: z.string().uuid().describe("Existing scan UUID to evaluate the rule against.")
|
|
4394
4427
|
};
|
|
@@ -4428,13 +4461,13 @@ var UpdateCustomRuleInputShape = {
|
|
|
4428
4461
|
"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. " + PATTERN_RULE_CONFIG_DOC + " Read the rule first because `rule_type` is immutable and is not repeated in this update input. " + COMBO_MATCH_SCOPE_DOC
|
|
4429
4462
|
),
|
|
4430
4463
|
target: z.string().max(30).optional().describe(
|
|
4431
|
-
"Where to apply the rule. `regexp_request_url` and `regexp_request_body`
|
|
4464
|
+
"Where to apply the rule. `regexp_request_url` and `regexp_request_body` accept `'page'`, `'creative'` or `'creative_and_page'`. See API docs for the valid targets of other rule types."
|
|
4432
4465
|
),
|
|
4433
4466
|
is_active: z.boolean().optional().describe("Enable/disable the rule.")
|
|
4434
4467
|
};
|
|
4435
4468
|
var updateCustomRuleTool = {
|
|
4436
4469
|
name: "update_custom_rule",
|
|
4437
|
-
description: "Update a custom tag-detection rule. Only supplied fields are sent, but `config` replaces the stored object wholesale; read the rule first and resend every required key. `regexp_request_url` and `regexp_request_body` need a non-empty pattern (max 4,096), flags `''`/`'i'`, and
|
|
4470
|
+
description: "Update a custom tag-detection rule. Only supplied fields are sent, but `config` replaces the stored object wholesale; read the rule first and resend every required key. `regexp_request_url` and `regexp_request_body` need a non-empty pattern (max 4,096), flags `''`/`'i'`, and a target of `'page'`, `'creative'` or `'creative_and_page'`. Same-slug GLOBAL rule edits preserve separately managed tag metadata; use `update_tag_definition` to change it. Existing scans are not re-evaluated until `recheck_scans`.",
|
|
4438
4471
|
annotations: {
|
|
4439
4472
|
title: "Update Custom Rule",
|
|
4440
4473
|
readOnlyHint: false,
|
|
@@ -4989,7 +5022,7 @@ var GetPolicySetInputShape = {
|
|
|
4989
5022
|
};
|
|
4990
5023
|
var getPolicySetTool = {
|
|
4991
5024
|
name: "get_policy_set",
|
|
4992
|
-
description: "Get one policy set by UUID with its complete list of entries (each entry is one of five rule kinds \u2014 tag / iab_v3 / brand / ai_category / custom_taxonomy \u2014 plus applicable country codes).",
|
|
5025
|
+
description: "Get one policy set by UUID with its complete list of entries (each entry is one of five rule kinds \u2014 tag / iab_v3 / brand / ai_category / custom_taxonomy \u2014 plus applicable country codes). `is_default` is true when this owned set is the organization's default for new campaigns.",
|
|
4993
5026
|
annotations: {
|
|
4994
5027
|
title: "Get Policy Set",
|
|
4995
5028
|
readOnlyHint: true,
|
|
@@ -5040,7 +5073,7 @@ var ListPolicySetsInputShape = {
|
|
|
5040
5073
|
};
|
|
5041
5074
|
var listPolicySetsTool = {
|
|
5042
5075
|
name: "list_policy_sets",
|
|
5043
|
-
description: "Paginated list of policy sets: named collections of violation rules (tag / IAB V3 / brand / AI category / custom-taxonomy entries) that define what counts as a violation. Campaigns bind to one policy set. Returns `{items, total, page, limit}`. List items omit `entries` for payload size \u2014 fetch a single set via `get_policy_set` when you need them. Use `visibility=public` to discover Kaminari Ad-curated sets.",
|
|
5076
|
+
description: "Paginated list of policy sets: named collections of violation rules (tag / IAB V3 / brand / AI category / custom-taxonomy entries) that define what counts as a violation. Campaigns bind to one policy set. Returns `{items, total, page, limit}`. List items omit `entries` for payload size \u2014 fetch a single set via `get_policy_set` when you need them. `is_default` is true on the owned set that new campaigns bind when `policy_set_id` is omitted. Use `visibility=public` to discover Kaminari Ad-curated sets.",
|
|
5044
5077
|
annotations: {
|
|
5045
5078
|
title: "List Policy Sets",
|
|
5046
5079
|
readOnlyHint: true,
|
|
@@ -5079,6 +5112,29 @@ var requestPolicySetApprovalTool = {
|
|
|
5079
5112
|
return ok({ requested: true });
|
|
5080
5113
|
}
|
|
5081
5114
|
};
|
|
5115
|
+
var SetDefaultPolicySetInputShape = {
|
|
5116
|
+
policy_set_id: z.string().uuid().describe("Owned policy set UUID."),
|
|
5117
|
+
is_default: z.boolean().describe(
|
|
5118
|
+
"True makes this the organization's default for new campaigns (clearing any previous default). False clears the flag on this set and leaves the org without a default."
|
|
5119
|
+
)
|
|
5120
|
+
};
|
|
5121
|
+
var setDefaultPolicySetTool = {
|
|
5122
|
+
name: "set_default_policy_set",
|
|
5123
|
+
description: "Mark or clear one of your organization's policy sets as the default for new campaigns. At most one owned set may be default. Creating a campaign without `policy_set_id` then binds this set (or stays unbound if none is set). Pass `policy_set_id: null` on create_campaign to opt out of the default. Foreign public sets cannot be made default. The API returns 204; this tool echoes `is_default`.",
|
|
5124
|
+
annotations: {
|
|
5125
|
+
title: "Set Default Policy Set",
|
|
5126
|
+
readOnlyHint: false,
|
|
5127
|
+
destructiveHint: false,
|
|
5128
|
+
idempotentHint: true,
|
|
5129
|
+
openWorldHint: false
|
|
5130
|
+
},
|
|
5131
|
+
inputSchema: z.object(SetDefaultPolicySetInputShape),
|
|
5132
|
+
handler: async (input, ctx) => {
|
|
5133
|
+
const result = await ctx.api.setDefaultPolicySet(input.policy_set_id, input.is_default);
|
|
5134
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
5135
|
+
return ok({ is_default: input.is_default });
|
|
5136
|
+
}
|
|
5137
|
+
};
|
|
5082
5138
|
var UnpublishPolicySetInputShape = {
|
|
5083
5139
|
policy_set_id: z.string().uuid().describe("Policy set UUID.")
|
|
5084
5140
|
};
|
|
@@ -5271,6 +5327,7 @@ var CreateBulkScansInputShape = {
|
|
|
5271
5327
|
"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`."
|
|
5272
5328
|
),
|
|
5273
5329
|
referrer: scanReferrerField,
|
|
5330
|
+
ignore_first_n_domains: ignoreFirstNDomainsScanField,
|
|
5274
5331
|
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."),
|
|
5275
5332
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; same for every country in the batch."),
|
|
5276
5333
|
proxy: scanProxyField,
|
|
@@ -5296,6 +5353,7 @@ var createBulkScansTool = {
|
|
|
5296
5353
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
5297
5354
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
5298
5355
|
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
5356
|
+
...input.ignore_first_n_domains !== void 0 ? { ignore_first_n_domains: input.ignore_first_n_domains } : {},
|
|
5299
5357
|
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
5300
5358
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
5301
5359
|
...pickRepeatRetryBody(input)
|
|
@@ -5316,6 +5374,7 @@ var CreateScanInputShape = {
|
|
|
5316
5374
|
"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`."
|
|
5317
5375
|
),
|
|
5318
5376
|
referrer: scanReferrerField,
|
|
5377
|
+
ignore_first_n_domains: ignoreFirstNDomainsScanField,
|
|
5319
5378
|
country_code: z.string().length(2).describe("ISO 3166-1 alpha-2 country code, e.g. US, DE, JP. Determines proxy geo."),
|
|
5320
5379
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; use `list_emulators` to discover valid values."),
|
|
5321
5380
|
proxy: scanProxyField,
|
|
@@ -5347,6 +5406,7 @@ var createScanTool = {
|
|
|
5347
5406
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
5348
5407
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
5349
5408
|
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
5409
|
+
...input.ignore_first_n_domains !== void 0 ? { ignore_first_n_domains: input.ignore_first_n_domains } : {},
|
|
5350
5410
|
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
5351
5411
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
5352
5412
|
...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
|
|
@@ -6131,6 +6191,7 @@ function registerAllTools(register) {
|
|
|
6131
6191
|
register(deletePolicySetTool);
|
|
6132
6192
|
register(requestPolicySetApprovalTool);
|
|
6133
6193
|
register(unpublishPolicySetTool);
|
|
6194
|
+
register(setDefaultPolicySetTool);
|
|
6134
6195
|
register(listPolicySetCampaignsTool);
|
|
6135
6196
|
register(attachPolicySetCampaignsTool);
|
|
6136
6197
|
register(detachPolicySetCampaignsTool);
|
|
@@ -6240,5 +6301,5 @@ function formatToolError(error) {
|
|
|
6240
6301
|
}
|
|
6241
6302
|
|
|
6242
6303
|
export { BearerToken, SERVER_INSTRUCTIONS, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
|
|
6243
|
-
//# sourceMappingURL=chunk-
|
|
6244
|
-
//# sourceMappingURL=chunk-
|
|
6304
|
+
//# sourceMappingURL=chunk-VEPJEI3S.js.map
|
|
6305
|
+
//# sourceMappingURL=chunk-VEPJEI3S.js.map
|