@kaminari-ad/mcp 0.2.0 → 0.2.2

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.
@@ -0,0 +1,4289 @@
1
+ #!/usr/bin/env node
2
+ import { err, ok } from './chunk-BAZQPU6T.js';
3
+ import { createHash, randomUUID } from 'crypto';
4
+ import createClient from 'openapi-fetch';
5
+ import { fetch } from 'undici';
6
+ import { z } from 'zod';
7
+ import { stdTimeFunctions, pino } from 'pino';
8
+ import pinoPretty from 'pino-pretty';
9
+ import { ListResourcesRequestSchema, ListPromptsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
10
+
11
+ var REDACTED = "[BearerToken redacted]";
12
+ var BEARER_HASH_PREFIX_LEN = 8;
13
+ var MAX_HEADER_LEN = 4096;
14
+ var BearerToken = class _BearerToken {
15
+ #raw;
16
+ constructor(raw) {
17
+ this.#raw = raw;
18
+ }
19
+ /**
20
+ * Construct from a raw string. Trims surrounding whitespace; rejects
21
+ * empty input. Does NOT validate format — the API is the single
22
+ * source of truth for token validity.
23
+ */
24
+ static fromString(raw) {
25
+ const trimmed = raw.trim();
26
+ if (trimmed.length === 0) return void 0;
27
+ return new _BearerToken(trimmed);
28
+ }
29
+ /**
30
+ * Parse from a raw `Authorization` header value. Returns `undefined`
31
+ * for missing / malformed input (anything not `Bearer <token>`).
32
+ *
33
+ * **Normalization policy.** The regex is case-insensitive on the
34
+ * `Bearer` scheme (per RFC 6750 §2.1, the scheme name is
35
+ * case-insensitive) but the outbound header is always re-emitted
36
+ * with the canonical capitalization (`Bearer <token>`) by
37
+ * {@link toAuthorizationHeader}. We intentionally do NOT preserve
38
+ * the inbound casing byte-for-byte:
39
+ *
40
+ * - The Kaminari Ad API accepts the canonical form.
41
+ * - Outbound canonicalization simplifies any future signature /
42
+ * proxy that re-hashes the header.
43
+ * - The TOKEN value itself is preserved exactly (the capture
44
+ * group's `\S+` keeps the secret intact); only the SCHEME word
45
+ * is normalized.
46
+ *
47
+ * Token length is capped at {@link MAX_TOKEN_LEN} bytes to keep a
48
+ * pathological client from blowing the heap; longer headers are
49
+ * rejected as malformed.
50
+ */
51
+ static fromAuthorizationHeader(headerValue) {
52
+ if (headerValue === void 0) return void 0;
53
+ if (headerValue.length > MAX_HEADER_LEN) return void 0;
54
+ const match = /^Bearer\s+(\S+)\s*$/i.exec(headerValue);
55
+ if (match?.[1] === void 0) return void 0;
56
+ return _BearerToken.fromString(match[1]);
57
+ }
58
+ /**
59
+ * Returns the first {@link BEARER_HASH_PREFIX_LEN} hex chars of
60
+ * `sha256(token)`. Used as the `bearer_hash` log field.
61
+ */
62
+ hash() {
63
+ return createHash("sha256").update(this.#raw).digest("hex").slice(0, BEARER_HASH_PREFIX_LEN);
64
+ }
65
+ /**
66
+ * Returns the full SHA-256 hex digest. Used as the session-binding
67
+ * key in {@link SessionStore}. Not for logs.
68
+ */
69
+ fullHash() {
70
+ return createHash("sha256").update(this.#raw).digest("hex");
71
+ }
72
+ /**
73
+ * Returns the literal `Authorization` header value. Use ONLY when
74
+ * constructing an outbound HTTP request to the API.
75
+ */
76
+ toAuthorizationHeader() {
77
+ return `Bearer ${this.#raw}`;
78
+ }
79
+ /**
80
+ * Redaction. Logger / JSON serialisation / template strings see the
81
+ * placeholder, not the token.
82
+ */
83
+ toString() {
84
+ return REDACTED;
85
+ }
86
+ /**
87
+ * `JSON.stringify(bearer)` -> `"[BearerToken redacted]"`. Same
88
+ * intent as {@link toString}: any path that serializes the VO never
89
+ * sees the secret.
90
+ */
91
+ toJSON() {
92
+ return REDACTED;
93
+ }
94
+ /**
95
+ * Node's `util.inspect` hook — keeps `console.log(token)` /
96
+ * structured logs from accidentally revealing the secret.
97
+ */
98
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
99
+ return REDACTED;
100
+ }
101
+ };
102
+ function newRequestId() {
103
+ return randomUUID();
104
+ }
105
+
106
+ // src/infrastructure/api/error-mapping.ts
107
+ function detail(parsed) {
108
+ if (parsed === null || typeof parsed !== "object") return "Upstream error";
109
+ if (!("detail" in parsed)) return "Upstream error";
110
+ const d = parsed.detail;
111
+ if (typeof d === "string") return d;
112
+ if (Array.isArray(d)) {
113
+ const messages = d.map((entry) => {
114
+ if (entry === null || typeof entry !== "object") return void 0;
115
+ const e = entry;
116
+ const loc = Array.isArray(e.loc) ? e.loc.filter((p) => typeof p === "string").join(".") : "";
117
+ const msg = typeof e.msg === "string" ? e.msg : "";
118
+ if (loc === "" && msg === "") return void 0;
119
+ return loc === "" ? msg : `${loc}: ${msg}`;
120
+ }).filter((m) => m !== void 0);
121
+ if (messages.length > 0) return messages.join("; ");
122
+ }
123
+ return "Upstream error";
124
+ }
125
+ function errCode(parsed) {
126
+ if (parsed !== null && typeof parsed === "object" && "code" in parsed) {
127
+ const c = parsed.code;
128
+ if (typeof c === "string") return c;
129
+ }
130
+ return void 0;
131
+ }
132
+ function toApiError(status2, parsed, retryAfterHeader) {
133
+ const message = detail(parsed);
134
+ if (status2 === 401) return { kind: "unauthorized", detail: message };
135
+ if (status2 === 403) {
136
+ const code = errCode(parsed);
137
+ return code === void 0 ? { kind: "forbidden", detail: message } : { kind: "forbidden", detail: message, code };
138
+ }
139
+ if (status2 === 404) return { kind: "not-found", detail: message };
140
+ if (status2 === 422 || status2 === 400) {
141
+ const code = errCode(parsed);
142
+ return code === void 0 ? { kind: "invalid-input", detail: message } : { kind: "invalid-input", detail: message, code };
143
+ }
144
+ if (status2 === 429) {
145
+ const ra = Array.isArray(retryAfterHeader) ? retryAfterHeader[0] : retryAfterHeader;
146
+ const retryMs = ra === void 0 ? void 0 : Number.parseInt(ra, 10) * 1e3;
147
+ return retryMs === void 0 || Number.isNaN(retryMs) ? { kind: "rate-limited", detail: message } : { kind: "rate-limited", detail: message, retryAfterMs: retryMs };
148
+ }
149
+ return { kind: "upstream", detail: message, status: status2 };
150
+ }
151
+ var OrgResponse = z.object({
152
+ id: z.string().uuid(),
153
+ name: z.string(),
154
+ owner_id: z.string().uuid(),
155
+ is_active: z.boolean(),
156
+ created_at: z.string().datetime({ offset: true })
157
+ }).passthrough();
158
+ z.object({ name: z.union([z.string(), z.null()]) }).partial().passthrough();
159
+ var ValidationError = z.object({
160
+ loc: z.array(z.union([z.string(), z.number()])),
161
+ msg: z.string(),
162
+ type: z.string(),
163
+ input: z.unknown().optional(),
164
+ ctx: z.object({}).partial().passthrough().optional()
165
+ }).passthrough();
166
+ z.object({ detail: z.array(ValidationError) }).partial().passthrough();
167
+ z.object({
168
+ key: z.string(),
169
+ display_name: z.string(),
170
+ position: z.number().int(),
171
+ auto_extract: z.boolean()
172
+ }).passthrough();
173
+ var LabelDefinitionItem = z.object({
174
+ key: z.string().max(50),
175
+ display_name: z.string().max(100),
176
+ auto_extract: z.boolean().optional().default(false)
177
+ }).passthrough();
178
+ z.object({ labels: z.array(LabelDefinitionItem) }).passthrough();
179
+ var UserResponse = z.object({
180
+ id: z.string().uuid(),
181
+ email: z.string(),
182
+ name: z.string(),
183
+ role_name: z.string(),
184
+ is_active: z.boolean(),
185
+ created_at: z.string().datetime({ offset: true })
186
+ }).passthrough();
187
+ z.object({
188
+ email: z.string().max(254),
189
+ name: z.string().max(200).optional().default(""),
190
+ role_id: z.string().uuid(),
191
+ timezone: z.union([z.string(), z.null()]).optional()
192
+ });
193
+ z.object({ role_id: z.string().uuid() }).passthrough();
194
+ var ApiKeyResponse = z.object({
195
+ id: z.string().uuid(),
196
+ key_prefix: z.string(),
197
+ name: z.string(),
198
+ expires_at: z.union([z.string(), z.null()]),
199
+ created_at: z.string().datetime({ offset: true })
200
+ }).passthrough();
201
+ z.object({
202
+ name: z.string(),
203
+ expires_at: z.union([z.string(), z.null()]).optional()
204
+ }).passthrough();
205
+ var ApiKeyCreatedResponse = z.object({
206
+ id: z.string().uuid(),
207
+ key_prefix: z.string(),
208
+ full_key: z.string(),
209
+ name: z.string(),
210
+ expires_at: z.union([z.string(), z.null()]),
211
+ created_at: z.string().datetime({ offset: true })
212
+ }).passthrough();
213
+ var RoleResponse = z.object({
214
+ id: z.string().uuid(),
215
+ name: z.string(),
216
+ scope: z.string(),
217
+ is_system: z.boolean(),
218
+ permissions: z.array(z.string())
219
+ }).passthrough();
220
+ z.object({ name: z.string(), permissions: z.array(z.string()) }).passthrough();
221
+ var ProxyTargetRequest = z.object({
222
+ proxy_type: z.string().default("residential"),
223
+ region: z.string().default(""),
224
+ city: z.string().default(""),
225
+ isp: z.string().default("")
226
+ }).partial().passthrough();
227
+ z.object({
228
+ url: z.union([z.string(), z.null()]).optional(),
229
+ ad_tag: z.union([z.string(), z.null()]).optional(),
230
+ country_code: z.string().min(2).max(2),
231
+ emulator_id: z.string().min(1).max(100),
232
+ proxy: ProxyTargetRequest.optional(),
233
+ labels: z.record(z.string()).optional(),
234
+ campaign_id: z.union([z.string(), z.null()]).optional(),
235
+ run_id: z.union([z.string(), z.null()]).optional()
236
+ }).passthrough();
237
+ var ScanStatus = z.enum([
238
+ "pending",
239
+ "running",
240
+ "crawled",
241
+ "checking",
242
+ "checking_async",
243
+ "completed",
244
+ "partial",
245
+ "failed",
246
+ "cancelled"
247
+ ]);
248
+ var SubRequestResponse = z.lazy(
249
+ () => z.object({
250
+ url: z.string(),
251
+ resource_type: z.string(),
252
+ status_code: z.number().int(),
253
+ content_type: z.string(),
254
+ body_size: z.number().int(),
255
+ timestamp_ms: z.number().int(),
256
+ children: z.array(SubRequestResponse).optional()
257
+ }).passthrough()
258
+ );
259
+ var RedirectHopResponse = z.object({
260
+ url: z.string(),
261
+ status_code: z.number().int(),
262
+ content_type: z.string(),
263
+ body_size: z.number().int(),
264
+ timestamp_ms: z.number().int(),
265
+ redirected_from: z.string(),
266
+ sub_requests: z.array(SubRequestResponse)
267
+ }).passthrough();
268
+ var ProxyTargetResponse = z.object({
269
+ proxy_type: z.string(),
270
+ region: z.string().optional().default(""),
271
+ city: z.string().optional().default(""),
272
+ isp: z.string().optional().default("")
273
+ }).passthrough();
274
+ var IabCategoryResponse = z.object({
275
+ tier1: z.string(),
276
+ tier2: z.union([z.string(), z.null()]).optional(),
277
+ tier3: z.union([z.string(), z.null()]).optional(),
278
+ tier4: z.union([z.string(), z.null()]).optional()
279
+ }).passthrough();
280
+ var ScanClassificationResponse = z.object({
281
+ brand: z.union([z.string(), z.null()]),
282
+ iab_v2: z.union([IabCategoryResponse, z.null()]),
283
+ iab_v3: z.union([IabCategoryResponse, z.null()])
284
+ }).partial().passthrough();
285
+ var LandingResponse = z.object({
286
+ ord: z.number().int(),
287
+ opener_url: z.string().optional().default(""),
288
+ final_url: z.string().optional().default(""),
289
+ offer_url: z.string().optional().default(""),
290
+ page_title: z.string().optional().default(""),
291
+ screenshot_url: z.string().optional().default(""),
292
+ redirect_chain: z.array(RedirectHopResponse).optional(),
293
+ elapsed_ms: z.number().int().optional().default(0),
294
+ created_at: z.string().datetime({ offset: true })
295
+ }).passthrough();
296
+ var ScanResponse = z.object({
297
+ id: z.string().uuid(),
298
+ url: z.string(),
299
+ country_code: z.string(),
300
+ emulator_id: z.string(),
301
+ status: ScanStatus,
302
+ offer_url: z.string(),
303
+ redirect_chain: z.array(RedirectHopResponse),
304
+ screenshot_url: z.string().optional().default(""),
305
+ ad_tag: z.union([z.string(), z.null()]).optional(),
306
+ creative_screenshot_url: z.string().optional().default(""),
307
+ creative_width: z.number().int().optional().default(0),
308
+ creative_height: z.number().int().optional().default(0),
309
+ proxy: z.union([ProxyTargetResponse, z.null()]).optional(),
310
+ page_title: z.string(),
311
+ elapsed_ms: z.number().int(),
312
+ error: z.string(),
313
+ labels: z.record(z.string()).optional(),
314
+ classification: z.union([ScanClassificationResponse, z.null()]).optional(),
315
+ campaign_id: z.union([z.string(), z.null()]).optional(),
316
+ campaign_name: z.union([z.string(), z.null()]).optional(),
317
+ created_at: z.string().datetime({ offset: true }),
318
+ completed_at: z.union([z.string(), z.null()]),
319
+ landings: z.array(LandingResponse).optional()
320
+ }).passthrough();
321
+ z.union([z.string(), z.null()]).optional();
322
+ var ScanBriefResponse = z.object({
323
+ id: z.string().uuid(),
324
+ url: z.string(),
325
+ country_code: z.string(),
326
+ proxy_type: z.string().optional().default("residential"),
327
+ status: ScanStatus,
328
+ offer_url: z.string(),
329
+ screenshot_url: z.string().optional().default(""),
330
+ labels: z.record(z.string()).optional(),
331
+ classification: z.union([ScanClassificationResponse, z.null()]).optional(),
332
+ elapsed_ms: z.number().int(),
333
+ created_at: z.string().datetime({ offset: true }),
334
+ campaign_id: z.union([z.string(), z.null()]).optional(),
335
+ campaign_name: z.union([z.string(), z.null()]).optional(),
336
+ is_ad_tag: z.boolean().optional().default(false)
337
+ }).passthrough();
338
+ z.object({
339
+ items: z.array(ScanBriefResponse),
340
+ total: z.number().int(),
341
+ page: z.number().int(),
342
+ limit: z.number().int(),
343
+ pages: z.number().int()
344
+ }).passthrough();
345
+ z.object({
346
+ url: z.union([z.string(), z.null()]).optional(),
347
+ ad_tag: z.union([z.string(), z.null()]).optional(),
348
+ country_codes: z.array(z.string()).min(1),
349
+ emulator_id: z.string().min(1).max(100),
350
+ proxy: ProxyTargetRequest.optional(),
351
+ labels: z.record(z.string()).optional()
352
+ }).passthrough();
353
+ z.object({
354
+ scope_type: z.enum(["last_n", "hours"]),
355
+ scope_value: z.number().int().gt(0)
356
+ }).passthrough();
357
+ z.object({ queued_count: z.number().int() }).passthrough();
358
+ z.object({ cancelled_count: z.number().int() }).passthrough();
359
+ z.union([z.number(), z.null()]).optional();
360
+ var GeoResponse = z.object({
361
+ country_code: z.string(),
362
+ name: z.string(),
363
+ region: z.string(),
364
+ tier: z.string()
365
+ }).passthrough();
366
+ var EmulatorResponse = z.object({
367
+ id: z.string(),
368
+ display_name: z.string(),
369
+ category: z.string(),
370
+ browser: z.string()
371
+ }).passthrough();
372
+ z.object({ name: z.string().min(1).max(200) }).passthrough();
373
+ var CampaignGroupResponse = z.object({
374
+ id: z.string().uuid(),
375
+ name: z.string(),
376
+ is_default: z.boolean(),
377
+ is_archived: z.boolean(),
378
+ schedule_paused: z.boolean(),
379
+ created_at: z.string().datetime({ offset: true }),
380
+ campaign_count: z.union([z.number(), z.null()]).optional()
381
+ }).passthrough();
382
+ z.object({ name: z.union([z.string(), z.null()]) }).partial().passthrough();
383
+ var BulkCampaignFailure = z.object({
384
+ campaign_id: z.string().uuid(),
385
+ error_code: z.string(),
386
+ detail: z.string()
387
+ }).passthrough();
388
+ var GroupActionResponse = z.object({
389
+ group_id: z.string().uuid(),
390
+ affected_campaigns: z.number().int(),
391
+ cancelled_count: z.number().int().optional().default(0),
392
+ run_ids: z.array(z.string().uuid()).optional(),
393
+ failures: z.array(BulkCampaignFailure).optional()
394
+ }).passthrough();
395
+ z.object({
396
+ name: z.string().min(1).max(200),
397
+ campaign_type: z.string().optional().default("url"),
398
+ url: z.union([z.string(), z.null()]).optional(),
399
+ ad_tag: z.union([z.string(), z.null()]).optional(),
400
+ country_codes: z.array(z.string()).min(1),
401
+ group_id: z.union([z.string(), z.null()]).optional(),
402
+ emulator_categories: z.array(z.string()).optional(),
403
+ emulator_specific_ids: z.array(z.string()).optional(),
404
+ emulator_mode: z.string().optional().default("random"),
405
+ proxy_type: z.string().optional().default("residential"),
406
+ proxy_region: z.string().optional().default(""),
407
+ proxy_city: z.string().optional().default(""),
408
+ proxy_isp: z.string().optional().default(""),
409
+ labels: z.record(z.string()).optional(),
410
+ policy_set_id: z.union([z.string(), z.null()]).optional(),
411
+ schedule_type: z.union([z.string(), z.null()]).optional(),
412
+ schedule_weekly: z.union([z.record(z.array(z.number().int())), z.null()]).optional(),
413
+ schedule_interval_seconds: z.union([z.number(), z.null()]).optional(),
414
+ schedule_enabled: z.union([z.boolean(), z.null()]).optional(),
415
+ schedule_timezone: z.union([z.string(), z.null()]).optional()
416
+ }).passthrough();
417
+ var EmulatorSelectionResponse = z.object({
418
+ categories: z.array(z.string()),
419
+ specific_ids: z.array(z.string()),
420
+ mode: z.string()
421
+ }).passthrough();
422
+ var CampaignResponse = z.object({
423
+ id: z.string().uuid(),
424
+ name: z.string(),
425
+ campaign_type: z.string().optional().default("url"),
426
+ url: z.string(),
427
+ ad_tag: z.union([z.string(), z.null()]).optional(),
428
+ country_codes: z.array(z.string()),
429
+ group_id: z.string().uuid(),
430
+ emulator_selection: EmulatorSelectionResponse,
431
+ proxy_type: z.string().optional().default("residential"),
432
+ proxy_region: z.string().optional().default(""),
433
+ proxy_city: z.string().optional().default(""),
434
+ proxy_isp: z.string().optional().default(""),
435
+ schedule_type: z.union([z.string(), z.null()]).optional(),
436
+ schedule_weekly: z.union([z.record(z.array(z.number().int())), z.null()]).optional(),
437
+ schedule_interval_seconds: z.union([z.number(), z.null()]).optional(),
438
+ schedule_timezone: z.union([z.string(), z.null()]).optional(),
439
+ labels: z.record(z.string()).optional(),
440
+ policy_set_id: z.union([z.string(), z.null()]).optional(),
441
+ schedule_enabled: z.boolean(),
442
+ is_archived: z.boolean(),
443
+ created_at: z.string().datetime({ offset: true }),
444
+ last_run_at: z.union([z.string(), z.null()]).optional()
445
+ }).passthrough();
446
+ z.object({
447
+ items: z.array(CampaignResponse),
448
+ total: z.number().int(),
449
+ page: z.number().int(),
450
+ limit: z.number().int(),
451
+ pages: z.number().int()
452
+ }).passthrough();
453
+ var CampaignPickerItem = z.object({
454
+ id: z.string().uuid(),
455
+ name: z.string(),
456
+ group_id: z.string().uuid(),
457
+ is_archived: z.boolean()
458
+ }).passthrough();
459
+ z.object({
460
+ name: z.union([z.string(), z.null()]),
461
+ url: z.union([z.string(), z.null()]),
462
+ ad_tag: z.union([z.string(), z.null()]),
463
+ country_codes: z.union([z.array(z.string()), z.null()]),
464
+ group_id: z.union([z.string(), z.null()]),
465
+ emulator_categories: z.union([z.array(z.string()), z.null()]),
466
+ emulator_specific_ids: z.union([z.array(z.string()), z.null()]),
467
+ emulator_mode: z.union([z.string(), z.null()]),
468
+ proxy_type: z.union([z.string(), z.null()]),
469
+ proxy_region: z.union([z.string(), z.null()]),
470
+ proxy_city: z.union([z.string(), z.null()]),
471
+ proxy_isp: z.union([z.string(), z.null()]),
472
+ labels: z.union([z.record(z.string()), z.null()]),
473
+ policy_set_id: z.union([z.string(), z.null()]),
474
+ schedule_type: z.union([z.string(), z.null()]),
475
+ schedule_weekly: z.union([z.record(z.array(z.number().int())), z.null()]),
476
+ schedule_interval_seconds: z.union([z.number(), z.null()]),
477
+ schedule_enabled: z.union([z.boolean(), z.null()]),
478
+ schedule_timezone: z.union([z.string(), z.null()])
479
+ }).partial().passthrough();
480
+ var RunSource = z.enum(["ui", "api"]);
481
+ var RunResponse = z.object({
482
+ id: z.string().uuid(),
483
+ campaign_id: z.string().uuid(),
484
+ label: z.string(),
485
+ total: z.number().int(),
486
+ completed: z.number().int(),
487
+ failed: z.number().int(),
488
+ partial: z.number().int(),
489
+ cancelled: z.number().int(),
490
+ source: RunSource,
491
+ created_at: z.string().datetime({ offset: true })
492
+ }).passthrough();
493
+ z.object({
494
+ items: z.array(RunResponse),
495
+ total: z.number().int(),
496
+ page: z.number().int(),
497
+ limit: z.number().int(),
498
+ pages: z.number().int()
499
+ }).passthrough();
500
+ var ScanTileResponse = z.object({
501
+ id: z.string().uuid(),
502
+ country_code: z.string(),
503
+ status: z.string(),
504
+ offer_url: z.string().optional().default(""),
505
+ screenshot_url: z.string().optional().default(""),
506
+ elapsed_ms: z.number().int().optional().default(0),
507
+ error: z.string().optional().default("")
508
+ }).passthrough();
509
+ z.object({
510
+ items: z.array(ScanTileResponse),
511
+ total: z.number().int(),
512
+ page: z.number().int(),
513
+ limit: z.number().int(),
514
+ pages: z.number().int()
515
+ }).passthrough();
516
+ var ScanTagResponse = z.object({
517
+ id: z.string().uuid(),
518
+ scan_id: z.string().uuid(),
519
+ tag_slug: z.string(),
520
+ detail: z.string(),
521
+ url: z.string().optional().default(""),
522
+ display_name: z.string().optional().default(""),
523
+ category: z.string().optional().default(""),
524
+ severity: z.string().optional().default(""),
525
+ created_at: z.string().datetime({ offset: true })
526
+ }).passthrough();
527
+ var TagDefinitionWithStatsResponse = z.object({
528
+ slug: z.string(),
529
+ category: z.string(),
530
+ source: z.string(),
531
+ display_name: z.string(),
532
+ description: z.string(),
533
+ is_system: z.boolean(),
534
+ organization_id: z.union([z.string(), z.null()]),
535
+ show_in_public_report: z.boolean(),
536
+ severity: z.string(),
537
+ scans_count: z.number().int(),
538
+ rules_count: z.number().int()
539
+ }).passthrough();
540
+ var LinkedRuleResponse = z.object({
541
+ id: z.string().uuid(),
542
+ name: z.string(),
543
+ rule_type: z.string(),
544
+ target: z.string(),
545
+ is_active: z.boolean()
546
+ }).passthrough();
547
+ var TagDefinitionDetailResponse = z.object({
548
+ slug: z.string(),
549
+ category: z.string(),
550
+ source: z.string(),
551
+ display_name: z.string(),
552
+ description: z.string(),
553
+ is_system: z.boolean(),
554
+ organization_id: z.union([z.string(), z.null()]),
555
+ show_in_public_report: z.boolean(),
556
+ severity: z.string(),
557
+ scans_count: z.number().int(),
558
+ rules_count: z.number().int(),
559
+ linked_rules: z.array(LinkedRuleResponse).optional()
560
+ }).passthrough();
561
+ var TagSeverity = z.enum(["high", "medium", "low"]);
562
+ z.object({
563
+ display_name: z.union([z.string(), z.null()]),
564
+ description: z.union([z.string(), z.null()]),
565
+ show_in_public_report: z.union([z.boolean(), z.null()]),
566
+ severity: z.union([TagSeverity, z.null()])
567
+ }).partial().passthrough();
568
+ z.object({
569
+ name: z.string().min(1).max(200),
570
+ tag_slug: z.string().max(100).optional().default(""),
571
+ rule_type: z.string().max(50),
572
+ config: z.object({}).partial().passthrough(),
573
+ target: z.string().max(30).optional().default("page")
574
+ }).passthrough();
575
+ var CustomRuleResponse = z.object({
576
+ id: z.string().uuid(),
577
+ organization_id: z.string().uuid(),
578
+ name: z.string(),
579
+ tag_slug: z.string(),
580
+ rule_type: z.string(),
581
+ config: z.object({}).partial().passthrough(),
582
+ target: z.string(),
583
+ is_active: z.boolean(),
584
+ created_at: z.string().datetime({ offset: true })
585
+ }).passthrough();
586
+ z.object({
587
+ items: z.array(CustomRuleResponse),
588
+ total: z.number().int(),
589
+ page: z.number().int(),
590
+ limit: z.number().int(),
591
+ pages: z.number().int()
592
+ }).passthrough();
593
+ z.object({
594
+ name: z.union([z.string(), z.null()]),
595
+ tag_slug: z.union([z.string(), z.null()]),
596
+ config: z.union([z.object({}).partial().passthrough(), z.null()]),
597
+ target: z.union([z.string(), z.null()]),
598
+ is_active: z.union([z.boolean(), z.null()])
599
+ }).partial().passthrough();
600
+ z.object({
601
+ scan_id: z.string().uuid(),
602
+ rule_type: z.string(),
603
+ config: z.object({}).partial().passthrough(),
604
+ target: z.string().optional().default("page")
605
+ }).passthrough();
606
+ var RuleTestTagResult = z.object({ tag_slug: z.string(), detail: z.string().optional().default("") }).passthrough();
607
+ var RuleTestResponse = z.object({
608
+ matched: z.boolean(),
609
+ tags: z.array(RuleTestTagResult),
610
+ elapsed_ms: z.number().int(),
611
+ llm_failed: z.boolean().optional().default(false),
612
+ llm_call_id: z.union([z.string(), z.null()]).optional(),
613
+ llm_prompt_url: z.string().optional().default(""),
614
+ llm_response_url: z.string().optional().default("")
615
+ }).passthrough();
616
+ var PolicyEntryRequest = z.object({
617
+ tag_slug: z.string().min(1).max(100),
618
+ country_codes: z.array(z.string()).max(50).optional()
619
+ }).passthrough();
620
+ z.object({
621
+ name: z.string().min(1).max(200),
622
+ description: z.string().max(2e3).optional().default(""),
623
+ entries: z.array(PolicyEntryRequest).min(1).max(500)
624
+ }).passthrough();
625
+ var PolicyEntryResponse = z.object({
626
+ id: z.string().uuid(),
627
+ tag_slug: z.string(),
628
+ country_codes: z.array(z.string())
629
+ }).passthrough();
630
+ var PolicySetResponse = z.object({
631
+ id: z.string().uuid(),
632
+ name: z.string(),
633
+ description: z.string(),
634
+ organization_id: z.string().uuid(),
635
+ visibility: z.string(),
636
+ is_approved: z.boolean(),
637
+ entries: z.array(PolicyEntryResponse),
638
+ created_at: z.string().datetime({ offset: true })
639
+ }).passthrough();
640
+ var VisibilityType = z.enum(["private", "public"]);
641
+ z.union([VisibilityType, z.null()]).optional();
642
+ var PolicySetListItem = z.object({
643
+ id: z.string().uuid(),
644
+ name: z.string(),
645
+ description: z.string(),
646
+ organization_id: z.string().uuid(),
647
+ visibility: z.string(),
648
+ is_approved: z.boolean(),
649
+ created_at: z.string().datetime({ offset: true })
650
+ }).passthrough();
651
+ z.object({
652
+ items: z.array(PolicySetListItem),
653
+ total: z.number().int(),
654
+ page: z.number().int(),
655
+ limit: z.number().int(),
656
+ pages: z.number().int()
657
+ }).passthrough();
658
+ z.object({
659
+ name: z.string().min(1).max(200),
660
+ description: z.string().max(2e3).optional().default(""),
661
+ entries: z.array(PolicyEntryRequest).min(1).max(500)
662
+ }).passthrough();
663
+ var AlertStatus = z.enum(["open", "acknowledged", "resolved", "dismissed"]);
664
+ z.union([AlertStatus, z.null()]).optional();
665
+ var AlertResponse = z.object({
666
+ id: z.string().uuid(),
667
+ scan_id: z.string().uuid(),
668
+ campaign_id: z.string().uuid(),
669
+ policy_set_id: z.union([z.string(), z.null()]),
670
+ violation_rule_id: z.union([z.string(), z.null()]),
671
+ organization_id: z.string().uuid(),
672
+ tag_slug: z.string(),
673
+ country_code: z.string(),
674
+ status: z.string(),
675
+ closed_by: z.union([z.string(), z.null()]),
676
+ created_at: z.string().datetime({ offset: true }),
677
+ updated_at: z.union([z.string(), z.null()]),
678
+ scan_url: z.string(),
679
+ offer_url: z.string(),
680
+ tag_display_name: z.string()
681
+ }).passthrough();
682
+ z.object({
683
+ items: z.array(AlertResponse),
684
+ total: z.number().int(),
685
+ page: z.number().int(),
686
+ limit: z.number().int(),
687
+ pages: z.number().int()
688
+ }).passthrough();
689
+ z.object({ status: AlertStatus }).passthrough();
690
+ var AlertStatsResponse = z.object({
691
+ open: z.number().int(),
692
+ acknowledged: z.number().int(),
693
+ resolved: z.number().int(),
694
+ dismissed: z.number().int()
695
+ }).passthrough();
696
+ var BlockReason = z.enum(["no_subscription", "suspended", "insufficient_funds"]);
697
+ var BillingSummaryResponse = z.object({
698
+ balance_micros: z.number().int(),
699
+ plan_name: z.union([z.string(), z.null()]),
700
+ plan_id: z.union([z.string(), z.null()]),
701
+ checks_per_period: z.union([z.number(), z.null()]),
702
+ checks_used: z.union([z.number(), z.null()]),
703
+ period_start: z.union([z.string(), z.null()]),
704
+ period_end: z.union([z.string(), z.null()]),
705
+ price_per_extra_check_micros: z.union([z.number(), z.null()]),
706
+ current_plan_is_custom: z.boolean().optional().default(false),
707
+ is_suspended: z.boolean().optional().default(false),
708
+ scheduled_next_plan_id: z.union([z.string(), z.null()]).optional(),
709
+ scheduled_next_plan_name: z.union([z.string(), z.null()]).optional(),
710
+ scheduled_effective_at: z.union([z.string(), z.null()]).optional(),
711
+ can_create_scan: z.boolean().optional().default(true),
712
+ block_reason: z.union([BlockReason, z.null()]).optional(),
713
+ billing_mode: z.string().optional().default("prepaid"),
714
+ credit_limit_micros: z.number().int().optional().default(0),
715
+ effective_minimum_balance_micros: z.number().int().optional().default(0)
716
+ }).passthrough();
717
+ var UsageResponse = z.object({
718
+ id: z.string().uuid(),
719
+ scan_id: z.string().uuid(),
720
+ charged_micros: z.number().int(),
721
+ balance_after_micros: z.number().int(),
722
+ within_plan: z.boolean(),
723
+ event_type: z.string(),
724
+ created_at: z.string().datetime({ offset: true })
725
+ }).passthrough();
726
+ z.object({
727
+ items: z.array(UsageResponse),
728
+ total: z.number().int(),
729
+ page: z.number().int(),
730
+ limit: z.number().int(),
731
+ pages: z.number().int()
732
+ }).passthrough();
733
+ var UsagePeriodSummaryResponse = z.object({
734
+ period_start: z.string().datetime({ offset: true }),
735
+ period_end: z.string().datetime({ offset: true }),
736
+ checks: z.number().int(),
737
+ rechecks: z.number().int(),
738
+ within_plan: z.number().int(),
739
+ overage: z.number().int(),
740
+ charged_micros: z.number().int()
741
+ }).passthrough();
742
+ var BalanceTransactionType = z.enum([
743
+ "initial_balance",
744
+ "top_up_manual",
745
+ "usage_charge",
746
+ "subscription_renewal",
747
+ "subscription_upgrade",
748
+ "admin_adjustment",
749
+ "refund",
750
+ "invoice_settlement",
751
+ "crypto_top_up"
752
+ ]);
753
+ z.union([z.array(BalanceTransactionType), z.null()]).optional();
754
+ var BalanceTransactionResponse = z.object({
755
+ id: z.string().uuid(),
756
+ type: z.string(),
757
+ amount_micros: z.number().int(),
758
+ balance_after_micros: z.number().int(),
759
+ description: z.string(),
760
+ reference_kind: z.union([z.string(), z.null()]),
761
+ reference_id: z.union([z.string(), z.null()]),
762
+ actor_user_id: z.union([z.string(), z.null()]),
763
+ created_at: z.string().datetime({ offset: true })
764
+ }).passthrough();
765
+ z.object({
766
+ items: z.array(BalanceTransactionResponse),
767
+ total: z.number().int(),
768
+ page: z.number().int(),
769
+ limit: z.number().int(),
770
+ pages: z.number().int()
771
+ }).passthrough();
772
+ var EndpointHealthResponse = z.object({
773
+ consecutive_failures: z.number().int(),
774
+ last_delivery_at: z.union([z.string(), z.null()]),
775
+ last_delivery_status: z.union([z.number(), z.null()]),
776
+ success_rate_7d: z.number()
777
+ }).passthrough();
778
+ var WebhookResponse = z.object({
779
+ id: z.string().uuid(),
780
+ url: z.string(),
781
+ description: z.string(),
782
+ event_types: z.array(z.string()),
783
+ campaign_ids: z.union([z.array(z.string().uuid()), z.null()]),
784
+ is_active: z.boolean(),
785
+ disabled_reason: z.union([z.string(), z.null()]),
786
+ disabled_reason_detail: z.union([z.string(), z.null()]),
787
+ disabled_at: z.union([z.string(), z.null()]),
788
+ health: EndpointHealthResponse,
789
+ created_at: z.string().datetime({ offset: true }),
790
+ updated_at: z.string().datetime({ offset: true })
791
+ }).passthrough();
792
+ z.object({
793
+ url: z.string().min(1).max(2048),
794
+ description: z.string().max(256).optional().default(""),
795
+ event_types: z.array(z.string()).optional(),
796
+ campaign_ids: z.union([z.array(z.string().uuid()), z.null()]).optional()
797
+ }).passthrough();
798
+ z.object({ webhook: WebhookResponse, secret: z.string() }).passthrough();
799
+ var EventCatalogEntryResponse = z.object({
800
+ event_type: z.string(),
801
+ description: z.string(),
802
+ sample_payload: z.object({}).partial().passthrough()
803
+ }).passthrough();
804
+ var EventCatalogResponse = z.object({ entries: z.array(EventCatalogEntryResponse) }).passthrough();
805
+ z.object({
806
+ url: z.union([z.string(), z.null()]),
807
+ description: z.union([z.string(), z.null()]),
808
+ event_types: z.union([z.array(z.string()), z.null()]),
809
+ campaign_ids: z.union([z.array(z.string().uuid()), z.null()]),
810
+ clear_campaign_ids: z.boolean().default(false),
811
+ is_active: z.union([z.boolean(), z.null()])
812
+ }).partial().passthrough();
813
+ z.object({ event_type: z.string() }).passthrough();
814
+ var TestWebhookResponse = z.object({
815
+ success: z.boolean(),
816
+ response_status: z.union([z.number(), z.null()]),
817
+ elapsed_ms: z.number().int(),
818
+ error_code: z.union([z.string(), z.null()]),
819
+ response_body: z.string()
820
+ }).passthrough();
821
+ z.union([z.boolean(), z.null()]).optional();
822
+ var DeliveryAttemptResponse = z.object({
823
+ id: z.string().uuid(),
824
+ event_id: z.string().uuid(),
825
+ event_type: z.string(),
826
+ response_status: z.union([z.number(), z.null()]),
827
+ response_body: z.union([z.string(), z.null()]),
828
+ success: z.boolean(),
829
+ attempt_number: z.number().int(),
830
+ error_code: z.union([z.string(), z.null()]),
831
+ elapsed_ms: z.union([z.number(), z.null()]),
832
+ created_at: z.string().datetime({ offset: true })
833
+ }).passthrough();
834
+ z.object({
835
+ items: z.array(DeliveryAttemptResponse),
836
+ total: z.number().int(),
837
+ page: z.number().int(),
838
+ limit: z.number().int(),
839
+ pages: z.number().int()
840
+ }).passthrough();
841
+ z.object({
842
+ from_ts: z.string().datetime({ offset: true }),
843
+ to_ts: z.string().datetime({ offset: true })
844
+ }).passthrough();
845
+ var BulkReplayResponse = z.object({ replayed: z.number().int(), skipped: z.number().int() }).passthrough();
846
+ var AlertNotificationVersion = z.enum(["public", "internal"]);
847
+ var AlertNotificationDestinationResponse = z.object({
848
+ id: z.string().uuid(),
849
+ organization_id: z.string().uuid(),
850
+ channel: z.string(),
851
+ name: z.string(),
852
+ is_active: z.boolean(),
853
+ is_default_target: z.boolean(),
854
+ version: AlertNotificationVersion,
855
+ consecutive_failures: z.number().int(),
856
+ last_delivery_at: z.union([z.string(), z.null()]),
857
+ last_delivery_status: z.union([z.number(), z.null()]),
858
+ slack_workspace_id: z.union([z.string(), z.null()]),
859
+ slack_channel_id: z.union([z.string(), z.null()]),
860
+ slack_channel_name: z.union([z.string(), z.null()]),
861
+ telegram_chat_id: z.union([z.number(), z.null()]),
862
+ telegram_chat_title: z.union([z.string(), z.null()]),
863
+ telegram_chat_type: z.union([z.string(), z.null()]),
864
+ email_address: z.union([z.string(), z.null()]),
865
+ included_label_keys: z.array(z.string()),
866
+ created_at: z.string().datetime({ offset: true }),
867
+ updated_at: z.string().datetime({ offset: true })
868
+ }).passthrough();
869
+ z.object({ version: AlertNotificationVersion }).passthrough();
870
+ var CampaignOverridesResponse = z.object({
871
+ campaign_id: z.string().uuid(),
872
+ mode: z.string(),
873
+ destination_ids: z.array(z.string().uuid())
874
+ }).passthrough();
875
+ z.object({
876
+ mode: z.string().min(1).max(16),
877
+ destination_ids: z.array(z.string().uuid()).optional().default([])
878
+ }).passthrough();
879
+ var InvoiceType = z.enum(["proforma", "final"]);
880
+ z.union([InvoiceType, z.null()]).optional();
881
+ var InvoiceStatus = z.enum(["draft", "issued", "paid", "voided", "overdue"]);
882
+ z.union([InvoiceStatus, z.null()]).optional();
883
+ var InvoiceResponse = z.object({
884
+ id: z.string().uuid(),
885
+ number: z.string(),
886
+ organization_id: z.string().uuid(),
887
+ type: z.string(),
888
+ status: z.string(),
889
+ total_micros: z.number().int(),
890
+ currency: z.string(),
891
+ period_start: z.union([z.string(), z.null()]),
892
+ period_end: z.union([z.string(), z.null()]),
893
+ issued_at: z.union([z.string(), z.null()]),
894
+ paid_at: z.union([z.string(), z.null()]),
895
+ voided_at: z.union([z.string(), z.null()]),
896
+ has_pdf: z.boolean(),
897
+ description: z.string(),
898
+ payment_method: z.string(),
899
+ created_at: z.string().datetime({ offset: true })
900
+ }).passthrough();
901
+ z.object({
902
+ items: z.array(InvoiceResponse),
903
+ total: z.number().int(),
904
+ page: z.number().int(),
905
+ limit: z.number().int(),
906
+ pages: z.number().int()
907
+ }).passthrough();
908
+ z.object({ url: z.union([z.string(), z.null()]), ready: z.boolean() }).passthrough();
909
+ z.object({
910
+ name: z.string().min(2).max(100),
911
+ email: z.string().email(),
912
+ message: z.string().min(10).max(2e3),
913
+ source: z.string().max(512).optional().default("")
914
+ }).passthrough();
915
+ z.object({
916
+ id: z.string().uuid(),
917
+ received_at: z.string().datetime({ offset: true })
918
+ }).passthrough();
919
+ var PreferredContactChannel = z.enum(["telegram", "whatsapp", "email"]);
920
+ z.object({
921
+ first_name: z.string().min(2).max(60),
922
+ last_name: z.string().min(2).max(60),
923
+ company_email: z.string().email(),
924
+ company_name: z.string().min(2).max(120),
925
+ preferred_channel: PreferredContactChannel,
926
+ contact_handle: z.string().max(120).optional().default(""),
927
+ comment: z.string().max(2e3).optional().default(""),
928
+ privacy_accepted: z.boolean(),
929
+ source: z.string().max(512).optional().default("")
930
+ }).passthrough();
931
+ z.object({
932
+ id: z.string().uuid(),
933
+ received_at: z.string().datetime({ offset: true })
934
+ }).passthrough();
935
+ var schemas = {
936
+ OrgResponse,
937
+ UserResponse,
938
+ ApiKeyResponse,
939
+ ApiKeyCreatedResponse,
940
+ RoleResponse,
941
+ ScanResponse,
942
+ ScanBriefResponse,
943
+ GeoResponse,
944
+ EmulatorResponse,
945
+ CampaignGroupResponse,
946
+ GroupActionResponse,
947
+ CampaignResponse,
948
+ CampaignPickerItem,
949
+ RunResponse,
950
+ ScanTileResponse,
951
+ ScanTagResponse,
952
+ TagDefinitionWithStatsResponse,
953
+ LinkedRuleResponse,
954
+ TagDefinitionDetailResponse,
955
+ CustomRuleResponse,
956
+ RuleTestResponse,
957
+ PolicyEntryResponse,
958
+ PolicySetResponse,
959
+ PolicySetListItem,
960
+ AlertResponse,
961
+ AlertStatsResponse,
962
+ BillingSummaryResponse,
963
+ UsageResponse,
964
+ UsagePeriodSummaryResponse,
965
+ BalanceTransactionResponse,
966
+ WebhookResponse,
967
+ EventCatalogResponse,
968
+ TestWebhookResponse,
969
+ DeliveryAttemptResponse,
970
+ BulkReplayResponse,
971
+ AlertNotificationDestinationResponse,
972
+ CampaignOverridesResponse,
973
+ InvoiceResponse};
974
+ function parseWithSchema(schema, raw, label) {
975
+ const parsed = schema.safeParse(raw);
976
+ if (parsed.success) return ok(stripUndefinedKeys(parsed.data));
977
+ const first = parsed.error.issues[0];
978
+ const path = first?.path.join(".") ?? "";
979
+ const message = first?.message ?? "validation failed";
980
+ const detail2 = path === "" ? `malformed ${label}: ${message}` : `malformed ${label}: ${path}: ${message}`;
981
+ return err({ kind: "upstream", detail: detail2 });
982
+ }
983
+ function stripUndefinedKeys(value) {
984
+ if (Array.isArray(value)) return value.map(stripUndefinedKeys);
985
+ if (value === null || typeof value !== "object") return value;
986
+ const out = {};
987
+ for (const [k, v] of Object.entries(value)) {
988
+ if (v === void 0) continue;
989
+ out[k] = stripUndefinedKeys(v);
990
+ }
991
+ return out;
992
+ }
993
+ function parsePagedWithItemSchema(itemSchema, raw, label) {
994
+ const envelope = z.object({
995
+ items: z.array(itemSchema),
996
+ total: z.number(),
997
+ page: z.number(),
998
+ limit: z.number()
999
+ }).passthrough();
1000
+ return parseWithSchema(envelope, raw, `${label} page`);
1001
+ }
1002
+ function parseArrayOrItemsWithSchema(itemSchema, raw, label) {
1003
+ if (Array.isArray(raw)) {
1004
+ return parseWithSchema(z.array(itemSchema), raw, label);
1005
+ }
1006
+ if (raw !== null && typeof raw === "object" && "items" in raw) {
1007
+ const items = raw.items;
1008
+ return parseWithSchema(z.array(itemSchema), items, label);
1009
+ }
1010
+ return err({
1011
+ kind: "upstream",
1012
+ detail: `expected array (or {items:[]}) of ${label}`
1013
+ });
1014
+ }
1015
+
1016
+ // src/infrastructure/api/parsers/parse-alert.ts
1017
+ var AlertSchema = schemas.AlertResponse.pick({
1018
+ id: true,
1019
+ scan_id: true,
1020
+ campaign_id: true,
1021
+ policy_set_id: true,
1022
+ violation_rule_id: true,
1023
+ tag_slug: true,
1024
+ tag_display_name: true,
1025
+ country_code: true,
1026
+ status: true,
1027
+ closed_by: true,
1028
+ scan_url: true,
1029
+ offer_url: true,
1030
+ created_at: true,
1031
+ updated_at: true
1032
+ }).strip();
1033
+ var parseAlertPage = (raw) => parsePagedWithItemSchema(AlertSchema, raw, "alerts");
1034
+ var ApiKeySchema = schemas.ApiKeyResponse.pick({
1035
+ id: true,
1036
+ key_prefix: true,
1037
+ name: true,
1038
+ expires_at: true,
1039
+ created_at: true
1040
+ }).strip();
1041
+ var ApiKeyListSchema = z.array(ApiKeySchema);
1042
+ var parseApiKeyList = (raw) => parseWithSchema(ApiKeyListSchema, raw, "api-keys");
1043
+
1044
+ // src/infrastructure/api/parsers/parse-billing-summary.ts
1045
+ var BillingSummarySchema = schemas.BillingSummaryResponse.pick({
1046
+ balance_micros: true,
1047
+ plan_id: true,
1048
+ plan_name: true,
1049
+ checks_per_period: true,
1050
+ checks_used: true,
1051
+ period_start: true,
1052
+ period_end: true,
1053
+ price_per_extra_check_micros: true,
1054
+ is_suspended: true,
1055
+ can_create_scan: true,
1056
+ billing_mode: true,
1057
+ block_reason: true
1058
+ }).strip();
1059
+ var parseBillingSummary = (raw) => parseWithSchema(BillingSummarySchema, raw, "billing-summary");
1060
+
1061
+ // src/infrastructure/api/parsers/parse-campaign.ts
1062
+ var CampaignSchema = schemas.CampaignResponse.pick({
1063
+ id: true,
1064
+ name: true,
1065
+ campaign_type: true,
1066
+ url: true,
1067
+ ad_tag: true,
1068
+ country_codes: true,
1069
+ group_id: true,
1070
+ labels: true,
1071
+ policy_set_id: true,
1072
+ schedule_enabled: true,
1073
+ schedule_type: true,
1074
+ is_archived: true,
1075
+ created_at: true,
1076
+ last_run_at: true
1077
+ }).strip();
1078
+ var parseCampaign = (raw) => parseWithSchema(CampaignSchema, raw, "campaign");
1079
+ var parseCampaignPage = (raw) => parsePagedWithItemSchema(CampaignSchema, raw, "campaigns");
1080
+
1081
+ // src/infrastructure/api/parsers/parse-campaign-group.ts
1082
+ var CampaignGroupSchema = schemas.CampaignGroupResponse.pick({
1083
+ id: true,
1084
+ name: true,
1085
+ is_default: true,
1086
+ is_archived: true,
1087
+ schedule_paused: true,
1088
+ campaign_count: true,
1089
+ created_at: true
1090
+ }).strip();
1091
+ var parseCampaignGroup = (raw) => parseWithSchema(CampaignGroupSchema, raw, "campaign-group");
1092
+ var parseCampaignGroupArray = (raw) => parseArrayOrItemsWithSchema(CampaignGroupSchema, raw, "campaign-groups");
1093
+ var CampaignPickerSchema = schemas.CampaignPickerItem.pick({
1094
+ id: true,
1095
+ name: true,
1096
+ group_id: true,
1097
+ is_archived: true
1098
+ }).strip();
1099
+ var CampaignPickerArraySchema = z.array(CampaignPickerSchema);
1100
+ var parseCampaignPickerArray = (raw) => parseWithSchema(CampaignPickerArraySchema, raw, "campaigns-picker");
1101
+
1102
+ // src/infrastructure/api/parsers/shared.ts
1103
+ function isStringRecord(v) {
1104
+ return v !== null && typeof v === "object" && !Array.isArray(v);
1105
+ }
1106
+
1107
+ // src/infrastructure/api/parsers/parse-count-envelope.ts
1108
+ function parseIntField(raw, fieldName) {
1109
+ if (!isStringRecord(raw)) {
1110
+ return err({ kind: "upstream", detail: `expected object with ${fieldName}` });
1111
+ }
1112
+ const value = raw[fieldName];
1113
+ if (typeof value !== "number" || !Number.isInteger(value)) {
1114
+ return err({ kind: "upstream", detail: `${fieldName} must be an integer` });
1115
+ }
1116
+ return ok({ [fieldName]: value });
1117
+ }
1118
+
1119
+ // src/infrastructure/api/parsers/parse-custom-rule.ts
1120
+ var CustomRuleSchema = schemas.CustomRuleResponse.pick({
1121
+ id: true,
1122
+ organization_id: true,
1123
+ name: true,
1124
+ tag_slug: true,
1125
+ rule_type: true,
1126
+ config: true,
1127
+ target: true,
1128
+ is_active: true,
1129
+ created_at: true
1130
+ }).strip();
1131
+ var parseCustomRule = (raw) => parseWithSchema(CustomRuleSchema, raw, "custom-rule");
1132
+
1133
+ // src/infrastructure/api/parsers/parse-custom-rule-page.ts
1134
+ var CustomRuleSchema2 = schemas.CustomRuleResponse.pick({
1135
+ id: true,
1136
+ organization_id: true,
1137
+ name: true,
1138
+ tag_slug: true,
1139
+ rule_type: true,
1140
+ config: true,
1141
+ target: true,
1142
+ is_active: true,
1143
+ created_at: true
1144
+ }).strip();
1145
+ var parseCustomRulePage = (raw) => parsePagedWithItemSchema(CustomRuleSchema2, raw, "custom-rules");
1146
+
1147
+ // src/infrastructure/api/parsers/parse-empty.ts
1148
+ function parseEmpty(_raw) {
1149
+ return ok(null);
1150
+ }
1151
+ var EmulatorSchema = schemas.EmulatorResponse.pick({
1152
+ id: true,
1153
+ display_name: true,
1154
+ category: true,
1155
+ browser: true
1156
+ }).strip();
1157
+ var EmulatorListSchema = z.array(EmulatorSchema);
1158
+ var parseEmulatorList = (raw) => parseWithSchema(EmulatorListSchema, raw, "emulators");
1159
+
1160
+ // src/infrastructure/api/parsers/parse-generic.ts
1161
+ function parsePageOf(parseItem) {
1162
+ return (raw) => {
1163
+ if (raw === null || typeof raw !== "object") {
1164
+ return err({ kind: "upstream", detail: "malformed page envelope" });
1165
+ }
1166
+ const r = raw;
1167
+ if (!Array.isArray(r.items) || typeof r.total !== "number" || typeof r.page !== "number" || typeof r.limit !== "number") {
1168
+ return err({ kind: "upstream", detail: "page envelope: wrong field types" });
1169
+ }
1170
+ const out = [];
1171
+ for (const item of r.items) {
1172
+ const parsed = parseItem(item);
1173
+ if (parsed.isErr()) return err(parsed.error);
1174
+ out.push(parsed.value);
1175
+ }
1176
+ return ok({ items: out, total: r.total, page: r.page, limit: r.limit });
1177
+ };
1178
+ }
1179
+ function parseArrayOf(parseItem) {
1180
+ return (raw) => {
1181
+ if (!Array.isArray(raw)) return err({ kind: "upstream", detail: "expected array" });
1182
+ const out = [];
1183
+ for (const item of raw) {
1184
+ const r = parseItem(item);
1185
+ if (r.isErr()) return err(r.error);
1186
+ out.push(r.value);
1187
+ }
1188
+ return ok(out);
1189
+ };
1190
+ }
1191
+ var OrgSchema = schemas.OrgResponse.pick({
1192
+ id: true,
1193
+ name: true,
1194
+ owner_id: true,
1195
+ is_active: true,
1196
+ created_at: true
1197
+ }).strip();
1198
+ var parseOrg = (raw) => parseWithSchema(OrgSchema, raw, "org");
1199
+ var UserSchema = schemas.UserResponse.pick({
1200
+ id: true,
1201
+ email: true,
1202
+ name: true,
1203
+ role_name: true,
1204
+ is_active: true,
1205
+ created_at: true
1206
+ }).strip();
1207
+ var parseUser = (raw) => parseWithSchema(UserSchema, raw, "user");
1208
+ var RoleSchema = schemas.RoleResponse.pick({
1209
+ id: true,
1210
+ name: true,
1211
+ scope: true,
1212
+ is_system: true,
1213
+ permissions: true
1214
+ }).strip();
1215
+ var parseRole = (raw) => parseWithSchema(RoleSchema, raw, "role");
1216
+ var ApiKeyCreatedSchema = schemas.ApiKeyCreatedResponse.pick({
1217
+ id: true,
1218
+ key_prefix: true,
1219
+ full_key: true,
1220
+ name: true,
1221
+ expires_at: true,
1222
+ created_at: true
1223
+ }).strip();
1224
+ var parseApiKeyCreated = (raw) => parseWithSchema(ApiKeyCreatedSchema, raw, "api-key-created");
1225
+ var ScanTagSchema = schemas.ScanTagResponse.pick({
1226
+ id: true,
1227
+ scan_id: true,
1228
+ tag_slug: true,
1229
+ detail: true,
1230
+ url: true,
1231
+ display_name: true,
1232
+ category: true,
1233
+ severity: true,
1234
+ created_at: true
1235
+ }).strip();
1236
+ var parseScanTag = (raw) => parseWithSchema(ScanTagSchema, raw, "scan-tag");
1237
+ var RuleTestSchema = schemas.RuleTestResponse.pick({
1238
+ matched: true,
1239
+ elapsed_ms: true,
1240
+ tags: true
1241
+ }).strip();
1242
+ var parseRuleTest = (raw) => parseWithSchema(RuleTestSchema, raw, "rule-test");
1243
+ var AlertStatsSchema = schemas.AlertStatsResponse.pick({
1244
+ open: true,
1245
+ acknowledged: true,
1246
+ resolved: true,
1247
+ dismissed: true
1248
+ }).strip();
1249
+ var parseAlertStats = (raw) => parseWithSchema(AlertStatsSchema, raw, "alert-stats");
1250
+ var UsageSchema = schemas.UsageResponse.pick({
1251
+ id: true,
1252
+ scan_id: true,
1253
+ charged_micros: true,
1254
+ balance_after_micros: true,
1255
+ within_plan: true,
1256
+ event_type: true,
1257
+ created_at: true
1258
+ }).strip();
1259
+ var parseUsage = (raw) => parseWithSchema(UsageSchema, raw, "usage");
1260
+ var UsageSummarySchema = schemas.UsagePeriodSummaryResponse.pick({
1261
+ period_start: true,
1262
+ period_end: true,
1263
+ checks: true,
1264
+ rechecks: true,
1265
+ within_plan: true,
1266
+ overage: true,
1267
+ charged_micros: true
1268
+ }).strip();
1269
+ var parseUsageSummary = (raw) => parseWithSchema(UsageSummarySchema, raw, "usage-summary");
1270
+ var BalanceTxSchema = schemas.BalanceTransactionResponse.pick({
1271
+ id: true,
1272
+ type: true,
1273
+ amount_micros: true,
1274
+ balance_after_micros: true,
1275
+ description: true,
1276
+ reference_kind: true,
1277
+ reference_id: true,
1278
+ actor_user_id: true,
1279
+ created_at: true
1280
+ }).strip();
1281
+ var parseBalanceTx = (raw) => parseWithSchema(BalanceTxSchema, raw, "balance-tx");
1282
+ var InvoiceSchema = schemas.InvoiceResponse.pick({
1283
+ id: true,
1284
+ number: true,
1285
+ type: true,
1286
+ status: true,
1287
+ total_micros: true,
1288
+ currency: true,
1289
+ period_start: true,
1290
+ period_end: true,
1291
+ issued_at: true,
1292
+ paid_at: true,
1293
+ voided_at: true,
1294
+ has_pdf: true,
1295
+ description: true,
1296
+ payment_method: true,
1297
+ created_at: true
1298
+ }).strip();
1299
+ var parseInvoice = (raw) => parseWithSchema(InvoiceSchema, raw, "invoice");
1300
+ var WebhookDeliverySchema = schemas.DeliveryAttemptResponse.pick({
1301
+ id: true,
1302
+ event_id: true,
1303
+ event_type: true,
1304
+ response_status: true,
1305
+ success: true,
1306
+ attempt_number: true,
1307
+ error_code: true,
1308
+ elapsed_ms: true,
1309
+ created_at: true
1310
+ }).strip();
1311
+ var parseWebhookDelivery = (raw) => parseWithSchema(WebhookDeliverySchema, raw, "webhook-delivery");
1312
+ var EventCatalogSchema = schemas.EventCatalogResponse.pick({
1313
+ entries: true
1314
+ }).strip();
1315
+ var parseEventCatalog = (raw) => parseWithSchema(EventCatalogSchema, raw, "event-catalog");
1316
+ var AlertDestinationSchema = schemas.AlertNotificationDestinationResponse.pick({
1317
+ id: true,
1318
+ channel: true,
1319
+ name: true,
1320
+ is_active: true,
1321
+ is_default_target: true,
1322
+ version: true,
1323
+ consecutive_failures: true,
1324
+ last_delivery_at: true,
1325
+ last_delivery_status: true,
1326
+ slack_workspace_id: true,
1327
+ slack_channel_name: true,
1328
+ telegram_chat_title: true,
1329
+ telegram_chat_type: true,
1330
+ email_address: true,
1331
+ included_label_keys: true,
1332
+ created_at: true,
1333
+ updated_at: true
1334
+ }).strip();
1335
+ var parseAlertDestination = (raw) => parseWithSchema(AlertDestinationSchema, raw, "alert-destination");
1336
+ var CampaignOverridesSchema = schemas.CampaignOverridesResponse.pick({
1337
+ campaign_id: true,
1338
+ mode: true,
1339
+ destination_ids: true
1340
+ }).strip();
1341
+ var parseCampaignAlertOverrides = (raw) => parseWithSchema(CampaignOverridesSchema, raw, "campaign-overrides");
1342
+ var BulkReplaySchema = schemas.BulkReplayResponse.pick({
1343
+ replayed: true,
1344
+ skipped: true
1345
+ }).strip();
1346
+ var parseBulkReplay = (raw) => parseWithSchema(BulkReplaySchema, raw, "bulk-replay");
1347
+ var GroupActionSchema = schemas.GroupActionResponse.pick({
1348
+ group_id: true,
1349
+ affected_campaigns: true,
1350
+ cancelled_count: true,
1351
+ run_ids: true,
1352
+ failures: true
1353
+ }).strip();
1354
+ var parseGroupAction = (raw) => parseWithSchema(GroupActionSchema, raw, "group-action");
1355
+ schemas.PolicyEntryResponse.pick({
1356
+ id: true,
1357
+ tag_slug: true,
1358
+ country_codes: true
1359
+ }).strip();
1360
+ var GeoSchema = schemas.GeoResponse.pick({
1361
+ country_code: true,
1362
+ name: true,
1363
+ region: true,
1364
+ tier: true
1365
+ }).strip();
1366
+ var GeoListSchema = z.array(GeoSchema);
1367
+ var parseGeoList = (raw) => parseWithSchema(GeoListSchema, raw, "geos");
1368
+
1369
+ // src/infrastructure/api/parsers/parse-policy-set.ts
1370
+ var PolicySetSchema = schemas.PolicySetResponse.pick({
1371
+ id: true,
1372
+ name: true,
1373
+ description: true,
1374
+ organization_id: true,
1375
+ visibility: true,
1376
+ is_approved: true,
1377
+ entries: true,
1378
+ created_at: true
1379
+ }).strip();
1380
+ var parsePolicySet = (raw) => parseWithSchema(PolicySetSchema, raw, "policy-set");
1381
+
1382
+ // src/infrastructure/api/parsers/parse-policy-set-page.ts
1383
+ var PolicySetListItemSchema = schemas.PolicySetListItem.pick({
1384
+ id: true,
1385
+ name: true,
1386
+ description: true,
1387
+ organization_id: true,
1388
+ visibility: true,
1389
+ is_approved: true,
1390
+ created_at: true
1391
+ }).strip();
1392
+ var parsePolicySetPage = (raw) => parsePagedWithItemSchema(PolicySetListItemSchema, raw, "policy-sets");
1393
+
1394
+ // src/infrastructure/api/parsers/parse-run.ts
1395
+ var RunSchema = schemas.RunResponse.pick({
1396
+ id: true,
1397
+ campaign_id: true,
1398
+ label: true,
1399
+ total: true,
1400
+ completed: true,
1401
+ failed: true,
1402
+ partial: true,
1403
+ cancelled: true,
1404
+ source: true,
1405
+ created_at: true
1406
+ }).strip();
1407
+ var parseRun = (raw) => parseWithSchema(RunSchema, raw, "run");
1408
+
1409
+ // src/infrastructure/api/parsers/parse-run-scan-page.ts
1410
+ var ScanTileSchema = schemas.ScanTileResponse.pick({
1411
+ id: true,
1412
+ country_code: true,
1413
+ status: true,
1414
+ offer_url: true,
1415
+ screenshot_url: true,
1416
+ elapsed_ms: true,
1417
+ error: true
1418
+ }).strip();
1419
+ var parseRunScanPage = (raw) => parsePagedWithItemSchema(ScanTileSchema, raw, "run-scans");
1420
+ var ScanSchema = schemas.ScanResponse.pick({
1421
+ id: true,
1422
+ url: true,
1423
+ country_code: true,
1424
+ emulator_id: true,
1425
+ status: true,
1426
+ offer_url: true,
1427
+ screenshot_url: true,
1428
+ ad_tag: true,
1429
+ creative_screenshot_url: true,
1430
+ page_title: true,
1431
+ elapsed_ms: true,
1432
+ error: true,
1433
+ labels: true,
1434
+ campaign_id: true,
1435
+ campaign_name: true,
1436
+ created_at: true,
1437
+ completed_at: true
1438
+ }).strip();
1439
+ var ScanArraySchema = z.array(ScanSchema);
1440
+ var parseScan = (raw) => parseWithSchema(ScanSchema, raw, "scan");
1441
+ var parseScanArray = (raw) => parseWithSchema(ScanArraySchema, raw, "scans");
1442
+
1443
+ // src/infrastructure/api/parsers/parse-scan-page.ts
1444
+ var ScanBriefSchema = schemas.ScanBriefResponse.pick({
1445
+ id: true,
1446
+ url: true,
1447
+ country_code: true,
1448
+ status: true,
1449
+ offer_url: true,
1450
+ screenshot_url: true,
1451
+ labels: true,
1452
+ elapsed_ms: true,
1453
+ campaign_id: true,
1454
+ campaign_name: true,
1455
+ is_ad_tag: true,
1456
+ created_at: true
1457
+ }).strip();
1458
+ var parseScanPage = (raw) => parsePagedWithItemSchema(ScanBriefSchema, raw, "scans");
1459
+ var TagDefinitionSchema = schemas.TagDefinitionWithStatsResponse.pick({
1460
+ slug: true,
1461
+ category: true,
1462
+ source: true,
1463
+ display_name: true,
1464
+ description: true,
1465
+ severity: true,
1466
+ is_system: true,
1467
+ organization_id: true,
1468
+ show_in_public_report: true,
1469
+ scans_count: true,
1470
+ rules_count: true
1471
+ }).strip();
1472
+ var TagDefinitionArraySchema = z.array(TagDefinitionSchema);
1473
+ var LinkedRuleSchema = schemas.LinkedRuleResponse.pick({
1474
+ id: true,
1475
+ name: true,
1476
+ is_active: true
1477
+ }).strip();
1478
+ var TagDetailSchema = schemas.TagDefinitionDetailResponse.pick({
1479
+ slug: true,
1480
+ category: true,
1481
+ source: true,
1482
+ display_name: true,
1483
+ description: true,
1484
+ severity: true,
1485
+ is_system: true,
1486
+ organization_id: true,
1487
+ show_in_public_report: true,
1488
+ scans_count: true,
1489
+ rules_count: true
1490
+ }).extend({ linked_rules: z.array(LinkedRuleSchema).optional() }).strip();
1491
+ var parseTagDefinitionArray = (raw) => parseWithSchema(TagDefinitionArraySchema, raw, "tag-definitions");
1492
+ var parseTagDetail = (raw) => parseWithSchema(TagDetailSchema, raw, "tag-definition");
1493
+ var WebhookSchema = schemas.WebhookResponse.pick({
1494
+ id: true,
1495
+ url: true,
1496
+ description: true,
1497
+ event_types: true,
1498
+ campaign_ids: true,
1499
+ is_active: true,
1500
+ disabled_reason: true,
1501
+ disabled_at: true,
1502
+ health: true,
1503
+ created_at: true,
1504
+ updated_at: true
1505
+ }).strip();
1506
+ var WebhookListSchema = z.array(WebhookSchema);
1507
+ var WebhookCreatedSchema = z.object({
1508
+ webhook: WebhookSchema,
1509
+ secret: z.string()
1510
+ }).strip();
1511
+ var TestWebhookResponseSchema = schemas.TestWebhookResponse.pick({
1512
+ success: true,
1513
+ response_status: true,
1514
+ elapsed_ms: true,
1515
+ error_code: true,
1516
+ response_body: true
1517
+ }).strip();
1518
+ var parseWebhook = (raw) => parseWithSchema(WebhookSchema, raw, "webhook");
1519
+ var parseWebhookList = (raw) => parseWithSchema(WebhookListSchema, raw, "webhooks");
1520
+ var parseWebhookCreated = (raw) => parseWithSchema(WebhookCreatedSchema, raw, "webhook-created");
1521
+ var parseTestWebhookResponse = (raw) => parseWithSchema(TestWebhookResponseSchema, raw, "test-webhook");
1522
+
1523
+ // src/infrastructure/api/http-api-gateway.ts
1524
+ function createHttpApiGateway(config) {
1525
+ const { baseUrl, bearer, requestId, logger, dispatcher } = config;
1526
+ const fetchImpl = async (input, init) => {
1527
+ if (typeof input === "string" || input instanceof URL) {
1528
+ throw new TypeError(
1529
+ "createHttpApiGateway.fetchImpl: openapi-fetch is expected to always pass a Request"
1530
+ );
1531
+ }
1532
+ const baseInit = { ...init ?? {} };
1533
+ baseInit["method"] ??= input.method;
1534
+ if (baseInit["headers"] === void 0) {
1535
+ const merged = {};
1536
+ input.headers.forEach((value, key) => {
1537
+ merged[key] = value;
1538
+ });
1539
+ baseInit["headers"] = merged;
1540
+ }
1541
+ if (baseInit["body"] === void 0 && input.body !== null) {
1542
+ baseInit["body"] = await input.text();
1543
+ baseInit["duplex"] ??= "half";
1544
+ }
1545
+ if (dispatcher !== void 0) {
1546
+ baseInit["dispatcher"] = dispatcher;
1547
+ }
1548
+ return fetch(
1549
+ input.url,
1550
+ baseInit
1551
+ );
1552
+ };
1553
+ const client = createClient({
1554
+ baseUrl,
1555
+ fetch: fetchImpl,
1556
+ headers: {
1557
+ authorization: bearer.toAuthorizationHeader(),
1558
+ "content-type": "application/json",
1559
+ accept: "application/json",
1560
+ "user-agent": "kaminari-ad-mcp",
1561
+ "x-request-id": requestId
1562
+ }
1563
+ });
1564
+ const dispatch = {
1565
+ GET: client.GET.bind(client),
1566
+ POST: client.POST.bind(client),
1567
+ PATCH: client.PATCH.bind(client),
1568
+ PUT: client.PUT.bind(client),
1569
+ DELETE: client.DELETE.bind(client)
1570
+ };
1571
+ async function call(method, path, init, parse) {
1572
+ const startedAtMs = Date.now();
1573
+ let result;
1574
+ try {
1575
+ result = await dispatch[method](path, init);
1576
+ } catch (cause) {
1577
+ logger.warn({ api_path: path, elapsed_ms: Date.now() - startedAtMs }, "api.network_error");
1578
+ const inner = cause instanceof Error && cause.cause instanceof Error ? cause.cause : cause instanceof Error ? cause : null;
1579
+ return err({
1580
+ kind: "upstream",
1581
+ detail: inner !== null ? inner.message : "network error"
1582
+ });
1583
+ }
1584
+ const { data, error, response } = result;
1585
+ const status2 = response.status;
1586
+ logger.info(
1587
+ { api_path: path, api_status: status2, elapsed_ms: Date.now() - startedAtMs },
1588
+ "api.done"
1589
+ );
1590
+ if (status2 >= 200 && status2 < 300) {
1591
+ return parse(data === void 0 ? null : data);
1592
+ }
1593
+ return err(toApiError(status2, error ?? data, response.headers.get("retry-after") ?? void 0));
1594
+ }
1595
+ return {
1596
+ // ── Account ───────────────────────────────────────────────────
1597
+ async getAccount() {
1598
+ return call("GET", "/api/v1/account", {}, parseOrg);
1599
+ },
1600
+ async updateOrg(body) {
1601
+ return call("PATCH", "/api/v1/account", { body }, parseOrg);
1602
+ },
1603
+ async listOrgUsers() {
1604
+ return call("GET", "/api/v1/account/users", {}, parseArrayOf(parseUser));
1605
+ },
1606
+ async inviteUser(body) {
1607
+ return call("POST", "/api/v1/account/users/invite", { body }, parseUser);
1608
+ },
1609
+ async updateUserRole(userId, body) {
1610
+ return call(
1611
+ "PATCH",
1612
+ "/api/v1/account/users/{user_id}/role",
1613
+ { params: { path: { user_id: userId } }, body },
1614
+ parseEmpty
1615
+ );
1616
+ },
1617
+ async removeUser(userId) {
1618
+ return call(
1619
+ "DELETE",
1620
+ "/api/v1/account/users/{user_id}",
1621
+ { params: { path: { user_id: userId } } },
1622
+ parseEmpty
1623
+ );
1624
+ },
1625
+ async transferOwnership(userId) {
1626
+ return call(
1627
+ "POST",
1628
+ "/api/v1/account/users/{user_id}/transfer-ownership",
1629
+ { params: { path: { user_id: userId } } },
1630
+ parseEmpty
1631
+ );
1632
+ },
1633
+ async listOrgRoles() {
1634
+ return call("GET", "/api/v1/account/roles", {}, parseArrayOf(parseRole));
1635
+ },
1636
+ async listApiKeys() {
1637
+ return call("GET", "/api/v1/account/api-keys", {}, parseApiKeyList);
1638
+ },
1639
+ async createApiKey(body) {
1640
+ return call("POST", "/api/v1/account/api-keys", { body }, parseApiKeyCreated);
1641
+ },
1642
+ async revokeApiKey(keyId) {
1643
+ return call(
1644
+ "DELETE",
1645
+ "/api/v1/account/api-keys/{key_id}",
1646
+ { params: { path: { key_id: keyId } } },
1647
+ parseEmpty
1648
+ );
1649
+ },
1650
+ // ── Scans ─────────────────────────────────────────────────────
1651
+ async listScans(filters) {
1652
+ return call("GET", "/api/v1/scans", { params: { query: filters } }, parseScanPage);
1653
+ },
1654
+ async getScan(scanId) {
1655
+ return call(
1656
+ "GET",
1657
+ "/api/v1/scans/{scan_id}",
1658
+ { params: { path: { scan_id: scanId } } },
1659
+ parseScan
1660
+ );
1661
+ },
1662
+ async createScan(body) {
1663
+ return call("POST", "/api/v1/scans", { body }, parseScan);
1664
+ },
1665
+ async createBulkScans(body) {
1666
+ return call("POST", "/api/v1/scans/bulk", { body }, parseScanArray);
1667
+ },
1668
+ async recheckScans(body) {
1669
+ return call(
1670
+ "POST",
1671
+ "/api/v1/scans/recheck",
1672
+ { body },
1673
+ (raw) => parseIntField(raw, "queued_count")
1674
+ );
1675
+ },
1676
+ async cancelScan(scanId) {
1677
+ return call(
1678
+ "POST",
1679
+ "/api/v1/scans/{scan_id}/cancel",
1680
+ { params: { path: { scan_id: scanId } } },
1681
+ (raw) => parseIntField(raw, "cancelled_count")
1682
+ );
1683
+ },
1684
+ async listScanTags(scanId) {
1685
+ return call(
1686
+ "GET",
1687
+ "/api/v1/scans/{scan_id}/tags",
1688
+ { params: { path: { scan_id: scanId } } },
1689
+ parseArrayOf(parseScanTag)
1690
+ );
1691
+ },
1692
+ // ── Geos / emulators ──────────────────────────────────────────
1693
+ async listGeos() {
1694
+ return call("GET", "/api/v1/geos", {}, parseGeoList);
1695
+ },
1696
+ async listEmulators() {
1697
+ return call("GET", "/api/v1/emulators", {}, parseEmulatorList);
1698
+ },
1699
+ // ── Campaigns ─────────────────────────────────────────────────
1700
+ async listCampaigns(filters) {
1701
+ return call("GET", "/api/v1/campaigns", { params: { query: filters } }, parseCampaignPage);
1702
+ },
1703
+ async getCampaign(id) {
1704
+ return call(
1705
+ "GET",
1706
+ "/api/v1/campaigns/{campaign_id}",
1707
+ { params: { path: { campaign_id: id } } },
1708
+ parseCampaign
1709
+ );
1710
+ },
1711
+ async createCampaign(body) {
1712
+ return call("POST", "/api/v1/campaigns", { body }, parseCampaign);
1713
+ },
1714
+ async updateCampaign(id, body) {
1715
+ return call(
1716
+ "PATCH",
1717
+ "/api/v1/campaigns/{campaign_id}",
1718
+ { params: { path: { campaign_id: id } }, body },
1719
+ parseCampaign
1720
+ );
1721
+ },
1722
+ async runCampaign(id) {
1723
+ return call(
1724
+ "POST",
1725
+ "/api/v1/campaigns/{campaign_id}/run",
1726
+ { params: { path: { campaign_id: id } } },
1727
+ parseRun
1728
+ );
1729
+ },
1730
+ async archiveCampaign(id) {
1731
+ return call(
1732
+ "POST",
1733
+ "/api/v1/campaigns/{campaign_id}/archive",
1734
+ { params: { path: { campaign_id: id } } },
1735
+ parseCampaign
1736
+ );
1737
+ },
1738
+ async unarchiveCampaign(id) {
1739
+ return call(
1740
+ "POST",
1741
+ "/api/v1/campaigns/{campaign_id}/unarchive",
1742
+ { params: { path: { campaign_id: id } } },
1743
+ parseCampaign
1744
+ );
1745
+ },
1746
+ async cancelCampaign(id) {
1747
+ return call(
1748
+ "POST",
1749
+ "/api/v1/campaigns/{campaign_id}/cancel",
1750
+ { params: { path: { campaign_id: id } } },
1751
+ (raw) => parseIntField(raw, "cancelled_count")
1752
+ );
1753
+ },
1754
+ async listCampaignRuns(campaignId, filters) {
1755
+ return call(
1756
+ "GET",
1757
+ "/api/v1/campaigns/{campaign_id}/runs",
1758
+ { params: { path: { campaign_id: campaignId }, query: filters } },
1759
+ parsePageOf(parseRun)
1760
+ );
1761
+ },
1762
+ async listCampaignsPicker() {
1763
+ return call("GET", "/api/v1/campaigns/picker", {}, parseCampaignPickerArray);
1764
+ },
1765
+ // ── Runs ──────────────────────────────────────────────────────
1766
+ async getRun(id) {
1767
+ return call("GET", "/api/v1/runs/{run_id}", { params: { path: { run_id: id } } }, parseRun);
1768
+ },
1769
+ async cancelRun(id) {
1770
+ return call(
1771
+ "POST",
1772
+ "/api/v1/runs/{run_id}/cancel",
1773
+ { params: { path: { run_id: id } } },
1774
+ (raw) => parseIntField(raw, "cancelled_count")
1775
+ );
1776
+ },
1777
+ async listRunScans(runId, filters) {
1778
+ return call(
1779
+ "GET",
1780
+ "/api/v1/runs/{run_id}/scans",
1781
+ { params: { path: { run_id: runId }, query: filters } },
1782
+ parseRunScanPage
1783
+ );
1784
+ },
1785
+ // ── Campaign groups ───────────────────────────────────────────
1786
+ async listCampaignGroups(filters) {
1787
+ return call(
1788
+ "GET",
1789
+ "/api/v1/campaign-groups",
1790
+ { params: { query: filters ?? {} } },
1791
+ parseCampaignGroupArray
1792
+ );
1793
+ },
1794
+ async getCampaignGroup(id) {
1795
+ return call(
1796
+ "GET",
1797
+ "/api/v1/campaign-groups/{group_id}",
1798
+ { params: { path: { group_id: id } } },
1799
+ parseCampaignGroup
1800
+ );
1801
+ },
1802
+ async createCampaignGroup(body) {
1803
+ return call("POST", "/api/v1/campaign-groups", { body }, parseCampaignGroup);
1804
+ },
1805
+ async updateCampaignGroup(id, body) {
1806
+ return call(
1807
+ "PATCH",
1808
+ "/api/v1/campaign-groups/{group_id}",
1809
+ { params: { path: { group_id: id } }, body },
1810
+ parseCampaignGroup
1811
+ );
1812
+ },
1813
+ async runCampaignGroup(id) {
1814
+ return call(
1815
+ "POST",
1816
+ "/api/v1/campaign-groups/{group_id}/run",
1817
+ { params: { path: { group_id: id } } },
1818
+ parseGroupAction
1819
+ );
1820
+ },
1821
+ async cancelCampaignGroup(id) {
1822
+ return call(
1823
+ "POST",
1824
+ "/api/v1/campaign-groups/{group_id}/cancel",
1825
+ { params: { path: { group_id: id } } },
1826
+ parseGroupAction
1827
+ );
1828
+ },
1829
+ async archiveCampaignGroup(id) {
1830
+ return call(
1831
+ "POST",
1832
+ "/api/v1/campaign-groups/{group_id}/archive",
1833
+ { params: { path: { group_id: id } } },
1834
+ parseGroupAction
1835
+ );
1836
+ },
1837
+ async unarchiveCampaignGroup(id) {
1838
+ return call(
1839
+ "POST",
1840
+ "/api/v1/campaign-groups/{group_id}/unarchive",
1841
+ { params: { path: { group_id: id } } },
1842
+ parseGroupAction
1843
+ );
1844
+ },
1845
+ async pauseCampaignGroupSchedule(id) {
1846
+ return call(
1847
+ "POST",
1848
+ "/api/v1/campaign-groups/{group_id}/pause-schedule",
1849
+ { params: { path: { group_id: id } } },
1850
+ parseCampaignGroup
1851
+ );
1852
+ },
1853
+ async resumeCampaignGroupSchedule(id) {
1854
+ return call(
1855
+ "POST",
1856
+ "/api/v1/campaign-groups/{group_id}/resume-schedule",
1857
+ { params: { path: { group_id: id } } },
1858
+ parseCampaignGroup
1859
+ );
1860
+ },
1861
+ // ── Tag definitions ───────────────────────────────────────────
1862
+ async listTags() {
1863
+ return call("GET", "/api/v1/tag-definitions", {}, parseTagDefinitionArray);
1864
+ },
1865
+ async getTagDefinition(slug) {
1866
+ return call(
1867
+ "GET",
1868
+ "/api/v1/tag-definitions/{slug}",
1869
+ { params: { path: { slug } } },
1870
+ parseTagDetail
1871
+ );
1872
+ },
1873
+ async updateTagDefinition(slug, body) {
1874
+ return call(
1875
+ "PATCH",
1876
+ "/api/v1/tag-definitions/{slug}",
1877
+ { params: { path: { slug } }, body },
1878
+ parseEmpty
1879
+ );
1880
+ },
1881
+ async deleteTagDefinition(slug) {
1882
+ return call(
1883
+ "DELETE",
1884
+ "/api/v1/tag-definitions/{slug}",
1885
+ { params: { path: { slug } } },
1886
+ parseEmpty
1887
+ );
1888
+ },
1889
+ // ── Custom rules ──────────────────────────────────────────────
1890
+ async listCustomRules(filters) {
1891
+ return call(
1892
+ "GET",
1893
+ "/api/v1/custom-rules",
1894
+ { params: { query: filters } },
1895
+ parseCustomRulePage
1896
+ );
1897
+ },
1898
+ async getCustomRule(id) {
1899
+ return call(
1900
+ "GET",
1901
+ "/api/v1/custom-rules/{rule_id}",
1902
+ { params: { path: { rule_id: id } } },
1903
+ parseCustomRule
1904
+ );
1905
+ },
1906
+ async createCustomRule(body) {
1907
+ return call("POST", "/api/v1/custom-rules", { body }, parseCustomRule);
1908
+ },
1909
+ async updateCustomRule(id, body) {
1910
+ return call(
1911
+ "PUT",
1912
+ "/api/v1/custom-rules/{rule_id}",
1913
+ { params: { path: { rule_id: id } }, body },
1914
+ parseCustomRule
1915
+ );
1916
+ },
1917
+ async deleteCustomRule(id) {
1918
+ return call(
1919
+ "DELETE",
1920
+ "/api/v1/custom-rules/{rule_id}",
1921
+ { params: { path: { rule_id: id } } },
1922
+ parseEmpty
1923
+ );
1924
+ },
1925
+ async testCustomRule(body) {
1926
+ return call("POST", "/api/v1/custom-rules/test", { body }, parseRuleTest);
1927
+ },
1928
+ // ── Policy sets ───────────────────────────────────────────────
1929
+ async listPolicySets(filters) {
1930
+ return call("GET", "/api/v1/policy-sets", { params: { query: filters } }, parsePolicySetPage);
1931
+ },
1932
+ async getPolicySet(id) {
1933
+ return call(
1934
+ "GET",
1935
+ "/api/v1/policy-sets/{policy_set_id}",
1936
+ { params: { path: { policy_set_id: id } } },
1937
+ parsePolicySet
1938
+ );
1939
+ },
1940
+ async createPolicySet(body) {
1941
+ return call("POST", "/api/v1/policy-sets", { body }, parsePolicySet);
1942
+ },
1943
+ async updatePolicySet(id, body) {
1944
+ return call(
1945
+ "PUT",
1946
+ "/api/v1/policy-sets/{policy_set_id}",
1947
+ { params: { path: { policy_set_id: id } }, body },
1948
+ parsePolicySet
1949
+ );
1950
+ },
1951
+ async deletePolicySet(id) {
1952
+ return call(
1953
+ "DELETE",
1954
+ "/api/v1/policy-sets/{policy_set_id}",
1955
+ { params: { path: { policy_set_id: id } } },
1956
+ parseEmpty
1957
+ );
1958
+ },
1959
+ async requestPolicySetApproval(id) {
1960
+ return call(
1961
+ "POST",
1962
+ "/api/v1/policy-sets/{policy_set_id}/request-approval",
1963
+ { params: { path: { policy_set_id: id } } },
1964
+ parseEmpty
1965
+ );
1966
+ },
1967
+ // ── Alerts ────────────────────────────────────────────────────
1968
+ async listAlerts(filters) {
1969
+ return call("GET", "/api/v1/alerts", { params: { query: filters } }, parseAlertPage);
1970
+ },
1971
+ async updateAlertStatus(alertId, body) {
1972
+ return call(
1973
+ "PATCH",
1974
+ "/api/v1/alerts/{alert_id}/status",
1975
+ { params: { path: { alert_id: alertId } }, body },
1976
+ parseEmpty
1977
+ );
1978
+ },
1979
+ async getAlertStats() {
1980
+ return call("GET", "/api/v1/alerts/stats", {}, parseAlertStats);
1981
+ },
1982
+ // ── Webhooks ──────────────────────────────────────────────────
1983
+ async listWebhooks() {
1984
+ return call("GET", "/api/v1/webhooks", {}, parseWebhookList);
1985
+ },
1986
+ async getWebhook(id) {
1987
+ return call(
1988
+ "GET",
1989
+ "/api/v1/webhooks/{endpoint_id}",
1990
+ { params: { path: { endpoint_id: id } } },
1991
+ parseWebhook
1992
+ );
1993
+ },
1994
+ async createWebhook(body) {
1995
+ return call("POST", "/api/v1/webhooks", { body }, parseWebhookCreated);
1996
+ },
1997
+ async updateWebhook(id, body) {
1998
+ return call(
1999
+ "PATCH",
2000
+ "/api/v1/webhooks/{endpoint_id}",
2001
+ { params: { path: { endpoint_id: id } }, body },
2002
+ parseWebhook
2003
+ );
2004
+ },
2005
+ async deleteWebhook(id) {
2006
+ return call(
2007
+ "DELETE",
2008
+ "/api/v1/webhooks/{endpoint_id}",
2009
+ { params: { path: { endpoint_id: id } } },
2010
+ parseEmpty
2011
+ );
2012
+ },
2013
+ async testWebhook(endpointId, body) {
2014
+ return call(
2015
+ "POST",
2016
+ "/api/v1/webhooks/{endpoint_id}/test",
2017
+ { params: { path: { endpoint_id: endpointId } }, body },
2018
+ parseTestWebhookResponse
2019
+ );
2020
+ },
2021
+ async rotateWebhookSecret(endpointId) {
2022
+ return call(
2023
+ "POST",
2024
+ "/api/v1/webhooks/{endpoint_id}/rotate-secret",
2025
+ { params: { path: { endpoint_id: endpointId } } },
2026
+ parseWebhookCreated
2027
+ );
2028
+ },
2029
+ async listWebhookEventTypes() {
2030
+ return call("GET", "/api/v1/webhooks/event-types", {}, parseEventCatalog);
2031
+ },
2032
+ async listWebhookDeliveries(endpointId, filters) {
2033
+ return call(
2034
+ "GET",
2035
+ "/api/v1/webhooks/{endpoint_id}/deliveries",
2036
+ { params: { path: { endpoint_id: endpointId }, query: filters } },
2037
+ parsePageOf(parseWebhookDelivery)
2038
+ );
2039
+ },
2040
+ async replayWebhookDelivery(attemptId) {
2041
+ return call(
2042
+ "POST",
2043
+ "/api/v1/webhooks/deliveries/{attempt_id}/replay",
2044
+ { params: { path: { attempt_id: attemptId } } },
2045
+ parseEmpty
2046
+ );
2047
+ },
2048
+ async bulkReplayWebhook(endpointId, body) {
2049
+ return call(
2050
+ "POST",
2051
+ "/api/v1/webhooks/{endpoint_id}/replay",
2052
+ { params: { path: { endpoint_id: endpointId } }, body },
2053
+ parseBulkReplay
2054
+ );
2055
+ },
2056
+ // ── Billing ───────────────────────────────────────────────────
2057
+ async getBillingSummary() {
2058
+ return call("GET", "/api/v1/billing", {}, parseBillingSummary);
2059
+ },
2060
+ async listUsage(filters) {
2061
+ return call(
2062
+ "GET",
2063
+ "/api/v1/billing/usage",
2064
+ { params: { query: filters } },
2065
+ parsePageOf(parseUsage)
2066
+ );
2067
+ },
2068
+ async getUsageSummary() {
2069
+ return call("GET", "/api/v1/billing/usage/summary", {}, parseUsageSummary);
2070
+ },
2071
+ async listBalanceHistory(filters) {
2072
+ return call(
2073
+ "GET",
2074
+ "/api/v1/billing/history",
2075
+ { params: { query: filters } },
2076
+ parsePageOf(parseBalanceTx)
2077
+ );
2078
+ },
2079
+ // ── Invoicing ─────────────────────────────────────────────────
2080
+ async listInvoices(filters) {
2081
+ return call(
2082
+ "GET",
2083
+ "/api/v1/invoices",
2084
+ { params: { query: filters } },
2085
+ parsePageOf(parseInvoice)
2086
+ );
2087
+ },
2088
+ // ── Alert notifications ───────────────────────────────────────
2089
+ async listAlertDestinations() {
2090
+ return call(
2091
+ "GET",
2092
+ "/api/v1/alert-notifications/destinations",
2093
+ {},
2094
+ parseArrayOf(parseAlertDestination)
2095
+ );
2096
+ },
2097
+ async deleteAlertDestination(id) {
2098
+ return call(
2099
+ "DELETE",
2100
+ "/api/v1/alert-notifications/destinations/{destination_id}",
2101
+ { params: { path: { destination_id: id } } },
2102
+ parseEmpty
2103
+ );
2104
+ },
2105
+ async setAlertDestinationVersion(id, body) {
2106
+ return call(
2107
+ "PATCH",
2108
+ "/api/v1/alert-notifications/destinations/{destination_id}/version",
2109
+ { params: { path: { destination_id: id } }, body },
2110
+ parseEmpty
2111
+ );
2112
+ },
2113
+ async getCampaignAlertOverrides(campaignId) {
2114
+ return call(
2115
+ "GET",
2116
+ "/api/v1/alert-notifications/campaigns/{campaign_id}/overrides",
2117
+ { params: { path: { campaign_id: campaignId } } },
2118
+ parseCampaignAlertOverrides
2119
+ );
2120
+ },
2121
+ async setCampaignAlertOverrides(campaignId, body) {
2122
+ return call(
2123
+ "PUT",
2124
+ "/api/v1/alert-notifications/campaigns/{campaign_id}/overrides",
2125
+ { params: { path: { campaign_id: campaignId } }, body },
2126
+ parseEmpty
2127
+ );
2128
+ }
2129
+ };
2130
+ }
2131
+ var REDACTION_PATHS = [
2132
+ "authorization",
2133
+ "Authorization",
2134
+ "bearer",
2135
+ "Bearer",
2136
+ "*.authorization",
2137
+ "*.Authorization",
2138
+ "headers.authorization",
2139
+ "headers.Authorization",
2140
+ "req.headers.authorization",
2141
+ "req.headers.Authorization"
2142
+ ];
2143
+ function createPinoLogger(level, format = "json", destination) {
2144
+ const options = {
2145
+ level,
2146
+ redact: { paths: [...REDACTION_PATHS], censor: "[REDACTED]" },
2147
+ base: null,
2148
+ timestamp: stdTimeFunctions.isoTime,
2149
+ formatters: {
2150
+ level: (label) => ({ level: label })
2151
+ }
2152
+ };
2153
+ const sink = destination ?? (format === "pretty" ? pinoPretty({
2154
+ colorize: true,
2155
+ ignore: "pid,hostname",
2156
+ destination: process.stderr.fd,
2157
+ sync: true
2158
+ }) : pino.destination(process.stderr.fd));
2159
+ return wrap(pino(options, sink));
2160
+ }
2161
+ function wrap(impl) {
2162
+ return {
2163
+ child(fields) {
2164
+ return wrap(impl.child({ ...fields }));
2165
+ },
2166
+ trace(fields, message) {
2167
+ impl.trace({ ...fields }, message);
2168
+ },
2169
+ debug(fields, message) {
2170
+ impl.debug({ ...fields }, message);
2171
+ },
2172
+ info(fields, message) {
2173
+ impl.info({ ...fields }, message);
2174
+ },
2175
+ warn(fields, message) {
2176
+ impl.warn({ ...fields }, message);
2177
+ },
2178
+ error(fields, message) {
2179
+ impl.error({ ...fields }, message);
2180
+ },
2181
+ fatal(fields, message) {
2182
+ impl.fatal({ ...fields }, message);
2183
+ }
2184
+ };
2185
+ }
2186
+ function declareEmptyResourcesAndPrompts(server) {
2187
+ server.server.registerCapabilities({ resources: {}, prompts: {} });
2188
+ server.server.setRequestHandler(ListResourcesRequestSchema, async () => {
2189
+ await Promise.resolve();
2190
+ return { resources: [] };
2191
+ });
2192
+ server.server.setRequestHandler(ListPromptsRequestSchema, async () => {
2193
+ await Promise.resolve();
2194
+ return { prompts: [] };
2195
+ });
2196
+ }
2197
+
2198
+ // src/application/services/api-error-mapper.ts
2199
+ function mapApiError(apiError) {
2200
+ switch (apiError.kind) {
2201
+ case "unauthorized":
2202
+ return { kind: "unauthorized", message: apiError.detail };
2203
+ case "forbidden":
2204
+ return apiError.code === void 0 ? { kind: "forbidden", message: apiError.detail } : { kind: "forbidden", message: apiError.detail, code: apiError.code };
2205
+ case "not-found":
2206
+ return { kind: "not-found", message: apiError.detail };
2207
+ case "rate-limited":
2208
+ return apiError.retryAfterMs === void 0 ? { kind: "rate-limited", message: apiError.detail } : { kind: "rate-limited", message: apiError.detail, retryAfterMs: apiError.retryAfterMs };
2209
+ case "invalid-input": {
2210
+ const out = { kind: "invalid-input", message: apiError.detail };
2211
+ if (apiError.fieldErrors !== void 0) out.fieldErrors = apiError.fieldErrors;
2212
+ if (apiError.code !== void 0) out.code = apiError.code;
2213
+ return out;
2214
+ }
2215
+ case "upstream":
2216
+ return apiError.status === void 0 ? { kind: "upstream", message: apiError.detail } : { kind: "upstream", message: apiError.detail, status: apiError.status };
2217
+ }
2218
+ }
2219
+
2220
+ // src/application/tools/account/create-api-key.tool.ts
2221
+ var CreateApiKeyInputShape = {
2222
+ name: z.string().min(1).max(100).describe("Human-readable label (e.g. `ci-pipeline`, `claude-mcp`)."),
2223
+ // Accept BOTH `null` and omit for "no expiry" — many JSON clients
2224
+ // (including a hand-written curl) send `null` explicitly; rejecting
2225
+ // it forces awkward request-body construction on the caller side.
2226
+ // Either form maps to API-side `expires_at: null` (the only shape
2227
+ // the API accepts for "no expiry").
2228
+ expires_at: z.string().datetime().nullable().optional().describe(
2229
+ "Optional ISO-8601 expiry timestamp. Omit or send `null` for a non-expiring key (operator can revoke any time)."
2230
+ )
2231
+ };
2232
+ var createApiKeyTool = {
2233
+ name: "create_api_key",
2234
+ description: "Mint a new API key for the caller's organization. The full secret is returned in `full_key` THIS ONE TIME ONLY \u2014 show it to the user and instruct them to store it; the server keeps only a hash and cannot reveal it again.",
2235
+ annotations: {
2236
+ title: "Create API Key",
2237
+ readOnlyHint: false,
2238
+ destructiveHint: false,
2239
+ idempotentHint: false,
2240
+ openWorldHint: false
2241
+ },
2242
+ inputSchema: z.object(CreateApiKeyInputShape),
2243
+ handler: async (input, ctx) => {
2244
+ const body = {
2245
+ name: input.name,
2246
+ // Forward both `null` and omitted as omitted to the gateway DTO
2247
+ // (gateway treats absent field as "no expiry"). `null` would
2248
+ // collide with the gateway's `Pick<CreateApiKeyRequest, ...>`
2249
+ // type that does not include `null`.
2250
+ ...input.expires_at !== void 0 && input.expires_at !== null ? { expires_at: input.expires_at } : {}
2251
+ };
2252
+ const result = await ctx.api.createApiKey(body);
2253
+ if (result.isErr()) return err(mapApiError(result.error));
2254
+ return ok(result.value);
2255
+ }
2256
+ };
2257
+ var GetAccountInputShape = {};
2258
+ var getAccountTool = {
2259
+ name: "get_account",
2260
+ description: "Get the organization owning the current API key (id, name, owner_id, is_active, created_at). Use this to confirm authentication and capture the org context for follow-up tool calls.",
2261
+ annotations: {
2262
+ title: "Get Account",
2263
+ readOnlyHint: true,
2264
+ destructiveHint: false,
2265
+ idempotentHint: true,
2266
+ openWorldHint: false
2267
+ },
2268
+ inputSchema: z.object(GetAccountInputShape),
2269
+ handler: async (_input, ctx) => {
2270
+ const result = await ctx.api.getAccount();
2271
+ if (result.isErr()) return err(mapApiError(result.error));
2272
+ return ok(result.value);
2273
+ }
2274
+ };
2275
+ var InviteUserInputShape = {
2276
+ email: z.string().email().describe("Email of the person to invite. They get a signup link."),
2277
+ 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.")
2279
+ };
2280
+ var inviteUserTool = {
2281
+ name: "invite_user",
2282
+ description: "Send an invitation email so a new person can join the caller's organization with a chosen role. Returns the pending member record.",
2283
+ annotations: {
2284
+ title: "Invite User",
2285
+ readOnlyHint: false,
2286
+ destructiveHint: false,
2287
+ idempotentHint: false,
2288
+ openWorldHint: false
2289
+ },
2290
+ inputSchema: z.object(InviteUserInputShape),
2291
+ handler: async (input, ctx) => {
2292
+ const body = {
2293
+ email: input.email,
2294
+ role_id: input.role_id
2295
+ };
2296
+ if (input.name !== void 0) body.name = input.name;
2297
+ const result = await ctx.api.inviteUser(body);
2298
+ if (result.isErr()) return err(mapApiError(result.error));
2299
+ return ok(result.value);
2300
+ }
2301
+ };
2302
+ var ListApiKeysInputShape = {};
2303
+ var listApiKeysTool = {
2304
+ name: "list_api_keys",
2305
+ description: "List the organization's API keys: id, key prefix (first 8 chars of the secret), display name, expiry, created_at. The full secret is NEVER returned by this endpoint \u2014 only the prefix.",
2306
+ annotations: {
2307
+ title: "List Api Keys",
2308
+ readOnlyHint: true,
2309
+ destructiveHint: false,
2310
+ idempotentHint: true,
2311
+ openWorldHint: false
2312
+ },
2313
+ inputSchema: z.object(ListApiKeysInputShape),
2314
+ handler: async (_input, ctx) => {
2315
+ const result = await ctx.api.listApiKeys();
2316
+ if (result.isErr()) return err(mapApiError(result.error));
2317
+ return ok({ items: result.value, total: result.value.length });
2318
+ }
2319
+ };
2320
+ var ListOrgRolesInputShape = {};
2321
+ var listOrgRolesTool = {
2322
+ name: "list_org_roles",
2323
+ description: "List the roles defined for the organization \u2014 built-in (owner, admin, member) plus any custom roles, with each role's permission set.",
2324
+ annotations: {
2325
+ title: "List Organization Roles",
2326
+ readOnlyHint: true,
2327
+ destructiveHint: false,
2328
+ idempotentHint: true,
2329
+ openWorldHint: false
2330
+ },
2331
+ inputSchema: z.object(ListOrgRolesInputShape),
2332
+ handler: async (_input, ctx) => {
2333
+ const result = await ctx.api.listOrgRoles();
2334
+ if (result.isErr()) return err(mapApiError(result.error));
2335
+ return ok({ items: result.value, total: result.value.length });
2336
+ }
2337
+ };
2338
+ var ListOrgUsersInputShape = {};
2339
+ var listOrgUsersTool = {
2340
+ name: "list_org_users",
2341
+ description: "List every member of the caller's organization with their role, ownership flag, and join date.",
2342
+ annotations: {
2343
+ title: "List Organization Members",
2344
+ readOnlyHint: true,
2345
+ destructiveHint: false,
2346
+ idempotentHint: true,
2347
+ openWorldHint: false
2348
+ },
2349
+ inputSchema: z.object(ListOrgUsersInputShape),
2350
+ handler: async (_input, ctx) => {
2351
+ const result = await ctx.api.listOrgUsers();
2352
+ if (result.isErr()) return err(mapApiError(result.error));
2353
+ return ok({ items: result.value, total: result.value.length });
2354
+ }
2355
+ };
2356
+ var RemoveUserInputShape = {
2357
+ user_id: z.string().uuid().describe("UUID of the member to remove.")
2358
+ };
2359
+ var removeUserTool = {
2360
+ name: "remove_user",
2361
+ description: "Revoke a member's access to the organization. The user is signed out and any active API keys they created remain unless revoked separately. CANNOT remove the owner \u2014 use `transfer_ownership` first.",
2362
+ annotations: {
2363
+ title: "Remove User",
2364
+ readOnlyHint: false,
2365
+ destructiveHint: true,
2366
+ idempotentHint: true,
2367
+ openWorldHint: false
2368
+ },
2369
+ inputSchema: z.object(RemoveUserInputShape),
2370
+ handler: async (input, ctx) => {
2371
+ const result = await ctx.api.removeUser(input.user_id);
2372
+ if (result.isErr()) return err(mapApiError(result.error));
2373
+ return ok({ removed: true });
2374
+ }
2375
+ };
2376
+ var RevokeApiKeyInputShape = {
2377
+ key_id: z.string().uuid().describe("UUID of the key to revoke (from `list_api_keys`).")
2378
+ };
2379
+ var revokeApiKeyTool = {
2380
+ name: "revoke_api_key",
2381
+ description: "Permanently invalidate an API key. Any subsequent request using it returns 401. Cannot be undone \u2014 the user would have to `create_api_key` again.",
2382
+ annotations: {
2383
+ title: "Revoke API Key",
2384
+ readOnlyHint: false,
2385
+ destructiveHint: true,
2386
+ idempotentHint: true,
2387
+ openWorldHint: false
2388
+ },
2389
+ inputSchema: z.object(RevokeApiKeyInputShape),
2390
+ handler: async (input, ctx) => {
2391
+ const result = await ctx.api.revokeApiKey(input.key_id);
2392
+ if (result.isErr()) return err(mapApiError(result.error));
2393
+ return ok({ revoked: true });
2394
+ }
2395
+ };
2396
+ var TransferOwnershipInputShape = {
2397
+ user_id: z.string().uuid().describe("UUID of the existing member to become the new owner.")
2398
+ };
2399
+ var transferOwnershipTool = {
2400
+ name: "transfer_ownership",
2401
+ description: "Hand the organization owner role to another existing member. ONE-WAY: the previous owner becomes a regular member afterwards. Require explicit confirmation from the user.",
2402
+ annotations: {
2403
+ title: "Transfer Organization Ownership",
2404
+ readOnlyHint: false,
2405
+ destructiveHint: true,
2406
+ idempotentHint: false,
2407
+ openWorldHint: false
2408
+ },
2409
+ inputSchema: z.object(TransferOwnershipInputShape),
2410
+ handler: async (input, ctx) => {
2411
+ const result = await ctx.api.transferOwnership(input.user_id);
2412
+ if (result.isErr()) return err(mapApiError(result.error));
2413
+ return ok({ transferred: true });
2414
+ }
2415
+ };
2416
+ 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.")
2419
+ };
2420
+ var updateOrgTool = {
2421
+ name: "update_org",
2422
+ description: "Update the caller's organization \u2014 display name and/or settings object. Only supplied fields are touched.",
2423
+ annotations: {
2424
+ title: "Update Organization",
2425
+ readOnlyHint: false,
2426
+ destructiveHint: false,
2427
+ idempotentHint: true,
2428
+ openWorldHint: false
2429
+ },
2430
+ inputSchema: z.object(UpdateOrgInputShape),
2431
+ handler: async (input, ctx) => {
2432
+ const body = {
2433
+ ...input.name !== void 0 ? { name: input.name } : {},
2434
+ ...input.settings !== void 0 ? { settings: input.settings } : {}
2435
+ };
2436
+ const result = await ctx.api.updateOrg(body);
2437
+ if (result.isErr()) return err(mapApiError(result.error));
2438
+ return ok(result.value);
2439
+ }
2440
+ };
2441
+ var UpdateUserRoleInputShape = {
2442
+ user_id: z.string().uuid().describe("UUID of the member to update."),
2443
+ role_id: z.string().uuid().describe("UUID of the new role (see `list_org_roles`).")
2444
+ };
2445
+ var updateUserRoleTool = {
2446
+ name: "update_user_role",
2447
+ description: "Change an organization member's role. The owner role can only be transferred via `transfer_ownership`. Returns `{updated: true}` on success; refetch with `list_org_users` if you need the new role echoed.",
2448
+ annotations: {
2449
+ title: "Update User Role",
2450
+ readOnlyHint: false,
2451
+ destructiveHint: false,
2452
+ idempotentHint: true,
2453
+ openWorldHint: false
2454
+ },
2455
+ inputSchema: z.object(UpdateUserRoleInputShape),
2456
+ handler: async (input, ctx) => {
2457
+ const result = await ctx.api.updateUserRole(input.user_id, { role_id: input.role_id });
2458
+ if (result.isErr()) return err(mapApiError(result.error));
2459
+ return ok({ updated: true });
2460
+ }
2461
+ };
2462
+ var DeleteAlertDestinationInputShape = {
2463
+ destination_id: z.string().uuid().describe("Destination UUID.")
2464
+ };
2465
+ var deleteAlertDestinationTool = {
2466
+ name: "delete_alert_destination",
2467
+ description: "Remove an alert-notification destination. The org stops receiving pings on this channel immediately; campaign-level overrides referencing it are pruned.",
2468
+ annotations: {
2469
+ title: "Delete Alert Destination",
2470
+ readOnlyHint: false,
2471
+ destructiveHint: true,
2472
+ idempotentHint: true,
2473
+ openWorldHint: false
2474
+ },
2475
+ inputSchema: z.object(DeleteAlertDestinationInputShape),
2476
+ handler: async (input, ctx) => {
2477
+ const result = await ctx.api.deleteAlertDestination(input.destination_id);
2478
+ if (result.isErr()) return err(mapApiError(result.error));
2479
+ return ok({ deleted: true });
2480
+ }
2481
+ };
2482
+ var GetCampaignAlertOverridesInputShape = {
2483
+ campaign_id: z.string().uuid().describe("Campaign UUID.")
2484
+ };
2485
+ var getCampaignAlertOverridesTool = {
2486
+ name: "get_campaign_alert_overrides",
2487
+ description: "Get the per-campaign override of which alert destinations receive its alerts. `mode` is one of `inherit` (use org defaults), `include` (use the listed destinations), or `exclude` (use everything EXCEPT the listed destinations).",
2488
+ annotations: {
2489
+ title: "Get Campaign Alert Overrides",
2490
+ readOnlyHint: true,
2491
+ destructiveHint: false,
2492
+ idempotentHint: true,
2493
+ openWorldHint: false
2494
+ },
2495
+ inputSchema: z.object(GetCampaignAlertOverridesInputShape),
2496
+ handler: async (input, ctx) => {
2497
+ const result = await ctx.api.getCampaignAlertOverrides(input.campaign_id);
2498
+ if (result.isErr()) return err(mapApiError(result.error));
2499
+ return ok(result.value);
2500
+ }
2501
+ };
2502
+ var ListAlertDestinationsInputShape = {};
2503
+ var listAlertDestinationsTool = {
2504
+ name: "list_alert_destinations",
2505
+ description: "List configured alert-notification destinations (channels): Slack workspaces, Telegram chats, email lists, generic webhooks. Each entry has id, kind, display name, version, and creation timestamp.",
2506
+ annotations: {
2507
+ title: "List Alert Destinations",
2508
+ readOnlyHint: true,
2509
+ destructiveHint: false,
2510
+ idempotentHint: true,
2511
+ openWorldHint: false
2512
+ },
2513
+ inputSchema: z.object(ListAlertDestinationsInputShape),
2514
+ handler: async (_input, ctx) => {
2515
+ const result = await ctx.api.listAlertDestinations();
2516
+ if (result.isErr()) return err(mapApiError(result.error));
2517
+ return ok({ items: result.value, total: result.value.length });
2518
+ }
2519
+ };
2520
+ var SetAlertDestinationVersionInputShape = {
2521
+ destination_id: z.string().uuid().describe("Destination UUID."),
2522
+ version: z.enum(["public", "internal"]).describe(
2523
+ "Which version of the scan-report link to embed in alert messages: `public` (anonymous, no auth) or `internal` (requires UI login)."
2524
+ )
2525
+ };
2526
+ var setAlertDestinationVersionTool = {
2527
+ name: "set_alert_destination_version",
2528
+ description: "Switch a destination to a specific versioned config \u2014 used after re-authorizing a Slack workspace, rotating a Telegram bot token, etc. The new version must already exist in the destination's history. The API returns no body on success; this tool reports `{ updated: true }`. Use `list_alert_destinations` to read the new state if needed.",
2529
+ annotations: {
2530
+ title: "Set Destination Version",
2531
+ readOnlyHint: false,
2532
+ destructiveHint: false,
2533
+ idempotentHint: true,
2534
+ openWorldHint: false
2535
+ },
2536
+ inputSchema: z.object(SetAlertDestinationVersionInputShape),
2537
+ handler: async (input, ctx) => {
2538
+ const result = await ctx.api.setAlertDestinationVersion(input.destination_id, {
2539
+ version: input.version
2540
+ });
2541
+ if (result.isErr()) return err(mapApiError(result.error));
2542
+ return ok({ updated: true });
2543
+ }
2544
+ };
2545
+ var SetCampaignAlertOverridesInputShape = {
2546
+ campaign_id: z.string().uuid().describe("Campaign UUID."),
2547
+ mode: z.enum(["inherit", "include", "exclude"]).describe(
2548
+ "Routing mode: `inherit` (use org defaults), `include` (route ONLY to listed destinations), `exclude` (route everywhere EXCEPT listed)."
2549
+ ),
2550
+ destination_ids: z.array(z.string().uuid()).max(50).default([]).describe(
2551
+ "Destination UUIDs the mode acts on. Required for `include`/`exclude`; ignored for `inherit`."
2552
+ )
2553
+ };
2554
+ var setCampaignAlertOverridesTool = {
2555
+ name: "set_campaign_alert_overrides",
2556
+ description: "REPLACE the per-campaign alert-routing override. `mode=inherit` falls back to org defaults; `mode=include` routes ONLY to the listed destinations; `mode=exclude` routes everywhere EXCEPT the listed destinations. To read the new state, follow up with `get_campaign_alert_overrides`.",
2557
+ annotations: {
2558
+ title: "Set Campaign Alert Overrides",
2559
+ readOnlyHint: false,
2560
+ destructiveHint: false,
2561
+ idempotentHint: true,
2562
+ openWorldHint: false
2563
+ },
2564
+ inputSchema: z.object(SetCampaignAlertOverridesInputShape),
2565
+ handler: async (input, ctx) => {
2566
+ const result = await ctx.api.setCampaignAlertOverrides(input.campaign_id, {
2567
+ mode: input.mode,
2568
+ destination_ids: input.destination_ids
2569
+ });
2570
+ if (result.isErr()) return err(mapApiError(result.error));
2571
+ return ok({ updated: true });
2572
+ }
2573
+ };
2574
+ var GetAlertStatsInputShape = {};
2575
+ var getAlertStatsTool = {
2576
+ name: "get_alert_stats",
2577
+ description: "Get alert counts grouped by status: open, ack, resolved, ignored, total.",
2578
+ annotations: {
2579
+ title: "Get Alert Stats",
2580
+ readOnlyHint: true,
2581
+ destructiveHint: false,
2582
+ idempotentHint: true,
2583
+ openWorldHint: false
2584
+ },
2585
+ inputSchema: z.object(GetAlertStatsInputShape),
2586
+ handler: async (_input, ctx) => {
2587
+ const result = await ctx.api.getAlertStats();
2588
+ if (result.isErr()) return err(mapApiError(result.error));
2589
+ return ok(result.value);
2590
+ }
2591
+ };
2592
+ var ListAlertsInputShape = {
2593
+ campaign_id: z.string().uuid().optional().describe("Filter to one campaign's alerts."),
2594
+ status: z.enum(["open", "ack", "resolved", "ignored"]).optional().describe("Filter by alert status."),
2595
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
2596
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
2597
+ };
2598
+ var listAlertsTool = {
2599
+ name: "list_alerts",
2600
+ description: "List violation alerts (one per scan + violating-tag combo) with offer URL, tag, country, status, scan back-reference.",
2601
+ annotations: {
2602
+ title: "List Alerts",
2603
+ readOnlyHint: true,
2604
+ destructiveHint: false,
2605
+ idempotentHint: true,
2606
+ openWorldHint: false
2607
+ },
2608
+ inputSchema: z.object(ListAlertsInputShape),
2609
+ handler: async (input, ctx) => {
2610
+ const filters = {
2611
+ page: input.page,
2612
+ limit: input.limit,
2613
+ ...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
2614
+ ...input.status !== void 0 ? { status: input.status } : {}
2615
+ };
2616
+ const result = await ctx.api.listAlerts(filters);
2617
+ if (result.isErr()) return err(mapApiError(result.error));
2618
+ return ok(result.value);
2619
+ }
2620
+ };
2621
+ var UpdateAlertStatusInputShape = {
2622
+ alert_id: z.string().uuid().describe("Alert UUID."),
2623
+ status: z.enum(["open", "acknowledged", "resolved", "dismissed"]).describe("New status: open | acknowledged | resolved | dismissed.")
2624
+ };
2625
+ var updateAlertStatusTool = {
2626
+ name: "update_alert_status",
2627
+ description: "Update an alert's status in its lifecycle: open \u2192 acknowledged \u2192 resolved | dismissed. The API enforces valid transitions; an invalid one returns 422.",
2628
+ annotations: {
2629
+ title: "Update Alert Status",
2630
+ readOnlyHint: false,
2631
+ destructiveHint: false,
2632
+ idempotentHint: true,
2633
+ openWorldHint: false
2634
+ },
2635
+ inputSchema: z.object(UpdateAlertStatusInputShape),
2636
+ handler: async (input, ctx) => {
2637
+ const result = await ctx.api.updateAlertStatus(input.alert_id, { status: input.status });
2638
+ if (result.isErr()) return err(mapApiError(result.error));
2639
+ return ok({ updated: true });
2640
+ }
2641
+ };
2642
+ var GetBillingSummaryInputShape = {};
2643
+ var getBillingSummaryTool = {
2644
+ name: "get_billing_summary",
2645
+ description: "Get the organization's billing snapshot: balance (in micros), current plan, period usage counters, suspension state, and whether new scans are accepted right now.",
2646
+ annotations: {
2647
+ title: "Get Billing Summary",
2648
+ readOnlyHint: true,
2649
+ destructiveHint: false,
2650
+ idempotentHint: true,
2651
+ openWorldHint: false
2652
+ },
2653
+ inputSchema: z.object(GetBillingSummaryInputShape),
2654
+ handler: async (_input, ctx) => {
2655
+ const result = await ctx.api.getBillingSummary();
2656
+ if (result.isErr()) return err(mapApiError(result.error));
2657
+ return ok(result.value);
2658
+ }
2659
+ };
2660
+ var GetUsageSummaryInputShape = {};
2661
+ var getUsageSummaryTool = {
2662
+ name: "get_usage_summary",
2663
+ description: "Get a one-liner aggregate of usage for the current billing period: total cost (micros), check count, period start/end.",
2664
+ annotations: {
2665
+ title: "Get Usage Summary",
2666
+ readOnlyHint: true,
2667
+ destructiveHint: false,
2668
+ idempotentHint: true,
2669
+ openWorldHint: false
2670
+ },
2671
+ inputSchema: z.object(GetUsageSummaryInputShape),
2672
+ handler: async (_input, ctx) => {
2673
+ const result = await ctx.api.getUsageSummary();
2674
+ if (result.isErr()) return err(mapApiError(result.error));
2675
+ return ok(result.value);
2676
+ }
2677
+ };
2678
+ var ListBalanceHistoryInputShape = {
2679
+ date_from: z.string().date().optional().describe("ISO date, inclusive."),
2680
+ date_to: z.string().date().optional().describe("ISO date, inclusive."),
2681
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
2682
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
2683
+ };
2684
+ var listBalanceHistoryTool = {
2685
+ 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.",
2687
+ annotations: {
2688
+ title: "List Balance History",
2689
+ readOnlyHint: true,
2690
+ destructiveHint: false,
2691
+ idempotentHint: true,
2692
+ openWorldHint: false
2693
+ },
2694
+ inputSchema: z.object(ListBalanceHistoryInputShape),
2695
+ handler: async (input, ctx) => {
2696
+ const filters = {
2697
+ page: input.page,
2698
+ limit: input.limit,
2699
+ ...input.date_from !== void 0 ? { date_from: input.date_from } : {},
2700
+ ...input.date_to !== void 0 ? { date_to: input.date_to } : {}
2701
+ };
2702
+ const result = await ctx.api.listBalanceHistory(filters);
2703
+ if (result.isErr()) return err(mapApiError(result.error));
2704
+ return ok(result.value);
2705
+ }
2706
+ };
2707
+ var ListUsageInputShape = {
2708
+ date_from: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive."),
2709
+ date_to: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive."),
2710
+ scan_id: z.string().uuid().optional().describe("Filter to one scan's cost rows."),
2711
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
2712
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
2713
+ };
2714
+ var listUsageTool = {
2715
+ name: "list_usage",
2716
+ description: "List per-scan usage rows (cost in micros, kind, scan id, timestamp). Use to attribute cost to specific scans or campaigns.",
2717
+ annotations: {
2718
+ title: "List Usage",
2719
+ readOnlyHint: true,
2720
+ destructiveHint: false,
2721
+ idempotentHint: true,
2722
+ openWorldHint: false
2723
+ },
2724
+ inputSchema: z.object(ListUsageInputShape),
2725
+ handler: async (input, ctx) => {
2726
+ const filters = {
2727
+ page: input.page,
2728
+ limit: input.limit,
2729
+ ...input.date_from !== void 0 ? { date_from: input.date_from } : {},
2730
+ ...input.date_to !== void 0 ? { date_to: input.date_to } : {},
2731
+ ...input.scan_id !== void 0 ? { scan_id: input.scan_id } : {}
2732
+ };
2733
+ const result = await ctx.api.listUsage(filters);
2734
+ if (result.isErr()) return err(mapApiError(result.error));
2735
+ return ok(result.value);
2736
+ }
2737
+ };
2738
+ var ArchiveCampaignGroupInputShape = {
2739
+ group_id: z.string().uuid().describe("Campaign group UUID.")
2740
+ };
2741
+ var archiveCampaignGroupTool = {
2742
+ name: "archive_campaign_group",
2743
+ description: "Soft-delete the group AND every campaign in it. The default group cannot be archived; ask the user to move campaigns out first.",
2744
+ annotations: {
2745
+ title: "Archive Campaign Group",
2746
+ readOnlyHint: false,
2747
+ destructiveHint: false,
2748
+ idempotentHint: true,
2749
+ openWorldHint: false
2750
+ },
2751
+ inputSchema: z.object(ArchiveCampaignGroupInputShape),
2752
+ handler: async (input, ctx) => {
2753
+ const result = await ctx.api.archiveCampaignGroup(input.group_id);
2754
+ if (result.isErr()) return err(mapApiError(result.error));
2755
+ return ok(result.value);
2756
+ }
2757
+ };
2758
+ var CancelCampaignGroupInputShape = {
2759
+ group_id: z.string().uuid().describe("Campaign group UUID.")
2760
+ };
2761
+ var cancelCampaignGroupTool = {
2762
+ name: "cancel_campaign_group",
2763
+ description: "Cancel every pending scan across every campaign in the group. Refunds credits for cancelled scans. Returns the total cancelled count.",
2764
+ annotations: {
2765
+ title: "Cancel Campaign Group",
2766
+ readOnlyHint: false,
2767
+ destructiveHint: false,
2768
+ idempotentHint: true,
2769
+ openWorldHint: false
2770
+ },
2771
+ inputSchema: z.object(CancelCampaignGroupInputShape),
2772
+ handler: async (input, ctx) => {
2773
+ const result = await ctx.api.cancelCampaignGroup(input.group_id);
2774
+ if (result.isErr()) return err(mapApiError(result.error));
2775
+ return ok(result.value);
2776
+ }
2777
+ };
2778
+ var CreateCampaignGroupInputShape = {
2779
+ name: z.string().min(1).max(200).describe("Display name (1-200 chars).")
2780
+ };
2781
+ var createCampaignGroupTool = {
2782
+ name: "create_campaign_group",
2783
+ description: "Create a new campaign group (folder). Free operation, no scans queued.",
2784
+ annotations: {
2785
+ title: "Create Campaign Group",
2786
+ readOnlyHint: false,
2787
+ destructiveHint: false,
2788
+ idempotentHint: false,
2789
+ openWorldHint: false
2790
+ },
2791
+ inputSchema: z.object(CreateCampaignGroupInputShape),
2792
+ handler: async (input, ctx) => {
2793
+ const result = await ctx.api.createCampaignGroup({ name: input.name });
2794
+ if (result.isErr()) return err(mapApiError(result.error));
2795
+ return ok(result.value);
2796
+ }
2797
+ };
2798
+ var GetCampaignGroupInputShape = {
2799
+ group_id: z.string().uuid().describe("Campaign group UUID.")
2800
+ };
2801
+ var getCampaignGroupTool = {
2802
+ name: "get_campaign_group",
2803
+ description: "Get one campaign group by UUID with default/archive/pause flags and campaign count.",
2804
+ annotations: {
2805
+ title: "Get Campaign Group",
2806
+ readOnlyHint: true,
2807
+ destructiveHint: false,
2808
+ idempotentHint: true,
2809
+ openWorldHint: false
2810
+ },
2811
+ inputSchema: z.object(GetCampaignGroupInputShape),
2812
+ handler: async (input, ctx) => {
2813
+ const result = await ctx.api.getCampaignGroup(input.group_id);
2814
+ if (result.isErr()) return err(mapApiError(result.error));
2815
+ return ok(result.value);
2816
+ }
2817
+ };
2818
+ var ListCampaignGroupsInputShape = {
2819
+ archived: z.boolean().optional().describe("If true, list ONLY archived groups. Default: only active groups.")
2820
+ };
2821
+ var listCampaignGroupsTool = {
2822
+ name: "list_campaign_groups",
2823
+ description: "List campaign groups \u2014 folders that group related campaigns. Includes per-group campaign count. Not paginated; the org-scoped list is typically small (a few dozen groups max).",
2824
+ annotations: {
2825
+ title: "List Campaign Groups",
2826
+ readOnlyHint: true,
2827
+ destructiveHint: false,
2828
+ idempotentHint: true,
2829
+ openWorldHint: false
2830
+ },
2831
+ inputSchema: z.object(ListCampaignGroupsInputShape),
2832
+ handler: async (input, ctx) => {
2833
+ const filters = input.archived === void 0 ? {} : { archived: input.archived };
2834
+ const result = await ctx.api.listCampaignGroups(filters);
2835
+ if (result.isErr()) return err(mapApiError(result.error));
2836
+ return ok({ items: result.value });
2837
+ }
2838
+ };
2839
+ var PauseCampaignGroupScheduleInputShape = {
2840
+ group_id: z.string().uuid().describe("Campaign group UUID.")
2841
+ };
2842
+ var pauseCampaignGroupScheduleTool = {
2843
+ name: "pause_campaign_group_schedule",
2844
+ description: "Pause the scheduler for EVERY campaign in the group. Already-pending scans complete; no new scheduled runs are produced until you `resume_campaign_group_schedule`.",
2845
+ annotations: {
2846
+ title: "Pause Group Schedule",
2847
+ readOnlyHint: false,
2848
+ destructiveHint: false,
2849
+ idempotentHint: true,
2850
+ openWorldHint: false
2851
+ },
2852
+ inputSchema: z.object(PauseCampaignGroupScheduleInputShape),
2853
+ handler: async (input, ctx) => {
2854
+ const result = await ctx.api.pauseCampaignGroupSchedule(input.group_id);
2855
+ if (result.isErr()) return err(mapApiError(result.error));
2856
+ return ok(result.value);
2857
+ }
2858
+ };
2859
+ var ResumeCampaignGroupScheduleInputShape = {
2860
+ group_id: z.string().uuid().describe("Campaign group UUID.")
2861
+ };
2862
+ var resumeCampaignGroupScheduleTool = {
2863
+ name: "resume_campaign_group_schedule",
2864
+ description: "Re-enable the scheduler for every campaign in the group. Inverse of `pause_campaign_group_schedule`.",
2865
+ annotations: {
2866
+ title: "Resume Group Schedule",
2867
+ readOnlyHint: false,
2868
+ destructiveHint: false,
2869
+ idempotentHint: true,
2870
+ openWorldHint: false
2871
+ },
2872
+ inputSchema: z.object(ResumeCampaignGroupScheduleInputShape),
2873
+ handler: async (input, ctx) => {
2874
+ const result = await ctx.api.resumeCampaignGroupSchedule(input.group_id);
2875
+ if (result.isErr()) return err(mapApiError(result.error));
2876
+ return ok(result.value);
2877
+ }
2878
+ };
2879
+ var RunCampaignGroupInputShape = {
2880
+ group_id: z.string().uuid().describe("Campaign group UUID.")
2881
+ };
2882
+ var runCampaignGroupTool = {
2883
+ name: "run_campaign_group",
2884
+ description: "Fire an immediate run of every active (non-archived, non-paused) campaign in the group. Returns aggregate stats: how many campaigns triggered, the per-campaign run UUIDs, and any per-campaign failures.",
2885
+ annotations: {
2886
+ title: "Run Campaign Group",
2887
+ readOnlyHint: false,
2888
+ destructiveHint: false,
2889
+ idempotentHint: false,
2890
+ openWorldHint: false
2891
+ },
2892
+ inputSchema: z.object(RunCampaignGroupInputShape),
2893
+ handler: async (input, ctx) => {
2894
+ const result = await ctx.api.runCampaignGroup(input.group_id);
2895
+ if (result.isErr()) return err(mapApiError(result.error));
2896
+ return ok(result.value);
2897
+ }
2898
+ };
2899
+ var UnarchiveCampaignGroupInputShape = {
2900
+ group_id: z.string().uuid().describe("Campaign group UUID.")
2901
+ };
2902
+ var unarchiveCampaignGroupTool = {
2903
+ name: "unarchive_campaign_group",
2904
+ description: "Restore an archived campaign group and re-expose its campaigns in default lists.",
2905
+ annotations: {
2906
+ title: "Unarchive Campaign Group",
2907
+ readOnlyHint: false,
2908
+ destructiveHint: false,
2909
+ idempotentHint: true,
2910
+ openWorldHint: false
2911
+ },
2912
+ inputSchema: z.object(UnarchiveCampaignGroupInputShape),
2913
+ handler: async (input, ctx) => {
2914
+ const result = await ctx.api.unarchiveCampaignGroup(input.group_id);
2915
+ if (result.isErr()) return err(mapApiError(result.error));
2916
+ return ok(result.value);
2917
+ }
2918
+ };
2919
+ var UpdateCampaignGroupInputShape = {
2920
+ 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.")
2923
+ };
2924
+ var updateCampaignGroupTool = {
2925
+ name: "update_campaign_group",
2926
+ description: "Update a campaign group: rename, or pause/resume the scheduler (the pause cascades to every campaign in the group).",
2927
+ annotations: {
2928
+ title: "Update Campaign Group",
2929
+ readOnlyHint: false,
2930
+ destructiveHint: false,
2931
+ idempotentHint: true,
2932
+ openWorldHint: false
2933
+ },
2934
+ inputSchema: z.object(UpdateCampaignGroupInputShape),
2935
+ handler: async (input, ctx) => {
2936
+ const body = {
2937
+ ...input.name !== void 0 ? { name: input.name } : {},
2938
+ ...input.schedule_paused !== void 0 ? { schedule_paused: input.schedule_paused } : {}
2939
+ };
2940
+ const result = await ctx.api.updateCampaignGroup(input.group_id, body);
2941
+ if (result.isErr()) return err(mapApiError(result.error));
2942
+ return ok(result.value);
2943
+ }
2944
+ };
2945
+ var ArchiveCampaignInputShape = {
2946
+ campaign_id: z.string().uuid().describe("Campaign UUID to archive.")
2947
+ };
2948
+ var archiveCampaignTool = {
2949
+ name: "archive_campaign",
2950
+ description: "Soft-delete (archive) a campaign. Removes it from default lists and stops the scheduler; previously-collected scans are preserved.",
2951
+ annotations: {
2952
+ title: "Archive Campaign",
2953
+ readOnlyHint: false,
2954
+ destructiveHint: false,
2955
+ idempotentHint: true,
2956
+ openWorldHint: false
2957
+ },
2958
+ inputSchema: z.object(ArchiveCampaignInputShape),
2959
+ handler: async (input, ctx) => {
2960
+ const result = await ctx.api.archiveCampaign(input.campaign_id);
2961
+ if (result.isErr()) return err(mapApiError(result.error));
2962
+ return ok(result.value);
2963
+ }
2964
+ };
2965
+ var CancelCampaignInputShape = {
2966
+ campaign_id: z.string().uuid().describe("Campaign UUID.")
2967
+ };
2968
+ var cancelCampaignTool = {
2969
+ name: "cancel_campaign",
2970
+ description: "Cancel every pending scan across all unfinished runs of a campaign. Running scans complete normally; pending scans are marked cancelled and credits refunded. Returns count of cancelled scans.",
2971
+ annotations: {
2972
+ title: "Cancel Campaign",
2973
+ readOnlyHint: false,
2974
+ destructiveHint: false,
2975
+ idempotentHint: true,
2976
+ openWorldHint: false
2977
+ },
2978
+ inputSchema: z.object(CancelCampaignInputShape),
2979
+ handler: async (input, ctx) => {
2980
+ const result = await ctx.api.cancelCampaign(input.campaign_id);
2981
+ if (result.isErr()) return err(mapApiError(result.error));
2982
+ return ok(result.value);
2983
+ }
2984
+ };
2985
+ var CreateCampaignInputShape = {
2986
+ name: z.string().min(1).max(200).describe("Display name (1-200 chars)."),
2987
+ campaign_type: z.enum(["url", "ad_tag"]).describe("`url` or `ad_tag` \u2014 must match the target field below."),
2988
+ url: z.string().url().optional().describe("Target URL (required if campaign_type=url)."),
2989
+ ad_tag: z.string().optional().describe("Ad-tag HTML/JS (required if campaign_type=ad_tag)."),
2990
+ 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
+ group_id: z.string().uuid().optional().describe("Parent group UUID; defaults to the org's default group."),
2992
+ emulator_categories: z.array(z.string()).optional().describe("Categories of device profiles to rotate through. Default: all available."),
2993
+ labels: z.record(z.string()).optional().describe("Arbitrary metadata applied to every queued scan."),
2994
+ policy_set_id: z.string().uuid().optional().describe("Policy set to evaluate every scan against."),
2995
+ schedule_enabled: z.boolean().optional().describe("If true, the scheduler runs immediately. Default: false (manual run).")
2996
+ };
2997
+ var createCampaignTool = {
2998
+ name: "create_campaign",
2999
+ description: "Create a recurring scan campaign (template). The schedule produces N scans per run where N = number of countries times number of device profiles. Scans cost credits when they run, not when the campaign is created.",
3000
+ annotations: {
3001
+ title: "Create Campaign",
3002
+ readOnlyHint: false,
3003
+ destructiveHint: false,
3004
+ idempotentHint: false,
3005
+ openWorldHint: false
3006
+ },
3007
+ inputSchema: z.object(CreateCampaignInputShape),
3008
+ handler: async (input, ctx) => {
3009
+ const body = {
3010
+ name: input.name,
3011
+ campaign_type: input.campaign_type,
3012
+ country_codes: input.country_codes,
3013
+ ...input.url !== void 0 ? { url: input.url } : {},
3014
+ ...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
3015
+ ...input.group_id !== void 0 ? { group_id: input.group_id } : {},
3016
+ ...input.emulator_categories !== void 0 ? { emulator_categories: input.emulator_categories } : {},
3017
+ ...input.labels !== void 0 ? { labels: input.labels } : {},
3018
+ ...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
3019
+ ...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {}
3020
+ };
3021
+ const result = await ctx.api.createCampaign(body);
3022
+ if (result.isErr()) return err(mapApiError(result.error));
3023
+ return ok(result.value);
3024
+ }
3025
+ };
3026
+ var GetCampaignInputShape = {
3027
+ campaign_id: z.string().uuid().describe("Campaign UUID.")
3028
+ };
3029
+ var getCampaignTool = {
3030
+ name: "get_campaign",
3031
+ description: "Get one campaign by UUID: name, type, target URL/ad-tag, countries, schedule status, archive status, parent group.",
3032
+ annotations: {
3033
+ title: "Get Campaign",
3034
+ readOnlyHint: true,
3035
+ destructiveHint: false,
3036
+ idempotentHint: true,
3037
+ openWorldHint: false
3038
+ },
3039
+ inputSchema: z.object(GetCampaignInputShape),
3040
+ handler: async (input, ctx) => {
3041
+ const result = await ctx.api.getCampaign(input.campaign_id);
3042
+ if (result.isErr()) return err(mapApiError(result.error));
3043
+ return ok(result.value);
3044
+ }
3045
+ };
3046
+ var ListCampaignRunsInputShape = {
3047
+ campaign_id: z.string().uuid().describe("Campaign UUID."),
3048
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
3049
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
3050
+ };
3051
+ var listCampaignRunsTool = {
3052
+ name: "list_campaign_runs",
3053
+ description: "List every run (scheduled execution) of one campaign, paginated, with per-run counters.",
3054
+ annotations: {
3055
+ title: "List Campaign Runs",
3056
+ readOnlyHint: true,
3057
+ destructiveHint: false,
3058
+ idempotentHint: true,
3059
+ openWorldHint: false
3060
+ },
3061
+ inputSchema: z.object(ListCampaignRunsInputShape),
3062
+ handler: async (input, ctx) => {
3063
+ const result = await ctx.api.listCampaignRuns(input.campaign_id, {
3064
+ page: input.page,
3065
+ limit: input.limit
3066
+ });
3067
+ if (result.isErr()) return err(mapApiError(result.error));
3068
+ return ok(result.value);
3069
+ }
3070
+ };
3071
+ var ListCampaignsInputShape = {
3072
+ group_id: z.string().uuid().optional().describe("Optional campaign-group UUID to filter by."),
3073
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
3074
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size (1-200).")
3075
+ };
3076
+ var listCampaignsTool = {
3077
+ name: "list_campaigns",
3078
+ description: "List campaigns for the caller's organization, optionally filtered by group, paginated.",
3079
+ annotations: {
3080
+ title: "List Campaigns",
3081
+ readOnlyHint: true,
3082
+ destructiveHint: false,
3083
+ idempotentHint: true,
3084
+ openWorldHint: false
3085
+ },
3086
+ inputSchema: z.object(ListCampaignsInputShape),
3087
+ handler: async (input, ctx) => {
3088
+ const filters = {
3089
+ page: input.page,
3090
+ limit: input.limit,
3091
+ ...input.group_id !== void 0 ? { group_id: input.group_id } : {}
3092
+ };
3093
+ const result = await ctx.api.listCampaigns(filters);
3094
+ if (result.isErr()) return err(mapApiError(result.error));
3095
+ return ok(result.value);
3096
+ }
3097
+ };
3098
+ var ListCampaignsPickerInputShape = {};
3099
+ var listCampaignsPickerTool = {
3100
+ name: "list_campaigns_picker",
3101
+ 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.",
3102
+ annotations: {
3103
+ title: "List Campaigns (Picker)",
3104
+ readOnlyHint: true,
3105
+ destructiveHint: false,
3106
+ idempotentHint: true,
3107
+ openWorldHint: false
3108
+ },
3109
+ inputSchema: z.object(ListCampaignsPickerInputShape),
3110
+ handler: async (_input, ctx) => {
3111
+ const result = await ctx.api.listCampaignsPicker();
3112
+ if (result.isErr()) return err(mapApiError(result.error));
3113
+ return ok(result.value);
3114
+ }
3115
+ };
3116
+ var RunCampaignInputShape = {
3117
+ campaign_id: z.string().uuid().describe("Campaign UUID to run now.")
3118
+ };
3119
+ var runCampaignTool = {
3120
+ name: "run_campaign",
3121
+ description: "Trigger an immediate, ad-hoc run of a campaign. Costs N credits where N = number of countries \xD7 number of emulators in the campaign config. Returns the new run with progress counters (total / completed / failed / partial / cancelled); track further progress via `get_run`.",
3122
+ annotations: {
3123
+ title: "Run Campaign Now",
3124
+ readOnlyHint: false,
3125
+ destructiveHint: false,
3126
+ idempotentHint: false,
3127
+ openWorldHint: false
3128
+ },
3129
+ inputSchema: z.object(RunCampaignInputShape),
3130
+ handler: async (input, ctx) => {
3131
+ const result = await ctx.api.runCampaign(input.campaign_id);
3132
+ if (result.isErr()) return err(mapApiError(result.error));
3133
+ return ok(result.value);
3134
+ }
3135
+ };
3136
+ var UnarchiveCampaignInputShape = {
3137
+ campaign_id: z.string().uuid().describe("Campaign UUID.")
3138
+ };
3139
+ var unarchiveCampaignTool = {
3140
+ name: "unarchive_campaign",
3141
+ description: "Restore an archived campaign. Inverse of `archive_campaign`. The campaign re-appears in default lists; if `schedule_enabled` was true, the scheduler resumes producing runs.",
3142
+ annotations: {
3143
+ title: "Unarchive Campaign",
3144
+ readOnlyHint: false,
3145
+ destructiveHint: false,
3146
+ idempotentHint: true,
3147
+ openWorldHint: false
3148
+ },
3149
+ inputSchema: z.object(UnarchiveCampaignInputShape),
3150
+ handler: async (input, ctx) => {
3151
+ const result = await ctx.api.unarchiveCampaign(input.campaign_id);
3152
+ if (result.isErr()) return err(mapApiError(result.error));
3153
+ return ok(result.value);
3154
+ }
3155
+ };
3156
+ var UpdateCampaignInputShape = {
3157
+ campaign_id: z.string().uuid().describe("Campaign UUID to update."),
3158
+ name: z.string().min(1).max(200).optional().describe("New display name."),
3159
+ country_codes: z.array(z.string().length(2)).optional().describe("Replace the country list."),
3160
+ labels: z.record(z.string()).optional().describe("Replace the label map."),
3161
+ policy_set_id: z.string().uuid().nullable().optional().describe("New policy set UUID; pass null to clear."),
3162
+ schedule_enabled: z.boolean().optional().describe("Pause / resume the scheduler.")
3163
+ };
3164
+ var updateCampaignTool = {
3165
+ name: "update_campaign",
3166
+ description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding.",
3167
+ annotations: {
3168
+ title: "Update Campaign",
3169
+ readOnlyHint: false,
3170
+ destructiveHint: false,
3171
+ idempotentHint: true,
3172
+ openWorldHint: false
3173
+ },
3174
+ inputSchema: z.object(UpdateCampaignInputShape),
3175
+ handler: async (input, ctx) => {
3176
+ const body = {
3177
+ ...input.name !== void 0 ? { name: input.name } : {},
3178
+ ...input.country_codes !== void 0 ? { country_codes: input.country_codes } : {},
3179
+ ...input.labels !== void 0 ? { labels: input.labels } : {},
3180
+ ...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
3181
+ ...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {}
3182
+ };
3183
+ const result = await ctx.api.updateCampaign(input.campaign_id, body);
3184
+ if (result.isErr()) return err(mapApiError(result.error));
3185
+ return ok(result.value);
3186
+ }
3187
+ };
3188
+ var CreateCustomRuleInputShape = {
3189
+ name: z.string().min(1).max(200).describe("Display name."),
3190
+ tag_slug: z.string().max(100).optional().describe("Tag slug to assign on match. Empty = create-only (advanced)."),
3191
+ rule_type: z.string().max(50).describe("Rule engine: regex | substring | iab_category | etc. (API validates)."),
3192
+ config: z.record(z.unknown()).describe("Rule-type-specific configuration object. Shape depends on rule_type."),
3193
+ target: z.string().max(30).optional().describe(
3194
+ "Where to apply the rule (e.g. 'page' for landing HTML). Default: page. See API docs for the full set of valid values."
3195
+ )
3196
+ };
3197
+ var createCustomRuleTool = {
3198
+ name: "create_custom_rule",
3199
+ description: "Define a custom tag-detection rule (regex / substring / category). Matches will tag every future scan; existing scans are untouched until you call `recheck_scans`.",
3200
+ annotations: {
3201
+ title: "Create Custom Rule",
3202
+ readOnlyHint: false,
3203
+ destructiveHint: false,
3204
+ idempotentHint: false,
3205
+ openWorldHint: false
3206
+ },
3207
+ inputSchema: z.object(CreateCustomRuleInputShape),
3208
+ handler: async (input, ctx) => {
3209
+ const body = {
3210
+ name: input.name,
3211
+ rule_type: input.rule_type,
3212
+ config: input.config
3213
+ };
3214
+ if (input.tag_slug !== void 0) body.tag_slug = input.tag_slug;
3215
+ if (input.target !== void 0) body.target = input.target;
3216
+ const result = await ctx.api.createCustomRule(body);
3217
+ if (result.isErr()) return err(mapApiError(result.error));
3218
+ return ok(result.value);
3219
+ }
3220
+ };
3221
+ var DeleteCustomRuleInputShape = {
3222
+ rule_id: z.string().uuid().describe("Rule UUID to delete.")
3223
+ };
3224
+ var deleteCustomRuleTool = {
3225
+ name: "delete_custom_rule",
3226
+ description: "Permanently delete a custom rule. Already-applied tags on past scans are preserved; the rule simply stops running on future scans.",
3227
+ annotations: {
3228
+ title: "Delete Custom Rule",
3229
+ readOnlyHint: false,
3230
+ destructiveHint: true,
3231
+ idempotentHint: true,
3232
+ openWorldHint: false
3233
+ },
3234
+ inputSchema: z.object(DeleteCustomRuleInputShape),
3235
+ handler: async (input, ctx) => {
3236
+ const result = await ctx.api.deleteCustomRule(input.rule_id);
3237
+ if (result.isErr()) return err(mapApiError(result.error));
3238
+ return ok({ deleted: true });
3239
+ }
3240
+ };
3241
+ var GetCustomRuleInputShape = { rule_id: z.string().uuid().describe("Rule UUID.") };
3242
+ var getCustomRuleTool = {
3243
+ name: "get_custom_rule",
3244
+ description: "Get one custom rule by UUID with name, tag-slug, type, config object, target, active flag.",
3245
+ annotations: {
3246
+ title: "Get Custom Rule",
3247
+ readOnlyHint: true,
3248
+ destructiveHint: false,
3249
+ idempotentHint: true,
3250
+ openWorldHint: false
3251
+ },
3252
+ inputSchema: z.object(GetCustomRuleInputShape),
3253
+ handler: async (input, ctx) => {
3254
+ const result = await ctx.api.getCustomRule(input.rule_id);
3255
+ if (result.isErr()) return err(mapApiError(result.error));
3256
+ return ok(result.value);
3257
+ }
3258
+ };
3259
+ var ListCustomRulesInputShape = {
3260
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
3261
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
3262
+ };
3263
+ var listCustomRulesTool = {
3264
+ name: "list_custom_rules",
3265
+ description: "Paginated list of the organization's custom tag-detection rules (regex / heuristics) with their config, target, and active flag. Returns `{items, total, page, limit}`. To iterate: there is a next page iff `page * limit < total` (or equivalently `items.length === limit` until the last page). `page` is 1-indexed.",
3266
+ annotations: {
3267
+ title: "List Custom Rules",
3268
+ readOnlyHint: true,
3269
+ destructiveHint: false,
3270
+ idempotentHint: true,
3271
+ openWorldHint: false
3272
+ },
3273
+ inputSchema: z.object(ListCustomRulesInputShape),
3274
+ handler: async (input, ctx) => {
3275
+ const result = await ctx.api.listCustomRules({ page: input.page, limit: input.limit });
3276
+ if (result.isErr()) return err(mapApiError(result.error));
3277
+ return ok(result.value);
3278
+ }
3279
+ };
3280
+ var TestCustomRuleInputShape = {
3281
+ rule_type: z.string().max(50).describe("Rule engine type (regex, substring, ...)."),
3282
+ config: z.record(z.unknown()).describe("Rule-type-specific config to test."),
3283
+ target: z.string().max(30).describe(
3284
+ "Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
3285
+ ),
3286
+ scan_id: z.string().uuid().describe("Existing scan UUID to evaluate the rule against.")
3287
+ };
3288
+ var testCustomRuleTool = {
3289
+ 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`.",
3291
+ annotations: {
3292
+ title: "Test Custom Rule",
3293
+ readOnlyHint: true,
3294
+ destructiveHint: false,
3295
+ idempotentHint: true,
3296
+ openWorldHint: false
3297
+ },
3298
+ inputSchema: z.object(TestCustomRuleInputShape),
3299
+ handler: async (input, ctx) => {
3300
+ const result = await ctx.api.testCustomRule({
3301
+ rule_type: input.rule_type,
3302
+ config: input.config,
3303
+ target: input.target,
3304
+ scan_id: input.scan_id
3305
+ });
3306
+ if (result.isErr()) return err(mapApiError(result.error));
3307
+ return ok(result.value);
3308
+ }
3309
+ };
3310
+ var UpdateCustomRuleInputShape = {
3311
+ rule_id: z.string().uuid().describe("Rule UUID to update."),
3312
+ name: z.string().min(1).max(200).optional().describe("New display name."),
3313
+ tag_slug: z.string().max(100).optional().describe("New tag slug to assign on match."),
3314
+ config: z.record(z.unknown()).optional().describe("New rule-type-specific config object."),
3315
+ target: z.string().max(30).optional().describe(
3316
+ "Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
3317
+ ),
3318
+ is_active: z.boolean().optional().describe("Enable/disable the rule.")
3319
+ };
3320
+ var updateCustomRuleTool = {
3321
+ 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.)",
3323
+ annotations: {
3324
+ title: "Update Custom Rule",
3325
+ readOnlyHint: false,
3326
+ destructiveHint: false,
3327
+ idempotentHint: true,
3328
+ openWorldHint: false
3329
+ },
3330
+ inputSchema: z.object(UpdateCustomRuleInputShape),
3331
+ handler: async (input, ctx) => {
3332
+ const body = {};
3333
+ if (input.name !== void 0) body.name = input.name;
3334
+ if (input.tag_slug !== void 0) body.tag_slug = input.tag_slug;
3335
+ if (input.config !== void 0) body.config = input.config;
3336
+ if (input.target !== void 0) body.target = input.target;
3337
+ if (input.is_active !== void 0) body.is_active = input.is_active;
3338
+ const result = await ctx.api.updateCustomRule(input.rule_id, body);
3339
+ if (result.isErr()) return err(mapApiError(result.error));
3340
+ return ok(result.value);
3341
+ }
3342
+ };
3343
+ var ListEmulatorsInputShape = {};
3344
+ var listEmulatorsTool = {
3345
+ name: "list_emulators",
3346
+ description: "List every device/OS emulator profile available for scans (id, display name, category, browser). Use the `id` as `emulator_id` in `create_scan` / `create_campaign`.",
3347
+ annotations: {
3348
+ title: "List Emulators",
3349
+ readOnlyHint: true,
3350
+ destructiveHint: false,
3351
+ idempotentHint: true,
3352
+ openWorldHint: false
3353
+ },
3354
+ inputSchema: z.object(ListEmulatorsInputShape),
3355
+ handler: async (_input, ctx) => {
3356
+ const result = await ctx.api.listEmulators();
3357
+ if (result.isErr()) return err(mapApiError(result.error));
3358
+ return ok({ items: result.value, total: result.value.length });
3359
+ }
3360
+ };
3361
+ var ListGeosInputShape = {};
3362
+ var listGeosTool = {
3363
+ name: "list_geos",
3364
+ description: "List every country the Kaminari Ad platform can scan ads from, with ISO 3166-1 alpha-2 code, name, continent, and emoji.",
3365
+ annotations: {
3366
+ title: "List Geos",
3367
+ readOnlyHint: true,
3368
+ destructiveHint: false,
3369
+ idempotentHint: true,
3370
+ openWorldHint: false
3371
+ },
3372
+ inputSchema: z.object(ListGeosInputShape),
3373
+ handler: async (_input, ctx) => {
3374
+ const result = await ctx.api.listGeos();
3375
+ if (result.isErr()) {
3376
+ return err(mapApiError(result.error));
3377
+ }
3378
+ return ok({ items: result.value, total: result.value.length });
3379
+ }
3380
+ };
3381
+ var ListInvoicesInputShape = {
3382
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
3383
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
3384
+ };
3385
+ var listInvoicesTool = {
3386
+ name: "list_invoices",
3387
+ description: "List invoices issued to the organization with number, status, total in micros, currency, due/paid dates. Use to find an invoice id; PDFs are available at https://app.kaminari.ad/api/v1/invoices/{id}/pdf (not exposed via MCP \u2014 agents return the URL to the user).",
3388
+ annotations: {
3389
+ title: "List Invoices",
3390
+ readOnlyHint: true,
3391
+ destructiveHint: false,
3392
+ idempotentHint: true,
3393
+ openWorldHint: false
3394
+ },
3395
+ inputSchema: z.object(ListInvoicesInputShape),
3396
+ handler: async (input, ctx) => {
3397
+ const result = await ctx.api.listInvoices({ page: input.page, limit: input.limit });
3398
+ if (result.isErr()) return err(mapApiError(result.error));
3399
+ return ok(result.value);
3400
+ }
3401
+ };
3402
+ var PolicyEntryShape = z.object({
3403
+ tag_slug: z.string().min(1).max(100).describe("Tag slug that triggers a violation."),
3404
+ country_codes: z.array(z.string().length(2)).max(50).describe("Restrict the violation to these countries. Empty array = all countries.")
3405
+ });
3406
+ var CreatePolicySetInputShape = {
3407
+ name: z.string().min(1).max(200).describe("Display name."),
3408
+ description: z.string().max(2e3).describe("Free-form description (use empty string for none)."),
3409
+ entries: z.array(PolicyEntryShape).min(1).max(500).describe("At least one entry. Each entry pairs a tag-slug with country codes.")
3410
+ };
3411
+ var createPolicySetTool = {
3412
+ name: "create_policy_set",
3413
+ description: "Create a new policy set (named collection of tag + country-list entries). Once created, you can bind campaigns to it via `update_campaign`.",
3414
+ annotations: {
3415
+ title: "Create Policy Set",
3416
+ readOnlyHint: false,
3417
+ destructiveHint: false,
3418
+ idempotentHint: false,
3419
+ openWorldHint: false
3420
+ },
3421
+ inputSchema: z.object(CreatePolicySetInputShape),
3422
+ handler: async (input, ctx) => {
3423
+ const result = await ctx.api.createPolicySet({
3424
+ name: input.name,
3425
+ description: input.description,
3426
+ entries: input.entries.map((e) => ({ tag_slug: e.tag_slug, country_codes: e.country_codes }))
3427
+ });
3428
+ if (result.isErr()) return err(mapApiError(result.error));
3429
+ return ok(result.value);
3430
+ }
3431
+ };
3432
+ var DeletePolicySetInputShape = {
3433
+ policy_set_id: z.string().uuid().describe("Policy set UUID.")
3434
+ };
3435
+ var deletePolicySetTool = {
3436
+ name: "delete_policy_set",
3437
+ description: "Permanently delete a policy set. IMPORTANT: API returns HTTP 400 if any active campaign is still bound to this set. To unbind first, call `list_campaigns` with a `policy_set_id` filter (when available) or scan your campaigns for matches, then `update_campaign` for each match setting `policy_set_id=null`, then retry delete. Alerts created under this set persist (their `policy_set_id` becomes `null`).",
3438
+ annotations: {
3439
+ title: "Delete Policy Set",
3440
+ readOnlyHint: false,
3441
+ destructiveHint: true,
3442
+ idempotentHint: true,
3443
+ openWorldHint: false
3444
+ },
3445
+ inputSchema: z.object(DeletePolicySetInputShape),
3446
+ handler: async (input, ctx) => {
3447
+ const result = await ctx.api.deletePolicySet(input.policy_set_id);
3448
+ if (result.isErr()) return err(mapApiError(result.error));
3449
+ return ok({ deleted: true });
3450
+ }
3451
+ };
3452
+ var GetPolicySetInputShape = {
3453
+ policy_set_id: z.string().uuid().describe("Policy set UUID.")
3454
+ };
3455
+ var getPolicySetTool = {
3456
+ name: "get_policy_set",
3457
+ description: "Get one policy set by UUID with its complete list of entries (tag-slug + applicable country codes).",
3458
+ annotations: {
3459
+ title: "Get Policy Set",
3460
+ readOnlyHint: true,
3461
+ destructiveHint: false,
3462
+ idempotentHint: true,
3463
+ openWorldHint: false
3464
+ },
3465
+ inputSchema: z.object(GetPolicySetInputShape),
3466
+ handler: async (input, ctx) => {
3467
+ const result = await ctx.api.getPolicySet(input.policy_set_id);
3468
+ if (result.isErr()) return err(mapApiError(result.error));
3469
+ return ok(result.value);
3470
+ }
3471
+ };
3472
+ var ListPolicySetsInputShape = {
3473
+ 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.")
3475
+ };
3476
+ var listPolicySetsTool = {
3477
+ name: "list_policy_sets",
3478
+ description: "Paginated list of policy sets: named collections of (tag, country-list) 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.",
3479
+ annotations: {
3480
+ title: "List Policy Sets",
3481
+ readOnlyHint: true,
3482
+ destructiveHint: false,
3483
+ idempotentHint: true,
3484
+ openWorldHint: false
3485
+ },
3486
+ inputSchema: z.object(ListPolicySetsInputShape),
3487
+ handler: async (input, ctx) => {
3488
+ const result = await ctx.api.listPolicySets({ page: input.page, limit: input.limit });
3489
+ if (result.isErr()) return err(mapApiError(result.error));
3490
+ return ok(result.value);
3491
+ }
3492
+ };
3493
+ var RequestPolicySetApprovalInputShape = {
3494
+ policy_set_id: z.string().uuid().describe("Policy set UUID.")
3495
+ };
3496
+ var requestPolicySetApprovalTool = {
3497
+ name: "request_policy_set_approval",
3498
+ description: "Submit a private policy set for Kaminari Ad team review so it can be marked PUBLIC and used by other organizations. The set must be complete and well-formed. Returns immediately; approval status is reflected on the policy set entity once the review completes.",
3499
+ annotations: {
3500
+ title: "Request Policy Set Approval",
3501
+ readOnlyHint: false,
3502
+ destructiveHint: false,
3503
+ idempotentHint: false,
3504
+ openWorldHint: false
3505
+ },
3506
+ inputSchema: z.object(RequestPolicySetApprovalInputShape),
3507
+ handler: async (input, ctx) => {
3508
+ const result = await ctx.api.requestPolicySetApproval(input.policy_set_id);
3509
+ if (result.isErr()) return err(mapApiError(result.error));
3510
+ return ok({ requested: true });
3511
+ }
3512
+ };
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
+ var UpdatePolicySetInputShape = {
3518
+ policy_set_id: z.string().uuid().describe("Policy set UUID."),
3519
+ name: z.string().min(1).max(200).describe("New name (always required by the API on update)."),
3520
+ description: z.string().max(2e3).describe("New description (empty string allowed)."),
3521
+ entries: z.array(PolicyEntryShape2).min(1).max(500).describe("REPLACEMENT entries list \u2014 REPLACES the current list, not a merge.")
3522
+ };
3523
+ var updatePolicySetTool = {
3524
+ 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.",
3526
+ annotations: {
3527
+ title: "Update Policy Set",
3528
+ readOnlyHint: false,
3529
+ destructiveHint: false,
3530
+ idempotentHint: true,
3531
+ openWorldHint: false
3532
+ },
3533
+ inputSchema: z.object(UpdatePolicySetInputShape),
3534
+ handler: async (input, ctx) => {
3535
+ const result = await ctx.api.updatePolicySet(input.policy_set_id, {
3536
+ name: input.name,
3537
+ description: input.description,
3538
+ entries: input.entries.map((e) => ({ tag_slug: e.tag_slug, country_codes: e.country_codes }))
3539
+ });
3540
+ if (result.isErr()) return err(mapApiError(result.error));
3541
+ return ok(result.value);
3542
+ }
3543
+ };
3544
+ var CancelRunInputShape = { run_id: z.string().uuid().describe("Run UUID.") };
3545
+ var cancelRunTool = {
3546
+ name: "cancel_run",
3547
+ description: "Cancel every pending scan within one run. Running scans complete; pending ones get refunded. Returns the count of cancelled scans.",
3548
+ annotations: {
3549
+ title: "Cancel Run",
3550
+ readOnlyHint: false,
3551
+ destructiveHint: false,
3552
+ idempotentHint: true,
3553
+ openWorldHint: false
3554
+ },
3555
+ inputSchema: z.object(CancelRunInputShape),
3556
+ handler: async (input, ctx) => {
3557
+ const result = await ctx.api.cancelRun(input.run_id);
3558
+ if (result.isErr()) return err(mapApiError(result.error));
3559
+ return ok(result.value);
3560
+ }
3561
+ };
3562
+ var GetRunInputShape = { run_id: z.string().uuid().describe("Run UUID.") };
3563
+ var getRunTool = {
3564
+ name: "get_run",
3565
+ description: "Get one run by UUID with totals (queued, completed, failed, partial, cancelled), parent campaign, label, source.",
3566
+ annotations: {
3567
+ title: "Get Run",
3568
+ readOnlyHint: true,
3569
+ destructiveHint: false,
3570
+ idempotentHint: true,
3571
+ openWorldHint: false
3572
+ },
3573
+ inputSchema: z.object(GetRunInputShape),
3574
+ handler: async (input, ctx) => {
3575
+ const result = await ctx.api.getRun(input.run_id);
3576
+ if (result.isErr()) return err(mapApiError(result.error));
3577
+ return ok(result.value);
3578
+ }
3579
+ };
3580
+ var ListRunScansInputShape = {
3581
+ run_id: z.string().uuid().describe("Run UUID."),
3582
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
3583
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
3584
+ };
3585
+ var listRunScansTool = {
3586
+ name: "list_run_scans",
3587
+ description: "List the tile-scan items produced by one run (status, country, offer URL, screenshot, elapsed ms, error). For full scan details (input URL, labels, classification, redirect chain) fetch a specific scan via `get_scan`.",
3588
+ annotations: {
3589
+ title: "List Run Scans",
3590
+ readOnlyHint: true,
3591
+ destructiveHint: false,
3592
+ idempotentHint: true,
3593
+ openWorldHint: false
3594
+ },
3595
+ inputSchema: z.object(ListRunScansInputShape),
3596
+ handler: async (input, ctx) => {
3597
+ const result = await ctx.api.listRunScans(input.run_id, {
3598
+ page: input.page,
3599
+ limit: input.limit
3600
+ });
3601
+ if (result.isErr()) return err(mapApiError(result.error));
3602
+ return ok(result.value);
3603
+ }
3604
+ };
3605
+ var CancelScanInputShape = {
3606
+ scan_id: z.string().uuid().describe("UUID of the pending scan to cancel.")
3607
+ };
3608
+ var cancelScanTool = {
3609
+ name: "cancel_scan",
3610
+ description: "Cancel one pending scan by UUID. Already-running or completed scans are no-ops. Cancellation refunds the scan credit.",
3611
+ annotations: {
3612
+ title: "Cancel Scan",
3613
+ readOnlyHint: false,
3614
+ destructiveHint: false,
3615
+ idempotentHint: true,
3616
+ openWorldHint: false
3617
+ },
3618
+ inputSchema: z.object(CancelScanInputShape),
3619
+ handler: async (input, ctx) => {
3620
+ const result = await ctx.api.cancelScan(input.scan_id);
3621
+ if (result.isErr()) return err(mapApiError(result.error));
3622
+ return ok(result.value);
3623
+ }
3624
+ };
3625
+ var CreateBulkScansInputShape = {
3626
+ url: z.string().url().optional().describe("Direct URL. EITHER `url` OR `ad_tag` is required."),
3627
+ ad_tag: z.string().optional().describe("Raw ad-tag HTML/JS. EITHER `url` OR `ad_tag` is required."),
3628
+ 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
+ emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; same for every country in the batch."),
3630
+ labels: z.record(z.string()).optional().describe("Arbitrary metadata copied onto every created scan.")
3631
+ };
3632
+ var createBulkScansTool = {
3633
+ name: "create_bulk_scans",
3634
+ description: "Queue one new scan per country in a single call (e.g. test the same URL from US + DE + JP). COSTS N CREDITS where N = number of countries. Returns the list of created scans.",
3635
+ annotations: {
3636
+ title: "Create Bulk Scans",
3637
+ readOnlyHint: false,
3638
+ destructiveHint: false,
3639
+ idempotentHint: false,
3640
+ openWorldHint: false
3641
+ },
3642
+ inputSchema: z.object(CreateBulkScansInputShape),
3643
+ handler: async (input, ctx) => {
3644
+ const body = {
3645
+ country_codes: input.country_codes,
3646
+ emulator_id: input.emulator_id,
3647
+ ...input.url !== void 0 ? { url: input.url } : {},
3648
+ ...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
3649
+ ...input.labels !== void 0 ? { labels: input.labels } : {}
3650
+ };
3651
+ const result = await ctx.api.createBulkScans(body);
3652
+ if (result.isErr()) return err(mapApiError(result.error));
3653
+ return ok({ items: result.value, total: result.value.length });
3654
+ }
3655
+ };
3656
+ var CreateScanInputShape = {
3657
+ url: z.string().url().optional().describe("Direct URL of the ad / landing page. EITHER `url` OR `ad_tag` is required."),
3658
+ ad_tag: z.string().optional().describe("Raw HTML/JS ad tag (script, iframe, image). EITHER `url` OR `ad_tag` is required."),
3659
+ country_code: z.string().length(2).describe("ISO 3166-1 alpha-2 country code, e.g. US, DE, JP. Determines proxy geo."),
3660
+ emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; use `list_emulators` to discover valid values."),
3661
+ labels: z.record(z.string()).optional().describe("Arbitrary string -> string metadata attached to the scan."),
3662
+ campaign_id: z.string().uuid().optional().describe("Optional campaign UUID to attribute the scan to."),
3663
+ run_id: z.string().uuid().optional().describe("Optional run UUID inside the campaign.")
3664
+ };
3665
+ var createScanTool = {
3666
+ name: "create_scan",
3667
+ description: "Queue a single new scan for a URL or ad-tag against one country. COSTS CREDITS and bills the caller's organization. Returns the newly-created scan record.",
3668
+ annotations: {
3669
+ title: "Create Scan",
3670
+ readOnlyHint: false,
3671
+ destructiveHint: false,
3672
+ idempotentHint: false,
3673
+ openWorldHint: false
3674
+ },
3675
+ inputSchema: z.object(CreateScanInputShape),
3676
+ handler: async (input, ctx) => {
3677
+ const body = {
3678
+ country_code: input.country_code,
3679
+ emulator_id: input.emulator_id,
3680
+ ...input.url !== void 0 ? { url: input.url } : {},
3681
+ ...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
3682
+ ...input.labels !== void 0 ? { labels: input.labels } : {},
3683
+ ...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
3684
+ ...input.run_id !== void 0 ? { run_id: input.run_id } : {}
3685
+ };
3686
+ const result = await ctx.api.createScan(body);
3687
+ if (result.isErr()) return err(mapApiError(result.error));
3688
+ return ok(result.value);
3689
+ }
3690
+ };
3691
+ var GetScanInputShape = {
3692
+ scan_id: z.string().uuid().describe("The scan's UUID (returned by `list_scans` or `create_scan`).")
3693
+ };
3694
+ var getScanTool = {
3695
+ name: "get_scan",
3696
+ description: "Get full detail for one scan by UUID: status, offer URL, screenshot URL, timing, labels, and the parent campaign if any.",
3697
+ annotations: {
3698
+ title: "Get Scan",
3699
+ readOnlyHint: true,
3700
+ destructiveHint: false,
3701
+ idempotentHint: true,
3702
+ openWorldHint: false
3703
+ },
3704
+ inputSchema: z.object(GetScanInputShape),
3705
+ handler: async (input, ctx) => {
3706
+ const result = await ctx.api.getScan(input.scan_id);
3707
+ if (result.isErr()) return err(mapApiError(result.error));
3708
+ return ok(result.value);
3709
+ }
3710
+ };
3711
+ var ListScansInputShape = {
3712
+ status: z.string().optional().describe(
3713
+ "Comma-separated statuses to filter by. One of: pending, running, done, failed, cancelled."
3714
+ ),
3715
+ country_code: z.string().optional().describe("Comma-separated ISO 3166-1 alpha-2 country codes, e.g. US,DE,JP."),
3716
+ url: z.string().optional().describe("Substring match against the scanned URL."),
3717
+ scan_id: z.string().optional().describe("Comma-separated scan UUIDs to fetch a specific set."),
3718
+ date_from: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive lower bound on scan creation."),
3719
+ date_to: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive upper bound on scan creation."),
3720
+ tag: z.string().optional().describe("Comma-separated tag slugs to filter by."),
3721
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
3722
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size (1-200). Default 50.")
3723
+ };
3724
+ var listScansTool = {
3725
+ name: "list_scans",
3726
+ description: "List scans for the caller's organization with optional filters (status, country, URL substring, date range, tags). Returns a paginated envelope.",
3727
+ annotations: {
3728
+ title: "List Scans",
3729
+ readOnlyHint: true,
3730
+ destructiveHint: false,
3731
+ idempotentHint: true,
3732
+ openWorldHint: false
3733
+ },
3734
+ inputSchema: z.object(ListScansInputShape),
3735
+ handler: async (input, ctx) => {
3736
+ const filters = {
3737
+ page: input.page,
3738
+ limit: input.limit,
3739
+ ...input.status !== void 0 ? { status: input.status } : {},
3740
+ ...input.country_code !== void 0 ? { country_code: input.country_code } : {},
3741
+ ...input.url !== void 0 ? { url: input.url } : {},
3742
+ ...input.scan_id !== void 0 ? { scan_id: input.scan_id } : {},
3743
+ ...input.date_from !== void 0 ? { date_from: input.date_from } : {},
3744
+ ...input.date_to !== void 0 ? { date_to: input.date_to } : {},
3745
+ ...input.tag !== void 0 ? { tag: input.tag } : {}
3746
+ };
3747
+ const result = await ctx.api.listScans(filters);
3748
+ if (result.isErr()) {
3749
+ return err(mapApiError(result.error));
3750
+ }
3751
+ return ok(result.value);
3752
+ }
3753
+ };
3754
+ var RecheckScansInputShape = {
3755
+ scope_type: z.enum(["last_n", "hours"]).describe(
3756
+ "Selection mode. `last_n` = most recent N scans; `hours` = scans from the past N hours."
3757
+ ),
3758
+ scope_value: z.number().int().min(1).max(5e3).describe(
3759
+ "Number of scans (`last_n`, max 5000) OR number of hours (`hours`, max 72). API enforces tighter caps per mode."
3760
+ )
3761
+ };
3762
+ var recheckScansTool = {
3763
+ name: "recheck_scans",
3764
+ description: "Re-run the checker pipeline against recent COMPLETED scans (e.g. after updating policies or custom rules). Returns the number of scans queued for re-evaluation. No new crawl fee \u2014 only the checker cost.",
3765
+ annotations: {
3766
+ title: "Recheck Scans",
3767
+ readOnlyHint: false,
3768
+ destructiveHint: false,
3769
+ idempotentHint: false,
3770
+ openWorldHint: false
3771
+ },
3772
+ inputSchema: z.object(RecheckScansInputShape),
3773
+ handler: async (input, ctx) => {
3774
+ const result = await ctx.api.recheckScans({
3775
+ scope_type: input.scope_type,
3776
+ scope_value: input.scope_value
3777
+ });
3778
+ if (result.isErr()) return err(mapApiError(result.error));
3779
+ return ok(result.value);
3780
+ }
3781
+ };
3782
+ var DeleteTagDefinitionInputShape = {
3783
+ slug: z.string().min(1).max(100).describe("Custom tag slug to delete.")
3784
+ };
3785
+ var deleteTagDefinitionTool = {
3786
+ name: "delete_tag_definition",
3787
+ description: "Delete a CUSTOM tag definition. Historical tag assignments are preserved; future scans will not receive this tag. System tags cannot be deleted.",
3788
+ annotations: {
3789
+ title: "Delete Tag Definition",
3790
+ readOnlyHint: false,
3791
+ destructiveHint: true,
3792
+ idempotentHint: true,
3793
+ openWorldHint: false
3794
+ },
3795
+ inputSchema: z.object(DeleteTagDefinitionInputShape),
3796
+ handler: async (input, ctx) => {
3797
+ const result = await ctx.api.deleteTagDefinition(input.slug);
3798
+ if (result.isErr()) return err(mapApiError(result.error));
3799
+ return ok({ deleted: true });
3800
+ }
3801
+ };
3802
+ var GetTagDefinitionInputShape = {
3803
+ slug: z.string().min(1).max(100).describe("Tag slug (e.g. `malware`, `redirect_chain_too_long`).")
3804
+ };
3805
+ var getTagDefinitionTool = {
3806
+ name: "get_tag_definition",
3807
+ description: "Get full definition of one tag: display name, description, severity, category, source (system vs custom), public-report visibility, usage counts, plus `linked_rules` \u2014 the custom rules currently producing this tag (id, name, active flag). Fetch a specific rule's full config via `get_custom_rule`.",
3808
+ annotations: {
3809
+ title: "Get Tag Definition",
3810
+ readOnlyHint: true,
3811
+ destructiveHint: false,
3812
+ idempotentHint: true,
3813
+ openWorldHint: false
3814
+ },
3815
+ inputSchema: z.object(GetTagDefinitionInputShape),
3816
+ handler: async (input, ctx) => {
3817
+ const result = await ctx.api.getTagDefinition(input.slug);
3818
+ if (result.isErr()) return err(mapApiError(result.error));
3819
+ return ok(result.value);
3820
+ }
3821
+ };
3822
+ var ListScanTagsInputShape = {
3823
+ scan_id: z.string().uuid().describe("Scan UUID.")
3824
+ };
3825
+ var listScanTagsTool = {
3826
+ name: "list_scan_tags",
3827
+ description: "List every tag (system + custom) attached to one scan by the checker pipeline, with display name, category, and severity.",
3828
+ annotations: {
3829
+ title: "List Scan Tags",
3830
+ readOnlyHint: true,
3831
+ destructiveHint: false,
3832
+ idempotentHint: true,
3833
+ openWorldHint: false
3834
+ },
3835
+ inputSchema: z.object(ListScanTagsInputShape),
3836
+ handler: async (input, ctx) => {
3837
+ const result = await ctx.api.listScanTags(input.scan_id);
3838
+ if (result.isErr()) return err(mapApiError(result.error));
3839
+ return ok({ items: result.value, total: result.value.length });
3840
+ }
3841
+ };
3842
+ var ListTagsInputShape = {};
3843
+ var listTagsTool = {
3844
+ 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).",
3846
+ annotations: {
3847
+ title: "List Tags",
3848
+ readOnlyHint: true,
3849
+ destructiveHint: false,
3850
+ idempotentHint: true,
3851
+ openWorldHint: false
3852
+ },
3853
+ inputSchema: z.object(ListTagsInputShape),
3854
+ handler: async (_input, ctx) => {
3855
+ const result = await ctx.api.listTags();
3856
+ if (result.isErr()) return err(mapApiError(result.error));
3857
+ return ok({ items: result.value, total: result.value.length });
3858
+ }
3859
+ };
3860
+ var UpdateTagDefinitionInputShape = {
3861
+ slug: z.string().min(1).max(100).describe("Tag slug."),
3862
+ display_name: z.string().min(1).max(200).optional().describe("New human-readable name."),
3863
+ description: z.string().max(2e3).optional().describe("New description."),
3864
+ severity: z.enum(["high", "medium", "low"]).optional().describe("New severity level."),
3865
+ show_in_public_report: z.boolean().optional().describe("Whether the tag appears in the public scan-report view.")
3866
+ };
3867
+ var updateTagDefinitionTool = {
3868
+ name: "update_tag_definition",
3869
+ description: "Update display fields of a CUSTOM tag (system tags are read-only). Only supplied fields are touched. To read the updated definition, follow up with `get_tag_definition`.",
3870
+ annotations: {
3871
+ title: "Update Tag Definition",
3872
+ readOnlyHint: false,
3873
+ destructiveHint: false,
3874
+ idempotentHint: true,
3875
+ openWorldHint: false
3876
+ },
3877
+ inputSchema: z.object(UpdateTagDefinitionInputShape),
3878
+ handler: async (input, ctx) => {
3879
+ const body = {};
3880
+ if (input.display_name !== void 0) body.display_name = input.display_name;
3881
+ if (input.description !== void 0) body.description = input.description;
3882
+ if (input.severity !== void 0) body.severity = input.severity;
3883
+ if (input.show_in_public_report !== void 0) {
3884
+ body.show_in_public_report = input.show_in_public_report;
3885
+ }
3886
+ const result = await ctx.api.updateTagDefinition(input.slug, body);
3887
+ if (result.isErr()) return err(mapApiError(result.error));
3888
+ return ok({ updated: true });
3889
+ }
3890
+ };
3891
+ var BulkReplayWebhookInputShape = {
3892
+ webhook_id: z.string().uuid().describe("Webhook endpoint UUID."),
3893
+ from_ts: z.string().datetime().describe("ISO-8601 lower bound (inclusive) of the time window to replay."),
3894
+ to_ts: z.string().datetime().describe("ISO-8601 upper bound (exclusive) of the time window to replay.")
3895
+ };
3896
+ var bulkReplayWebhookTool = {
3897
+ name: "bulk_replay_webhook",
3898
+ description: "Replay every delivery attempt for this webhook endpoint that landed in [from_ts, to_ts). Returns `{ replayed, skipped }` counts. Use to recover after a downstream outage \u2014 every event in the window is re-fired.",
3899
+ annotations: {
3900
+ title: "Bulk Replay Webhook",
3901
+ readOnlyHint: false,
3902
+ destructiveHint: false,
3903
+ idempotentHint: false,
3904
+ openWorldHint: false
3905
+ },
3906
+ inputSchema: z.object(BulkReplayWebhookInputShape),
3907
+ handler: async (input, ctx) => {
3908
+ const result = await ctx.api.bulkReplayWebhook(input.webhook_id, {
3909
+ from_ts: input.from_ts,
3910
+ to_ts: input.to_ts
3911
+ });
3912
+ if (result.isErr()) return err(mapApiError(result.error));
3913
+ return ok(result.value);
3914
+ }
3915
+ };
3916
+ var CreateWebhookInputShape = {
3917
+ url: z.string().url().describe("HTTPS endpoint URL."),
3918
+ description: z.string().max(200).default("").describe("Free-form label shown in the dashboard."),
3919
+ event_types: z.array(z.string()).min(0).max(50).default([]).describe(
3920
+ "Event-type slugs to subscribe to (see `list_webhook_event_types`). Empty array = subscribe to ALL events."
3921
+ ),
3922
+ campaign_ids: z.array(z.string().uuid()).max(50).default([]).describe("Restrict to events from these campaigns. Empty array = events from every campaign.")
3923
+ };
3924
+ var createWebhookTool = {
3925
+ name: "create_webhook",
3926
+ description: "Register a webhook endpoint for the chosen event types. Response is a `{ webhook, secret }` envelope \u2014 the HMAC-SHA256 SIGNING SECRET is returned once and the caller MUST store it to verify event signatures.",
3927
+ annotations: {
3928
+ title: "Create Webhook",
3929
+ readOnlyHint: false,
3930
+ destructiveHint: false,
3931
+ idempotentHint: false,
3932
+ openWorldHint: false
3933
+ },
3934
+ inputSchema: z.object(CreateWebhookInputShape),
3935
+ handler: async (input, ctx) => {
3936
+ const result = await ctx.api.createWebhook({
3937
+ url: input.url,
3938
+ description: input.description,
3939
+ event_types: input.event_types,
3940
+ campaign_ids: input.campaign_ids
3941
+ });
3942
+ if (result.isErr()) return err(mapApiError(result.error));
3943
+ return ok(result.value);
3944
+ }
3945
+ };
3946
+ var DeleteWebhookInputShape = {
3947
+ webhook_id: z.string().uuid().describe("Webhook endpoint UUID to remove.")
3948
+ };
3949
+ var deleteWebhookTool = {
3950
+ name: "delete_webhook",
3951
+ description: "Unregister a webhook endpoint. No further events are delivered; in-flight retries are dropped. Past delivery history is preserved.",
3952
+ annotations: {
3953
+ title: "Delete Webhook",
3954
+ readOnlyHint: false,
3955
+ destructiveHint: true,
3956
+ idempotentHint: true,
3957
+ openWorldHint: false
3958
+ },
3959
+ inputSchema: z.object(DeleteWebhookInputShape),
3960
+ handler: async (input, ctx) => {
3961
+ const result = await ctx.api.deleteWebhook(input.webhook_id);
3962
+ if (result.isErr()) return err(mapApiError(result.error));
3963
+ return ok({ deleted: true });
3964
+ }
3965
+ };
3966
+ var GetWebhookInputShape = {
3967
+ webhook_id: z.string().uuid().describe("Webhook endpoint UUID.")
3968
+ };
3969
+ var getWebhookTool = {
3970
+ name: "get_webhook",
3971
+ description: "Get one webhook endpoint by UUID with URL, subscribed event types, active flag.",
3972
+ annotations: {
3973
+ title: "Get Webhook",
3974
+ readOnlyHint: true,
3975
+ destructiveHint: false,
3976
+ idempotentHint: true,
3977
+ openWorldHint: false
3978
+ },
3979
+ inputSchema: z.object(GetWebhookInputShape),
3980
+ handler: async (input, ctx) => {
3981
+ const result = await ctx.api.getWebhook(input.webhook_id);
3982
+ if (result.isErr()) return err(mapApiError(result.error));
3983
+ return ok(result.value);
3984
+ }
3985
+ };
3986
+ var ListWebhookDeliveriesInputShape = {
3987
+ webhook_id: z.string().uuid().describe("Webhook endpoint UUID."),
3988
+ page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
3989
+ limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
3990
+ };
3991
+ var listWebhookDeliveriesTool = {
3992
+ 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.",
3994
+ annotations: {
3995
+ title: "List Webhook Deliveries",
3996
+ readOnlyHint: true,
3997
+ destructiveHint: false,
3998
+ idempotentHint: true,
3999
+ openWorldHint: false
4000
+ },
4001
+ inputSchema: z.object(ListWebhookDeliveriesInputShape),
4002
+ handler: async (input, ctx) => {
4003
+ const result = await ctx.api.listWebhookDeliveries(input.webhook_id, {
4004
+ page: input.page,
4005
+ limit: input.limit
4006
+ });
4007
+ if (result.isErr()) return err(mapApiError(result.error));
4008
+ return ok(result.value);
4009
+ }
4010
+ };
4011
+ var ListWebhookEventTypesInputShape = {};
4012
+ var listWebhookEventTypesTool = {
4013
+ name: "list_webhook_event_types",
4014
+ description: "List the catalog of event types a webhook can subscribe to (e.g. `scan.done`, `alert.opened`, `campaign.run.completed`) with each event's description and a sample payload.",
4015
+ annotations: {
4016
+ title: "List Webhook Event Types",
4017
+ readOnlyHint: true,
4018
+ destructiveHint: false,
4019
+ idempotentHint: true,
4020
+ openWorldHint: false
4021
+ },
4022
+ inputSchema: z.object(ListWebhookEventTypesInputShape),
4023
+ handler: async (_input, ctx) => {
4024
+ const result = await ctx.api.listWebhookEventTypes();
4025
+ if (result.isErr()) return err(mapApiError(result.error));
4026
+ return ok({ items: result.value.entries, total: result.value.entries.length });
4027
+ }
4028
+ };
4029
+ var ListWebhooksInputShape = {};
4030
+ var listWebhooksTool = {
4031
+ name: "list_webhooks",
4032
+ description: "List the organization's registered webhook endpoints with their URL, subscribed event types, and active flag.",
4033
+ annotations: {
4034
+ title: "List Webhooks",
4035
+ readOnlyHint: true,
4036
+ destructiveHint: false,
4037
+ idempotentHint: true,
4038
+ openWorldHint: false
4039
+ },
4040
+ inputSchema: z.object(ListWebhooksInputShape),
4041
+ handler: async (_input, ctx) => {
4042
+ const result = await ctx.api.listWebhooks();
4043
+ if (result.isErr()) return err(mapApiError(result.error));
4044
+ return ok({ items: result.value, total: result.value.length });
4045
+ }
4046
+ };
4047
+ var ReplayWebhookDeliveryInputShape = {
4048
+ attempt_id: z.string().uuid().describe("Delivery-attempt UUID (from `list_webhook_deliveries`).")
4049
+ };
4050
+ var replayWebhookDeliveryTool = {
4051
+ name: "replay_webhook_delivery",
4052
+ description: "Queue a re-attempt for one specific webhook-delivery attempt by id. Useful when an endpoint was temporarily down. Result of the replay shows up as a new entry in `list_webhook_deliveries`.",
4053
+ annotations: {
4054
+ title: "Replay Webhook Delivery",
4055
+ readOnlyHint: false,
4056
+ destructiveHint: false,
4057
+ idempotentHint: false,
4058
+ openWorldHint: false
4059
+ },
4060
+ inputSchema: z.object(ReplayWebhookDeliveryInputShape),
4061
+ handler: async (input, ctx) => {
4062
+ const result = await ctx.api.replayWebhookDelivery(input.attempt_id);
4063
+ if (result.isErr()) return err(mapApiError(result.error));
4064
+ return ok({ queued: true });
4065
+ }
4066
+ };
4067
+ var RotateWebhookSecretInputShape = {
4068
+ webhook_id: z.string().uuid().describe("Webhook endpoint UUID.")
4069
+ };
4070
+ var rotateWebhookSecretTool = {
4071
+ name: "rotate_webhook_secret",
4072
+ description: "Generate a new signing secret for a webhook. The new secret is returned IN FULL once \u2014 tell the user to store it. Subsequent deliveries are signed with the new secret; the old one stops working immediately.",
4073
+ annotations: {
4074
+ title: "Rotate Webhook Secret",
4075
+ readOnlyHint: false,
4076
+ destructiveHint: true,
4077
+ idempotentHint: false,
4078
+ openWorldHint: false
4079
+ },
4080
+ inputSchema: z.object(RotateWebhookSecretInputShape),
4081
+ handler: async (input, ctx) => {
4082
+ const result = await ctx.api.rotateWebhookSecret(input.webhook_id);
4083
+ if (result.isErr()) return err(mapApiError(result.error));
4084
+ return ok(result.value);
4085
+ }
4086
+ };
4087
+ var TestWebhookInputShape = {
4088
+ webhook_id: z.string().uuid().describe("Webhook endpoint UUID."),
4089
+ event_type: z.string().min(1).max(100).describe(
4090
+ "Event type slug whose sample payload to send (see `list_webhook_event_types` for the catalog)."
4091
+ )
4092
+ };
4093
+ var testWebhookTool = {
4094
+ name: "test_webhook",
4095
+ description: "Dispatch a synthetic event with the sample payload for the given `event_type` to the webhook endpoint and return the receiver's response synchronously. Includes HTTP status, elapsed time, and a snippet of the response body so the operator can diagnose receiver bugs.",
4096
+ annotations: {
4097
+ title: "Test Webhook",
4098
+ readOnlyHint: false,
4099
+ destructiveHint: false,
4100
+ idempotentHint: false,
4101
+ openWorldHint: true
4102
+ },
4103
+ inputSchema: z.object(TestWebhookInputShape),
4104
+ handler: async (input, ctx) => {
4105
+ const result = await ctx.api.testWebhook(input.webhook_id, { event_type: input.event_type });
4106
+ if (result.isErr()) return err(mapApiError(result.error));
4107
+ return ok(result.value);
4108
+ }
4109
+ };
4110
+ var UpdateWebhookInputShape = {
4111
+ webhook_id: z.string().uuid().describe("Webhook UUID."),
4112
+ url: z.string().url().optional().describe("New endpoint URL."),
4113
+ event_types: z.array(z.string()).max(50).optional().describe("Replace the subscribed-event-types list."),
4114
+ is_active: z.boolean().optional().describe("Enable / disable delivery.")
4115
+ };
4116
+ var updateWebhookTool = {
4117
+ name: "update_webhook",
4118
+ description: "Update a webhook endpoint's URL, event-type subscriptions, and/or active flag. Signing secret is NOT rotated by this call \u2014 use `rotate_webhook_secret` for that.",
4119
+ annotations: {
4120
+ title: "Update Webhook",
4121
+ readOnlyHint: false,
4122
+ destructiveHint: false,
4123
+ idempotentHint: true,
4124
+ openWorldHint: false
4125
+ },
4126
+ inputSchema: z.object(UpdateWebhookInputShape),
4127
+ handler: async (input, ctx) => {
4128
+ const body = {
4129
+ ...input.url !== void 0 ? { url: input.url } : {},
4130
+ ...input.event_types !== void 0 ? { event_types: input.event_types } : {},
4131
+ ...input.is_active !== void 0 ? { is_active: input.is_active } : {}
4132
+ };
4133
+ const result = await ctx.api.updateWebhook(input.webhook_id, body);
4134
+ if (result.isErr()) return err(mapApiError(result.error));
4135
+ return ok(result.value);
4136
+ }
4137
+ };
4138
+
4139
+ // src/application/tool-registry.ts
4140
+ function registerAllTools(register) {
4141
+ register(getAccountTool);
4142
+ register(updateOrgTool);
4143
+ register(listOrgUsersTool);
4144
+ register(inviteUserTool);
4145
+ register(updateUserRoleTool);
4146
+ register(removeUserTool);
4147
+ register(transferOwnershipTool);
4148
+ register(listOrgRolesTool);
4149
+ register(listApiKeysTool);
4150
+ register(createApiKeyTool);
4151
+ register(revokeApiKeyTool);
4152
+ register(listGeosTool);
4153
+ register(listEmulatorsTool);
4154
+ register(getScanTool);
4155
+ register(listScansTool);
4156
+ register(cancelScanTool);
4157
+ register(createBulkScansTool);
4158
+ register(createScanTool);
4159
+ register(recheckScansTool);
4160
+ register(getCampaignTool);
4161
+ register(listCampaignsTool);
4162
+ register(archiveCampaignTool);
4163
+ register(unarchiveCampaignTool);
4164
+ register(cancelCampaignTool);
4165
+ register(runCampaignTool);
4166
+ register(createCampaignTool);
4167
+ register(updateCampaignTool);
4168
+ register(listCampaignRunsTool);
4169
+ register(listCampaignsPickerTool);
4170
+ register(getRunTool);
4171
+ register(listRunScansTool);
4172
+ register(cancelRunTool);
4173
+ register(getCampaignGroupTool);
4174
+ register(listCampaignGroupsTool);
4175
+ register(createCampaignGroupTool);
4176
+ register(updateCampaignGroupTool);
4177
+ register(runCampaignGroupTool);
4178
+ register(cancelCampaignGroupTool);
4179
+ register(archiveCampaignGroupTool);
4180
+ register(unarchiveCampaignGroupTool);
4181
+ register(pauseCampaignGroupScheduleTool);
4182
+ register(resumeCampaignGroupScheduleTool);
4183
+ register(listTagsTool);
4184
+ register(getTagDefinitionTool);
4185
+ register(updateTagDefinitionTool);
4186
+ register(deleteTagDefinitionTool);
4187
+ register(listScanTagsTool);
4188
+ register(listCustomRulesTool);
4189
+ register(getCustomRuleTool);
4190
+ register(createCustomRuleTool);
4191
+ register(updateCustomRuleTool);
4192
+ register(deleteCustomRuleTool);
4193
+ register(testCustomRuleTool);
4194
+ register(listPolicySetsTool);
4195
+ register(getPolicySetTool);
4196
+ register(createPolicySetTool);
4197
+ register(updatePolicySetTool);
4198
+ register(deletePolicySetTool);
4199
+ register(requestPolicySetApprovalTool);
4200
+ register(listAlertsTool);
4201
+ register(updateAlertStatusTool);
4202
+ register(getAlertStatsTool);
4203
+ register(listWebhooksTool);
4204
+ register(getWebhookTool);
4205
+ register(createWebhookTool);
4206
+ register(updateWebhookTool);
4207
+ register(deleteWebhookTool);
4208
+ register(listWebhookEventTypesTool);
4209
+ register(listWebhookDeliveriesTool);
4210
+ register(testWebhookTool);
4211
+ register(rotateWebhookSecretTool);
4212
+ register(replayWebhookDeliveryTool);
4213
+ register(bulkReplayWebhookTool);
4214
+ register(getBillingSummaryTool);
4215
+ register(listUsageTool);
4216
+ register(getUsageSummaryTool);
4217
+ register(listBalanceHistoryTool);
4218
+ register(listInvoicesTool);
4219
+ register(listAlertDestinationsTool);
4220
+ register(deleteAlertDestinationTool);
4221
+ register(setAlertDestinationVersionTool);
4222
+ register(getCampaignAlertOverridesTool);
4223
+ register(setCampaignAlertOverridesTool);
4224
+ }
4225
+
4226
+ // src/presentation/shared/wire-tools.ts
4227
+ function wireToolsIntoMcpServer(server, ctxProvider) {
4228
+ registerAllTools((tool) => {
4229
+ server.registerTool(
4230
+ tool.name,
4231
+ {
4232
+ title: tool.annotations.title,
4233
+ description: tool.description,
4234
+ inputSchema: tool.inputSchema,
4235
+ annotations: {
4236
+ title: tool.annotations.title,
4237
+ readOnlyHint: tool.annotations.readOnlyHint,
4238
+ destructiveHint: tool.annotations.destructiveHint,
4239
+ idempotentHint: tool.annotations.idempotentHint,
4240
+ openWorldHint: tool.annotations.openWorldHint
4241
+ }
4242
+ },
4243
+ (async (rawArgs, _extra) => {
4244
+ const ctx = ctxProvider();
4245
+ const parsed = tool.inputSchema.parse(rawArgs);
4246
+ const result = await tool.handler(parsed, ctx);
4247
+ if (result.isErr()) {
4248
+ return toolErrorToMcpResult(result.error);
4249
+ }
4250
+ return toolOkToMcpResult(result.value);
4251
+ })
4252
+ );
4253
+ });
4254
+ }
4255
+ function toolOkToMcpResult(value) {
4256
+ const isPlainObject = value !== null && typeof value === "object" && !Array.isArray(value);
4257
+ return {
4258
+ content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
4259
+ ...isPlainObject ? { structuredContent: value } : {}
4260
+ };
4261
+ }
4262
+ function toolErrorToMcpResult(error) {
4263
+ return {
4264
+ isError: true,
4265
+ content: [{ type: "text", text: formatToolError(error) }]
4266
+ };
4267
+ }
4268
+ function formatToolError(error) {
4269
+ switch (error.kind) {
4270
+ case "unauthorized":
4271
+ return `Unauthorized: ${error.message}`;
4272
+ case "forbidden":
4273
+ return `Forbidden${error.code === void 0 ? "" : ` (${error.code})`}: ${error.message}`;
4274
+ case "not-found":
4275
+ return `Not found: ${error.message}`;
4276
+ case "rate-limited":
4277
+ return `Rate limited: ${error.message}${error.retryAfterMs === void 0 ? "" : ` (retry after ${String(error.retryAfterMs)} ms)`}`;
4278
+ case "invalid-input":
4279
+ return `Invalid input${error.code === void 0 ? "" : ` (${error.code})`}: ${error.message}`;
4280
+ case "upstream":
4281
+ return `Upstream error: ${error.message}`;
4282
+ case "internal":
4283
+ return `Internal error: ${error.message}`;
4284
+ }
4285
+ }
4286
+
4287
+ export { BearerToken, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
4288
+ //# sourceMappingURL=chunk-4MNLSWSZ.js.map
4289
+ //# sourceMappingURL=chunk-4MNLSWSZ.js.map