@kaminari-ad/mcp 0.13.0 → 0.14.1

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,114 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.14.1] - 2026-08-24
11
+
12
+ ### Changed
13
+
14
+ - **Three tag tools described archived tags as still reachable
15
+ (KAMIAD-120).** The API now retires an archived tag everywhere: it is
16
+ no longer assigned to scans, no longer raises alerts, and an archived
17
+ platform tag is gone from the catalogue entirely. `list_tags` told the
18
+ agent the opposite — to pass `include_archived` "when resolving a slug
19
+ seen on an older scan", which now returns nothing for exactly those
20
+ slugs. `get_tag_definition` now documents the 404, and
21
+ `list_scan_tags` that a scan can report fewer tags than when it ran.
22
+ Descriptions only; no schema or shape change, so
23
+ `src/shared/api/{openapi,zod-schemas}.ts` are unaffected — the two
24
+ stale JSDoc lines about `include_archived` are regenerated from the
25
+ live spec once the API side is deployed.
26
+
27
+ ## [0.14.0] - 2026-08-23
28
+
29
+ Resyncs the tool surface with `/api/v1` (KAMIAD-158). The generated
30
+ types were already current; the hand-written gateway and tool schemas
31
+ were not, so this release is mostly about the drift between them — plus
32
+ two gates so the next one fails CI instead of reaching a user.
33
+
34
+ ### Fixed
35
+
36
+ - **Artifact downloads were unbounded.** `binaryGet` called
37
+ `arrayBuffer()` with no ceiling, so one oversized MediaFile decided
38
+ how much memory the hosted server used — the API caps none of these
39
+ endpoints. It now refuses on an oversized `content-length` and
40
+ counts bytes while reading the stream, cancelling past the limit
41
+ (256 KiB text, 8 MiB binary). The cap covers the screenshot and
42
+ invoice-PDF tools too, which had the same exposure.
43
+ - **`attach_policy_set_campaigns` described a side effect it does not
44
+ have.** A campaign belongs to exactly one policy set, so attaching
45
+ one that is bound elsewhere MOVES it; the tool said existing
46
+ bindings survive. Now flagged `destructiveHint: true` and the
47
+ description says so.
48
+ - **`bulk_update_alert_status` forwarded `filter_*` alongside `ids`**,
49
+ which the API rejects. Refused locally with the offending field
50
+ names.
51
+ - **`list_campaign_groups` advertised `last_run_at`** while the
52
+ response projection stripped it, so the new last-run filters could
53
+ not be interpreted. Projected now.
54
+ - **`set_campaign_alert_overrides` accepted values the API rejects.**
55
+ `mode` was `inherit | include | exclude`; the API has only ever
56
+ accepted `inherit | override | silence`. Every value the schema
57
+ allowed was refused upstream and every value the API wanted was
58
+ refused locally, so an agent could not change campaign alert routing
59
+ at all. `mode` now matches the API, `destination_ids` is refused
60
+ locally unless `mode` is `override` (mirroring the API's
61
+ `notifications.invalid_override_combination`), and the setter's
62
+ description explains that "route everywhere except these" does not
63
+ exist — invert the list under `override` instead.
64
+ - **`list_custom_taxonomies` claimed soft-deleted taxonomies were
65
+ listed.** They are excluded unless the new `include_inactive` is
66
+ `true`, so an agent looking for one to restore concluded it was gone.
67
+ - `list_scans` now documents `rechecking` as a filterable status.
68
+
69
+ ### Added
70
+
71
+ - **`bulk_update_alert_status`** — `POST /api/v1/alerts/bulk-status`.
72
+ Select with `ids` (max 1000) or `all_matching: true` plus `filter_*`
73
+ fields; returns `updated` / `skipped`.
74
+ - **`list_policy_set_campaigns`**, **`attach_policy_set_campaigns`**,
75
+ **`detach_policy_set_campaigns`** — policy-set membership, which was
76
+ previously not editable through MCP at all: `update_policy_set` sends
77
+ only name, description and rules. Attach warns that a campaign bound
78
+ to another set is moved rather than rejected, since a campaign can
79
+ belong to only one set.
80
+ - **`get_scan_creative_html`**, **`get_scan_vast_xml`** — creative
81
+ markup and the resolved VAST document, returned as text the model
82
+ reads directly. **`get_scan_creative_video`** — the MP4 as a resource
83
+ block. All three refuse oversized payloads (256 KiB text, 8 MiB
84
+ binary) rather than inlining them.
85
+ - Filters the API had grown and the tools never picked up:
86
+ `list_alerts` and `get_alert_stats` take `policy_set_id`, `tag`,
87
+ `country_code`, `date_from`, `date_to`, `timezone` (`get_alert_stats`
88
+ previously took no arguments at all, so its counts could not be
89
+ reconciled with a filtered list); `list_scans` takes `tag_match` and
90
+ `parent_scan_id`; `list_campaigns` and `list_campaign_groups` take
91
+ `created_from`/`created_to`/`last_run_from`/`last_run_to`/`timezone`,
92
+ and `list_campaign_groups` also takes `q`; `list_tags` takes
93
+ `include_archived`.
94
+ - **`check:api-coverage`** — asserts every `/api/v1` path + method in
95
+ the generated `openapi.ts` is reachable from the gateway, and that
96
+ each of their query parameters is representable by the gateway
97
+ method. The parameter half is the point: a path-only gate would have
98
+ passed on the state this release fixes.
99
+ - **`check:tool-enum-drift`** — every `z.enum` in a tool must have the
100
+ same value set as a generated enum, or carry a justified exemption.
101
+ Reintroducing the `mode` bug now fails CI. Equality rather than
102
+ subset: with 68 values across the generated enums, "subset of any"
103
+ accepted almost anything (`z.enum(["open"])` passed as a subset of
104
+ `AlertStatus`). A `z.enum` whose argument is not an inline array of
105
+ literals is reported rather than skipped, since an unreadable enum
106
+ is how a wrong value set would slip past.
107
+
108
+ ### Changed
109
+
110
+ - `npm run gen:api-types` formats its output with Prettier. The raw
111
+ generator emits 4-space TypeScript while `format:check` covers
112
+ `src/**`, so a no-op regen used to produce a ~17k-line indentation
113
+ diff and a failing gate.
114
+ - `.prettierignore` no longer lists `src/infrastructure/api/openapi.ts`
115
+ — the generated files moved to `src/shared/api/` long ago, so the
116
+ entry was dead and pointed at the wrong policy.
117
+
10
118
  ## [0.13.0] - 2026-08-20
