@kaminari-ad/mcp 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.23.0] - 2026-09-29
11
+
12
+ > Requires the API-side ad-format deploy. Until it lands, production
13
+ > silently ignores `ad_formats` as an unknown body field: a `['pop']` request
14
+ > checks and bills every format and reads back without the field. Do not tag
15
+ > this release ahead of the API.
16
+
17
+ ### Added
18
+
19
+ - **`ad_formats` on `create_scan`, `create_campaign` and `update_campaign`.**
20
+ An ad-discovery run can now be narrowed to some formats: any of
21
+ `banner`, `video` and `pop` (pop-unders and tab-unders). A format left out
22
+ is neither captured nor billed, so `['pop']` checks a publisher's pops
23
+ without screenshotting or clicking every banner on the page. Omit the
24
+ field to check every format; on `update_campaign`, pass `null` to go back
25
+ to every format.
26
+
27
+ `ScanResponse` and `CampaignResponse` echo the selection (`null` for
28
+ every format). The read side is parsed as plain strings rather than the
29
+ request enum, so a format added on the API side cannot fail a whole
30
+ `get_scan` / `get_campaign` call. The generated schemas were left alone:
31
+ the field is declared through the port's escape hatch until the next
32
+ regen against the deployed API, like `max_discovered_ads` before it.
33
+
34
+ ### Security
35
+
36
+ - `undici` 8.10.0 → 8.11.2 (GHSA-3wwx-pv8p-q78v, WebSocket
37
+ permessage-deflate DoS) and transitive `ip-address` 10.4.0 → 10.7.2
38
+ (link-local and NAT64 classification bypasses), which were failing
39
+ `audit:deps`.
40
+
10
41
  ## [0.22.0] - 2026-09-15
11
42
 
12
43
  ### Changed
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { NAME, VERSION, err, ok } from './chunk-J6SALJR4.js';
2
+ import { NAME, VERSION, err, ok } from './chunk-6INS6YMW.js';
3
3
  import process from 'process';
4
4
  import { z } from 'zod';
5
5
 
@@ -160,10 +160,10 @@ async function main() {
160
160
  }
161
161
  const config = configResult.value;
162
162
  if (config.transport === "stdio") {
163
- const { bootstrapStdio } = await import('./stdio-bootstrap-6PXVYXHT.js');
163
+ const { bootstrapStdio } = await import('./stdio-bootstrap-M7I2WD5F.js');
164
164
  return bootstrapStdio(config);
165
165
  }
166
- const { bootstrapHttp } = await import('./http-bootstrap-JX5RQALF.js');
166
+ const { bootstrapHttp } = await import('./http-bootstrap-NSZVATPV.js');
167
167
  return bootstrapHttp(config);
168
168
  }
169
169
  main().then(
@@ -3,8 +3,8 @@ export { err, ok } from 'neverthrow';
3
3
 
4
4
  // src/shared/version.ts
5
5
  var NAME = "@kaminari-ad/mcp";
6
- var VERSION = "0.22.0";
6
+ var VERSION = "0.23.0";
7
7
 
8
8
  export { NAME, VERSION };
9
- //# sourceMappingURL=chunk-J6SALJR4.js.map
10
- //# sourceMappingURL=chunk-J6SALJR4.js.map
9
+ //# sourceMappingURL=chunk-6INS6YMW.js.map
10
+ //# sourceMappingURL=chunk-6INS6YMW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;;AASO,IAAM,IAAA,GAAO;AACb,IAAM,OAAA,GAAU","file":"chunk-J6SALJR4.js","sourcesContent":["/**\n * Package version and name. Hard-coded as constants here, asserted to\n * match `package.json` by a unit test.\n *\n * Why not import `package.json`: it would force JSON-module support at\n * runtime and tsup-bundling would inline the entire manifest. Two\n * constants + one assertion test is simpler and gives the same safety.\n */\n\nexport const NAME = \"@kaminari-ad/mcp\";\nexport const VERSION = \"0.22.0\";\n"]}
1
+ {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;;AASO,IAAM,IAAA,GAAO;AACb,IAAM,OAAA,GAAU","file":"chunk-6INS6YMW.js","sourcesContent":["/**\n * Package version and name. Hard-coded as constants here, asserted to\n * match `package.json` by a unit test.\n *\n * Why not import `package.json`: it would force JSON-module support at\n * runtime and tsup-bundling would inline the entire manifest. Two\n * constants + one assertion test is simpler and gives the same safety.\n */\n\nexport const NAME = \"@kaminari-ad/mcp\";\nexport const VERSION = \"0.23.0\";\n"]}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { err, ok } from './chunk-J6SALJR4.js';
2
+ import { err, ok } from './chunk-6INS6YMW.js';
3
3
  import { createHash, randomUUID } from 'crypto';
4
4
  import createClient from 'openapi-fetch';
5
5
  import { fetch } from 'undici';
