@kaminari-ad/mcp 0.14.1 → 0.17.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 +120 -1
- package/README.md +4 -4
- package/dist/bin.js +3 -3
- package/dist/{chunk-IEOPWWSC.js → chunk-FA5A6Y4H.js} +153 -76
- package/dist/chunk-FA5A6Y4H.js.map +1 -0
- package/dist/{chunk-YS7IDW24.js → chunk-OX65SNEN.js} +3 -3
- package/dist/{chunk-YS7IDW24.js.map → chunk-OX65SNEN.js.map} +1 -1
- package/dist/{http-bootstrap-XAA75RA3.js → http-bootstrap-DKGGQURI.js} +4 -4
- package/dist/{http-bootstrap-XAA75RA3.js.map → http-bootstrap-DKGGQURI.js.map} +1 -1
- package/dist/{stdio-bootstrap-II6IVJWD.js → stdio-bootstrap-NVDBVJ7K.js} +4 -4
- package/dist/{stdio-bootstrap-II6IVJWD.js.map → stdio-bootstrap-NVDBVJ7K.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-IEOPWWSC.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,124 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.17.0] - 2026-09-03
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`regexp_request_body` — rules can match what a page's sub-resources
|
|
15
|
+
contain.** The three custom-rule writers (`create_custom_rule`,
|
|
16
|
+
`update_custom_rule`, `test_custom_rule`) now accept the new rule type
|
|
17
|
+
and enforce its contract locally: the same `{pattern, flags}` config as
|
|
18
|
+
`regexp_request_url` and the same fixed `page` target, rejected before
|
|
19
|
+
the request leaves the process.
|
|
20
|
+
|
|
21
|
+
It exists because address-based matching stopped being enough. The
|
|
22
|
+
malvertising kits rotate every filename on every visit, so a
|
|
23
|
+
`regexp_request_url` pattern matches the names it was written against
|
|
24
|
+
and nothing after; the code inside those files changes far more slowly.
|
|
25
|
+
|
|
26
|
+
Two limits the tool descriptions now state, because both look like a
|
|
27
|
+
non-match when they bite: only the scripts, fetch/XHR responses and
|
|
28
|
+
iframe documents are captured (never images, video, fonts or
|
|
29
|
+
stylesheets), up to 400 resources, 128 KB each and 8 MB per scan — and
|
|
30
|
+
the captured contents are kept for **one day**, so
|
|
31
|
+
`test_custom_rule` against an older scan reports no match with nothing
|
|
32
|
+
left to read.
|
|
33
|
+
|
|
34
|
+
Needs the api and crawler sides deployed first.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- The internal request-URL rule guard is now a shared pattern-rule guard
|
|
39
|
+
covering both types, so their contracts cannot drift apart.
|
|
40
|
+
|
|
41
|
+
## [0.16.0] - 2026-08-26
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **`unpublish_policy_set` — publication is no longer a one-way door
|
|
46
|
+
(KAMIAD-167).** `request_policy_set_approval` had no inverse, so an
|
|
47
|
+
agent could put a set into the shared catalog but never take it out;
|
|
48
|
+
the only escape was asking a human to reject it, and that worked only
|
|
49
|
+
while the request was still pending. The new tool returns a set to
|
|
50
|
+
private from either state — approved or awaiting review — and is
|
|
51
|
+
idempotent on a set that is already private. It is annotated
|
|
52
|
+
`destructiveHint: true` because other organizations lose access to a
|
|
53
|
+
set they may be browsing. Campaigns already bound to the set keep
|
|
54
|
+
running against it; going private blocks new attachments instead.
|
|
55
|
+
|
|
56
|
+
Needs the API side deployed first: it wraps
|
|
57
|
+
`POST /api/v1/policy-sets/{id}/unpublish`, added in `adverif/api!378`.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- **`get_campaign_alert_overrides` validates `mode` strictly — this
|
|
62
|
+
actually shipped in 0.15.0, undocumented.** That release's regen
|
|
63
|
+
narrowed `CampaignOverridesResponse.mode` from a bare `string` to the
|
|
64
|
+
generated `CampaignOverrideMode` enum (`inherit | override |
|
|
65
|
+
silence`), and the response parser enforces it, so a fourth value
|
|
66
|
+
from the API now surfaces as an `upstream` error instead of passing
|
|
67
|
+
through. That is the intent — the api types the field, so drift
|
|
68
|
+
should fail loudly — but it is a behaviour change and 0.15.0 recorded
|
|
69
|
+
only "regenerated openapi.ts / zod-schemas.ts".
|
|
70
|
+
- `set_campaign_alert_overrides` now takes its `mode` input from that
|
|
71
|
+
same generated enum instead of a hand-written `z.enum` with identical
|
|
72
|
+
values. Behaviour is unchanged; the point is that `check-tool-enum-drift`
|
|
73
|
+
can finally catch future value changes on this field, so its
|
|
74
|
+
exemption is gone.
|
|
75
|
+
|
|
76
|
+
### Removed
|
|
77
|
+
|
|
78
|
+
- The two `/api/forms/*` entries in `check-api-coverage`'s
|
|
79
|
+
`EXEMPT_OPERATIONS`. Those routes are `include_in_schema=False` and
|
|
80
|
+
dropped out of the spec in the 0.15.0 regen, so the exemptions had
|
|
81
|
+
become dead config — exactly as their own comment predicted.
|
|
82
|
+
|
|
83
|
+
## [0.15.0] - 2026-08-24
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
|
|
87
|
+
- **`get_proxy_targeting` — the accepted proxy values, instead of
|
|
88
|
+
guesswork.** A scan's `proxy` block takes a region, city, and ISP whose
|
|
89
|
+
valid values come from our upstream network provider's catalogue, so
|
|
90
|
+
they cannot be an enum in the schema: they differ per country and per
|
|
91
|
+
connection type. Until now an agent had to guess, and a wrong guess
|
|
92
|
+
came back as a 422 with a prose message. The new tool wraps
|
|
93
|
+
`GET /api/v1/proxy/targeting` and returns the accepted regions,
|
|
94
|
+
cities, and ISPs for a country, ordered by pool size so the agent can
|
|
95
|
+
prefer values likely to yield an exit node. Anything it lists is
|
|
96
|
+
accepted by `create_scan`.
|
|
97
|
+
|
|
98
|
+
Two things the tool's description makes explicit, because both are
|
|
99
|
+
easy to get wrong: pass `proxy_type: "mobile"` when the scan is mobile
|
|
100
|
+
(the two networks are separate pools — in the US that is 1500+ ISPs
|
|
101
|
+
against roughly a dozen carriers), and take a city from the same
|
|
102
|
+
response that produced the region, since a region and a city that do
|
|
103
|
+
not belong together leave the provider nothing to route through.
|
|
104
|
+
|
|
105
|
+
- **The write tools now point at it.** A tool nobody is told about is a
|
|
106
|
+
tool nobody calls, and an agent starts from `create_scan`, not from the
|
|
107
|
+
catalogue. The `proxy` fields on `create_scan` / `create_bulk_scans`
|
|
108
|
+
and the `proxy_*` fields on `create_campaign` / `update_campaign` now
|
|
109
|
+
name `get_proxy_targeting` and carry the same two warnings. The
|
|
110
|
+
campaign `proxy_region` description previously said "free-text", which
|
|
111
|
+
told the agent to invent a value — precisely the behaviour that
|
|
112
|
+
produces the 422 this release exists to remove.
|
|
113
|
+
|
|
114
|
+
### Changed
|
|
115
|
+
|
|
116
|
+
- **Regenerated `openapi.ts` / `zod-schemas.ts`** against the API
|
|
117
|
+
release that adds `/api/v1/proxy/targeting`.
|
|
118
|
+
|
|
119
|
+
- **A rejected field value now arrives as a `detail` array.** The API
|
|
120
|
+
changed 422 responses for values checked against a runtime vocabulary
|
|
121
|
+
(`proxy.region`, `proxy.city`, `proxy.isp`, `country_code`,
|
|
122
|
+
`emulator_id`) from a prose string to the `HTTPValidationError`
|
|
123
|
+
array its OpenAPI schema always declared. No change was needed here —
|
|
124
|
+
`toApiError` has handled both shapes since the parser-drift work — and
|
|
125
|
+
the agent-visible message is now `body.proxy.region: Unsupported proxy
|
|
126
|
+
region …` instead of an undifferentiated sentence.
|
|
127
|
+
|
|
10
128
|
## [0.14.1] - 2026-08-24
|
|
11
129
|
|
|
12
130
|
### Changed
|
|
@@ -1145,7 +1263,8 @@ Initial public release. The first version that ships to npm under
|
|
|
1145
1263
|
need them.
|
|
1146
1264
|
- Invoice PDF fetcher — same reason.
|
|
1147
1265
|
|
|
1148
|
-
[Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.
|
|
1266
|
+
[Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.15.0...HEAD
|
|
1267
|
+
[0.15.0]: https://github.com/kaminari-ad/mcp/compare/v0.14.1...v0.15.0
|
|
1149
1268
|
[0.14.1]: https://github.com/kaminari-ad/mcp/compare/v0.14.0...v0.14.1
|
|
1150
1269
|
[0.14.0]: https://github.com/kaminari-ad/mcp/compare/v0.13.0...v0.14.0
|
|
1151
1270
|
[0.13.0]: https://github.com/kaminari-ad/mcp/compare/v0.12.0...v0.13.0
|
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 107 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
|
+
107 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,13 +123,13 @@ 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** (10) — `list_policy_sets`, `get_policy_set`, `create_policy_set`, `update_policy_set`, `delete_policy_set`, `request_policy_set_approval`, `unpublish_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`
|
|
130
130
|
- **Invoicing** (2) — `list_invoices`, `get_invoice_pdf`
|
|
131
131
|
- **Alert notifications** (5) — `list_alert_destinations`, `delete_alert_destination`, `set_alert_destination_version`, `get_campaign_alert_overrides`, `set_campaign_alert_overrides`
|
|
132
|
-
- **Reference data** (
|
|
132
|
+
- **Reference data** (3) — `list_geos`, `list_emulators`, `get_proxy_targeting`
|
|
133
133
|
|
|
134
134
|
Screenshots (`get_scan_screenshot`, `get_scan_creative_screenshot`, `get_scan_landing_screenshot`) come back as inline MCP `image` blocks; `get_invoice_pdf` and `get_scan_creative_video` as inline resource blocks — no second fetch, no presigned URL. The two text artifacts (`get_scan_creative_html`, `get_scan_vast_xml`) come back as strings the model can read directly. Every artifact download is size-capped in the gateway — 256 KiB for the text artifacts, 8 MiB for the binary ones — and refused while reading rather than buffered and then rejected.
|
|
135
135
|
|
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-OX65SNEN.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-NVDBVJ7K.js');
|
|
164
164
|
return bootstrapStdio(config);
|
|
165
165
|
}
|
|
166
|
-
const { bootstrapHttp } = await import('./http-bootstrap-
|
|
166
|
+
const { bootstrapHttp } = await import('./http-bootstrap-DKGGQURI.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-OX65SNEN.js';
|
|
3
3
|
import { createHash, randomUUID } from 'crypto';
|
|
4
4
|
import createClient from 'openapi-fetch';
|
|
5
5
|
import { fetch } from 'undici';
|
|
@@ -471,6 +471,15 @@ var GeoResponse = z.object({
|
|
|
471
471
|
region: z.string(),
|
|
472
472
|
tier: z.string()
|
|
473
473
|
}).passthrough();
|
|
474
|
+
var ProxyTargetingResponse = z.object({
|
|
475
|
+
country_code: z.string(),
|
|
476
|
+
proxy_type: z.string(),
|
|
477
|
+
regions: z.array(z.string()),
|
|
478
|
+
cities: z.array(z.string()),
|
|
479
|
+
isps: z.array(z.string()),
|
|
480
|
+
refreshed_at: z.union([z.string(), z.null()]),
|
|
481
|
+
ttl_seconds: z.number().int()
|
|
482
|
+
}).passthrough();
|
|
474
483
|
var EmulatorResponse = z.object({
|
|
475
484
|
id: z.string(),
|
|
476
485
|
display_name: z.string(),
|
|
@@ -1093,13 +1102,14 @@ var AlertNotificationDestinationResponse = z.object({
|
|
|
1093
1102
|
updated_at: z.string().datetime({ offset: true })
|
|
1094
1103
|
}).passthrough();
|
|
1095
1104
|
z.object({ version: AlertNotificationVersion }).passthrough();
|
|
1105
|
+
var CampaignOverrideMode = z.enum(["inherit", "override", "silence"]);
|
|
1096
1106
|
var CampaignOverridesResponse = z.object({
|
|
1097
1107
|
campaign_id: z.string().uuid(),
|
|
1098
|
-
mode:
|
|
1108
|
+
mode: CampaignOverrideMode,
|
|
1099
1109
|
destination_ids: z.array(z.string().uuid())
|
|
1100
1110
|
}).passthrough();
|
|
1101
1111
|
z.object({
|
|
1102
|
-
mode:
|
|
1112
|
+
mode: CampaignOverrideMode,
|
|
1103
1113
|
destination_ids: z.array(z.string().uuid()).optional().default([])
|
|
1104
1114
|
}).passthrough();
|
|
1105
1115
|
var InvoiceType = z.enum(["proforma", "final"]);
|
|
@@ -1135,32 +1145,6 @@ z.object({
|
|
|
1135
1145
|
limit: z.number().int(),
|
|
1136
1146
|
pages: z.number().int()
|
|
1137
1147
|
}).passthrough();
|
|
1138
|
-
z.object({
|
|
1139
|
-
name: z.string().min(2).max(100),
|
|
1140
|
-
email: z.string().email(),
|
|
1141
|
-
message: z.string().min(10).max(2e3),
|
|
1142
|
-
source: z.string().max(512).optional().default("")
|
|
1143
|
-
}).passthrough();
|
|
1144
|
-
z.object({
|
|
1145
|
-
id: z.string().uuid(),
|
|
1146
|
-
received_at: z.string().datetime({ offset: true })
|
|
1147
|
-
}).passthrough();
|
|
1148
|
-
var PreferredContactChannel = z.enum(["telegram", "whatsapp", "email"]);
|
|
1149
|
-
z.object({
|
|
1150
|
-
first_name: z.string().min(2).max(60),
|
|
1151
|
-
last_name: z.string().min(2).max(60),
|
|
1152
|
-
company_email: z.string().email(),
|
|
1153
|
-
company_name: z.string().min(2).max(120),
|
|
1154
|
-
preferred_channel: PreferredContactChannel,
|
|
1155
|
-
contact_handle: z.string().max(120).optional().default(""),
|
|
1156
|
-
comment: z.string().max(2e3).optional().default(""),
|
|
1157
|
-
privacy_accepted: z.boolean(),
|
|
1158
|
-
source: z.string().max(512).optional().default("")
|
|
1159
|
-
}).passthrough();
|
|
1160
|
-
z.object({
|
|
1161
|
-
id: z.string().uuid(),
|
|
1162
|
-
received_at: z.string().datetime({ offset: true })
|
|
1163
|
-
}).passthrough();
|
|
1164
1148
|
var CustomTaxonomyListItem = z.object({
|
|
1165
1149
|
id: z.string().uuid(),
|
|
1166
1150
|
name: z.string(),
|
|
@@ -1230,6 +1214,7 @@ var schemas = {
|
|
|
1230
1214
|
ScanResponse,
|
|
1231
1215
|
ScanBriefResponse,
|
|
1232
1216
|
GeoResponse,
|
|
1217
|
+
ProxyTargetingResponse,
|
|
1233
1218
|
EmulatorResponse,
|
|
1234
1219
|
CampaignGroupResponse,
|
|
1235
1220
|
GroupActionResponse,
|
|
@@ -1261,6 +1246,7 @@ var schemas = {
|
|
|
1261
1246
|
DeliveryAttemptResponse,
|
|
1262
1247
|
BulkReplayResponse,
|
|
1263
1248
|
AlertNotificationDestinationResponse,
|
|
1249
|
+
CampaignOverrideMode,
|
|
1264
1250
|
CampaignOverridesResponse,
|
|
1265
1251
|
InvoiceResponse,
|
|
1266
1252
|
CustomTaxonomyListItem,
|
|
@@ -1779,6 +1765,18 @@ var PolicySetListItemSchema = schemas.PolicySetListItem.pick({
|
|
|
1779
1765
|
}).strip();
|
|
1780
1766
|
var parsePolicySetPage = (raw) => parsePagedWithItemSchema(PolicySetListItemSchema, raw, "policy-sets");
|
|
1781
1767
|
|
|
1768
|
+
// src/infrastructure/api/parsers/parse-proxy-targeting.ts
|
|
1769
|
+
var ProxyTargetingSchema = schemas.ProxyTargetingResponse.pick({
|
|
1770
|
+
country_code: true,
|
|
1771
|
+
proxy_type: true,
|
|
1772
|
+
regions: true,
|
|
1773
|
+
cities: true,
|
|
1774
|
+
isps: true,
|
|
1775
|
+
refreshed_at: true,
|
|
1776
|
+
ttl_seconds: true
|
|
1777
|
+
}).strip();
|
|
1778
|
+
var parseProxyTargeting = (raw) => parseWithSchema(ProxyTargetingSchema, raw, "proxy targeting");
|
|
1779
|
+
|
|
1782
1780
|
// src/infrastructure/api/parsers/parse-run.ts
|
|
1783
1781
|
var RunSchema = schemas.RunResponse.pick({
|
|
1784
1782
|
id: true,
|
|
@@ -2254,6 +2252,9 @@ function createHttpApiGateway(config) {
|
|
|
2254
2252
|
async listEmulators() {
|
|
2255
2253
|
return call("GET", "/api/v1/emulators", {}, parseEmulatorList);
|
|
2256
2254
|
},
|
|
2255
|
+
async getProxyTargeting(query) {
|
|
2256
|
+
return call("GET", "/api/v1/proxy/targeting", { params: { query } }, parseProxyTargeting);
|
|
2257
|
+
},
|
|
2257
2258
|
// ── Campaigns ─────────────────────────────────────────────────
|
|
2258
2259
|
async listCampaigns(filters) {
|
|
2259
2260
|
return call("GET", "/api/v1/campaigns", { params: { query: filters } }, parseCampaignPage);
|
|
@@ -2556,6 +2557,14 @@ function createHttpApiGateway(config) {
|
|
|
2556
2557
|
parseEmpty
|
|
2557
2558
|
);
|
|
2558
2559
|
},
|
|
2560
|
+
async unpublishPolicySet(id) {
|
|
2561
|
+
return call(
|
|
2562
|
+
"POST",
|
|
2563
|
+
"/api/v1/policy-sets/{policy_set_id}/unpublish",
|
|
2564
|
+
{ params: { path: { policy_set_id: id } } },
|
|
2565
|
+
parseEmpty
|
|
2566
|
+
);
|
|
2567
|
+
},
|
|
2559
2568
|
// ── Custom taxonomies ─────────────────────────────────────────
|
|
2560
2569
|
async listCustomTaxonomies(filters) {
|
|
2561
2570
|
return call(
|
|
@@ -3288,12 +3297,7 @@ var setAlertDestinationVersionTool = {
|
|
|
3288
3297
|
};
|
|
3289
3298
|
var SetCampaignAlertOverridesInputShape = {
|
|
3290
3299
|
campaign_id: z.string().uuid().describe("Campaign UUID."),
|
|
3291
|
-
|
|
3292
|
-
// the field as a plain string, so no generated schema constrains it.
|
|
3293
|
-
// Swap to `schemas.CampaignOverrideMode` after the api enum change
|
|
3294
|
-
// (`fix/type-override-mode-and-unlist-forms`) reaches prod and the
|
|
3295
|
-
// next `make gen-api-types` picks it up.
|
|
3296
|
-
mode: z.enum(["inherit", "override", "silence"]).describe(
|
|
3300
|
+
mode: schemas.CampaignOverrideMode.describe(
|
|
3297
3301
|
"Routing mode: `inherit` (fall back to the org-wide destinations), `override` (route ONLY to `destination_ids`), `silence` (send nothing for this campaign)."
|
|
3298
3302
|
),
|
|
3299
3303
|
destination_ids: z.array(z.string().uuid()).max(50).default([]).describe(
|
|
@@ -3920,10 +3924,18 @@ var campaignConfigFields = {
|
|
|
3920
3924
|
emulator_mode: z.enum(["random", "all"]).optional().describe(
|
|
3921
3925
|
"How selected categories expand per run: 'random' = one random device per category (the UI's 'Random (1 per group)'), 'all' = every device in each category ('All checked'). Does not affect emulator_specific_ids. Default: random."
|
|
3922
3926
|
),
|
|
3923
|
-
proxy_type: z.enum(["residential", "mobile"]).optional().describe(
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
+
proxy_type: z.enum(["residential", "mobile"]).optional().describe(
|
|
3928
|
+
"Proxy network type. Default: residential. Residential and mobile are separate pools with separate catalogues, so pass the same value to `get_proxy_targeting` that you send here."
|
|
3929
|
+
),
|
|
3930
|
+
proxy_region: z.string().optional().describe(
|
|
3931
|
+
"Proxy region/state; use a value from `get_proxy_targeting` for one of this campaign's countries. Only honoured when the campaign targets a single country."
|
|
3932
|
+
),
|
|
3933
|
+
proxy_city: z.string().optional().describe(
|
|
3934
|
+
"Proxy city from `get_proxy_targeting`. If you also set `proxy_region`, take the city from a call made with that same region \u2014 a city from a different region passes validation but leaves the provider no exit node."
|
|
3935
|
+
),
|
|
3936
|
+
proxy_isp: z.string().optional().describe(
|
|
3937
|
+
"Proxy ISP, or mobile carrier when `proxy_type` is mobile. Use a value from `get_proxy_targeting`."
|
|
3938
|
+
),
|
|
3927
3939
|
...repeatRetryFields,
|
|
3928
3940
|
schedule_type: z.enum(["weekly", "interval"]).optional().describe(
|
|
3929
3941
|
"Scheduling mode: 'weekly' (run on a weekday/hour grid via schedule_weekly) or 'interval' (run every schedule_interval_seconds). Omit for a manual (run-on-demand) campaign."
|
|
@@ -4211,28 +4223,32 @@ var updateCampaignTool = {
|
|
|
4211
4223
|
return ok(result.value);
|
|
4212
4224
|
}
|
|
4213
4225
|
};
|
|
4214
|
-
var
|
|
4215
|
-
|
|
4226
|
+
var PATTERN_RULE_TYPES = ["regexp_request_url", "regexp_request_body"];
|
|
4227
|
+
var patternRuleConfigSchema = z.object({
|
|
4228
|
+
pattern: z.string().min(1).max(4096).describe("Non-empty regular expression, at most 4,096 characters."),
|
|
4216
4229
|
flags: z.enum(["", "i"]).optional().describe("Omit or use '' for case-sensitive matching; use 'i' to ignore case.")
|
|
4217
4230
|
}).strict();
|
|
4218
|
-
var
|
|
4219
|
-
function
|
|
4220
|
-
|
|
4231
|
+
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` must be `'page'` for both. `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.";
|
|
4232
|
+
function isPatternRuleType(ruleType) {
|
|
4233
|
+
return PATTERN_RULE_TYPES.some((known) => known === ruleType);
|
|
4234
|
+
}
|
|
4235
|
+
function patternRuleInputError(input) {
|
|
4236
|
+
if (!isPatternRuleType(input.rule_type)) return null;
|
|
4221
4237
|
if (input.target !== void 0 && input.target !== "page") {
|
|
4222
4238
|
return {
|
|
4223
4239
|
kind: "invalid-input",
|
|
4224
|
-
message:
|
|
4225
|
-
fieldErrors: { target: [
|
|
4240
|
+
message: `${input.rule_type} requires target='page'.`,
|
|
4241
|
+
fieldErrors: { target: [`Must be 'page' for ${input.rule_type}.`] }
|
|
4226
4242
|
};
|
|
4227
4243
|
}
|
|
4228
|
-
const parsed =
|
|
4244
|
+
const parsed = patternRuleConfigSchema.safeParse(input.config);
|
|
4229
4245
|
if (parsed.success) return null;
|
|
4230
4246
|
const issue = parsed.error.issues[0];
|
|
4231
4247
|
const field = issue?.path.length ? `config.${issue.path.join(".")}` : "config";
|
|
4232
|
-
const message = issue?.message ?? "Invalid
|
|
4248
|
+
const message = issue?.message ?? "Invalid pattern rule config.";
|
|
4233
4249
|
return {
|
|
4234
4250
|
kind: "invalid-input",
|
|
4235
|
-
message: `Invalid
|
|
4251
|
+
message: `Invalid ${input.rule_type} config: ${message}`,
|
|
4236
4252
|
fieldErrors: { [field]: [message] }
|
|
4237
4253
|
};
|
|
4238
4254
|
}
|
|
@@ -4248,10 +4264,7 @@ var ruleConfigField = z.record(z.unknown()).superRefine((config, ctx) => {
|
|
|
4248
4264
|
});
|
|
4249
4265
|
}
|
|
4250
4266
|
});
|
|
4251
|
-
var
|
|
4252
|
-
requestUrlRuleConfigSchema,
|
|
4253
|
-
ruleConfigField
|
|
4254
|
-
]);
|
|
4267
|
+
var patternAwareRuleConfigField = z.union([patternRuleConfigSchema, ruleConfigField]);
|
|
4255
4268
|
|
|
4256
4269
|
// src/application/tools/custom-rules/create-custom-rule.tool.ts
|
|
4257
4270
|
var CreateCustomRuleInputShape = {
|
|
@@ -4262,18 +4275,18 @@ var CreateCustomRuleInputShape = {
|
|
|
4262
4275
|
"Tag slug to assign on match. Empty = create-only (advanced). The API auto-registers a custom tag definition for this slug with `display_name = name`. **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`. For `rule_type='llm'` use `config.tags` keys instead and leave `tag_slug` empty."
|
|
4263
4276
|
),
|
|
4264
4277
|
rule_type: z.string().max(50).describe(
|
|
4265
|
-
"Rule engine. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `regexp_request_url`, `blacklist_domain`, `combo`, `llm`. `regexp_url` checks redirect-chain URLs only; `regexp_request_url` checks captured network and subresource URLs. The API validates."
|
|
4278
|
+
"Rule engine. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `regexp_request_url`, `regexp_request_body`, `blacklist_domain`, `combo`, `llm`. `regexp_url` checks redirect-chain URLs only; `regexp_request_url` checks captured network and subresource URLs; `regexp_request_body` checks what those sub-resources contained. The API validates."
|
|
4266
4279
|
),
|
|
4267
|
-
config:
|
|
4268
|
-
"Rule-type-specific configuration object. Shape depends on `rule_type`. " +
|
|
4280
|
+
config: patternAwareRuleConfigField.describe(
|
|
4281
|
+
"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
|
|
4269
4282
|
),
|
|
4270
4283
|
target: z.string().max(30).optional().describe(
|
|
4271
|
-
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url`
|
|
4284
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` and `regexp_request_body` require `target='page'`. Default: page. See API docs for the full set of valid values."
|
|
4272
4285
|
)
|
|
4273
4286
|
};
|
|
4274
4287
|
var createCustomRuleTool = {
|
|
4275
4288
|
name: "create_custom_rule",
|
|
4276
|
-
description: "Define a custom tag-detection rule. Use `rule_type='regexp_request_url'` to match captured network and subresource URLs on the fixed `page` target; fresh scans carry up to 5,000 URLs, while later tests/rechecks use a reduced persisted request tree and are best-effort. `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`.",
|
|
4289
|
+
description: "Define a custom tag-detection rule. Use `rule_type='regexp_request_url'` to match captured network and subresource URLs on the fixed `page` target; 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`.",
|
|
4277
4290
|
annotations: {
|
|
4278
4291
|
title: "Create Custom Rule",
|
|
4279
4292
|
readOnlyHint: false,
|
|
@@ -4283,7 +4296,7 @@ var createCustomRuleTool = {
|
|
|
4283
4296
|
},
|
|
4284
4297
|
inputSchema: z.object(CreateCustomRuleInputShape),
|
|
4285
4298
|
handler: async (input, ctx) => {
|
|
4286
|
-
const inputError =
|
|
4299
|
+
const inputError = patternRuleInputError(input);
|
|
4287
4300
|
if (inputError) return err(inputError);
|
|
4288
4301
|
const body = {
|
|
4289
4302
|
name: input.name,
|
|
@@ -4358,19 +4371,19 @@ var listCustomRulesTool = {
|
|
|
4358
4371
|
};
|
|
4359
4372
|
var TestCustomRuleInputShape = {
|
|
4360
4373
|
rule_type: z.string().max(50).describe(
|
|
4361
|
-
"Rule engine type. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `regexp_request_url`, `blacklist_domain`, `combo`, `llm`. `regexp_url` checks redirect-chain URLs only; `regexp_request_url` checks captured network and subresource URLs."
|
|
4374
|
+
"Rule engine type. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `regexp_request_url`, `regexp_request_body`, `blacklist_domain`, `combo`, `llm`. `regexp_url` checks redirect-chain URLs only; `regexp_request_url` checks captured network and subresource URLs; `regexp_request_body` checks what those sub-resources contained."
|
|
4362
4375
|
),
|
|
4363
|
-
config:
|
|
4364
|
-
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. " +
|
|
4376
|
+
config: patternAwareRuleConfigField.describe(
|
|
4377
|
+
"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
|
|
4365
4378
|
),
|
|
4366
4379
|
target: z.string().max(30).describe(
|
|
4367
|
-
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url`
|
|
4380
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` and `regexp_request_body` require `target='page'`. See API docs for the full set of valid values."
|
|
4368
4381
|
),
|
|
4369
4382
|
scan_id: z.string().uuid().describe("Existing scan UUID to evaluate the rule against.")
|
|
4370
4383
|
};
|
|
4371
4384
|
var testCustomRuleTool = {
|
|
4372
4385
|
name: "test_custom_rule",
|
|
4373
|
-
description: "Preview-test a rule definition against a stored scan without persisting it. For `regexp_request_url`, the historical snapshot contains main-frame hops plus at most 200 persisted subrequests with selected resource types omitted, so a no-match does not prove the original fresh scan lacked the request. Returns match state, elapsed time, and per-tag detail; the preview response does not expose the matched request URL separately. Slug-collision validation does not run in preview mode.",
|
|
4386
|
+
description: "Preview-test a rule definition against a stored scan without persisting it. For `regexp_request_url`, the historical snapshot contains main-frame hops plus at most 200 persisted subrequests with selected resource types omitted, so a no-match does not prove the original fresh scan lacked the request. For `regexp_request_body`, the captured contents live for ONE DAY \u2014 pick a scan from the last 24 hours, because an older one reports no match with nothing left to read. Returns match state, elapsed time, and per-tag detail; the preview response does not expose the matched request URL separately. Slug-collision validation does not run in preview mode.",
|
|
4374
4387
|
annotations: {
|
|
4375
4388
|
title: "Test Custom Rule",
|
|
4376
4389
|
readOnlyHint: true,
|
|
@@ -4380,7 +4393,7 @@ var testCustomRuleTool = {
|
|
|
4380
4393
|
},
|
|
4381
4394
|
inputSchema: z.object(TestCustomRuleInputShape),
|
|
4382
4395
|
handler: async (input, ctx) => {
|
|
4383
|
-
const inputError =
|
|
4396
|
+
const inputError = patternRuleInputError(input);
|
|
4384
4397
|
if (inputError) return err(inputError);
|
|
4385
4398
|
const result = await ctx.api.testCustomRule({
|
|
4386
4399
|
rule_type: input.rule_type,
|
|
@@ -4400,17 +4413,17 @@ var UpdateCustomRuleInputShape = {
|
|
|
4400
4413
|
tag_slug: z.string().max(100).optional().describe(
|
|
4401
4414
|
"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`. Leaving a GLOBAL rule on the same slug preserves its admin-managed tag metadata."
|
|
4402
4415
|
),
|
|
4403
|
-
config:
|
|
4404
|
-
"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. " +
|
|
4416
|
+
config: patternAwareRuleConfigField.optional().describe(
|
|
4417
|
+
"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
|
|
4405
4418
|
),
|
|
4406
4419
|
target: z.string().max(30).optional().describe(
|
|
4407
|
-
"Where to apply the rule. `regexp_request_url`
|
|
4420
|
+
"Where to apply the rule. `regexp_request_url` and `regexp_request_body` are fixed to `page`; do not change it. See API docs for the valid targets of other rule types."
|
|
4408
4421
|
),
|
|
4409
4422
|
is_active: z.boolean().optional().describe("Enable/disable the rule.")
|
|
4410
4423
|
};
|
|
4411
4424
|
var updateCustomRuleTool = {
|
|
4412
4425
|
name: "update_custom_rule",
|
|
4413
|
-
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`
|
|
4426
|
+
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 the fixed `page` target. 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`.",
|
|
4414
4427
|
annotations: {
|
|
4415
4428
|
title: "Update Custom Rule",
|
|
4416
4429
|
readOnlyHint: false,
|
|
@@ -5055,6 +5068,26 @@ var requestPolicySetApprovalTool = {
|
|
|
5055
5068
|
return ok({ requested: true });
|
|
5056
5069
|
}
|
|
5057
5070
|
};
|
|
5071
|
+
var UnpublishPolicySetInputShape = {
|
|
5072
|
+
policy_set_id: z.string().uuid().describe("Policy set UUID.")
|
|
5073
|
+
};
|
|
5074
|
+
var unpublishPolicySetTool = {
|
|
5075
|
+
name: "unpublish_policy_set",
|
|
5076
|
+
description: "Return one of your organization's policy sets to PRIVATE. Inverse of request_policy_set_approval: removes an approved set from the shared catalog other organizations browse, or withdraws a publication request that is still awaiting review. The set stays fully usable inside your own organization. Campaigns in other organizations that already reference the set keep their binding \u2014 going private blocks new attachments rather than breaking existing ones. Safe to call on a set that is already private.",
|
|
5077
|
+
annotations: {
|
|
5078
|
+
title: "Unpublish Policy Set",
|
|
5079
|
+
readOnlyHint: false,
|
|
5080
|
+
destructiveHint: true,
|
|
5081
|
+
idempotentHint: true,
|
|
5082
|
+
openWorldHint: false
|
|
5083
|
+
},
|
|
5084
|
+
inputSchema: z.object(UnpublishPolicySetInputShape),
|
|
5085
|
+
handler: async (input, ctx) => {
|
|
5086
|
+
const result = await ctx.api.unpublishPolicySet(input.policy_set_id);
|
|
5087
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
5088
|
+
return ok({ unpublished: true });
|
|
5089
|
+
}
|
|
5090
|
+
};
|
|
5058
5091
|
var UpdatePolicySetInputShape = {
|
|
5059
5092
|
policy_set_id: z.string().uuid().describe("Policy set UUID."),
|
|
5060
5093
|
name: z.string().min(1).max(200).describe("New name (always required by the API on update)."),
|
|
@@ -5084,6 +5117,38 @@ var updatePolicySetTool = {
|
|
|
5084
5117
|
return ok(result.value);
|
|
5085
5118
|
}
|
|
5086
5119
|
};
|
|
5120
|
+
var GetProxyTargetingInputShape = {
|
|
5121
|
+
country_code: z.string().length(2).describe("ISO 3166-1 alpha-2 country code, e.g. 'US'. Case-insensitive."),
|
|
5122
|
+
proxy_type: z.enum(["residential", "mobile"]).optional().describe(
|
|
5123
|
+
"Which network to describe. Defaults to residential. Pass 'mobile' when the scan will use mobile: the two are separate pools with different catalogues, so a residential ISP is normally rejected for a mobile scan."
|
|
5124
|
+
),
|
|
5125
|
+
region: z.string().optional().describe(
|
|
5126
|
+
"Narrow the returned cities to one region. Pass a value from this tool's own 'regions' array. Omit it for the country-wide city list."
|
|
5127
|
+
)
|
|
5128
|
+
};
|
|
5129
|
+
var getProxyTargetingTool = {
|
|
5130
|
+
name: "get_proxy_targeting",
|
|
5131
|
+
description: "List the proxy regions, cities, and ISPs accepted for a country. Call this before setting proxy targeting on create_scan, create_bulk_scans, create_campaign, or update_campaign \u2014 the accepted values come from the upstream network provider and cannot be listed in those tools' schemas, so a guess is a 422. Anything listed here is accepted. Values are ordered by pool size, largest first; prefer values near the front. Two traps: residential and mobile are separate pools, so pass the proxy_type the scan will use; and without a region the cities are the whole country's, so if you target a region, call again with that region and take the city from that narrower list \u2014 a region and a city that do not belong together are accepted but leave the provider no exit node.",
|
|
5132
|
+
annotations: {
|
|
5133
|
+
title: "Get Proxy Targeting",
|
|
5134
|
+
readOnlyHint: true,
|
|
5135
|
+
destructiveHint: false,
|
|
5136
|
+
idempotentHint: true,
|
|
5137
|
+
openWorldHint: false
|
|
5138
|
+
},
|
|
5139
|
+
inputSchema: z.object(GetProxyTargetingInputShape),
|
|
5140
|
+
handler: async (input, ctx) => {
|
|
5141
|
+
const result = await ctx.api.getProxyTargeting({
|
|
5142
|
+
country_code: input.country_code,
|
|
5143
|
+
...input.proxy_type !== void 0 && { proxy_type: input.proxy_type },
|
|
5144
|
+
...input.region !== void 0 && { region: input.region }
|
|
5145
|
+
});
|
|
5146
|
+
if (result.isErr()) {
|
|
5147
|
+
return err(mapApiError(result.error));
|
|
5148
|
+
}
|
|
5149
|
+
return ok(result.value);
|
|
5150
|
+
}
|
|
5151
|
+
};
|
|
5087
5152
|
var CancelRunInputShape = { run_id: z.string().uuid().describe("Run UUID.") };
|
|
5088
5153
|
var cancelRunTool = {
|
|
5089
5154
|
name: "cancel_run",
|
|
@@ -5166,11 +5231,21 @@ var cancelScanTool = {
|
|
|
5166
5231
|
}
|
|
5167
5232
|
};
|
|
5168
5233
|
var scanProxyField = z.object({
|
|
5169
|
-
proxy_type: z.enum(["residential", "mobile"]).optional().describe(
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5234
|
+
proxy_type: z.enum(["residential", "mobile"]).optional().describe(
|
|
5235
|
+
"Proxy network type. Default: residential. Residential and mobile are separate pools with separate catalogues, so pass the same value to `get_proxy_targeting` that you send here."
|
|
5236
|
+
),
|
|
5237
|
+
region: z.string().optional().describe(
|
|
5238
|
+
"Proxy region/state. Use a value from `get_proxy_targeting`; anything else is rejected with 422."
|
|
5239
|
+
),
|
|
5240
|
+
city: z.string().optional().describe(
|
|
5241
|
+
"Proxy city. Use a value from `get_proxy_targeting`. If you also set `region`, take the city from a `get_proxy_targeting` call made with that same region \u2014 a city from a different region passes validation but leaves the provider no exit node."
|
|
5242
|
+
),
|
|
5243
|
+
isp: z.string().optional().describe(
|
|
5244
|
+
"Proxy ISP, or mobile carrier when `proxy_type` is mobile. Use a value from `get_proxy_targeting`."
|
|
5245
|
+
)
|
|
5246
|
+
}).optional().describe(
|
|
5247
|
+
"Optional proxy geo targeting for the crawl. Omit to use the org default. Call `get_proxy_targeting` for the country first: the accepted values come from the upstream network provider and cannot be listed in this schema."
|
|
5248
|
+
);
|
|
5174
5249
|
var scanReferrerField = z.string().url().regex(/^https?:\/\//i, "Must be an http(s) URL.").max(2048).optional().describe(
|
|
5175
5250
|
"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."
|
|
5176
5251
|
);
|
|
@@ -5980,6 +6055,7 @@ function registerAllTools(register) {
|
|
|
5980
6055
|
register(createApiKeyTool);
|
|
5981
6056
|
register(revokeApiKeyTool);
|
|
5982
6057
|
register(listGeosTool);
|
|
6058
|
+
register(getProxyTargetingTool);
|
|
5983
6059
|
register(listEmulatorsTool);
|
|
5984
6060
|
register(getScanTool);
|
|
5985
6061
|
register(listScanChildrenTool);
|
|
@@ -6041,6 +6117,7 @@ function registerAllTools(register) {
|
|
|
6041
6117
|
register(updatePolicySetTool);
|
|
6042
6118
|
register(deletePolicySetTool);
|
|
6043
6119
|
register(requestPolicySetApprovalTool);
|
|
6120
|
+
register(unpublishPolicySetTool);
|
|
6044
6121
|
register(listPolicySetCampaignsTool);
|
|
6045
6122
|
register(attachPolicySetCampaignsTool);
|
|
6046
6123
|
register(detachPolicySetCampaignsTool);
|
|
@@ -6150,5 +6227,5 @@ function formatToolError(error) {
|
|
|
6150
6227
|
}
|
|
6151
6228
|
|
|
6152
6229
|
export { BearerToken, SERVER_INSTRUCTIONS, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
|
|
6153
|
-
//# sourceMappingURL=chunk-
|
|
6154
|
-
//# sourceMappingURL=chunk-
|
|
6230
|
+
//# sourceMappingURL=chunk-FA5A6Y4H.js.map
|
|
6231
|
+
//# sourceMappingURL=chunk-FA5A6Y4H.js.map
|