11
119
 
12
120
  ### Added
@@ -1037,7 +1145,9 @@ Initial public release. The first version that ships to npm under
1037
1145
  need them.
1038
1146
  - Invoice PDF fetcher — same reason.
1039
1147
 
1040
- [Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.13.0...HEAD
1148
+ [Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.14.1...HEAD
1149
+ [0.14.1]: https://github.com/kaminari-ad/mcp/compare/v0.14.0...v0.14.1
1150
+ [0.14.0]: https://github.com/kaminari-ad/mcp/compare/v0.13.0...v0.14.0
1041
1151
  [0.13.0]: https://github.com/kaminari-ad/mcp/compare/v0.12.0...v0.13.0
1042
1152
  [0.12.0]: https://github.com/kaminari-ad/mcp/compare/v0.11.0...v0.12.0
1043
1153
  [0.11.0]: https://github.com/kaminari-ad/mcp/compare/v0.10.0...v0.11.0
package/README.md CHANGED
@@ -63,7 +63,7 @@ Add to your MCP client config (Cursor: `~/.cursor/mcp.json`; Claude Desktop: `~/
63
63
  }
64
64
  ```
65
65
 
66
- Restart your client. You should see `kaminari-ad` in the MCP servers list with 98 tools exposed.
66
+ Restart your client. You should see `kaminari-ad` in the MCP servers list with 105 tools exposed.
67
67
 
68
68
  ### 2b. Hosted HTTP transport (no install)
69
69
 
@@ -113,34 +113,34 @@ which decides which credential type minted it.
113
113
 
114
114
  ## Tools
115
115
 
116
- 98 tools mirroring most of the public `/api/v1` surface of Kaminari Ad. Every tool carries MCP behaviour annotations (`title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) so MCP clients can warn before destructive actions. The complete list, by domain:
116
+ 105 tools covering the public `/api/v1` surface of Kaminari Ad. Every tool carries MCP behaviour annotations (`title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) so MCP clients can warn before destructive actions. The complete list, by domain:
117
117
 
118
118
  - **Account** (14) — `get_account`, `update_org`, `list_org_users`, `invite_user`, `update_user_role`, `remove_user`, `transfer_ownership`, `list_org_roles`, `create_custom_role`, `list_account_labels`, `update_account_labels`, `list_api_keys`, `create_api_key`, `revoke_api_key`
119
- - **Scans** (10) — `list_scans`, `get_scan`, `list_scan_children`, `create_scan`, `create_bulk_scans`, `recheck_scans`, `cancel_scan`, `get_scan_screenshot`, `get_scan_creative_screenshot`, `get_scan_landing_screenshot`
119
+ - **Scans** (13) — `list_scans`, `get_scan`, `list_scan_children`, `create_scan`, `create_bulk_scans`, `recheck_scans`, `cancel_scan`, `get_scan_screenshot`, `get_scan_creative_screenshot`, `get_scan_landing_screenshot`, `get_scan_creative_html`, `get_scan_creative_video`, `get_scan_vast_xml`
120
120
  - **Campaigns** (10) — `list_campaigns`, `list_campaigns_picker`, `get_campaign`, `create_campaign`, `update_campaign`, `archive_campaign`, `unarchive_campaign`, `cancel_campaign`, `run_campaign`, `list_campaign_runs`
121
121
  - **Campaign groups** (10) — list/get/create/update/run/cancel/archive/unarchive + `pause_campaign_group_schedule`, `resume_campaign_group_schedule`
122
122
  - **Runs** (3) — `get_run`, `list_run_scans`, `cancel_run` (use `list_campaign_runs` to enumerate runs of a campaign — the API has no standalone `/runs` index)
123
123
  - **Tags** (5) — `list_tags`, `get_tag_definition`, `update_tag_definition`, `delete_tag_definition`, `list_scan_tags`
124
124
  - **Custom rules** (6) — `list_custom_rules`, `get_custom_rule`, `create_custom_rule`, `update_custom_rule`, `delete_custom_rule`, `test_custom_rule`
125
125
  - **Custom taxonomies** (7) — `list_custom_taxonomies`, `get_custom_taxonomy`, `create_custom_taxonomy`, `update_custom_taxonomy`, `delete_custom_taxonomy`, `restore_custom_taxonomy`, `parse_custom_taxonomy_text`
126
- - **Policy sets** (6) — `list_policy_sets`, `get_policy_set`, `create_policy_set`, `update_policy_set`, `delete_policy_set`, `request_policy_set_approval`
127
- - **Alerts** (3) — `list_alerts`, `update_alert_status`, `get_alert_stats`
126
+ - **Policy sets** (9) — `list_policy_sets`, `get_policy_set`, `create_policy_set`, `update_policy_set`, `delete_policy_set`, `request_policy_set_approval`, `list_policy_set_campaigns`, `attach_policy_set_campaigns`, `detach_policy_set_campaigns`
127
+ - **Alerts** (4) — `list_alerts`, `update_alert_status`, `bulk_update_alert_status`, `get_alert_stats`
128
128
  - **Webhooks** (11) — `list_webhooks`, `get_webhook`, `create_webhook`, `update_webhook`, `delete_webhook`, `list_webhook_event_types`, `list_webhook_deliveries`, `test_webhook`, `rotate_webhook_secret`, `replay_webhook_delivery`, `bulk_replay_webhook`
129
129
  - **Billing** (4) — `get_billing_summary`, `list_usage`, `get_usage_summary`, `list_balance_history`
130
130
  - **Invoicing** (2) — `list_invoices`, `get_invoice_pdf`
131
131
  - **Alert notifications** (5) — `list_alert_destinations`, `delete_alert_destination`, `set_alert_destination_version`, `get_campaign_alert_overrides`, `set_campaign_alert_overrides`
132
132
  - **Reference data** (2) — `list_geos`, `list_emulators`
133
133
 
134
- Screenshots (`get_scan_screenshot`, `get_scan_creative_screenshot`, `get_scan_landing_screenshot`) come back as inline MCP `image` blocks and `get_invoice_pdf` as an inline resource block — no second fetch, no presigned URL.
134
+ Screenshots (`get_scan_screenshot`, `get_scan_creative_screenshot`, `get_scan_landing_screenshot`) come back as inline MCP `image` blocks; `get_invoice_pdf` and `get_scan_creative_video` as inline resource blocks — no second fetch, no presigned URL. The two text artifacts (`get_scan_creative_html`, `get_scan_vast_xml`) come back as strings the model can read directly. Every artifact download is size-capped in the gateway — 256 KiB for the text artifacts, 8 MiB for the binary ones — and refused while reading rather than buffered and then rejected.
135
135
 
136
- Not exposed (intentionally): the raw creative artifacts (`creative-html`, `creative-video`, `vast-xml`) and the public marketing forms (`/contact`, `/demo-inquiries`). Open an issue if you need one of those.
136
+ Not exposed (intentionally): the public marketing forms, which are anonymous intake for kaminari.ad itself rather than an agent capability.
137
137
 
138
138
  ## Example agent prompts
139
139
 
140
140
  These three prompts each exercise a different cross-section of tools and demonstrate the typical agent workflow:
141
141
 
142
142
  1. **"Scan https://news.example.com/article-promo across US, UK, DE on mobile profiles, flag anything that redirects to a paywall."** Touches `list_emulators` → `create_bulk_scans` → wait → `list_scans` (status=completed) → `get_scan` → `list_scan_tags`.
143
- 2. **"Create a campaign that re-checks the homepage of brand-x.com every hour from JP and US; alert me on Slack if it ever shows a malware tag."** Touches `list_emulators` → `list_policy_sets` (find one with `malware`) → `create_campaign` (schedule_enabled=true) → `list_alert_destinations` → `set_campaign_alert_overrides`.
143
+ 2. **"Create a campaign that re-checks the homepage of brand-x.com every hour from JP and US; alert me on Slack if it ever shows a malware tag."** Touches `list_emulators` → `list_policy_sets` (find one with `malware`) → `create_campaign` (schedule_enabled=true) → `attach_policy_set_campaigns` → `list_alert_destinations` → `set_campaign_alert_overrides` (`mode: "override"` with the Slack destination).
144
144
  3. **"What did I spend on ad verification last month, and which campaigns drove the cost?"** Touches `get_usage_summary` → `list_usage` (with date_from/date_to) → group by `scan_id` → `get_scan` → `get_campaign` for attribution.
145
145
 
146
146
  Full machine-readable tool listing is exposed by the server itself — connect with any MCP client and call `tools/list`.
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { NAME, VERSION, err, ok } from './chunk-VFDW3UL2.js';
2
+ import { NAME, VERSION, err, ok } from './chunk-YS7IDW24.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-VM6OOG2O.js');
163
+ const { bootstrapStdio } = await import('./stdio-bootstrap-II6IVJWD.js');
164
164
  return bootstrapStdio(config);
165
165
  }
166
- const { bootstrapHttp } = await import('./http-bootstrap-ROIMZVK2.js');
166
+ const { bootstrapHttp } = await import('./http-bootstrap-XAA75RA3.js');
167
167
  return bootstrapHttp(config);
168
168
  }
169
169
  main().then(