@@ -1380,7 +1380,7 @@ var CampaignSchema = schemas.CampaignResponse.pick({
1380
1380
  is_archived: true,
1381
1381
  created_at: true,
1382
1382
  last_run_at: true
1383
- }).extend({ repeat_mode: schemas.RepeatMode.default("isolated") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).extend({ ignore_first_n_domains: z.number().int().optional() }).strip();
1383
+ }).extend({ repeat_mode: schemas.RepeatMode.default("isolated") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).extend({ ad_formats: z.array(z.string()).nullable().optional() }).extend({ ignore_first_n_domains: z.number().int().optional() }).strip();
1384
1384
  var parseCampaign = (raw) => parseWithSchema(CampaignSchema, raw, "campaign");
1385
1385
  var parseCampaignPage = (raw) => parsePagedWithItemSchema(CampaignSchema, raw, "campaigns");
1386
1386
 
@@ -1844,7 +1844,7 @@ var ScanSchema = schemas.ScanResponse.pick({
1844
1844
  repeat_scan_ids: true,
1845
1845
  retry_attempt: true,
1846
1846
  retry_max_attempts: true
1847
- }).extend({ creative_kind: z.string().default("banner") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).extend({ network_host: z.string().optional() }).extend({ ignore_first_n_domains: z.number().int().optional() }).strip();
1847
+ }).extend({ creative_kind: z.string().default("banner") }).extend({ max_discovered_ads: z.number().int().nullable().optional() }).extend({ ad_formats: z.array(z.string()).nullable().optional() }).extend({ network_host: z.string().optional() }).extend({ ignore_first_n_domains: z.number().int().optional() }).strip();
1848
1848
  var ScanArraySchema = z.array(ScanSchema);
1849
1849
  var parseScan = (raw) => parseWithSchema(ScanSchema, raw, "scan");
1850
1850
  var parseScanArray = (raw) => parseWithSchema(ScanArraySchema, raw, "scans");
@@ -3903,6 +3903,13 @@ var cancelCampaignTool = {
3903
3903
  return ok(result.value);
3904
3904
  }
3905
3905
  };
3906
+ var AD_FORMATS_DESCRIPTION = "Which ad formats an ad-discovery run checks: any of `banner`, `video` and `pop` (`pop` covers pop-unders and tab-unders), each at most once. A format left out is neither captured nor billed \u2014 `['pop']` checks the page's pops without screenshotting or clicking a single banner. A tab-under the page forces on its own is still reported as a pop. Only valid on an ad-discovery target \u2014 `ad_discovery: true` on a scan, or `campaign_type: \"ad_discovery\"` on a campaign \u2014 and rejected with 422 anywhere else, as is an empty list.";
3907
+ var adFormat = z.enum(["banner", "video", "pop"]);
3908
+ var adFormats = z.array(adFormat).min(1).max(adFormat.options.length);
3909
+ var adFormatsField = adFormats.optional().describe(`${AD_FORMATS_DESCRIPTION} Omit it to check every format.`);
3910
+ var adFormatsUpdateField = adFormats.nullable().optional().describe(
3911
+ `${AD_FORMATS_DESCRIPTION} Omitting the field leaves the campaign's current selection unchanged; pass null to go back to checking every format.`
3912
+ );
3906
3913
  var IGNORE_FIRST_N_DOMAINS_DESCRIPTION = "How many leading domains of the redirect chain to exclude from detection and tagging (0-5). Use it when the check enters through the caller's OWN click or tracking domains: without it a reputation hit on one of those tags the material and raises an alert about the caller's infrastructure rather than about the offer. Counting starts at the entry point and follows chain order; domains fold to their registrable form, so www.example.com and example.com consume one slot between them, and any other request to one of those domains is excluded too. A skipped domain is checked by NOTHING, so never set this higher than the number of domains the caller actually owns at the head of the chain \u2014 ask rather than guess. The full redirect chain is still captured and returned either way.";
3907
3914
  var ignoreFirstNDomains = z.number().int().min(0).max(5);
3908
3915
  var ignoreFirstNDomainsField = ignoreFirstNDomains.optional().describe(`${IGNORE_FIRST_N_DOMAINS_DESCRIPTION} Omit it to skip nothing (0).`);
