@kaminari-ad/mcp 0.2.2 → 0.4.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 +150 -0
- package/README.md +27 -0
- package/dist/bin.js +48 -5
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-4MNLSWSZ.js → chunk-7DSLLKEK.js} +1165 -98
- package/dist/chunk-7DSLLKEK.js.map +1 -0
- package/dist/{chunk-BAZQPU6T.js → chunk-RBCMUVI6.js} +3 -3
- package/dist/{chunk-BAZQPU6T.js.map → chunk-RBCMUVI6.js.map} +1 -1
- package/dist/{http-bootstrap-PW4K2NDQ.js → http-bootstrap-TGX6STYD.js} +41 -5
- package/dist/http-bootstrap-TGX6STYD.js.map +1 -0
- package/dist/{stdio-bootstrap-PSX3TI3I.js → stdio-bootstrap-7GVABHZQ.js} +4 -4
- package/dist/{stdio-bootstrap-PSX3TI3I.js.map → stdio-bootstrap-7GVABHZQ.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-4MNLSWSZ.js.map +0 -1
- package/dist/http-bootstrap-PW4K2NDQ.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { err, ok } from './chunk-
|
|
2
|
+
import { err, ok } from './chunk-RBCMUVI6.js';
|
|
3
3
|
import { createHash, randomUUID } from 'crypto';
|
|
4
4
|
import createClient from 'openapi-fetch';
|
|
5
5
|
import { fetch } from 'undici';
|
|
@@ -148,12 +148,20 @@ function toApiError(status2, parsed, retryAfterHeader) {
|
|
|
148
148
|
}
|
|
149
149
|
return { kind: "upstream", detail: message, status: status2 };
|
|
150
150
|
}
|
|
151
|
+
var OrganizationStatus = z.enum([
|
|
152
|
+
"pending_email_verification",
|
|
153
|
+
"pending_admin_approval",
|
|
154
|
+
"active",
|
|
155
|
+
"suspended",
|
|
156
|
+
"rejected"
|
|
157
|
+
]);
|
|
151
158
|
var OrgResponse = z.object({
|
|
152
159
|
id: z.string().uuid(),
|
|
153
160
|
name: z.string(),
|
|
154
161
|
owner_id: z.string().uuid(),
|
|
155
|
-
|
|
156
|
-
created_at: z.string().datetime({ offset: true })
|
|
162
|
+
status: OrganizationStatus,
|
|
163
|
+
created_at: z.string().datetime({ offset: true }),
|
|
164
|
+
is_active: z.boolean()
|
|
157
165
|
}).passthrough();
|
|
158
166
|
z.object({ name: z.union([z.string(), z.null()]) }).partial().passthrough();
|
|
159
167
|
var ValidationError = z.object({
|
|
@@ -164,7 +172,7 @@ var ValidationError = z.object({
|
|
|
164
172
|
ctx: z.object({}).partial().passthrough().optional()
|
|
165
173
|
}).passthrough();
|
|
166
174
|
z.object({ detail: z.array(ValidationError) }).partial().passthrough();
|
|
167
|
-
z.object({
|
|
175
|
+
var LabelDefinitionResponse = z.object({
|
|
168
176
|
key: z.string(),
|
|
169
177
|
display_name: z.string(),
|
|
170
178
|
position: z.number().int(),
|
|
@@ -180,7 +188,10 @@ var UserResponse = z.object({
|
|
|
180
188
|
id: z.string().uuid(),
|
|
181
189
|
email: z.string(),
|
|
182
190
|
name: z.string(),
|
|
191
|
+
role_id: z.string().uuid(),
|
|
183
192
|
role_name: z.string(),
|
|
193
|
+
organization_id: z.string().uuid(),
|
|
194
|
+
organization_name: z.string(),
|
|
184
195
|
is_active: z.boolean(),
|
|
185
196
|
created_at: z.string().datetime({ offset: true })
|
|
186
197
|
}).passthrough();
|
|
@@ -271,16 +282,35 @@ var ProxyTargetResponse = z.object({
|
|
|
271
282
|
city: z.string().optional().default(""),
|
|
272
283
|
isp: z.string().optional().default("")
|
|
273
284
|
}).passthrough();
|
|
274
|
-
var
|
|
285
|
+
var AiCategoryResponse = z.object({
|
|
275
286
|
tier1: z.string(),
|
|
276
287
|
tier2: z.union([z.string(), z.null()]).optional(),
|
|
277
288
|
tier3: z.union([z.string(), z.null()]).optional(),
|
|
278
289
|
tier4: z.union([z.string(), z.null()]).optional()
|
|
279
290
|
}).passthrough();
|
|
291
|
+
var IabV3CategoryResponse = z.object({
|
|
292
|
+
tier1: z.string(),
|
|
293
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
294
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
295
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
296
|
+
}).passthrough();
|
|
297
|
+
var ScanTaxonomyClassificationResponse = z.object({
|
|
298
|
+
taxonomy_id: z.string().uuid(),
|
|
299
|
+
taxonomy_name: z.string(),
|
|
300
|
+
taxonomy_slug: z.string(),
|
|
301
|
+
taxonomy_version: z.number().int(),
|
|
302
|
+
leaf_node_id: z.union([z.string(), z.null()]),
|
|
303
|
+
tier1: z.union([z.string(), z.null()]),
|
|
304
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
305
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
306
|
+
tier4: z.union([z.string(), z.null()]).optional(),
|
|
307
|
+
used_default: z.boolean().optional().default(false)
|
|
308
|
+
}).passthrough();
|
|
280
309
|
var ScanClassificationResponse = z.object({
|
|
281
310
|
brand: z.union([z.string(), z.null()]),
|
|
282
|
-
|
|
283
|
-
iab_v3: z.union([
|
|
311
|
+
ai_category: z.union([AiCategoryResponse, z.null()]),
|
|
312
|
+
iab_v3: z.union([IabV3CategoryResponse, z.null()]),
|
|
313
|
+
custom_taxonomies: z.array(ScanTaxonomyClassificationResponse).default([])
|
|
284
314
|
}).partial().passthrough();
|
|
285
315
|
var LandingResponse = z.object({
|
|
286
316
|
ord: z.number().int(),
|
|
@@ -532,7 +562,7 @@ var TagDefinitionWithStatsResponse = z.object({
|
|
|
532
562
|
description: z.string(),
|
|
533
563
|
is_system: z.boolean(),
|
|
534
564
|
organization_id: z.union([z.string(), z.null()]),
|
|
535
|
-
|
|
565
|
+
visibility: z.string(),
|
|
536
566
|
severity: z.string(),
|
|
537
567
|
scans_count: z.number().int(),
|
|
538
568
|
rules_count: z.number().int()
|
|
@@ -552,17 +582,18 @@ var TagDefinitionDetailResponse = z.object({
|
|
|
552
582
|
description: z.string(),
|
|
553
583
|
is_system: z.boolean(),
|
|
554
584
|
organization_id: z.union([z.string(), z.null()]),
|
|
555
|
-
|
|
585
|
+
visibility: z.string(),
|
|
556
586
|
severity: z.string(),
|
|
557
587
|
scans_count: z.number().int(),
|
|
558
588
|
rules_count: z.number().int(),
|
|
559
589
|
linked_rules: z.array(LinkedRuleResponse).optional()
|
|
560
590
|
}).passthrough();
|
|
591
|
+
var TagVisibility = z.enum(["hidden", "internal", "public"]);
|
|
561
592
|
var TagSeverity = z.enum(["high", "medium", "low"]);
|
|
562
593
|
z.object({
|
|
563
594
|
display_name: z.union([z.string(), z.null()]),
|
|
564
595
|
description: z.union([z.string(), z.null()]),
|
|
565
|
-
|
|
596
|
+
visibility: z.union([TagVisibility, z.null()]),
|
|
566
597
|
severity: z.union([TagSeverity, z.null()])
|
|
567
598
|
}).partial().passthrough();
|
|
568
599
|
z.object({
|
|
@@ -613,18 +644,66 @@ var RuleTestResponse = z.object({
|
|
|
613
644
|
llm_prompt_url: z.string().optional().default(""),
|
|
614
645
|
llm_response_url: z.string().optional().default("")
|
|
615
646
|
}).passthrough();
|
|
616
|
-
var
|
|
617
|
-
|
|
618
|
-
|
|
647
|
+
var IabV3PolicyCategoryRequest = z.object({
|
|
648
|
+
tier1: z.string().min(1).max(200),
|
|
649
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
650
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
651
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
652
|
+
}).passthrough();
|
|
653
|
+
var AiCategoryRequest = z.object({
|
|
654
|
+
tier1: z.string().min(1).max(200),
|
|
655
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
656
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
657
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
658
|
+
}).passthrough();
|
|
659
|
+
var CustomTaxonomyRefRequest = z.object({
|
|
660
|
+
taxonomy_id: z.string().uuid(),
|
|
661
|
+
tier1: z.string().min(1).max(200),
|
|
662
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
663
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
664
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
619
665
|
}).passthrough();
|
|
666
|
+
var PolicyEntryRequest = z.object({
|
|
667
|
+
rule_type: z.enum(["tag", "iab_v3", "brand", "ai_category", "custom_taxonomy"]).default("tag"),
|
|
668
|
+
tag_slug: z.union([z.string(), z.null()]),
|
|
669
|
+
iab_v3: z.union([IabV3PolicyCategoryRequest, z.null()]),
|
|
670
|
+
brand: z.union([z.string(), z.null()]),
|
|
671
|
+
ai_category: z.union([AiCategoryRequest, z.null()]),
|
|
672
|
+
custom_taxonomy: z.union([CustomTaxonomyRefRequest, z.null()]),
|
|
673
|
+
country_codes: z.array(z.string()).max(50)
|
|
674
|
+
}).partial().passthrough();
|
|
620
675
|
z.object({
|
|
621
676
|
name: z.string().min(1).max(200),
|
|
622
677
|
description: z.string().max(2e3).optional().default(""),
|
|
623
678
|
entries: z.array(PolicyEntryRequest).min(1).max(500)
|
|
624
679
|
}).passthrough();
|
|
680
|
+
var PolicyEntryIabV3Response = z.object({
|
|
681
|
+
tier1: z.string(),
|
|
682
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
683
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
684
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
685
|
+
}).passthrough();
|
|
686
|
+
var PolicyEntryAiCategoryResponse = z.object({
|
|
687
|
+
tier1: z.string(),
|
|
688
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
689
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
690
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
691
|
+
}).passthrough();
|
|
692
|
+
var PolicyEntryCustomTaxonomyResponse = z.object({
|
|
693
|
+
taxonomy_id: z.string().uuid(),
|
|
694
|
+
tier1: z.string(),
|
|
695
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
696
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
697
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
698
|
+
}).passthrough();
|
|
625
699
|
var PolicyEntryResponse = z.object({
|
|
626
700
|
id: z.string().uuid(),
|
|
627
|
-
|
|
701
|
+
rule_type: z.string(),
|
|
702
|
+
tag_slug: z.union([z.string(), z.null()]).optional(),
|
|
703
|
+
iab_v3: z.union([PolicyEntryIabV3Response, z.null()]).optional(),
|
|
704
|
+
brand: z.union([z.string(), z.null()]).optional(),
|
|
705
|
+
ai_category: z.union([PolicyEntryAiCategoryResponse, z.null()]).optional(),
|
|
706
|
+
custom_taxonomy: z.union([PolicyEntryCustomTaxonomyResponse, z.null()]).optional(),
|
|
628
707
|
country_codes: z.array(z.string())
|
|
629
708
|
}).passthrough();
|
|
630
709
|
var PolicySetResponse = z.object({
|
|
@@ -677,7 +756,9 @@ var AlertResponse = z.object({
|
|
|
677
756
|
updated_at: z.union([z.string(), z.null()]),
|
|
678
757
|
scan_url: z.string(),
|
|
679
758
|
offer_url: z.string(),
|
|
680
|
-
tag_display_name: z.string()
|
|
759
|
+
tag_display_name: z.string(),
|
|
760
|
+
rule_type: z.string().optional().default("tag"),
|
|
761
|
+
matched_value: z.union([z.string(), z.null()]).optional()
|
|
681
762
|
}).passthrough();
|
|
682
763
|
z.object({
|
|
683
764
|
items: z.array(AlertResponse),
|
|
@@ -932,8 +1013,67 @@ z.object({
|
|
|
932
1013
|
id: z.string().uuid(),
|
|
933
1014
|
received_at: z.string().datetime({ offset: true })
|
|
934
1015
|
}).passthrough();
|
|
1016
|
+
var CustomTaxonomyListItem = z.object({
|
|
1017
|
+
id: z.string().uuid(),
|
|
1018
|
+
name: z.string(),
|
|
1019
|
+
slug: z.string(),
|
|
1020
|
+
description: z.string(),
|
|
1021
|
+
is_active: z.boolean(),
|
|
1022
|
+
version: z.number().int(),
|
|
1023
|
+
node_count: z.number().int(),
|
|
1024
|
+
created_at: z.string().datetime({ offset: true }),
|
|
1025
|
+
updated_at: z.string().datetime({ offset: true })
|
|
1026
|
+
}).passthrough();
|
|
1027
|
+
var TaxonomyNodeRequest = z.object({
|
|
1028
|
+
client_id: z.string().min(1).max(100),
|
|
1029
|
+
parent_client_id: z.union([z.string(), z.null()]).optional(),
|
|
1030
|
+
name: z.string().min(1).max(100),
|
|
1031
|
+
description: z.string().max(200).optional().default(""),
|
|
1032
|
+
is_default: z.boolean().optional().default(false)
|
|
1033
|
+
});
|
|
1034
|
+
z.object({
|
|
1035
|
+
name: z.string().min(1).max(100),
|
|
1036
|
+
description: z.string().max(500).optional().default(""),
|
|
1037
|
+
nodes: z.array(TaxonomyNodeRequest).optional().default([])
|
|
1038
|
+
});
|
|
1039
|
+
var TaxonomyNodeResponse = z.object({
|
|
1040
|
+
id: z.string().uuid(),
|
|
1041
|
+
parent_id: z.union([z.string(), z.null()]),
|
|
1042
|
+
level: z.number().int(),
|
|
1043
|
+
position: z.number().int(),
|
|
1044
|
+
name: z.string(),
|
|
1045
|
+
description: z.string(),
|
|
1046
|
+
is_default: z.boolean()
|
|
1047
|
+
}).passthrough();
|
|
1048
|
+
var CustomTaxonomyResponse = z.object({
|
|
1049
|
+
id: z.string().uuid(),
|
|
1050
|
+
organization_id: z.string().uuid(),
|
|
1051
|
+
name: z.string(),
|
|
1052
|
+
slug: z.string(),
|
|
1053
|
+
description: z.string(),
|
|
1054
|
+
is_active: z.boolean(),
|
|
1055
|
+
version: z.number().int(),
|
|
1056
|
+
nodes: z.array(TaxonomyNodeResponse),
|
|
1057
|
+
created_at: z.string().datetime({ offset: true }),
|
|
1058
|
+
updated_at: z.string().datetime({ offset: true })
|
|
1059
|
+
}).passthrough();
|
|
1060
|
+
z.object({
|
|
1061
|
+
text: z.string().min(1).max(5e4)
|
|
1062
|
+
});
|
|
1063
|
+
var ParsedTaxonomyNode = z.object({
|
|
1064
|
+
level: z.number().int(),
|
|
1065
|
+
name: z.string(),
|
|
1066
|
+
description: z.string()
|
|
1067
|
+
}).passthrough();
|
|
1068
|
+
var ParseTaxonomyTextResponse = z.object({ nodes: z.array(ParsedTaxonomyNode), warnings: z.array(z.string()) }).passthrough();
|
|
1069
|
+
z.object({
|
|
1070
|
+
name: z.string().min(1).max(100),
|
|
1071
|
+
description: z.string().max(500).optional().default(""),
|
|
1072
|
+
nodes: z.array(TaxonomyNodeRequest).optional().default([])
|
|
1073
|
+
});
|
|
935
1074
|
var schemas = {
|
|
936
1075
|
OrgResponse,
|
|
1076
|
+
LabelDefinitionResponse,
|
|
937
1077
|
UserResponse,
|
|
938
1078
|
ApiKeyResponse,
|
|
939
1079
|
ApiKeyCreatedResponse,
|
|
@@ -970,7 +1110,12 @@ var schemas = {
|
|
|
970
1110
|
BulkReplayResponse,
|
|
971
1111
|
AlertNotificationDestinationResponse,
|
|
972
1112
|
CampaignOverridesResponse,
|
|
973
|
-
InvoiceResponse
|
|
1113
|
+
InvoiceResponse,
|
|
1114
|
+
CustomTaxonomyListItem,
|
|
1115
|
+
TaxonomyNodeResponse,
|
|
1116
|
+
CustomTaxonomyResponse,
|
|
1117
|
+
ParsedTaxonomyNode,
|
|
1118
|
+
ParseTaxonomyTextResponse};
|
|
974
1119
|
function parseWithSchema(schema, raw, label) {
|
|
975
1120
|
const parsed = schema.safeParse(raw);
|
|
976
1121
|
if (parsed.success) return ok(stripUndefinedKeys(parsed.data));
|
|
@@ -1020,6 +1165,8 @@ var AlertSchema = schemas.AlertResponse.pick({
|
|
|
1020
1165
|
campaign_id: true,
|
|
1021
1166
|
policy_set_id: true,
|
|
1022
1167
|
violation_rule_id: true,
|
|
1168
|
+
rule_type: true,
|
|
1169
|
+
matched_value: true,
|
|
1023
1170
|
tag_slug: true,
|
|
1024
1171
|
tag_display_name: true,
|
|
1025
1172
|
country_code: true,
|
|
@@ -1054,7 +1201,13 @@ var BillingSummarySchema = schemas.BillingSummaryResponse.pick({
|
|
|
1054
1201
|
is_suspended: true,
|
|
1055
1202
|
can_create_scan: true,
|
|
1056
1203
|
billing_mode: true,
|
|
1057
|
-
block_reason: true
|
|
1204
|
+
block_reason: true,
|
|
1205
|
+
current_plan_is_custom: true,
|
|
1206
|
+
credit_limit_micros: true,
|
|
1207
|
+
effective_minimum_balance_micros: true,
|
|
1208
|
+
scheduled_next_plan_id: true,
|
|
1209
|
+
scheduled_next_plan_name: true,
|
|
1210
|
+
scheduled_effective_at: true
|
|
1058
1211
|
}).strip();
|
|
1059
1212
|
var parseBillingSummary = (raw) => parseWithSchema(BillingSummarySchema, raw, "billing-summary");
|
|
1060
1213
|
|
|
@@ -1067,10 +1220,18 @@ var CampaignSchema = schemas.CampaignResponse.pick({
|
|
|
1067
1220
|
ad_tag: true,
|
|
1068
1221
|
country_codes: true,
|
|
1069
1222
|
group_id: true,
|
|
1223
|
+
emulator_selection: true,
|
|
1224
|
+
proxy_type: true,
|
|
1225
|
+
proxy_region: true,
|
|
1226
|
+
proxy_city: true,
|
|
1227
|
+
proxy_isp: true,
|
|
1070
1228
|
labels: true,
|
|
1071
1229
|
policy_set_id: true,
|
|
1072
1230
|
schedule_enabled: true,
|
|
1073
1231
|
schedule_type: true,
|
|
1232
|
+
schedule_weekly: true,
|
|
1233
|
+
schedule_interval_seconds: true,
|
|
1234
|
+
schedule_timezone: true,
|
|
1074
1235
|
is_archived: true,
|
|
1075
1236
|
created_at: true,
|
|
1076
1237
|
last_run_at: true
|
|
@@ -1143,6 +1304,49 @@ var CustomRuleSchema2 = schemas.CustomRuleResponse.pick({
|
|
|
1143
1304
|
created_at: true
|
|
1144
1305
|
}).strip();
|
|
1145
1306
|
var parseCustomRulePage = (raw) => parsePagedWithItemSchema(CustomRuleSchema2, raw, "custom-rules");
|
|
1307
|
+
var CustomTaxonomyListItemSchema = schemas.CustomTaxonomyListItem.pick({
|
|
1308
|
+
id: true,
|
|
1309
|
+
name: true,
|
|
1310
|
+
slug: true,
|
|
1311
|
+
description: true,
|
|
1312
|
+
is_active: true,
|
|
1313
|
+
version: true,
|
|
1314
|
+
node_count: true,
|
|
1315
|
+
created_at: true,
|
|
1316
|
+
updated_at: true
|
|
1317
|
+
}).strip();
|
|
1318
|
+
var CustomTaxonomyListSchema = z.array(CustomTaxonomyListItemSchema);
|
|
1319
|
+
var TaxonomyNodeSchema = schemas.TaxonomyNodeResponse.pick({
|
|
1320
|
+
id: true,
|
|
1321
|
+
parent_id: true,
|
|
1322
|
+
level: true,
|
|
1323
|
+
position: true,
|
|
1324
|
+
name: true,
|
|
1325
|
+
description: true,
|
|
1326
|
+
is_default: true
|
|
1327
|
+
}).strip();
|
|
1328
|
+
var CustomTaxonomySchema = schemas.CustomTaxonomyResponse.pick({
|
|
1329
|
+
id: true,
|
|
1330
|
+
organization_id: true,
|
|
1331
|
+
name: true,
|
|
1332
|
+
slug: true,
|
|
1333
|
+
description: true,
|
|
1334
|
+
is_active: true,
|
|
1335
|
+
version: true,
|
|
1336
|
+
created_at: true,
|
|
1337
|
+
updated_at: true
|
|
1338
|
+
}).extend({ nodes: z.array(TaxonomyNodeSchema) }).strip();
|
|
1339
|
+
var ParsedTaxonomyNodeSchema = schemas.ParsedTaxonomyNode.pick({
|
|
1340
|
+
level: true,
|
|
1341
|
+
name: true,
|
|
1342
|
+
description: true
|
|
1343
|
+
}).strip();
|
|
1344
|
+
var ParseTaxonomyTextResponseSchema = schemas.ParseTaxonomyTextResponse.pick({
|
|
1345
|
+
warnings: true
|
|
1346
|
+
}).extend({ nodes: z.array(ParsedTaxonomyNodeSchema) }).strip();
|
|
1347
|
+
var parseCustomTaxonomyList = (raw) => parseWithSchema(CustomTaxonomyListSchema, raw, "custom-taxonomies");
|
|
1348
|
+
var parseCustomTaxonomy = (raw) => parseWithSchema(CustomTaxonomySchema, raw, "custom-taxonomy");
|
|
1349
|
+
var parseTaxonomyTextPreview = (raw) => parseWithSchema(ParseTaxonomyTextResponseSchema, raw, "custom-taxonomy-parse-text");
|
|
1146
1350
|
|
|
1147
1351
|
// src/infrastructure/api/parsers/parse-empty.ts
|
|
1148
1352
|
function parseEmpty(_raw) {
|
|
@@ -1200,6 +1404,7 @@ var UserSchema = schemas.UserResponse.pick({
|
|
|
1200
1404
|
id: true,
|
|
1201
1405
|
email: true,
|
|
1202
1406
|
name: true,
|
|
1407
|
+
role_id: true,
|
|
1203
1408
|
role_name: true,
|
|
1204
1409
|
is_active: true,
|
|
1205
1410
|
created_at: true
|
|
@@ -1213,6 +1418,13 @@ var RoleSchema = schemas.RoleResponse.pick({
|
|
|
1213
1418
|
permissions: true
|
|
1214
1419
|
}).strip();
|
|
1215
1420
|
var parseRole = (raw) => parseWithSchema(RoleSchema, raw, "role");
|
|
1421
|
+
var LabelDefinitionSchema = schemas.LabelDefinitionResponse.pick({
|
|
1422
|
+
key: true,
|
|
1423
|
+
display_name: true,
|
|
1424
|
+
position: true,
|
|
1425
|
+
auto_extract: true
|
|
1426
|
+
}).strip();
|
|
1427
|
+
var parseLabelDefinition = (raw) => parseWithSchema(LabelDefinitionSchema, raw, "label-definition");
|
|
1216
1428
|
var ApiKeyCreatedSchema = schemas.ApiKeyCreatedResponse.pick({
|
|
1217
1429
|
id: true,
|
|
1218
1430
|
key_prefix: true,
|
|
@@ -1354,7 +1566,12 @@ var GroupActionSchema = schemas.GroupActionResponse.pick({
|
|
|
1354
1566
|
var parseGroupAction = (raw) => parseWithSchema(GroupActionSchema, raw, "group-action");
|
|
1355
1567
|
schemas.PolicyEntryResponse.pick({
|
|
1356
1568
|
id: true,
|
|
1569
|
+
rule_type: true,
|
|
1357
1570
|
tag_slug: true,
|
|
1571
|
+
iab_v3: true,
|
|
1572
|
+
brand: true,
|
|
1573
|
+
ai_category: true,
|
|
1574
|
+
custom_taxonomy: true,
|
|
1358
1575
|
country_codes: true
|
|
1359
1576
|
}).strip();
|
|
1360
1577
|
var GeoSchema = schemas.GeoResponse.pick({
|
|
@@ -1465,7 +1682,7 @@ var TagDefinitionSchema = schemas.TagDefinitionWithStatsResponse.pick({
|
|
|
1465
1682
|
severity: true,
|
|
1466
1683
|
is_system: true,
|
|
1467
1684
|
organization_id: true,
|
|
1468
|
-
|
|
1685
|
+
visibility: true,
|
|
1469
1686
|
scans_count: true,
|
|
1470
1687
|
rules_count: true
|
|
1471
1688
|
}).strip();
|
|
@@ -1484,7 +1701,7 @@ var TagDetailSchema = schemas.TagDefinitionDetailResponse.pick({
|
|
|
1484
1701
|
severity: true,
|
|
1485
1702
|
is_system: true,
|
|
1486
1703
|
organization_id: true,
|
|
1487
|
-
|
|
1704
|
+
visibility: true,
|
|
1488
1705
|
scans_count: true,
|
|
1489
1706
|
rules_count: true
|
|
1490
1707
|
}).extend({ linked_rules: z.array(LinkedRuleSchema).optional() }).strip();
|
|
@@ -1592,6 +1809,56 @@ function createHttpApiGateway(config) {
|
|
|
1592
1809
|
}
|
|
1593
1810
|
return err(toApiError(status2, error ?? data, response.headers.get("retry-after") ?? void 0));
|
|
1594
1811
|
}
|
|
1812
|
+
async function binaryGet(path, query) {
|
|
1813
|
+
const startedAtMs = Date.now();
|
|
1814
|
+
const qs = query && Object.keys(query).length > 0 ? `?${new URLSearchParams(
|
|
1815
|
+
Object.fromEntries(Object.entries(query).map(([k, v]) => [k, String(v)]))
|
|
1816
|
+
).toString()}` : "";
|
|
1817
|
+
const url = `${baseUrl}${path}${qs}`;
|
|
1818
|
+
let response;
|
|
1819
|
+
try {
|
|
1820
|
+
const init = {
|
|
1821
|
+
method: "GET",
|
|
1822
|
+
// Header allowlist: see binaryGet docstring above. Keep this
|
|
1823
|
+
// map minimal; any future addition needs a CONTRIBUTING.md
|
|
1824
|
+
// update + a tenant-isolation review.
|
|
1825
|
+
headers: {
|
|
1826
|
+
authorization: bearer.toAuthorizationHeader(),
|
|
1827
|
+
accept: "image/*, application/pdf, application/octet-stream",
|
|
1828
|
+
"user-agent": "kaminari-ad-mcp",
|
|
1829
|
+
"x-request-id": requestId
|
|
1830
|
+
}
|
|
1831
|
+
};
|
|
1832
|
+
if (dispatcher !== void 0) init["dispatcher"] = dispatcher;
|
|
1833
|
+
response = await fetch(
|
|
1834
|
+
url,
|
|
1835
|
+
init
|
|
1836
|
+
);
|
|
1837
|
+
} catch (cause) {
|
|
1838
|
+
logger.warn({ api_path: path, elapsed_ms: Date.now() - startedAtMs }, "api.network_error");
|
|
1839
|
+
const inner = cause instanceof Error && cause.cause instanceof Error ? cause.cause : cause instanceof Error ? cause : null;
|
|
1840
|
+
return err({ kind: "upstream", detail: inner !== null ? inner.message : "network error" });
|
|
1841
|
+
}
|
|
1842
|
+
const status2 = response.status;
|
|
1843
|
+
logger.info(
|
|
1844
|
+
{ api_path: path, api_status: status2, elapsed_ms: Date.now() - startedAtMs },
|
|
1845
|
+
"api.done"
|
|
1846
|
+
);
|
|
1847
|
+
if (status2 >= 200 && status2 < 300) {
|
|
1848
|
+
const buf = await response.arrayBuffer();
|
|
1849
|
+
return ok({
|
|
1850
|
+
bytes: new Uint8Array(buf),
|
|
1851
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream"
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
let errBody;
|
|
1855
|
+
try {
|
|
1856
|
+
errBody = await response.json();
|
|
1857
|
+
} catch {
|
|
1858
|
+
errBody = void 0;
|
|
1859
|
+
}
|
|
1860
|
+
return err(toApiError(status2, errBody, response.headers.get("retry-after") ?? void 0));
|
|
1861
|
+
}
|
|
1595
1862
|
return {
|
|
1596
1863
|
// ── Account ───────────────────────────────────────────────────
|
|
1597
1864
|
async getAccount() {
|
|
@@ -1633,6 +1900,33 @@ function createHttpApiGateway(config) {
|
|
|
1633
1900
|
async listOrgRoles() {
|
|
1634
1901
|
return call("GET", "/api/v1/account/roles", {}, parseArrayOf(parseRole));
|
|
1635
1902
|
},
|
|
1903
|
+
async createCustomRole(body) {
|
|
1904
|
+
return call("POST", "/api/v1/account/roles", { body }, parseRole);
|
|
1905
|
+
},
|
|
1906
|
+
async listAccountLabels() {
|
|
1907
|
+
return call("GET", "/api/v1/account/labels", {}, parseArrayOf(parseLabelDefinition));
|
|
1908
|
+
},
|
|
1909
|
+
async updateAccountLabels(body) {
|
|
1910
|
+
return call("PUT", "/api/v1/account/labels", { body }, parseArrayOf(parseLabelDefinition));
|
|
1911
|
+
},
|
|
1912
|
+
async getScanScreenshot(scanId, w2) {
|
|
1913
|
+
return binaryGet(`/api/v1/scans/${scanId}/screenshot`, w2 !== void 0 ? { w: w2 } : void 0);
|
|
1914
|
+
},
|
|
1915
|
+
async getScanCreativeScreenshot(scanId, w2) {
|
|
1916
|
+
return binaryGet(
|
|
1917
|
+
`/api/v1/scans/${scanId}/creative-screenshot`,
|
|
1918
|
+
w2 !== void 0 ? { w: w2 } : void 0
|
|
1919
|
+
);
|
|
1920
|
+
},
|
|
1921
|
+
async getScanLandingScreenshot(scanId, landingOrd, w2) {
|
|
1922
|
+
return binaryGet(
|
|
1923
|
+
`/api/v1/scans/${scanId}/landings/${String(landingOrd)}/screenshot`,
|
|
1924
|
+
w2 !== void 0 ? { w: w2 } : void 0
|
|
1925
|
+
);
|
|
1926
|
+
},
|
|
1927
|
+
async getInvoicePdf(invoiceId) {
|
|
1928
|
+
return binaryGet(`/api/v1/invoices/${invoiceId}/pdf`);
|
|
1929
|
+
},
|
|
1636
1930
|
async listApiKeys() {
|
|
1637
1931
|
return call("GET", "/api/v1/account/api-keys", {}, parseApiKeyList);
|
|
1638
1932
|
},
|
|
@@ -1759,8 +2053,13 @@ function createHttpApiGateway(config) {
|
|
|
1759
2053
|
parsePageOf(parseRun)
|
|
1760
2054
|
);
|
|
1761
2055
|
},
|
|
1762
|
-
async listCampaignsPicker() {
|
|
1763
|
-
return call(
|
|
2056
|
+
async listCampaignsPicker(filters) {
|
|
2057
|
+
return call(
|
|
2058
|
+
"GET",
|
|
2059
|
+
"/api/v1/campaigns/picker",
|
|
2060
|
+
filters ? { params: { query: filters } } : {},
|
|
2061
|
+
parseCampaignPickerArray
|
|
2062
|
+
);
|
|
1764
2063
|
},
|
|
1765
2064
|
// ── Runs ──────────────────────────────────────────────────────
|
|
1766
2065
|
async getRun(id) {
|
|
@@ -1859,8 +2158,13 @@ function createHttpApiGateway(config) {
|
|
|
1859
2158
|
);
|
|
1860
2159
|
},
|
|
1861
2160
|
// ── Tag definitions ───────────────────────────────────────────
|
|
1862
|
-
async listTags() {
|
|
1863
|
-
return call(
|
|
2161
|
+
async listTags(filters) {
|
|
2162
|
+
return call(
|
|
2163
|
+
"GET",
|
|
2164
|
+
"/api/v1/tag-definitions",
|
|
2165
|
+
filters ? { params: { query: filters } } : {},
|
|
2166
|
+
parseTagDefinitionArray
|
|
2167
|
+
);
|
|
1864
2168
|
},
|
|
1865
2169
|
async getTagDefinition(slug) {
|
|
1866
2170
|
return call(
|
|
@@ -1964,6 +2268,53 @@ function createHttpApiGateway(config) {
|
|
|
1964
2268
|
parseEmpty
|
|
1965
2269
|
);
|
|
1966
2270
|
},
|
|
2271
|
+
// ── Custom taxonomies ─────────────────────────────────────────
|
|
2272
|
+
async listCustomTaxonomies() {
|
|
2273
|
+
return call("GET", "/api/v1/custom-taxonomies", {}, parseCustomTaxonomyList);
|
|
2274
|
+
},
|
|
2275
|
+
async getCustomTaxonomy(id) {
|
|
2276
|
+
return call(
|
|
2277
|
+
"GET",
|
|
2278
|
+
"/api/v1/custom-taxonomies/{taxonomy_id}",
|
|
2279
|
+
{ params: { path: { taxonomy_id: id } } },
|
|
2280
|
+
parseCustomTaxonomy
|
|
2281
|
+
);
|
|
2282
|
+
},
|
|
2283
|
+
async createCustomTaxonomy(body) {
|
|
2284
|
+
return call("POST", "/api/v1/custom-taxonomies", { body }, parseCustomTaxonomy);
|
|
2285
|
+
},
|
|
2286
|
+
async updateCustomTaxonomy(id, body) {
|
|
2287
|
+
return call(
|
|
2288
|
+
"PUT",
|
|
2289
|
+
"/api/v1/custom-taxonomies/{taxonomy_id}",
|
|
2290
|
+
{ params: { path: { taxonomy_id: id } }, body },
|
|
2291
|
+
parseCustomTaxonomy
|
|
2292
|
+
);
|
|
2293
|
+
},
|
|
2294
|
+
async deleteCustomTaxonomy(id) {
|
|
2295
|
+
return call(
|
|
2296
|
+
"DELETE",
|
|
2297
|
+
"/api/v1/custom-taxonomies/{taxonomy_id}",
|
|
2298
|
+
{ params: { path: { taxonomy_id: id } } },
|
|
2299
|
+
parseEmpty
|
|
2300
|
+
);
|
|
2301
|
+
},
|
|
2302
|
+
async restoreCustomTaxonomy(id) {
|
|
2303
|
+
return call(
|
|
2304
|
+
"POST",
|
|
2305
|
+
"/api/v1/custom-taxonomies/{taxonomy_id}/restore",
|
|
2306
|
+
{ params: { path: { taxonomy_id: id } } },
|
|
2307
|
+
parseCustomTaxonomy
|
|
2308
|
+
);
|
|
2309
|
+
},
|
|
2310
|
+
async parseCustomTaxonomyText(body) {
|
|
2311
|
+
return call(
|
|
2312
|
+
"POST",
|
|
2313
|
+
"/api/v1/custom-taxonomies/parse-text",
|
|
2314
|
+
{ body },
|
|
2315
|
+
parseTaxonomyTextPreview
|
|
2316
|
+
);
|
|
2317
|
+
},
|
|
1967
2318
|
// ── Alerts ────────────────────────────────────────────────────
|
|
1968
2319
|
async listAlerts(filters) {
|
|
1969
2320
|
return call("GET", "/api/v1/alerts", { params: { query: filters } }, parseAlertPage);
|
|
@@ -2254,6 +2605,32 @@ var createApiKeyTool = {
|
|
|
2254
2605
|
return ok(result.value);
|
|
2255
2606
|
}
|
|
2256
2607
|
};
|
|
2608
|
+
var CreateCustomRoleInputShape = {
|
|
2609
|
+
name: z.string().min(1).max(100).describe("Display name for the role."),
|
|
2610
|
+
permissions: z.array(z.string().min(1).max(100)).min(1).max(200).describe(
|
|
2611
|
+
"Permission slugs the role grants (e.g. 'scans.read', 'campaigns.write'). Use `list_org_roles` to see permissions on existing system roles."
|
|
2612
|
+
)
|
|
2613
|
+
};
|
|
2614
|
+
var createCustomRoleTool = {
|
|
2615
|
+
name: "create_custom_role",
|
|
2616
|
+
description: "Create a custom organization role with the supplied permission set. Returns the persisted role; assign it to users via `update_user_role`.",
|
|
2617
|
+
annotations: {
|
|
2618
|
+
title: "Create Custom Role",
|
|
2619
|
+
readOnlyHint: false,
|
|
2620
|
+
destructiveHint: false,
|
|
2621
|
+
idempotentHint: false,
|
|
2622
|
+
openWorldHint: false
|
|
2623
|
+
},
|
|
2624
|
+
inputSchema: z.object(CreateCustomRoleInputShape),
|
|
2625
|
+
handler: async (input, ctx) => {
|
|
2626
|
+
const result = await ctx.api.createCustomRole({
|
|
2627
|
+
name: input.name,
|
|
2628
|
+
permissions: [...input.permissions]
|
|
2629
|
+
});
|
|
2630
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2631
|
+
return ok(result.value);
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2257
2634
|
var GetAccountInputShape = {};
|
|
2258
2635
|
var getAccountTool = {
|
|
2259
2636
|
name: "get_account",
|
|
@@ -2275,7 +2652,8 @@ var getAccountTool = {
|
|
|
2275
2652
|
var InviteUserInputShape = {
|
|
2276
2653
|
email: z.string().email().describe("Email of the person to invite. They get a signup link."),
|
|
2277
2654
|
role_id: z.string().uuid().describe("UUID of the role to assign on accept. Get UUIDs from `list_org_roles`."),
|
|
2278
|
-
name: z.string().min(1).optional().describe("Optional display name for the invitee.")
|
|
2655
|
+
name: z.string().min(1).optional().describe("Optional display name for the invitee."),
|
|
2656
|
+
timezone: z.string().optional().describe("Optional IANA timezone for the invitee, e.g. 'Europe/Berlin'. Default: UTC.")
|
|
2279
2657
|
};
|
|
2280
2658
|
var inviteUserTool = {
|
|
2281
2659
|
name: "invite_user",
|
|
@@ -2294,11 +2672,30 @@ var inviteUserTool = {
|
|
|
2294
2672
|
role_id: input.role_id
|
|
2295
2673
|
};
|
|
2296
2674
|
if (input.name !== void 0) body.name = input.name;
|
|
2675
|
+
if (input.timezone !== void 0) body.timezone = input.timezone;
|
|
2297
2676
|
const result = await ctx.api.inviteUser(body);
|
|
2298
2677
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
2299
2678
|
return ok(result.value);
|
|
2300
2679
|
}
|
|
2301
2680
|
};
|
|
2681
|
+
var ListAccountLabelsInputShape = {};
|
|
2682
|
+
var listAccountLabelsTool = {
|
|
2683
|
+
name: "list_account_labels",
|
|
2684
|
+
description: "List the organization's custom label definitions (key, display_name, position, auto_extract). Labels enrich scans and can be used as filters in `list_scans`.",
|
|
2685
|
+
annotations: {
|
|
2686
|
+
title: "List Account Labels",
|
|
2687
|
+
readOnlyHint: true,
|
|
2688
|
+
destructiveHint: false,
|
|
2689
|
+
idempotentHint: true,
|
|
2690
|
+
openWorldHint: false
|
|
2691
|
+
},
|
|
2692
|
+
inputSchema: z.object(ListAccountLabelsInputShape),
|
|
2693
|
+
handler: async (_input, ctx) => {
|
|
2694
|
+
const result = await ctx.api.listAccountLabels();
|
|
2695
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2696
|
+
return ok({ items: result.value });
|
|
2697
|
+
}
|
|
2698
|
+
};
|
|
2302
2699
|
var ListApiKeysInputShape = {};
|
|
2303
2700
|
var listApiKeysTool = {
|
|
2304
2701
|
name: "list_api_keys",
|
|
@@ -2413,13 +2810,51 @@ var transferOwnershipTool = {
|
|
|
2413
2810
|
return ok({ transferred: true });
|
|
2414
2811
|
}
|
|
2415
2812
|
};
|
|
2813
|
+
var LabelItem = z.object({
|
|
2814
|
+
key: z.string().min(1).max(100).regex(/^[a-z0-9_]+$/, "key must be snake_case ([a-z0-9_]+)").describe("Stable snake_case identifier used in scan filters."),
|
|
2815
|
+
display_name: z.string().min(1).max(200).describe("Human-readable name shown in the UI."),
|
|
2816
|
+
auto_extract: z.boolean().optional().describe(
|
|
2817
|
+
"When true, the LLM auto-populates this label's value during scan classification. Defaults to false."
|
|
2818
|
+
)
|
|
2819
|
+
});
|
|
2820
|
+
var UpdateAccountLabelsInputShape = {
|
|
2821
|
+
labels: z.array(LabelItem).max(20).describe(
|
|
2822
|
+
"REPLACEMENT list of all labels \u2014 pass every label you want to keep. Order in the array becomes `position`. Empty array removes all labels."
|
|
2823
|
+
)
|
|
2824
|
+
};
|
|
2825
|
+
var updateAccountLabelsTool = {
|
|
2826
|
+
name: "update_account_labels",
|
|
2827
|
+
description: "REPLACE the organization's full set of custom label definitions. The API replaces the list (no merge); read the current list with `list_account_labels` first. Returns the persisted list with allocated positions.",
|
|
2828
|
+
annotations: {
|
|
2829
|
+
title: "Update Account Labels",
|
|
2830
|
+
readOnlyHint: false,
|
|
2831
|
+
// REPLACE semantics: passing `labels: []` wipes every label and
|
|
2832
|
+
// detaches the value on every scan that referenced one. Hint as
|
|
2833
|
+
// destructive so MCP clients that gate dangerous tools warn the
|
|
2834
|
+
// user before this call runs.
|
|
2835
|
+
destructiveHint: true,
|
|
2836
|
+
idempotentHint: true,
|
|
2837
|
+
openWorldHint: false
|
|
2838
|
+
},
|
|
2839
|
+
inputSchema: z.object(UpdateAccountLabelsInputShape),
|
|
2840
|
+
handler: async (input, ctx) => {
|
|
2841
|
+
const result = await ctx.api.updateAccountLabels({
|
|
2842
|
+
labels: input.labels.map((l) => ({
|
|
2843
|
+
key: l.key,
|
|
2844
|
+
display_name: l.display_name,
|
|
2845
|
+
auto_extract: l.auto_extract ?? false
|
|
2846
|
+
}))
|
|
2847
|
+
});
|
|
2848
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2849
|
+
return ok({ items: result.value });
|
|
2850
|
+
}
|
|
2851
|
+
};
|
|
2416
2852
|
var UpdateOrgInputShape = {
|
|
2417
|
-
name: z.string().min(1).max(200).optional().describe("New organization display name.")
|
|
2418
|
-
settings: z.record(z.unknown()).optional().describe("Replacement settings object. Fields not supplied are left unchanged.")
|
|
2853
|
+
name: z.string().min(1).max(200).optional().describe("New organization display name.")
|
|
2419
2854
|
};
|
|
2420
2855
|
var updateOrgTool = {
|
|
2421
2856
|
name: "update_org",
|
|
2422
|
-
description: "Update the caller's organization
|
|
2857
|
+
description: "Update the caller's organization display name.",
|
|
2423
2858
|
annotations: {
|
|
2424
2859
|
title: "Update Organization",
|
|
2425
2860
|
readOnlyHint: false,
|
|
@@ -2430,8 +2865,7 @@ var updateOrgTool = {
|
|
|
2430
2865
|
inputSchema: z.object(UpdateOrgInputShape),
|
|
2431
2866
|
handler: async (input, ctx) => {
|
|
2432
2867
|
const body = {
|
|
2433
|
-
...input.name !== void 0 ? { name: input.name } : {}
|
|
2434
|
-
...input.settings !== void 0 ? { settings: input.settings } : {}
|
|
2868
|
+
...input.name !== void 0 ? { name: input.name } : {}
|
|
2435
2869
|
};
|
|
2436
2870
|
const result = await ctx.api.updateOrg(body);
|
|
2437
2871
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -2574,7 +3008,7 @@ var setCampaignAlertOverridesTool = {
|
|
|
2574
3008
|
var GetAlertStatsInputShape = {};
|
|
2575
3009
|
var getAlertStatsTool = {
|
|
2576
3010
|
name: "get_alert_stats",
|
|
2577
|
-
description: "Get alert counts grouped by status: open,
|
|
3011
|
+
description: "Get alert counts grouped by status: open, acknowledged, resolved, dismissed (total is implicit in the sum). Status names match the canonical AlertStatus enum used by `list_alerts` and `update_alert_status`.",
|
|
2578
3012
|
annotations: {
|
|
2579
3013
|
title: "Get Alert Stats",
|
|
2580
3014
|
readOnlyHint: true,
|
|
@@ -2591,13 +3025,15 @@ var getAlertStatsTool = {
|
|
|
2591
3025
|
};
|
|
2592
3026
|
var ListAlertsInputShape = {
|
|
2593
3027
|
campaign_id: z.string().uuid().optional().describe("Filter to one campaign's alerts."),
|
|
2594
|
-
status: z.enum(["open", "
|
|
3028
|
+
status: z.enum(["open", "acknowledged", "resolved", "dismissed"]).optional().describe(
|
|
3029
|
+
"Filter by alert status. Open = unhandled; acknowledged = seen by an operator; resolved = closed; dismissed = closed without action."
|
|
3030
|
+
),
|
|
2595
3031
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
2596
3032
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
2597
3033
|
};
|
|
2598
3034
|
var listAlertsTool = {
|
|
2599
3035
|
name: "list_alerts",
|
|
2600
|
-
description: "List violation alerts (one per scan + violating
|
|
3036
|
+
description: "List violation alerts (one per scan + violating rule combo) with offer URL, country, status, scan back-reference, and the kind-aware fields `rule_type` (tag / iab_v3 / brand / ai_category / custom_taxonomy) + `matched_value` (the canonical text the scan matched against).",
|
|
2601
3037
|
annotations: {
|
|
2602
3038
|
title: "List Alerts",
|
|
2603
3039
|
readOnlyHint: true,
|
|
@@ -2675,15 +3111,29 @@ var getUsageSummaryTool = {
|
|
|
2675
3111
|
return ok(result.value);
|
|
2676
3112
|
}
|
|
2677
3113
|
};
|
|
3114
|
+
var TransactionTypeEnum = z.enum([
|
|
3115
|
+
"initial_balance",
|
|
3116
|
+
"top_up_manual",
|
|
3117
|
+
"usage_charge",
|
|
3118
|
+
"subscription_renewal",
|
|
3119
|
+
"subscription_upgrade",
|
|
3120
|
+
"admin_adjustment",
|
|
3121
|
+
"refund",
|
|
3122
|
+
"invoice_settlement",
|
|
3123
|
+
"crypto_top_up"
|
|
3124
|
+
]);
|
|
2678
3125
|
var ListBalanceHistoryInputShape = {
|
|
2679
3126
|
date_from: z.string().date().optional().describe("ISO date, inclusive."),
|
|
2680
3127
|
date_to: z.string().date().optional().describe("ISO date, inclusive."),
|
|
3128
|
+
type: z.array(TransactionTypeEnum).max(9).optional().describe(
|
|
3129
|
+
"Filter by transaction kind. Pass several values to OR them (e.g. ['top_up_manual','crypto_top_up'] for credits-only)."
|
|
3130
|
+
),
|
|
2681
3131
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
2682
3132
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
2683
3133
|
};
|
|
2684
3134
|
var listBalanceHistoryTool = {
|
|
2685
3135
|
name: "list_balance_history",
|
|
2686
|
-
description: "List ledger transactions (charges, refunds, top-ups, invoice settlements) on the organization's balance. Each row: type, amount in micros, description, timestamp.",
|
|
3136
|
+
description: "List ledger transactions (charges, refunds, top-ups, invoice settlements) on the organization's balance. Each row: type, amount in micros, description, timestamp. Filter by `type` (multi-select) and / or date range.",
|
|
2687
3137
|
annotations: {
|
|
2688
3138
|
title: "List Balance History",
|
|
2689
3139
|
readOnlyHint: true,
|
|
@@ -2697,7 +3147,8 @@ var listBalanceHistoryTool = {
|
|
|
2697
3147
|
page: input.page,
|
|
2698
3148
|
limit: input.limit,
|
|
2699
3149
|
...input.date_from !== void 0 ? { date_from: input.date_from } : {},
|
|
2700
|
-
...input.date_to !== void 0 ? { date_to: input.date_to } : {}
|
|
3150
|
+
...input.date_to !== void 0 ? { date_to: input.date_to } : {},
|
|
3151
|
+
...input.type !== void 0 ? { type: input.type } : {}
|
|
2701
3152
|
};
|
|
2702
3153
|
const result = await ctx.api.listBalanceHistory(filters);
|
|
2703
3154
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -2705,8 +3156,10 @@ var listBalanceHistoryTool = {
|
|
|
2705
3156
|
}
|
|
2706
3157
|
};
|
|
2707
3158
|
var ListUsageInputShape = {
|
|
2708
|
-
date_from: z.string().
|
|
2709
|
-
|
|
3159
|
+
date_from: z.string().datetime({ offset: true }).optional().describe(
|
|
3160
|
+
"ISO 8601 datetime (with timezone offset), inclusive lower bound on the usage timestamp. Plain dates also work \u2014 the API normalises to UTC midnight."
|
|
3161
|
+
),
|
|
3162
|
+
date_to: z.string().datetime({ offset: true }).optional().describe("ISO 8601 datetime (with offset), inclusive upper bound."),
|
|
2710
3163
|
scan_id: z.string().uuid().optional().describe("Filter to one scan's cost rows."),
|
|
2711
3164
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
2712
3165
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
@@ -2918,12 +3371,11 @@ var unarchiveCampaignGroupTool = {
|
|
|
2918
3371
|
};
|
|
2919
3372
|
var UpdateCampaignGroupInputShape = {
|
|
2920
3373
|
group_id: z.string().uuid().describe("Group UUID to update."),
|
|
2921
|
-
name: z.string().min(1).max(200).optional().describe("New display name.")
|
|
2922
|
-
schedule_paused: z.boolean().optional().describe("Pause/resume the scheduler for every campaign in this group.")
|
|
3374
|
+
name: z.string().min(1).max(200).optional().describe("New display name.")
|
|
2923
3375
|
};
|
|
2924
3376
|
var updateCampaignGroupTool = {
|
|
2925
3377
|
name: "update_campaign_group",
|
|
2926
|
-
description: "
|
|
3378
|
+
description: "Rename a campaign group. To pause/resume its scheduler, use `pause_campaign_group_schedule` / `resume_campaign_group_schedule` instead.",
|
|
2927
3379
|
annotations: {
|
|
2928
3380
|
title: "Update Campaign Group",
|
|
2929
3381
|
readOnlyHint: false,
|
|
@@ -2934,8 +3386,7 @@ var updateCampaignGroupTool = {
|
|
|
2934
3386
|
inputSchema: z.object(UpdateCampaignGroupInputShape),
|
|
2935
3387
|
handler: async (input, ctx) => {
|
|
2936
3388
|
const body = {
|
|
2937
|
-
...input.name !== void 0 ? { name: input.name } : {}
|
|
2938
|
-
...input.schedule_paused !== void 0 ? { schedule_paused: input.schedule_paused } : {}
|
|
3389
|
+
...input.name !== void 0 ? { name: input.name } : {}
|
|
2939
3390
|
};
|
|
2940
3391
|
const result = await ctx.api.updateCampaignGroup(input.group_id, body);
|
|
2941
3392
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -2982,6 +3433,52 @@ var cancelCampaignTool = {
|
|
|
2982
3433
|
return ok(result.value);
|
|
2983
3434
|
}
|
|
2984
3435
|
};
|
|
3436
|
+
var campaignConfigFields = {
|
|
3437
|
+
emulator_categories: z.array(z.string()).optional().describe(
|
|
3438
|
+
"Device categories to rotate through, e.g. ['android_phone']. Valid: android_phone, android_tablet, iphone, ipad, windows_desktop, macos_desktop. On create, omitting this defaults to ['android_phone']; pass [] together with emulator_specific_ids to target ONLY specific devices."
|
|
3439
|
+
),
|
|
3440
|
+
emulator_specific_ids: z.array(z.string()).optional().describe(
|
|
3441
|
+
"Pin exact device profile slugs from `list_emulators` (the `id` field), e.g. ['samsung_galaxy_s23_ultra_android16']. Always scanned regardless of emulator_mode."
|
|
3442
|
+
),
|
|
3443
|
+
emulator_mode: z.enum(["random", "all"]).optional().describe(
|
|
3444
|
+
"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."
|
|
3445
|
+
),
|
|
3446
|
+
proxy_type: z.enum(["residential", "mobile"]).optional().describe("Proxy network type. Default: residential."),
|
|
3447
|
+
proxy_region: z.string().optional().describe("Proxy region/state targeting (free-text; only honoured for a single country)."),
|
|
3448
|
+
proxy_city: z.string().optional().describe("Proxy city targeting."),
|
|
3449
|
+
proxy_isp: z.string().optional().describe("Proxy ISP targeting."),
|
|
3450
|
+
schedule_type: z.enum(["weekly", "interval"]).optional().describe(
|
|
3451
|
+
"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."
|
|
3452
|
+
),
|
|
3453
|
+
schedule_weekly: z.record(z.array(z.number().int().min(0).max(23))).optional().describe(
|
|
3454
|
+
"Weekly run grid for schedule_type='weekly'. Keys are weekdays '0'-'6' (Mon-Sun); values are hours 0-23 in schedule_timezone, e.g. { '0': [9, 17], '4': [12] }."
|
|
3455
|
+
),
|
|
3456
|
+
schedule_interval_seconds: z.number().int().optional().describe(
|
|
3457
|
+
"Run interval for schedule_type='interval'. Allowed presets (seconds): 60, 120, 300, 600, 900, 1800, 2700, 3600, 7200, 14400, 28800, 86400."
|
|
3458
|
+
),
|
|
3459
|
+
schedule_timezone: z.string().optional().describe("IANA timezone for the weekly grid, e.g. 'Europe/Berlin'. Default: UTC.")
|
|
3460
|
+
};
|
|
3461
|
+
function pickCampaignConfigBody(input) {
|
|
3462
|
+
const body = {};
|
|
3463
|
+
if (input.emulator_categories !== void 0) body.emulator_categories = input.emulator_categories;
|
|
3464
|
+
if (input.emulator_specific_ids !== void 0) {
|
|
3465
|
+
body.emulator_specific_ids = input.emulator_specific_ids;
|
|
3466
|
+
}
|
|
3467
|
+
if (input.emulator_mode !== void 0) body.emulator_mode = input.emulator_mode;
|
|
3468
|
+
if (input.proxy_type !== void 0) body.proxy_type = input.proxy_type;
|
|
3469
|
+
if (input.proxy_region !== void 0) body.proxy_region = input.proxy_region;
|
|
3470
|
+
if (input.proxy_city !== void 0) body.proxy_city = input.proxy_city;
|
|
3471
|
+
if (input.proxy_isp !== void 0) body.proxy_isp = input.proxy_isp;
|
|
3472
|
+
if (input.schedule_type !== void 0) body.schedule_type = input.schedule_type;
|
|
3473
|
+
if (input.schedule_weekly !== void 0) body.schedule_weekly = input.schedule_weekly;
|
|
3474
|
+
if (input.schedule_interval_seconds !== void 0) {
|
|
3475
|
+
body.schedule_interval_seconds = input.schedule_interval_seconds;
|
|
3476
|
+
}
|
|
3477
|
+
if (input.schedule_timezone !== void 0) body.schedule_timezone = input.schedule_timezone;
|
|
3478
|
+
return body;
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
// src/application/tools/campaigns/create-campaign.tool.ts
|
|
2985
3482
|
var CreateCampaignInputShape = {
|
|
2986
3483
|
name: z.string().min(1).max(200).describe("Display name (1-200 chars)."),
|
|
2987
3484
|
campaign_type: z.enum(["url", "ad_tag"]).describe("`url` or `ad_tag` \u2014 must match the target field below."),
|
|
@@ -2989,7 +3486,7 @@ var CreateCampaignInputShape = {
|
|
|
2989
3486
|
ad_tag: z.string().optional().describe("Ad-tag HTML/JS (required if campaign_type=ad_tag)."),
|
|
2990
3487
|
country_codes: z.array(z.string().length(2)).min(1).describe("ISO 3166-1 alpha-2 codes \u2014 one scan per country per run."),
|
|
2991
3488
|
group_id: z.string().uuid().optional().describe("Parent group UUID; defaults to the org's default group."),
|
|
2992
|
-
|
|
3489
|
+
...campaignConfigFields,
|
|
2993
3490
|
labels: z.record(z.string()).optional().describe("Arbitrary metadata applied to every queued scan."),
|
|
2994
3491
|
policy_set_id: z.string().uuid().optional().describe("Policy set to evaluate every scan against."),
|
|
2995
3492
|
schedule_enabled: z.boolean().optional().describe("If true, the scheduler runs immediately. Default: false (manual run).")
|
|
@@ -3013,10 +3510,10 @@ var createCampaignTool = {
|
|
|
3013
3510
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
3014
3511
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3015
3512
|
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3016
|
-
...input.emulator_categories !== void 0 ? { emulator_categories: input.emulator_categories } : {},
|
|
3017
3513
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3018
3514
|
...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
|
|
3019
|
-
...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {}
|
|
3515
|
+
...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {},
|
|
3516
|
+
...pickCampaignConfigBody(input)
|
|
3020
3517
|
};
|
|
3021
3518
|
const result = await ctx.api.createCampaign(body);
|
|
3022
3519
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -3070,12 +3567,16 @@ var listCampaignRunsTool = {
|
|
|
3070
3567
|
};
|
|
3071
3568
|
var ListCampaignsInputShape = {
|
|
3072
3569
|
group_id: z.string().uuid().optional().describe("Optional campaign-group UUID to filter by."),
|
|
3570
|
+
archived: z.boolean().optional().describe(
|
|
3571
|
+
"When omitted, the API returns active campaigns only. Pass `true` to see archived campaigns, `false` for active-only (explicit)."
|
|
3572
|
+
),
|
|
3573
|
+
q: z.string().min(1).max(200).optional().describe("Substring search against campaign name (case-insensitive)."),
|
|
3073
3574
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
3074
3575
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size (1-200).")
|
|
3075
3576
|
};
|
|
3076
3577
|
var listCampaignsTool = {
|
|
3077
3578
|
name: "list_campaigns",
|
|
3078
|
-
description: "List campaigns for the caller's organization, optionally filtered by group,
|
|
3579
|
+
description: "List campaigns for the caller's organization, optionally filtered by group, archived flag, or name substring. Paginated.",
|
|
3079
3580
|
annotations: {
|
|
3080
3581
|
title: "List Campaigns",
|
|
3081
3582
|
readOnlyHint: true,
|
|
@@ -3088,14 +3589,21 @@ var listCampaignsTool = {
|
|
|
3088
3589
|
const filters = {
|
|
3089
3590
|
page: input.page,
|
|
3090
3591
|
limit: input.limit,
|
|
3091
|
-
...input.group_id !== void 0 ? { group_id: input.group_id } : {}
|
|
3592
|
+
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3593
|
+
...input.archived !== void 0 ? { archived: input.archived } : {},
|
|
3594
|
+
...input.q !== void 0 ? { q: input.q } : {}
|
|
3092
3595
|
};
|
|
3093
3596
|
const result = await ctx.api.listCampaigns(filters);
|
|
3094
3597
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
3095
3598
|
return ok(result.value);
|
|
3096
3599
|
}
|
|
3097
3600
|
};
|
|
3098
|
-
var ListCampaignsPickerInputShape = {
|
|
3601
|
+
var ListCampaignsPickerInputShape = {
|
|
3602
|
+
archived: z.boolean().optional().describe("Pass true to include archived campaigns. Defaults to active-only."),
|
|
3603
|
+
group_id: z.string().uuid().optional().describe("Filter to one campaign group."),
|
|
3604
|
+
q: z.string().min(1).max(200).optional().describe("Substring search against campaign name (case-insensitive)."),
|
|
3605
|
+
limit: z.number().int().min(1).max(500).optional().describe("Maximum rows to return (default per API, typically 200).")
|
|
3606
|
+
};
|
|
3099
3607
|
var listCampaignsPickerTool = {
|
|
3100
3608
|
name: "list_campaigns_picker",
|
|
3101
3609
|
description: "Slim per-row campaign list for selection UIs \u2014 id, name, group_id, is_archived. Cheaper than `list_campaigns` for orgs with thousands of campaigns. Use `get_campaign(id)` after a selection to fetch full details.",
|
|
@@ -3107,8 +3615,16 @@ var listCampaignsPickerTool = {
|
|
|
3107
3615
|
openWorldHint: false
|
|
3108
3616
|
},
|
|
3109
3617
|
inputSchema: z.object(ListCampaignsPickerInputShape),
|
|
3110
|
-
handler: async (
|
|
3111
|
-
const
|
|
3618
|
+
handler: async (input, ctx) => {
|
|
3619
|
+
const filters = {
|
|
3620
|
+
...input.archived !== void 0 ? { archived: input.archived } : {},
|
|
3621
|
+
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3622
|
+
...input.q !== void 0 ? { q: input.q } : {},
|
|
3623
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
3624
|
+
};
|
|
3625
|
+
const result = await ctx.api.listCampaignsPicker(
|
|
3626
|
+
Object.keys(filters).length > 0 ? filters : void 0
|
|
3627
|
+
);
|
|
3112
3628
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
3113
3629
|
return ok(result.value);
|
|
3114
3630
|
}
|
|
@@ -3156,7 +3672,11 @@ var unarchiveCampaignTool = {
|
|
|
3156
3672
|
var UpdateCampaignInputShape = {
|
|
3157
3673
|
campaign_id: z.string().uuid().describe("Campaign UUID to update."),
|
|
3158
3674
|
name: z.string().min(1).max(200).optional().describe("New display name."),
|
|
3675
|
+
url: z.string().url().optional().describe("New target URL (url-type campaigns)."),
|
|
3676
|
+
ad_tag: z.string().optional().describe("New ad-tag HTML/JS (ad_tag-type campaigns)."),
|
|
3159
3677
|
country_codes: z.array(z.string().length(2)).optional().describe("Replace the country list."),
|
|
3678
|
+
group_id: z.string().uuid().optional().describe("Move the campaign to another group."),
|
|
3679
|
+
...campaignConfigFields,
|
|
3160
3680
|
labels: z.record(z.string()).optional().describe("Replace the label map."),
|
|
3161
3681
|
policy_set_id: z.string().uuid().nullable().optional().describe("New policy set UUID; pass null to clear."),
|
|
3162
3682
|
schedule_enabled: z.boolean().optional().describe("Pause / resume the scheduler.")
|
|
@@ -3175,10 +3695,14 @@ var updateCampaignTool = {
|
|
|
3175
3695
|
handler: async (input, ctx) => {
|
|
3176
3696
|
const body = {
|
|
3177
3697
|
...input.name !== void 0 ? { name: input.name } : {},
|
|
3698
|
+
...input.url !== void 0 ? { url: input.url } : {},
|
|
3699
|
+
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3178
3700
|
...input.country_codes !== void 0 ? { country_codes: input.country_codes } : {},
|
|
3701
|
+
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3179
3702
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3180
3703
|
...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
|
|
3181
|
-
...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {}
|
|
3704
|
+
...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {},
|
|
3705
|
+
...pickCampaignConfigBody(input)
|
|
3182
3706
|
};
|
|
3183
3707
|
const result = await ctx.api.updateCampaign(input.campaign_id, body);
|
|
3184
3708
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -3186,17 +3710,25 @@ var updateCampaignTool = {
|
|
|
3186
3710
|
}
|
|
3187
3711
|
};
|
|
3188
3712
|
var CreateCustomRuleInputShape = {
|
|
3189
|
-
name: z.string().min(1).max(200).describe(
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3713
|
+
name: z.string().min(1).max(200).describe(
|
|
3714
|
+
"Display name. For non-LLM rules this also becomes the `display_name` of the auto-registered tag definition (see `tag_slug`)."
|
|
3715
|
+
),
|
|
3716
|
+
tag_slug: z.string().max(100).optional().describe(
|
|
3717
|
+
"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 `is_system=true`); colliding requests return 422 with code `checking.system_slug_reserved`. For `rule_type='llm'` use `config.tags` keys instead and leave `tag_slug` empty."
|
|
3718
|
+
),
|
|
3719
|
+
rule_type: z.string().max(50).describe(
|
|
3720
|
+
"Rule engine. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`. The API validates."
|
|
3721
|
+
),
|
|
3722
|
+
config: z.record(z.unknown()).describe(
|
|
3723
|
+
"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`)."
|
|
3724
|
+
),
|
|
3193
3725
|
target: z.string().max(30).optional().describe(
|
|
3194
3726
|
"Where to apply the rule (e.g. 'page' for landing HTML). Default: page. See API docs for the full set of valid values."
|
|
3195
3727
|
)
|
|
3196
3728
|
};
|
|
3197
3729
|
var createCustomRuleTool = {
|
|
3198
3730
|
name: "create_custom_rule",
|
|
3199
|
-
description: "Define a custom tag-detection rule (
|
|
3731
|
+
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`.",
|
|
3200
3732
|
annotations: {
|
|
3201
3733
|
title: "Create Custom Rule",
|
|
3202
3734
|
readOnlyHint: false,
|
|
@@ -3278,8 +3810,12 @@ var listCustomRulesTool = {
|
|
|
3278
3810
|
}
|
|
3279
3811
|
};
|
|
3280
3812
|
var TestCustomRuleInputShape = {
|
|
3281
|
-
rule_type: z.string().max(50).describe(
|
|
3282
|
-
|
|
3813
|
+
rule_type: z.string().max(50).describe(
|
|
3814
|
+
"Rule engine type. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`."
|
|
3815
|
+
),
|
|
3816
|
+
config: z.record(z.unknown()).describe(
|
|
3817
|
+
"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` (`is_system=true`) before promoting to `create_custom_rule`."
|
|
3818
|
+
),
|
|
3283
3819
|
target: z.string().max(30).describe(
|
|
3284
3820
|
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
3285
3821
|
),
|
|
@@ -3287,7 +3823,7 @@ var TestCustomRuleInputShape = {
|
|
|
3287
3823
|
};
|
|
3288
3824
|
var testCustomRuleTool = {
|
|
3289
3825
|
name: "test_custom_rule",
|
|
3290
|
-
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`.",
|
|
3826
|
+
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` (`is_system=true`) before promoting to `create_custom_rule`, which would otherwise reject the same payload with HTTP 422 / code `checking.system_slug_reserved`.",
|
|
3291
3827
|
annotations: {
|
|
3292
3828
|
title: "Test Custom Rule",
|
|
3293
3829
|
readOnlyHint: true,
|
|
@@ -3309,9 +3845,15 @@ var testCustomRuleTool = {
|
|
|
3309
3845
|
};
|
|
3310
3846
|
var UpdateCustomRuleInputShape = {
|
|
3311
3847
|
rule_id: z.string().uuid().describe("Rule UUID to update."),
|
|
3312
|
-
name: z.string().min(1).max(200).optional().describe(
|
|
3313
|
-
|
|
3314
|
-
|
|
3848
|
+
name: z.string().min(1).max(200).optional().describe(
|
|
3849
|
+
"New display name. For non-LLM rules this also becomes the new `display_name` of the auto-registered tag definition."
|
|
3850
|
+
),
|
|
3851
|
+
tag_slug: z.string().max(100).optional().describe(
|
|
3852
|
+
"New tag slug to assign on match. **MUST NOT collide with a built-in system tag slug** (see `list_tags` where `is_system=true`); colliding requests return 422 with code `checking.system_slug_reserved`. Re-registering an existing custom slug refreshes its tag definition's `display_name` / `description`."
|
|
3853
|
+
),
|
|
3854
|
+
config: z.record(z.unknown()).optional().describe(
|
|
3855
|
+
"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."
|
|
3856
|
+
),
|
|
3315
3857
|
target: z.string().max(30).optional().describe(
|
|
3316
3858
|
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
3317
3859
|
),
|
|
@@ -3319,7 +3861,7 @@ var UpdateCustomRuleInputShape = {
|
|
|
3319
3861
|
};
|
|
3320
3862
|
var updateCustomRuleTool = {
|
|
3321
3863
|
name: "update_custom_rule",
|
|
3322
|
-
description: "Update a custom tag-detection rule. Only supplied fields are sent. 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.)",
|
|
3864
|
+
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.)",
|
|
3323
3865
|
annotations: {
|
|
3324
3866
|
title: "Update Custom Rule",
|
|
3325
3867
|
readOnlyHint: false,
|
|
@@ -3340,10 +3882,200 @@ var updateCustomRuleTool = {
|
|
|
3340
3882
|
return ok(result.value);
|
|
3341
3883
|
}
|
|
3342
3884
|
};
|
|
3885
|
+
var TaxonomyNodeShape = z.object({
|
|
3886
|
+
client_id: z.string().min(1).max(100).describe(
|
|
3887
|
+
"Caller-chosen string id used to wire parent/child links inside this request only. The API allocates real UUIDs on response."
|
|
3888
|
+
),
|
|
3889
|
+
parent_client_id: z.string().min(1).max(100).nullable().optional().describe("client_id of this node's parent. null/omitted = top-level node."),
|
|
3890
|
+
name: z.string().min(1).max(100).describe("Display name of the node."),
|
|
3891
|
+
description: z.string().max(200).optional().describe(
|
|
3892
|
+
"Free-form description shown to the LLM during classification \u2014 keep it concrete (one sentence)."
|
|
3893
|
+
),
|
|
3894
|
+
is_default: z.boolean().optional().describe(
|
|
3895
|
+
"Mark this node as the fallback bucket. Exactly ONE node per taxonomy must be is_default=true."
|
|
3896
|
+
)
|
|
3897
|
+
});
|
|
3898
|
+
|
|
3899
|
+
// src/application/tools/custom-taxonomies/create-custom-taxonomy.tool.ts
|
|
3900
|
+
var CreateCustomTaxonomyInputShape = {
|
|
3901
|
+
name: z.string().min(1).max(100).describe("Display name (1-100 chars)."),
|
|
3902
|
+
description: z.string().max(500).optional().describe("Free-form description (defaults to empty string)."),
|
|
3903
|
+
nodes: z.array(TaxonomyNodeShape).max(2e3).optional().describe(
|
|
3904
|
+
"Initial tree as a flat array (parents before children, linked via client_id / parent_client_id). Empty / omitted = empty taxonomy."
|
|
3905
|
+
)
|
|
3906
|
+
};
|
|
3907
|
+
var createCustomTaxonomyTool = {
|
|
3908
|
+
name: "create_custom_taxonomy",
|
|
3909
|
+
description: "Create a custom classification taxonomy with its initial tree. Exactly one node should have is_default=true (fallback for scans the LLM cannot classify confidently). Returns the persisted taxonomy with allocated node UUIDs and version=1.",
|
|
3910
|
+
annotations: {
|
|
3911
|
+
title: "Create Custom Taxonomy",
|
|
3912
|
+
readOnlyHint: false,
|
|
3913
|
+
destructiveHint: false,
|
|
3914
|
+
idempotentHint: false,
|
|
3915
|
+
openWorldHint: false
|
|
3916
|
+
},
|
|
3917
|
+
inputSchema: z.object(CreateCustomTaxonomyInputShape),
|
|
3918
|
+
handler: async (input, ctx) => {
|
|
3919
|
+
const result = await ctx.api.createCustomTaxonomy({
|
|
3920
|
+
name: input.name,
|
|
3921
|
+
description: input.description ?? "",
|
|
3922
|
+
nodes: (input.nodes ?? []).map(toRequestNode)
|
|
3923
|
+
});
|
|
3924
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3925
|
+
return ok(result.value);
|
|
3926
|
+
}
|
|
3927
|
+
};
|
|
3928
|
+
function toRequestNode(n) {
|
|
3929
|
+
return {
|
|
3930
|
+
client_id: n.client_id,
|
|
3931
|
+
parent_client_id: n.parent_client_id ?? null,
|
|
3932
|
+
name: n.name,
|
|
3933
|
+
description: n.description ?? "",
|
|
3934
|
+
is_default: n.is_default ?? false
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3937
|
+
var DeleteCustomTaxonomyInputShape = {
|
|
3938
|
+
taxonomy_id: z.string().uuid().describe("Custom taxonomy UUID.")
|
|
3939
|
+
};
|
|
3940
|
+
var deleteCustomTaxonomyTool = {
|
|
3941
|
+
name: "delete_custom_taxonomy",
|
|
3942
|
+
description: "Soft-delete a custom taxonomy (sets is_active=false). Historical scan classifications keep referencing the id; new scans skip this taxonomy. Use `restore_custom_taxonomy` to undo.",
|
|
3943
|
+
annotations: {
|
|
3944
|
+
title: "Delete Custom Taxonomy",
|
|
3945
|
+
readOnlyHint: false,
|
|
3946
|
+
destructiveHint: true,
|
|
3947
|
+
// Soft-delete: a second call returns 404 for the same id but
|
|
3948
|
+
// does not change observable state — same convention used by
|
|
3949
|
+
// `delete_policy_set` and `delete_tag_definition`.
|
|
3950
|
+
idempotentHint: true,
|
|
3951
|
+
openWorldHint: false
|
|
3952
|
+
},
|
|
3953
|
+
inputSchema: z.object(DeleteCustomTaxonomyInputShape),
|
|
3954
|
+
handler: async (input, ctx) => {
|
|
3955
|
+
const result = await ctx.api.deleteCustomTaxonomy(input.taxonomy_id);
|
|
3956
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3957
|
+
return ok({ deleted: true });
|
|
3958
|
+
}
|
|
3959
|
+
};
|
|
3960
|
+
var GetCustomTaxonomyInputShape = {
|
|
3961
|
+
taxonomy_id: z.string().uuid().describe("Custom taxonomy UUID.")
|
|
3962
|
+
};
|
|
3963
|
+
var getCustomTaxonomyTool = {
|
|
3964
|
+
name: "get_custom_taxonomy",
|
|
3965
|
+
description: "Get one custom taxonomy by UUID with its full node tree (each node has id, parent_id, level, position, name, description, is_default).",
|
|
3966
|
+
annotations: {
|
|
3967
|
+
title: "Get Custom Taxonomy",
|
|
3968
|
+
readOnlyHint: true,
|
|
3969
|
+
destructiveHint: false,
|
|
3970
|
+
idempotentHint: true,
|
|
3971
|
+
openWorldHint: false
|
|
3972
|
+
},
|
|
3973
|
+
inputSchema: z.object(GetCustomTaxonomyInputShape),
|
|
3974
|
+
handler: async (input, ctx) => {
|
|
3975
|
+
const result = await ctx.api.getCustomTaxonomy(input.taxonomy_id);
|
|
3976
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3977
|
+
return ok(result.value);
|
|
3978
|
+
}
|
|
3979
|
+
};
|
|
3980
|
+
var ListCustomTaxonomiesInputShape = {};
|
|
3981
|
+
var listCustomTaxonomiesTool = {
|
|
3982
|
+
name: "list_custom_taxonomies",
|
|
3983
|
+
description: "List the calling org's custom classification taxonomies. Returns slim summaries (id, name, slug, version, node_count, is_active). Soft-deleted taxonomies are included with `is_active=false`. Fetch the full tree via `get_custom_taxonomy`.",
|
|
3984
|
+
annotations: {
|
|
3985
|
+
title: "List Custom Taxonomies",
|
|
3986
|
+
readOnlyHint: true,
|
|
3987
|
+
destructiveHint: false,
|
|
3988
|
+
idempotentHint: true,
|
|
3989
|
+
openWorldHint: false
|
|
3990
|
+
},
|
|
3991
|
+
inputSchema: z.object(ListCustomTaxonomiesInputShape),
|
|
3992
|
+
handler: async (_input, ctx) => {
|
|
3993
|
+
const result = await ctx.api.listCustomTaxonomies();
|
|
3994
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3995
|
+
return ok({ items: result.value });
|
|
3996
|
+
}
|
|
3997
|
+
};
|
|
3998
|
+
var ParseCustomTaxonomyTextInputShape = {
|
|
3999
|
+
text: z.string().min(1).max(5e4).describe(
|
|
4000
|
+
"Free-form text describing the tree (one node per line; indentation expresses depth). Up to 50 000 chars."
|
|
4001
|
+
)
|
|
4002
|
+
};
|
|
4003
|
+
var parseCustomTaxonomyTextTool = {
|
|
4004
|
+
name: "parse_custom_taxonomy_text",
|
|
4005
|
+
description: "Preview-parse pasted free-form text into a taxonomy tree (NOT persisted). Returns `nodes: [{level, name, description}]` + `warnings: string[]`. After review, persist via `create_custom_taxonomy` (remember to flag exactly one node as is_default).",
|
|
4006
|
+
annotations: {
|
|
4007
|
+
title: "Parse Custom Taxonomy Text",
|
|
4008
|
+
readOnlyHint: true,
|
|
4009
|
+
destructiveHint: false,
|
|
4010
|
+
idempotentHint: true,
|
|
4011
|
+
openWorldHint: false
|
|
4012
|
+
},
|
|
4013
|
+
inputSchema: z.object(ParseCustomTaxonomyTextInputShape),
|
|
4014
|
+
handler: async (input, ctx) => {
|
|
4015
|
+
const result = await ctx.api.parseCustomTaxonomyText({ text: input.text });
|
|
4016
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4017
|
+
return ok(result.value);
|
|
4018
|
+
}
|
|
4019
|
+
};
|
|
4020
|
+
var RestoreCustomTaxonomyInputShape = {
|
|
4021
|
+
taxonomy_id: z.string().uuid().describe("Custom taxonomy UUID (must currently be is_active=false).")
|
|
4022
|
+
};
|
|
4023
|
+
var restoreCustomTaxonomyTool = {
|
|
4024
|
+
name: "restore_custom_taxonomy",
|
|
4025
|
+
description: "Re-activate a previously soft-deleted custom taxonomy. Returns the restored taxonomy with its full tree (is_active=true).",
|
|
4026
|
+
annotations: {
|
|
4027
|
+
title: "Restore Custom Taxonomy",
|
|
4028
|
+
readOnlyHint: false,
|
|
4029
|
+
destructiveHint: false,
|
|
4030
|
+
idempotentHint: true,
|
|
4031
|
+
openWorldHint: false
|
|
4032
|
+
},
|
|
4033
|
+
inputSchema: z.object(RestoreCustomTaxonomyInputShape),
|
|
4034
|
+
handler: async (input, ctx) => {
|
|
4035
|
+
const result = await ctx.api.restoreCustomTaxonomy(input.taxonomy_id);
|
|
4036
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4037
|
+
return ok(result.value);
|
|
4038
|
+
}
|
|
4039
|
+
};
|
|
4040
|
+
var UpdateCustomTaxonomyInputShape = {
|
|
4041
|
+
taxonomy_id: z.string().uuid().describe("Custom taxonomy UUID."),
|
|
4042
|
+
name: z.string().min(1).max(100).describe("New display name."),
|
|
4043
|
+
description: z.string().max(500).optional().describe("New description (defaults to empty string)."),
|
|
4044
|
+
nodes: z.array(TaxonomyNodeShape).max(2e3).optional().describe(
|
|
4045
|
+
"REPLACEMENT tree as a flat array (REPLACES the existing tree, not a merge). Read the current tree via `get_custom_taxonomy` first if you only want to tweak one node."
|
|
4046
|
+
)
|
|
4047
|
+
};
|
|
4048
|
+
var updateCustomTaxonomyTool = {
|
|
4049
|
+
name: "update_custom_taxonomy",
|
|
4050
|
+
description: "REPLACE a custom taxonomy's name, description, and full tree atomically. The API bumps `version` on every successful update; ongoing scan classifications continue with the previous tree until they finish, so updates are safe but not retroactive.",
|
|
4051
|
+
annotations: {
|
|
4052
|
+
title: "Update Custom Taxonomy",
|
|
4053
|
+
readOnlyHint: false,
|
|
4054
|
+
destructiveHint: false,
|
|
4055
|
+
idempotentHint: true,
|
|
4056
|
+
openWorldHint: false
|
|
4057
|
+
},
|
|
4058
|
+
inputSchema: z.object(UpdateCustomTaxonomyInputShape),
|
|
4059
|
+
handler: async (input, ctx) => {
|
|
4060
|
+
const result = await ctx.api.updateCustomTaxonomy(input.taxonomy_id, {
|
|
4061
|
+
name: input.name,
|
|
4062
|
+
description: input.description ?? "",
|
|
4063
|
+
nodes: (input.nodes ?? []).map((n) => ({
|
|
4064
|
+
client_id: n.client_id,
|
|
4065
|
+
parent_client_id: n.parent_client_id ?? null,
|
|
4066
|
+
name: n.name,
|
|
4067
|
+
description: n.description ?? "",
|
|
4068
|
+
is_default: n.is_default ?? false
|
|
4069
|
+
}))
|
|
4070
|
+
});
|
|
4071
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4072
|
+
return ok(result.value);
|
|
4073
|
+
}
|
|
4074
|
+
};
|
|
3343
4075
|
var ListEmulatorsInputShape = {};
|
|
3344
4076
|
var listEmulatorsTool = {
|
|
3345
4077
|
name: "list_emulators",
|
|
3346
|
-
description: "List every device/OS emulator profile available for scans (id, display name, category, browser).
|
|
4078
|
+
description: "List every device/OS emulator profile available for scans (id, display name, category, browser). For `create_scan`, use the `id` as `emulator_id`. For `create_campaign` / `update_campaign`, use the `category` in `emulator_categories` or the `id` in `emulator_specific_ids`.",
|
|
3347
4079
|
annotations: {
|
|
3348
4080
|
title: "List Emulators",
|
|
3349
4081
|
readOnlyHint: true,
|
|
@@ -3378,13 +4110,72 @@ var listGeosTool = {
|
|
|
3378
4110
|
return ok({ items: result.value, total: result.value.length });
|
|
3379
4111
|
}
|
|
3380
4112
|
};
|
|
4113
|
+
|
|
4114
|
+
// src/application/tools/_shared/binary-content.ts
|
|
4115
|
+
function imageBlock(bytes, mimeType) {
|
|
4116
|
+
return {
|
|
4117
|
+
content: [
|
|
4118
|
+
{
|
|
4119
|
+
type: "image",
|
|
4120
|
+
data: Buffer.from(bytes).toString("base64"),
|
|
4121
|
+
mimeType
|
|
4122
|
+
}
|
|
4123
|
+
]
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
function resourceBlock(bytes, mimeType, uri) {
|
|
4127
|
+
return {
|
|
4128
|
+
content: [
|
|
4129
|
+
{
|
|
4130
|
+
type: "resource",
|
|
4131
|
+
resource: {
|
|
4132
|
+
uri,
|
|
4133
|
+
mimeType,
|
|
4134
|
+
blob: Buffer.from(bytes).toString("base64")
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
]
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
// src/application/tools/invoicing/get-invoice-pdf.tool.ts
|
|
4142
|
+
var GetInvoicePdfInputShape = {
|
|
4143
|
+
invoice_id: z.string().uuid().describe("Invoice UUID \u2014 see `list_invoices`.")
|
|
4144
|
+
};
|
|
4145
|
+
var getInvoicePdfTool = {
|
|
4146
|
+
name: "get_invoice_pdf",
|
|
4147
|
+
description: "Download one invoice as an inline PDF (base64-encoded). Returned as a single MCP resource content block \u2014 agents can save / forward it without a second API call.",
|
|
4148
|
+
annotations: {
|
|
4149
|
+
title: "Get Invoice PDF",
|
|
4150
|
+
readOnlyHint: true,
|
|
4151
|
+
destructiveHint: false,
|
|
4152
|
+
idempotentHint: true,
|
|
4153
|
+
openWorldHint: false
|
|
4154
|
+
},
|
|
4155
|
+
inputSchema: z.object(GetInvoicePdfInputShape),
|
|
4156
|
+
handler: async (input, ctx) => {
|
|
4157
|
+
const result = await ctx.api.getInvoicePdf(input.invoice_id);
|
|
4158
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4159
|
+
return ok(
|
|
4160
|
+
resourceBlock(
|
|
4161
|
+
result.value.bytes,
|
|
4162
|
+
result.value.contentType,
|
|
4163
|
+
`kaminari-ad://invoices/${input.invoice_id}.pdf`
|
|
4164
|
+
)
|
|
4165
|
+
);
|
|
4166
|
+
}
|
|
4167
|
+
};
|
|
3381
4168
|
var ListInvoicesInputShape = {
|
|
4169
|
+
type: z.enum(["proforma", "final"]).optional().describe(
|
|
4170
|
+
"Filter by invoice kind: proforma (advance bill) or final (issued after the period)."
|
|
4171
|
+
),
|
|
4172
|
+
status: z.enum(["draft", "issued", "paid", "voided", "overdue"]).optional().describe("Filter by lifecycle status."),
|
|
3382
4173
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
3383
4174
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3384
4175
|
};
|
|
3385
4176
|
var listInvoicesTool = {
|
|
3386
4177
|
name: "list_invoices",
|
|
3387
|
-
description: "List invoices issued to the organization with number, status, total in micros, currency, due/paid dates.
|
|
4178
|
+
description: "List invoices issued to the organization with number, type, status, total in micros, currency, due/paid dates. Filter by `type` and / or `status`. Use `get_invoice_pdf` to download the PDF for a specific invoice.",
|
|
3388
4179
|
annotations: {
|
|
3389
4180
|
title: "List Invoices",
|
|
3390
4181
|
readOnlyHint: true,
|
|
@@ -3394,23 +4185,123 @@ var listInvoicesTool = {
|
|
|
3394
4185
|
},
|
|
3395
4186
|
inputSchema: z.object(ListInvoicesInputShape),
|
|
3396
4187
|
handler: async (input, ctx) => {
|
|
3397
|
-
const
|
|
4188
|
+
const filters = {
|
|
4189
|
+
page: input.page,
|
|
4190
|
+
limit: input.limit,
|
|
4191
|
+
...input.type !== void 0 ? { type: input.type } : {},
|
|
4192
|
+
...input.status !== void 0 ? { status: input.status } : {}
|
|
4193
|
+
};
|
|
4194
|
+
const result = await ctx.api.listInvoices(filters);
|
|
3398
4195
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
3399
4196
|
return ok(result.value);
|
|
3400
4197
|
}
|
|
3401
4198
|
};
|
|
3402
|
-
var
|
|
3403
|
-
|
|
3404
|
-
|
|
4199
|
+
var TIER1 = z.string().min(1).max(200);
|
|
4200
|
+
var TIER_OPT = z.string().max(200).nullable().optional();
|
|
4201
|
+
var IabV3Block = z.object({
|
|
4202
|
+
tier1: TIER1.describe("Top-level IAB Content Taxonomy V3 category (e.g. 'Sensitive Topics')."),
|
|
4203
|
+
tier2: TIER_OPT.describe("Sub-category (optional). Empty/null = subtree match."),
|
|
4204
|
+
tier3: TIER_OPT.describe("Sub-sub-category (optional)."),
|
|
4205
|
+
tier4: TIER_OPT.describe(
|
|
4206
|
+
"Leaf-level category (optional). When all 4 tiers set, leaf-only match."
|
|
4207
|
+
)
|
|
4208
|
+
});
|
|
4209
|
+
var AiCategoryBlock = z.object({
|
|
4210
|
+
tier1: TIER1.describe("Top-level freeform AI-generated category (e.g. 'Gambling')."),
|
|
4211
|
+
tier2: TIER_OPT.describe("Sub-category (optional)."),
|
|
4212
|
+
tier3: TIER_OPT.describe("Sub-sub-category (optional)."),
|
|
4213
|
+
tier4: TIER_OPT.describe("Leaf-level category (optional).")
|
|
3405
4214
|
});
|
|
4215
|
+
var CustomTaxonomyBlock = z.object({
|
|
4216
|
+
taxonomy_id: z.string().uuid().describe("ID of the per-org custom taxonomy (see `list_custom_taxonomies`)."),
|
|
4217
|
+
tier1: TIER1.describe("Top-level node name in the taxonomy."),
|
|
4218
|
+
tier2: TIER_OPT.describe("Tier 2 node name (optional)."),
|
|
4219
|
+
tier3: TIER_OPT.describe("Tier 3 node name (optional)."),
|
|
4220
|
+
tier4: TIER_OPT.describe("Leaf node name (optional).")
|
|
4221
|
+
});
|
|
4222
|
+
var COUNTRY_CODES = z.array(z.string().min(2).max(3)).max(50).describe("Restrict the rule to these countries (ISO 3166-1 alpha-2 / alpha-3). Empty = all.");
|
|
4223
|
+
var PolicyEntryInput = z.discriminatedUnion("rule_type", [
|
|
4224
|
+
z.object({
|
|
4225
|
+
rule_type: z.literal("tag").describe("Match a specific tag slug emitted by the scan pipeline."),
|
|
4226
|
+
tag_slug: z.string().min(1).max(100).describe("Tag slug (see `list_tags` for the catalogue)."),
|
|
4227
|
+
country_codes: COUNTRY_CODES
|
|
4228
|
+
}),
|
|
4229
|
+
z.object({
|
|
4230
|
+
rule_type: z.literal("iab_v3").describe("Match the scan's canonical IAB V3 category prefix."),
|
|
4231
|
+
iab_v3: IabV3Block,
|
|
4232
|
+
country_codes: COUNTRY_CODES
|
|
4233
|
+
}),
|
|
4234
|
+
z.object({
|
|
4235
|
+
rule_type: z.literal("brand").describe("Match the scan's advertiser brand (case-insensitive)."),
|
|
4236
|
+
brand: z.string().min(1).max(200).describe("Brand string."),
|
|
4237
|
+
country_codes: COUNTRY_CODES
|
|
4238
|
+
}),
|
|
4239
|
+
z.object({
|
|
4240
|
+
rule_type: z.literal("ai_category").describe("Match the freeform LLM-generated category prefix on the scan."),
|
|
4241
|
+
ai_category: AiCategoryBlock,
|
|
4242
|
+
country_codes: COUNTRY_CODES
|
|
4243
|
+
}),
|
|
4244
|
+
z.object({
|
|
4245
|
+
rule_type: z.literal("custom_taxonomy").describe("Match a per-org custom taxonomy node prefix on the scan."),
|
|
4246
|
+
custom_taxonomy: CustomTaxonomyBlock,
|
|
4247
|
+
country_codes: COUNTRY_CODES
|
|
4248
|
+
})
|
|
4249
|
+
]).describe(
|
|
4250
|
+
"One policy rule. Discriminated by `rule_type`; populate the matching value-block (tag_slug / iab_v3 / brand / ai_category / custom_taxonomy)."
|
|
4251
|
+
);
|
|
4252
|
+
function policyEntryToRequest(entry) {
|
|
4253
|
+
const base = {
|
|
4254
|
+
rule_type: entry.rule_type,
|
|
4255
|
+
tag_slug: null,
|
|
4256
|
+
iab_v3: null,
|
|
4257
|
+
brand: null,
|
|
4258
|
+
ai_category: null,
|
|
4259
|
+
custom_taxonomy: null,
|
|
4260
|
+
country_codes: [...entry.country_codes]
|
|
4261
|
+
};
|
|
4262
|
+
if (entry.rule_type === "tag") {
|
|
4263
|
+
return { ...base, tag_slug: entry.tag_slug };
|
|
4264
|
+
}
|
|
4265
|
+
if (entry.rule_type === "iab_v3") {
|
|
4266
|
+
return { ...base, iab_v3: normaliseTiers(entry.iab_v3) };
|
|
4267
|
+
}
|
|
4268
|
+
if (entry.rule_type === "brand") {
|
|
4269
|
+
return { ...base, brand: entry.brand };
|
|
4270
|
+
}
|
|
4271
|
+
if (entry.rule_type === "ai_category") {
|
|
4272
|
+
return { ...base, ai_category: normaliseTiers(entry.ai_category) };
|
|
4273
|
+
}
|
|
4274
|
+
return { ...base, custom_taxonomy: normaliseCustomTaxonomy(entry.custom_taxonomy) };
|
|
4275
|
+
}
|
|
4276
|
+
function normaliseTiers(t) {
|
|
4277
|
+
return {
|
|
4278
|
+
tier1: t.tier1,
|
|
4279
|
+
tier2: t.tier2 ?? null,
|
|
4280
|
+
tier3: t.tier3 ?? null,
|
|
4281
|
+
tier4: t.tier4 ?? null
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4284
|
+
function normaliseCustomTaxonomy(t) {
|
|
4285
|
+
return {
|
|
4286
|
+
taxonomy_id: t.taxonomy_id,
|
|
4287
|
+
tier1: t.tier1,
|
|
4288
|
+
tier2: t.tier2 ?? null,
|
|
4289
|
+
tier3: t.tier3 ?? null,
|
|
4290
|
+
tier4: t.tier4 ?? null
|
|
4291
|
+
};
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
// src/application/tools/policy-sets/create-policy-set.tool.ts
|
|
3406
4295
|
var CreatePolicySetInputShape = {
|
|
3407
4296
|
name: z.string().min(1).max(200).describe("Display name."),
|
|
3408
4297
|
description: z.string().max(2e3).describe("Free-form description (use empty string for none)."),
|
|
3409
|
-
entries: z.array(
|
|
4298
|
+
entries: z.array(PolicyEntryInput).min(1).max(500).describe(
|
|
4299
|
+
"At least one entry. Each entry is a discriminated union over five rule kinds: tag / iab_v3 / brand / ai_category / custom_taxonomy."
|
|
4300
|
+
)
|
|
3410
4301
|
};
|
|
3411
4302
|
var createPolicySetTool = {
|
|
3412
4303
|
name: "create_policy_set",
|
|
3413
|
-
description: "Create a new policy set (named collection of tag
|
|
4304
|
+
description: "Create a new policy set (named collection of violation rules). Each rule is one of: tag slug, IAB V3 category prefix, advertiser brand, freeform AI category, or per-org custom-taxonomy node. Bind campaigns to the set via `update_campaign`.",
|
|
3414
4305
|
annotations: {
|
|
3415
4306
|
title: "Create Policy Set",
|
|
3416
4307
|
readOnlyHint: false,
|
|
@@ -3423,7 +4314,7 @@ var createPolicySetTool = {
|
|
|
3423
4314
|
const result = await ctx.api.createPolicySet({
|
|
3424
4315
|
name: input.name,
|
|
3425
4316
|
description: input.description,
|
|
3426
|
-
entries: input.entries.map(
|
|
4317
|
+
entries: input.entries.map(policyEntryToRequest)
|
|
3427
4318
|
});
|
|
3428
4319
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
3429
4320
|
return ok(result.value);
|
|
@@ -3454,7 +4345,7 @@ var GetPolicySetInputShape = {
|
|
|
3454
4345
|
};
|
|
3455
4346
|
var getPolicySetTool = {
|
|
3456
4347
|
name: "get_policy_set",
|
|
3457
|
-
description: "Get one policy set by UUID with its complete list of entries (tag
|
|
4348
|
+
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).",
|
|
3458
4349
|
annotations: {
|
|
3459
4350
|
title: "Get Policy Set",
|
|
3460
4351
|
readOnlyHint: true,
|
|
@@ -3471,11 +4362,14 @@ var getPolicySetTool = {
|
|
|
3471
4362
|
};
|
|
3472
4363
|
var ListPolicySetsInputShape = {
|
|
3473
4364
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
3474
|
-
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
4365
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size."),
|
|
4366
|
+
visibility: z.enum(["private", "public"]).optional().describe(
|
|
4367
|
+
"Narrow to one scope. `private` = org-owned only, `public` = Kaminari Ad-curated sets visible to every org. Omit the filter to see BOTH combined."
|
|
4368
|
+
)
|
|
3475
4369
|
};
|
|
3476
4370
|
var listPolicySetsTool = {
|
|
3477
4371
|
name: "list_policy_sets",
|
|
3478
|
-
description: "Paginated list of policy sets: named collections of (tag
|
|
4372
|
+
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.",
|
|
3479
4373
|
annotations: {
|
|
3480
4374
|
title: "List Policy Sets",
|
|
3481
4375
|
readOnlyHint: true,
|
|
@@ -3485,7 +4379,11 @@ var listPolicySetsTool = {
|
|
|
3485
4379
|
},
|
|
3486
4380
|
inputSchema: z.object(ListPolicySetsInputShape),
|
|
3487
4381
|
handler: async (input, ctx) => {
|
|
3488
|
-
const result = await ctx.api.listPolicySets({
|
|
4382
|
+
const result = await ctx.api.listPolicySets({
|
|
4383
|
+
page: input.page,
|
|
4384
|
+
limit: input.limit,
|
|
4385
|
+
...input.visibility !== void 0 ? { visibility: input.visibility } : {}
|
|
4386
|
+
});
|
|
3489
4387
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
3490
4388
|
return ok(result.value);
|
|
3491
4389
|
}
|
|
@@ -3510,19 +4408,17 @@ var requestPolicySetApprovalTool = {
|
|
|
3510
4408
|
return ok({ requested: true });
|
|
3511
4409
|
}
|
|
3512
4410
|
};
|
|
3513
|
-
var PolicyEntryShape2 = z.object({
|
|
3514
|
-
tag_slug: z.string().min(1).max(100),
|
|
3515
|
-
country_codes: z.array(z.string().length(2)).max(50)
|
|
3516
|
-
});
|
|
3517
4411
|
var UpdatePolicySetInputShape = {
|
|
3518
4412
|
policy_set_id: z.string().uuid().describe("Policy set UUID."),
|
|
3519
4413
|
name: z.string().min(1).max(200).describe("New name (always required by the API on update)."),
|
|
3520
4414
|
description: z.string().max(2e3).describe("New description (empty string allowed)."),
|
|
3521
|
-
entries: z.array(
|
|
4415
|
+
entries: z.array(PolicyEntryInput).min(1).max(500).describe(
|
|
4416
|
+
"REPLACEMENT entries list \u2014 REPLACES the current list, not a merge. Each entry is a discriminated union over five rule kinds (tag / iab_v3 / brand / ai_category / custom_taxonomy)."
|
|
4417
|
+
)
|
|
3522
4418
|
};
|
|
3523
4419
|
var updatePolicySetTool = {
|
|
3524
4420
|
name: "update_policy_set",
|
|
3525
|
-
description: "REPLACE a policy set's name, description, and entry list. The API requires all three fields on every update \u2014 read the current set with `get_policy_set` first if you only want to change one thing.",
|
|
4421
|
+
description: "REPLACE a policy set's name, description, and entry list. The API requires all three fields on every update \u2014 read the current set with `get_policy_set` first if you only want to change one thing. Supports all five rule kinds (tag / iab_v3 / brand / ai_category / custom_taxonomy).",
|
|
3526
4422
|
annotations: {
|
|
3527
4423
|
title: "Update Policy Set",
|
|
3528
4424
|
readOnlyHint: false,
|
|
@@ -3535,7 +4431,7 @@ var updatePolicySetTool = {
|
|
|
3535
4431
|
const result = await ctx.api.updatePolicySet(input.policy_set_id, {
|
|
3536
4432
|
name: input.name,
|
|
3537
4433
|
description: input.description,
|
|
3538
|
-
entries: input.entries.map(
|
|
4434
|
+
entries: input.entries.map(policyEntryToRequest)
|
|
3539
4435
|
});
|
|
3540
4436
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
3541
4437
|
return ok(result.value);
|
|
@@ -3622,11 +4518,20 @@ var cancelScanTool = {
|
|
|
3622
4518
|
return ok(result.value);
|
|
3623
4519
|
}
|
|
3624
4520
|
};
|
|
4521
|
+
var scanProxyField = z.object({
|
|
4522
|
+
proxy_type: z.enum(["residential", "mobile"]).optional().describe("Proxy network type. Default: residential."),
|
|
4523
|
+
region: z.string().optional().describe("Proxy region/state targeting."),
|
|
4524
|
+
city: z.string().optional().describe("Proxy city targeting."),
|
|
4525
|
+
isp: z.string().optional().describe("Proxy ISP targeting.")
|
|
4526
|
+
}).optional().describe("Optional proxy geo targeting for the crawl. Omit to use the org default.");
|
|
4527
|
+
|
|
4528
|
+
// src/application/tools/scans/create-bulk-scans.tool.ts
|
|
3625
4529
|
var CreateBulkScansInputShape = {
|
|
3626
4530
|
url: z.string().url().optional().describe("Direct URL. EITHER `url` OR `ad_tag` is required."),
|
|
3627
4531
|
ad_tag: z.string().optional().describe("Raw ad-tag HTML/JS. EITHER `url` OR `ad_tag` is required."),
|
|
3628
4532
|
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."),
|
|
3629
4533
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; same for every country in the batch."),
|
|
4534
|
+
proxy: scanProxyField,
|
|
3630
4535
|
labels: z.record(z.string()).optional().describe("Arbitrary metadata copied onto every created scan.")
|
|
3631
4536
|
};
|
|
3632
4537
|
var createBulkScansTool = {
|
|
@@ -3646,6 +4551,7 @@ var createBulkScansTool = {
|
|
|
3646
4551
|
emulator_id: input.emulator_id,
|
|
3647
4552
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
3648
4553
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4554
|
+
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
3649
4555
|
...input.labels !== void 0 ? { labels: input.labels } : {}
|
|
3650
4556
|
};
|
|
3651
4557
|
const result = await ctx.api.createBulkScans(body);
|
|
@@ -3658,6 +4564,7 @@ var CreateScanInputShape = {
|
|
|
3658
4564
|
ad_tag: z.string().optional().describe("Raw HTML/JS ad tag (script, iframe, image). EITHER `url` OR `ad_tag` is required."),
|
|
3659
4565
|
country_code: z.string().length(2).describe("ISO 3166-1 alpha-2 country code, e.g. US, DE, JP. Determines proxy geo."),
|
|
3660
4566
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; use `list_emulators` to discover valid values."),
|
|
4567
|
+
proxy: scanProxyField,
|
|
3661
4568
|
labels: z.record(z.string()).optional().describe("Arbitrary string -> string metadata attached to the scan."),
|
|
3662
4569
|
campaign_id: z.string().uuid().optional().describe("Optional campaign UUID to attribute the scan to."),
|
|
3663
4570
|
run_id: z.string().uuid().optional().describe("Optional run UUID inside the campaign.")
|
|
@@ -3679,6 +4586,7 @@ var createScanTool = {
|
|
|
3679
4586
|
emulator_id: input.emulator_id,
|
|
3680
4587
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
3681
4588
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4589
|
+
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
3682
4590
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3683
4591
|
...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
|
|
3684
4592
|
...input.run_id !== void 0 ? { run_id: input.run_id } : {}
|
|
@@ -3708,22 +4616,110 @@ var getScanTool = {
|
|
|
3708
4616
|
return ok(result.value);
|
|
3709
4617
|
}
|
|
3710
4618
|
};
|
|
4619
|
+
var GetScanCreativeScreenshotInputShape = {
|
|
4620
|
+
scan_id: z.string().uuid().describe("Scan UUID \u2014 must be an ad-tag scan (is_ad_tag=true)."),
|
|
4621
|
+
width: z.number().int().min(50).max(2e3).optional().describe("Optional resize width in pixels.")
|
|
4622
|
+
};
|
|
4623
|
+
var getScanCreativeScreenshotTool = {
|
|
4624
|
+
name: "get_scan_creative_screenshot",
|
|
4625
|
+
description: "Fetch the creative screenshot for an ad-tag scan as an inline image (base64-encoded PNG). Returns 404 if the scan is not an ad-tag scan or has no creative captured yet.",
|
|
4626
|
+
annotations: {
|
|
4627
|
+
title: "Get Scan Creative Screenshot",
|
|
4628
|
+
readOnlyHint: true,
|
|
4629
|
+
destructiveHint: false,
|
|
4630
|
+
idempotentHint: true,
|
|
4631
|
+
openWorldHint: false
|
|
4632
|
+
},
|
|
4633
|
+
inputSchema: z.object(GetScanCreativeScreenshotInputShape),
|
|
4634
|
+
handler: async (input, ctx) => {
|
|
4635
|
+
const result = await ctx.api.getScanCreativeScreenshot(input.scan_id, input.width);
|
|
4636
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4637
|
+
return ok(imageBlock(result.value.bytes, result.value.contentType));
|
|
4638
|
+
}
|
|
4639
|
+
};
|
|
4640
|
+
var GetScanLandingScreenshotInputShape = {
|
|
4641
|
+
scan_id: z.string().uuid().describe("Scan UUID (ad-tag scan)."),
|
|
4642
|
+
landing_ord: z.number().int().min(0).max(50).describe("0-indexed landing slot (see `get_scan(...).landings[*].ord`)."),
|
|
4643
|
+
width: z.number().int().min(50).max(2e3).optional().describe("Optional resize width in pixels.")
|
|
4644
|
+
};
|
|
4645
|
+
var getScanLandingScreenshotTool = {
|
|
4646
|
+
name: "get_scan_landing_screenshot",
|
|
4647
|
+
description: "Fetch the screenshot of one landing tab on an ad-tag scan as an inline image. Use `get_scan` first to discover available `landings[*].ord` values.",
|
|
4648
|
+
annotations: {
|
|
4649
|
+
title: "Get Scan Landing Screenshot",
|
|
4650
|
+
readOnlyHint: true,
|
|
4651
|
+
destructiveHint: false,
|
|
4652
|
+
idempotentHint: true,
|
|
4653
|
+
openWorldHint: false
|
|
4654
|
+
},
|
|
4655
|
+
inputSchema: z.object(GetScanLandingScreenshotInputShape),
|
|
4656
|
+
handler: async (input, ctx) => {
|
|
4657
|
+
const result = await ctx.api.getScanLandingScreenshot(
|
|
4658
|
+
input.scan_id,
|
|
4659
|
+
input.landing_ord,
|
|
4660
|
+
input.width
|
|
4661
|
+
);
|
|
4662
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4663
|
+
return ok(imageBlock(result.value.bytes, result.value.contentType));
|
|
4664
|
+
}
|
|
4665
|
+
};
|
|
4666
|
+
var GetScanScreenshotInputShape = {
|
|
4667
|
+
scan_id: z.string().uuid().describe("Scan UUID."),
|
|
4668
|
+
width: z.number().int().min(50).max(2e3).optional().describe(
|
|
4669
|
+
"Optional resize width in pixels. The API resizes server-side to keep the image small for the agent's context window."
|
|
4670
|
+
)
|
|
4671
|
+
};
|
|
4672
|
+
var getScanScreenshotTool = {
|
|
4673
|
+
name: "get_scan_screenshot",
|
|
4674
|
+
description: "Fetch the primary screenshot for a scan as an inline image (base64-encoded PNG). Pass `width` to request a resized version (50-2000 px). Returns one MCP image content block.",
|
|
4675
|
+
annotations: {
|
|
4676
|
+
title: "Get Scan Screenshot",
|
|
4677
|
+
readOnlyHint: true,
|
|
4678
|
+
destructiveHint: false,
|
|
4679
|
+
idempotentHint: true,
|
|
4680
|
+
openWorldHint: false
|
|
4681
|
+
},
|
|
4682
|
+
inputSchema: z.object(GetScanScreenshotInputShape),
|
|
4683
|
+
handler: async (input, ctx) => {
|
|
4684
|
+
const result = await ctx.api.getScanScreenshot(input.scan_id, input.width);
|
|
4685
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4686
|
+
return ok(imageBlock(result.value.bytes, result.value.contentType));
|
|
4687
|
+
}
|
|
4688
|
+
};
|
|
3711
4689
|
var ListScansInputShape = {
|
|
3712
4690
|
status: z.string().optional().describe(
|
|
3713
|
-
"Comma-separated statuses to filter by.
|
|
4691
|
+
"Comma-separated statuses to filter by. Lifecycle values: pending, running, crawled, checking, checking_async, completed, partial, failed, cancelled."
|
|
3714
4692
|
),
|
|
3715
4693
|
country_code: z.string().optional().describe("Comma-separated ISO 3166-1 alpha-2 country codes, e.g. US,DE,JP."),
|
|
3716
4694
|
url: z.string().optional().describe("Substring match against the scanned URL."),
|
|
3717
4695
|
scan_id: z.string().optional().describe("Comma-separated scan UUIDs to fetch a specific set."),
|
|
3718
4696
|
date_from: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive lower bound on scan creation."),
|
|
3719
4697
|
date_to: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive upper bound on scan creation."),
|
|
4698
|
+
timezone: z.string().optional().describe(
|
|
4699
|
+
"IANA timezone (e.g. 'Europe/Berlin') applied to date_from / date_to. Defaults to UTC."
|
|
4700
|
+
),
|
|
4701
|
+
run_id: z.string().uuid().optional().describe("Filter to scans of one run."),
|
|
4702
|
+
campaign_id: z.string().uuid().optional().describe("Filter to scans of one campaign."),
|
|
4703
|
+
group_id: z.string().uuid().optional().describe("Filter to scans of one campaign group."),
|
|
3720
4704
|
tag: z.string().optional().describe("Comma-separated tag slugs to filter by."),
|
|
4705
|
+
ai_category: z.string().optional().describe(
|
|
4706
|
+
"Filter by the freeform LLM-generated category prefix on the scan (slash-separated tier path, e.g. 'Gambling/Online Casinos')."
|
|
4707
|
+
),
|
|
4708
|
+
iab_v3_category: z.string().optional().describe("Filter by canonical IAB Content Taxonomy V3 prefix (slash-separated tier path)."),
|
|
4709
|
+
iab_category: z.string().optional().describe("Filter by legacy IAB V2.2 category (only present on pre-P3 scans)."),
|
|
4710
|
+
brand: z.string().optional().describe("Filter by detected advertiser brand (case-insensitive)."),
|
|
4711
|
+
labels: z.record(
|
|
4712
|
+
z.string().min(1).max(100).regex(/^[a-z0-9_]+$/, "label keys must be snake_case ([a-z0-9_]+)"),
|
|
4713
|
+
z.string().min(1).max(200)
|
|
4714
|
+
).optional().describe(
|
|
4715
|
+
"Dynamic label filters as a flat object: { brand_safety: 'high', vertical: 'gambling' }. Keys are snake_case and must exist in the org's label catalogue. Each key becomes a `label_<key>=<value>` query param. See `list_account_labels`."
|
|
4716
|
+
),
|
|
3721
4717
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
3722
4718
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size (1-200). Default 50.")
|
|
3723
4719
|
};
|
|
3724
4720
|
var listScansTool = {
|
|
3725
4721
|
name: "list_scans",
|
|
3726
|
-
description: "List scans for the caller's organization with optional filters (status, country, URL substring, date range,
|
|
4722
|
+
description: "List scans for the caller's organization with optional filters (status, country, URL substring, date range, run/campaign/group, tag, AI/IAB/brand category, dynamic labels). Returns a paginated envelope.",
|
|
3727
4723
|
annotations: {
|
|
3728
4724
|
title: "List Scans",
|
|
3729
4725
|
readOnlyHint: true,
|
|
@@ -3733,6 +4729,12 @@ var listScansTool = {
|
|
|
3733
4729
|
},
|
|
3734
4730
|
inputSchema: z.object(ListScansInputShape),
|
|
3735
4731
|
handler: async (input, ctx) => {
|
|
4732
|
+
const labelFilters = {};
|
|
4733
|
+
if (input.labels !== void 0) {
|
|
4734
|
+
for (const [k, v] of Object.entries(input.labels)) {
|
|
4735
|
+
labelFilters[`label_${k}`] = v;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
3736
4738
|
const filters = {
|
|
3737
4739
|
page: input.page,
|
|
3738
4740
|
limit: input.limit,
|
|
@@ -3742,7 +4744,16 @@ var listScansTool = {
|
|
|
3742
4744
|
...input.scan_id !== void 0 ? { scan_id: input.scan_id } : {},
|
|
3743
4745
|
...input.date_from !== void 0 ? { date_from: input.date_from } : {},
|
|
3744
4746
|
...input.date_to !== void 0 ? { date_to: input.date_to } : {},
|
|
3745
|
-
...input.
|
|
4747
|
+
...input.timezone !== void 0 ? { timezone: input.timezone } : {},
|
|
4748
|
+
...input.run_id !== void 0 ? { run_id: input.run_id } : {},
|
|
4749
|
+
...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
|
|
4750
|
+
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
4751
|
+
...input.tag !== void 0 ? { tag: input.tag } : {},
|
|
4752
|
+
...input.ai_category !== void 0 ? { ai_category: input.ai_category } : {},
|
|
4753
|
+
...input.iab_v3_category !== void 0 ? { iab_v3_category: input.iab_v3_category } : {},
|
|
4754
|
+
...input.iab_category !== void 0 ? { iab_category: input.iab_category } : {},
|
|
4755
|
+
...input.brand !== void 0 ? { brand: input.brand } : {},
|
|
4756
|
+
...labelFilters
|
|
3746
4757
|
};
|
|
3747
4758
|
const result = await ctx.api.listScans(filters);
|
|
3748
4759
|
if (result.isErr()) {
|
|
@@ -3839,10 +4850,14 @@ var listScanTagsTool = {
|
|
|
3839
4850
|
return ok({ items: result.value, total: result.value.length });
|
|
3840
4851
|
}
|
|
3841
4852
|
};
|
|
3842
|
-
var ListTagsInputShape = {
|
|
4853
|
+
var ListTagsInputShape = {
|
|
4854
|
+
category: z.string().min(1).max(100).optional().describe(
|
|
4855
|
+
"Filter to tags in one category slug (e.g. 'security', 'malware', 'compliance'). Use the slug exactly as it appears in `category` of an existing tag."
|
|
4856
|
+
)
|
|
4857
|
+
};
|
|
3843
4858
|
var listTagsTool = {
|
|
3844
4859
|
name: "list_tags",
|
|
3845
|
-
description: "List every tag definition the platform knows (system tags + organization custom tags) with category, severity, and usage counters (scans + rules per tag).",
|
|
4860
|
+
description: "List every tag definition the platform knows (system tags + organization custom tags) with category, severity, visibility, and usage counters (scans + rules per tag). Optionally filter by category.",
|
|
3846
4861
|
annotations: {
|
|
3847
4862
|
title: "List Tags",
|
|
3848
4863
|
readOnlyHint: true,
|
|
@@ -3851,8 +4866,9 @@ var listTagsTool = {
|
|
|
3851
4866
|
openWorldHint: false
|
|
3852
4867
|
},
|
|
3853
4868
|
inputSchema: z.object(ListTagsInputShape),
|
|
3854
|
-
handler: async (
|
|
3855
|
-
const
|
|
4869
|
+
handler: async (input, ctx) => {
|
|
4870
|
+
const filters = input.category !== void 0 ? { category: input.category } : void 0;
|
|
4871
|
+
const result = await ctx.api.listTags(filters);
|
|
3856
4872
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
3857
4873
|
return ok({ items: result.value, total: result.value.length });
|
|
3858
4874
|
}
|
|
@@ -3862,7 +4878,9 @@ var UpdateTagDefinitionInputShape = {
|
|
|
3862
4878
|
display_name: z.string().min(1).max(200).optional().describe("New human-readable name."),
|
|
3863
4879
|
description: z.string().max(2e3).optional().describe("New description."),
|
|
3864
4880
|
severity: z.enum(["high", "medium", "low"]).optional().describe("New severity level."),
|
|
3865
|
-
|
|
4881
|
+
visibility: z.enum(["hidden", "internal", "public"]).optional().describe(
|
|
4882
|
+
"Tag visibility on the scan report: hidden = not surfaced; internal = visible to org members; public = appears in the public scan-share view."
|
|
4883
|
+
)
|
|
3866
4884
|
};
|
|
3867
4885
|
var updateTagDefinitionTool = {
|
|
3868
4886
|
name: "update_tag_definition",
|
|
@@ -3880,8 +4898,12 @@ var updateTagDefinitionTool = {
|
|
|
3880
4898
|
if (input.display_name !== void 0) body.display_name = input.display_name;
|
|
3881
4899
|
if (input.description !== void 0) body.description = input.description;
|
|
3882
4900
|
if (input.severity !== void 0) body.severity = input.severity;
|
|
3883
|
-
if (input.
|
|
3884
|
-
|
|
4901
|
+
if (input.visibility !== void 0) body.visibility = input.visibility;
|
|
4902
|
+
if (Object.keys(body).length === 0) {
|
|
4903
|
+
return err({
|
|
4904
|
+
kind: "invalid-input",
|
|
4905
|
+
message: "update_tag_definition requires at least one field to change (display_name, description, severity, or visibility)."
|
|
4906
|
+
});
|
|
3885
4907
|
}
|
|
3886
4908
|
const result = await ctx.api.updateTagDefinition(input.slug, body);
|
|
3887
4909
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -3985,12 +5007,15 @@ var getWebhookTool = {
|
|
|
3985
5007
|
};
|
|
3986
5008
|
var ListWebhookDeliveriesInputShape = {
|
|
3987
5009
|
webhook_id: z.string().uuid().describe("Webhook endpoint UUID."),
|
|
5010
|
+
success: z.boolean().optional().describe("Filter to delivered (true) or failed (false) attempts only."),
|
|
5011
|
+
from_ts: z.string().datetime({ offset: true }).optional().describe("ISO 8601 datetime (with offset), inclusive lower bound on attempt time."),
|
|
5012
|
+
to_ts: z.string().datetime({ offset: true }).optional().describe("ISO 8601 datetime (with offset), inclusive upper bound."),
|
|
3988
5013
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
3989
5014
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3990
5015
|
};
|
|
3991
5016
|
var listWebhookDeliveriesTool = {
|
|
3992
5017
|
name: "list_webhook_deliveries",
|
|
3993
|
-
description: "List delivery attempts for one webhook endpoint with event type, status (pending / delivered / failed), HTTP response status if any, and attempt timestamp. Paginated.",
|
|
5018
|
+
description: "List delivery attempts for one webhook endpoint with event type, status (pending / delivered / failed), HTTP response status if any, and attempt timestamp. Filter by `success` and / or a `from_ts` / `to_ts` range. Paginated.",
|
|
3994
5019
|
annotations: {
|
|
3995
5020
|
title: "List Webhook Deliveries",
|
|
3996
5021
|
readOnlyHint: true,
|
|
@@ -4000,10 +5025,14 @@ var listWebhookDeliveriesTool = {
|
|
|
4000
5025
|
},
|
|
4001
5026
|
inputSchema: z.object(ListWebhookDeliveriesInputShape),
|
|
4002
5027
|
handler: async (input, ctx) => {
|
|
4003
|
-
const
|
|
5028
|
+
const filters = {
|
|
4004
5029
|
page: input.page,
|
|
4005
|
-
limit: input.limit
|
|
4006
|
-
|
|
5030
|
+
limit: input.limit,
|
|
5031
|
+
...input.success !== void 0 ? { success: input.success } : {},
|
|
5032
|
+
...input.from_ts !== void 0 ? { from_ts: input.from_ts } : {},
|
|
5033
|
+
...input.to_ts !== void 0 ? { to_ts: input.to_ts } : {}
|
|
5034
|
+
};
|
|
5035
|
+
const result = await ctx.api.listWebhookDeliveries(input.webhook_id, filters);
|
|
4007
5036
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
4008
5037
|
return ok(result.value);
|
|
4009
5038
|
}
|
|
@@ -4110,7 +5139,12 @@ var testWebhookTool = {
|
|
|
4110
5139
|
var UpdateWebhookInputShape = {
|
|
4111
5140
|
webhook_id: z.string().uuid().describe("Webhook UUID."),
|
|
4112
5141
|
url: z.string().url().optional().describe("New endpoint URL."),
|
|
5142
|
+
description: z.string().max(256).optional().describe("New human-readable label."),
|
|
4113
5143
|
event_types: z.array(z.string()).max(50).optional().describe("Replace the subscribed-event-types list."),
|
|
5144
|
+
campaign_ids: z.array(z.string().uuid()).optional().describe("Restrict deliveries to these campaign UUIDs (replaces the current set)."),
|
|
5145
|
+
clear_campaign_ids: z.boolean().optional().describe(
|
|
5146
|
+
"Set true to remove the campaign restriction so the webhook fires for all campaigns."
|
|
5147
|
+
),
|
|
4114
5148
|
is_active: z.boolean().optional().describe("Enable / disable delivery.")
|
|
4115
5149
|
};
|
|
4116
5150
|
var updateWebhookTool = {
|
|
@@ -4127,7 +5161,10 @@ var updateWebhookTool = {
|
|
|
4127
5161
|
handler: async (input, ctx) => {
|
|
4128
5162
|
const body = {
|
|
4129
5163
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
5164
|
+
...input.description !== void 0 ? { description: input.description } : {},
|
|
4130
5165
|
...input.event_types !== void 0 ? { event_types: input.event_types } : {},
|
|
5166
|
+
...input.campaign_ids !== void 0 ? { campaign_ids: input.campaign_ids } : {},
|
|
5167
|
+
...input.clear_campaign_ids !== void 0 ? { clear_campaign_ids: input.clear_campaign_ids } : {},
|
|
4131
5168
|
...input.is_active !== void 0 ? { is_active: input.is_active } : {}
|
|
4132
5169
|
};
|
|
4133
5170
|
const result = await ctx.api.updateWebhook(input.webhook_id, body);
|
|
@@ -4146,6 +5183,9 @@ function registerAllTools(register) {
|
|
|
4146
5183
|
register(removeUserTool);
|
|
4147
5184
|
register(transferOwnershipTool);
|
|
4148
5185
|
register(listOrgRolesTool);
|
|
5186
|
+
register(createCustomRoleTool);
|
|
5187
|
+
register(listAccountLabelsTool);
|
|
5188
|
+
register(updateAccountLabelsTool);
|
|
4149
5189
|
register(listApiKeysTool);
|
|
4150
5190
|
register(createApiKeyTool);
|
|
4151
5191
|
register(revokeApiKeyTool);
|
|
@@ -4157,6 +5197,9 @@ function registerAllTools(register) {
|
|
|
4157
5197
|
register(createBulkScansTool);
|
|
4158
5198
|
register(createScanTool);
|
|
4159
5199
|
register(recheckScansTool);
|
|
5200
|
+
register(getScanScreenshotTool);
|
|
5201
|
+
register(getScanCreativeScreenshotTool);
|
|
5202
|
+
register(getScanLandingScreenshotTool);
|
|
4160
5203
|
register(getCampaignTool);
|
|
4161
5204
|
register(listCampaignsTool);
|
|
4162
5205
|
register(archiveCampaignTool);
|
|
@@ -4191,6 +5234,13 @@ function registerAllTools(register) {
|
|
|
4191
5234
|
register(updateCustomRuleTool);
|
|
4192
5235
|
register(deleteCustomRuleTool);
|
|
4193
5236
|
register(testCustomRuleTool);
|
|
5237
|
+
register(listCustomTaxonomiesTool);
|
|
5238
|
+
register(getCustomTaxonomyTool);
|
|
5239
|
+
register(createCustomTaxonomyTool);
|
|
5240
|
+
register(updateCustomTaxonomyTool);
|
|
5241
|
+
register(deleteCustomTaxonomyTool);
|
|
5242
|
+
register(restoreCustomTaxonomyTool);
|
|
5243
|
+
register(parseCustomTaxonomyTextTool);
|
|
4194
5244
|
register(listPolicySetsTool);
|
|
4195
5245
|
register(getPolicySetTool);
|
|
4196
5246
|
register(createPolicySetTool);
|
|
@@ -4216,6 +5266,7 @@ function registerAllTools(register) {
|
|
|
4216
5266
|
register(getUsageSummaryTool);
|
|
4217
5267
|
register(listBalanceHistoryTool);
|
|
4218
5268
|
register(listInvoicesTool);
|
|
5269
|
+
register(getInvoicePdfTool);
|
|
4219
5270
|
register(listAlertDestinationsTool);
|
|
4220
5271
|
register(deleteAlertDestinationTool);
|
|
4221
5272
|
register(setAlertDestinationVersionTool);
|
|
@@ -4253,12 +5304,28 @@ function wireToolsIntoMcpServer(server, ctxProvider) {
|
|
|
4253
5304
|
});
|
|
4254
5305
|
}
|
|
4255
5306
|
function toolOkToMcpResult(value) {
|
|
5307
|
+
if (isMcpContentEnvelope(value)) {
|
|
5308
|
+
return value;
|
|
5309
|
+
}
|
|
4256
5310
|
const isPlainObject = value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4257
5311
|
return {
|
|
4258
5312
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
4259
5313
|
...isPlainObject ? { structuredContent: value } : {}
|
|
4260
5314
|
};
|
|
4261
5315
|
}
|
|
5316
|
+
function isBinaryBlockType(type2) {
|
|
5317
|
+
return type2 === "image" || type2 === "audio" || type2 === "resource";
|
|
5318
|
+
}
|
|
5319
|
+
function isMcpContentEnvelope(value) {
|
|
5320
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
5321
|
+
const v = value;
|
|
5322
|
+
if (!Array.isArray(v.content) || v.content.length === 0) return false;
|
|
5323
|
+
return v.content.every((block) => {
|
|
5324
|
+
if (block === null || typeof block !== "object") return false;
|
|
5325
|
+
const type2 = block.type;
|
|
5326
|
+
return typeof type2 === "string" && isBinaryBlockType(type2);
|
|
5327
|
+
});
|
|
5328
|
+
}
|
|
4262
5329
|
function toolErrorToMcpResult(error) {
|
|
4263
5330
|
return {
|
|
4264
5331
|
isError: true,
|
|
@@ -4285,5 +5352,5 @@ function formatToolError(error) {
|
|
|
4285
5352
|
}
|
|
4286
5353
|
|
|
4287
5354
|
export { BearerToken, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
|
|
4288
|
-
//# sourceMappingURL=chunk-
|
|
4289
|
-
//# sourceMappingURL=chunk-
|
|
5355
|
+
//# sourceMappingURL=chunk-7DSLLKEK.js.map
|
|
5356
|
+
//# sourceMappingURL=chunk-7DSLLKEK.js.map
|