@@ -4017,6 +4024,7 @@ var CreateCampaignInputShape = {
4017
4024
  referrer: campaignReferrerField,
4018
4025
  ignore_first_n_domains: ignoreFirstNDomainsField,
4019
4026
  max_discovered_ads: maxDiscoveredAdsField,
4027
+ ad_formats: adFormatsField,
4020
4028
  country_codes: z.array(z.string().length(2)).min(1).describe("ISO 3166-1 alpha-2 codes \u2014 one scan per country per run."),
4021
4029
  group_id: z.string().uuid().optional().describe("Parent group UUID; defaults to the org's default group."),
4022
4030
  ...campaignConfigFields,
@@ -4048,6 +4056,7 @@ var createCampaignTool = {
4048
4056
  ...input.referrer !== void 0 ? { referrer: input.referrer } : {},
4049
4057
  ...input.ignore_first_n_domains !== void 0 ? { ignore_first_n_domains: input.ignore_first_n_domains } : {},
4050
4058
  ...input.max_discovered_ads !== void 0 ? { max_discovered_ads: input.max_discovered_ads } : {},
4059
+ ...input.ad_formats !== void 0 ? { ad_formats: input.ad_formats } : {},
4051
4060
  ...input.group_id !== void 0 ? { group_id: input.group_id } : {},
4052
4061
  ...input.labels !== void 0 ? { labels: input.labels } : {},
4053
4062
  ...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
@@ -4223,6 +4232,7 @@ var UpdateCampaignInputShape = {
4223
4232
  referrer: campaignReferrerUpdateField,
4224
4233
  ignore_first_n_domains: ignoreFirstNDomainsUpdateField,
4225
4234
  max_discovered_ads: maxDiscoveredAdsUpdateField,
4235
+ ad_formats: adFormatsUpdateField,
4226
4236
  country_codes: z.array(z.string().length(2)).optional().describe("Replace the country list."),
4227
4237
  group_id: z.string().uuid().optional().describe("Move the campaign to another group."),
4228
4238
  ...campaignConfigFields,
@@ -4232,7 +4242,7 @@ var UpdateCampaignInputShape = {
4232
4242
  };
4233
4243
  var updateCampaignTool = {
4234
4244
  name: "update_campaign",
4235
- description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding, `referrer` accepts null to clear the publisher page scans are checked from, and `max_discovered_ads` accepts null to go back to the platform ad cap. `ignore_first_n_domains` is the exception: it is not nullable, so pass 0 to go back to skipping nothing.",
4245
+ description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding, `referrer` accepts null to clear the publisher page scans are checked from, `max_discovered_ads` accepts null to go back to the platform ad cap, and `ad_formats` accepts null to go back to checking every ad format. `ignore_first_n_domains` is the exception: it is not nullable, so pass 0 to go back to skipping nothing.",
4236
4246
  annotations: {
4237
4247
  title: "Update Campaign",
4238
4248
  readOnlyHint: false,
@@ -4250,6 +4260,7 @@ var updateCampaignTool = {
4250
4260
  ...input.referrer !== void 0 ? { referrer: input.referrer } : {},
4251
4261
  ...input.ignore_first_n_domains !== void 0 ? { ignore_first_n_domains: input.ignore_first_n_domains } : {},
4252
4262
  ...input.max_discovered_ads !== void 0 ? { max_discovered_ads: input.max_discovered_ads } : {},
4263
+ ...input.ad_formats !== void 0 ? { ad_formats: input.ad_formats } : {},
4253
4264
  ...input.country_codes !== void 0 ? { country_codes: input.country_codes } : {},
4254
4265
  ...input.group_id !== void 0 ? { group_id: input.group_id } : {},
4255
4266
  ...input.labels !== void 0 ? { labels: input.labels } : {},
@@ -5382,9 +5393,10 @@ var CreateScanInputShape = {
5382
5393
  campaign_id: z.string().uuid().optional().describe("Optional campaign UUID to attribute the scan to."),
5383
5394
  run_id: z.string().uuid().optional().describe("Optional run UUID inside the campaign."),
5384
5395
  ad_discovery: z.boolean().optional().describe(
5385
- "Publisher ad discovery: detect ad blocks on the page and spawn one child scan per detected ad (banner/pop). Only valid with `url`. Each child is a separate billed scan; list them with `list_scan_children`."
5396
+ "Publisher ad discovery: detect ad blocks on the page and spawn one child scan per detected ad (banner/video/pop). Only valid with `url`. Each child is a separate billed scan; list them with `list_scan_children`."
5386
5397
  ),
5387
5398
  max_discovered_ads: maxDiscoveredAdsField,
5399
+ ad_formats: adFormatsField,
5388
5400
  ...repeatRetryFields
5389
5401
  };
5390
5402
  var createScanTool = {
@@ -5413,6 +5425,7 @@ var createScanTool = {
5413
5425
  ...input.run_id !== void 0 ? { run_id: input.run_id } : {},
5414
5426
  ...input.ad_discovery !== void 0 ? { ad_discovery: input.ad_discovery } : {},
5415
5427
  ...input.max_discovered_ads !== void 0 ? { max_discovered_ads: input.max_discovered_ads } : {},
5428
+ ...input.ad_formats !== void 0 ? { ad_formats: input.ad_formats } : {},
5416
5429
  ...pickRepeatRetryBody(input)
5417
5430
  };
5418
5431
  const result = await ctx.api.createScan(body);
@@ -6301,5 +6314,5 @@ function formatToolError(error) {
6301
6314
  }
6302
6315
 
6303
6316
  export { BearerToken, SERVER_INSTRUCTIONS, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
6304
- //# sourceMappingURL=chunk-VEPJEI3S.js.map
6305
- //# sourceMappingURL=chunk-VEPJEI3S.js.map
6317
+ //# sourceMappingURL=chunk-XJZE6KTY.js.map
6318
+ //# sourceMappingURL=chunk-XJZE6KTY.js.map