@kaminari-ad/mcp 0.13.0 → 0.14.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 +91 -0
- package/README.md +8 -8
- package/dist/bin.js +3 -3
- package/dist/{chunk-HOKDLKJN.js → chunk-CUJWJSNJ.js} +497 -38
- package/dist/chunk-CUJWJSNJ.js.map +1 -0
- package/dist/{chunk-VFDW3UL2.js → chunk-FQTVDC5D.js} +3 -3
- package/dist/{chunk-VFDW3UL2.js.map → chunk-FQTVDC5D.js.map} +1 -1
- package/dist/{http-bootstrap-ROIMZVK2.js → http-bootstrap-AYQ47NTT.js} +4 -4
- package/dist/{http-bootstrap-ROIMZVK2.js.map → http-bootstrap-AYQ47NTT.js.map} +1 -1
- package/dist/{stdio-bootstrap-VM6OOG2O.js → stdio-bootstrap-XZIU2X6Z.js} +4 -4
- package/dist/{stdio-bootstrap-VM6OOG2O.js.map → stdio-bootstrap-XZIU2X6Z.js.map} +1 -1
- package/package.json +3 -1
- package/dist/chunk-HOKDLKJN.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,97 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.14.0] - 2026-08-23
|
|
11
|
+
|
|
12
|
+
Resyncs the tool surface with `/api/v1` (KAMIAD-158). The generated
|
|
13
|
+
types were already current; the hand-written gateway and tool schemas
|
|
14
|
+
were not, so this release is mostly about the drift between them — plus
|
|
15
|
+
two gates so the next one fails CI instead of reaching a user.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Artifact downloads were unbounded.** `binaryGet` called
|
|
20
|
+
`arrayBuffer()` with no ceiling, so one oversized MediaFile decided
|
|
21
|
+
how much memory the hosted server used — the API caps none of these
|
|
22
|
+
endpoints. It now refuses on an oversized `content-length` and
|
|
23
|
+
counts bytes while reading the stream, cancelling past the limit
|
|
24
|
+
(256 KiB text, 8 MiB binary). The cap covers the screenshot and
|
|
25
|
+
invoice-PDF tools too, which had the same exposure.
|
|
26
|
+
- **`attach_policy_set_campaigns` described a side effect it does not
|
|
27
|
+
have.** A campaign belongs to exactly one policy set, so attaching
|
|
28
|
+
one that is bound elsewhere MOVES it; the tool said existing
|
|
29
|
+
bindings survive. Now flagged `destructiveHint: true` and the
|
|
30
|
+
description says so.
|
|
31
|
+
- **`bulk_update_alert_status` forwarded `filter_*` alongside `ids`**,
|
|
32
|
+
which the API rejects. Refused locally with the offending field
|
|
33
|
+
names.
|
|
34
|
+
- **`list_campaign_groups` advertised `last_run_at`** while the
|
|
35
|
+
response projection stripped it, so the new last-run filters could
|
|
36
|
+
not be interpreted. Projected now.
|
|
37
|
+
- **`set_campaign_alert_overrides` accepted values the API rejects.**
|
|
38
|
+
`mode` was `inherit | include | exclude`; the API has only ever
|
|
39
|
+
accepted `inherit | override | silence`. Every value the schema
|
|
40
|
+
allowed was refused upstream and every value the API wanted was
|
|
41
|
+
refused locally, so an agent could not change campaign alert routing
|
|
42
|
+
at all. `mode` now matches the API, `destination_ids` is refused
|
|
43
|
+
locally unless `mode` is `override` (mirroring the API's
|
|
44
|
+
`notifications.invalid_override_combination`), and the setter's
|
|
45
|
+
description explains that "route everywhere except these" does not
|
|
46
|
+
exist — invert the list under `override` instead.
|
|
47
|
+
- **`list_custom_taxonomies` claimed soft-deleted taxonomies were
|
|
48
|
+
listed.** They are excluded unless the new `include_inactive` is
|
|
49
|
+
`true`, so an agent looking for one to restore concluded it was gone.
|
|
50
|
+
- `list_scans` now documents `rechecking` as a filterable status.
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **`bulk_update_alert_status`** — `POST /api/v1/alerts/bulk-status`.
|
|
55
|
+
Select with `ids` (max 1000) or `all_matching: true` plus `filter_*`
|
|
56
|
+
fields; returns `updated` / `skipped`.
|
|
57
|
+
- **`list_policy_set_campaigns`**, **`attach_policy_set_campaigns`**,
|
|
58
|
+
**`detach_policy_set_campaigns`** — policy-set membership, which was
|
|
59
|
+
previously not editable through MCP at all: `update_policy_set` sends
|
|
60
|
+
only name, description and rules. Attach warns that a campaign bound
|
|
61
|
+
to another set is moved rather than rejected, since a campaign can
|
|
62
|
+
belong to only one set.
|
|
63
|
+
- **`get_scan_creative_html`**, **`get_scan_vast_xml`** — creative
|
|
64
|
+
markup and the resolved VAST document, returned as text the model
|
|
65
|
+
reads directly. **`get_scan_creative_video`** — the MP4 as a resource
|
|
66
|
+
block. All three refuse oversized payloads (256 KiB text, 8 MiB
|
|
67
|
+
binary) rather than inlining them.
|
|
68
|
+
- Filters the API had grown and the tools never picked up:
|
|
69
|
+
`list_alerts` and `get_alert_stats` take `policy_set_id`, `tag`,
|
|
70
|
+
`country_code`, `date_from`, `date_to`, `timezone` (`get_alert_stats`
|
|
71
|
+
previously took no arguments at all, so its counts could not be
|
|
72
|
+
reconciled with a filtered list); `list_scans` takes `tag_match` and
|
|
73
|
+
`parent_scan_id`; `list_campaigns` and `list_campaign_groups` take
|
|
74
|
+
`created_from`/`created_to`/`last_run_from`/`last_run_to`/`timezone`,
|
|
75
|
+
and `list_campaign_groups` also takes `q`; `list_tags` takes
|
|
76
|
+
`include_archived`.
|
|
77
|
+
- **`check:api-coverage`** — asserts every `/api/v1` path + method in
|
|
78
|
+
the generated `openapi.ts` is reachable from the gateway, and that
|
|
79
|
+
each of their query parameters is representable by the gateway
|
|
80
|
+
method. The parameter half is the point: a path-only gate would have
|
|
81
|
+
passed on the state this release fixes.
|
|
82
|
+
- **`check:tool-enum-drift`** — every `z.enum` in a tool must have the
|
|
83
|
+
same value set as a generated enum, or carry a justified exemption.
|
|
84
|
+
Reintroducing the `mode` bug now fails CI. Equality rather than
|
|
85
|
+
subset: with 68 values across the generated enums, "subset of any"
|
|
86
|
+
accepted almost anything (`z.enum(["open"])` passed as a subset of
|
|
87
|
+
`AlertStatus`). A `z.enum` whose argument is not an inline array of
|
|
88
|
+
literals is reported rather than skipped, since an unreadable enum
|
|
89
|
+
is how a wrong value set would slip past.
|
|
90
|
+
|
|
91
|
+
### Changed
|
|
92
|
+
|
|
93
|
+
- `npm run gen:api-types` formats its output with Prettier. The raw
|
|
94
|
+
generator emits 4-space TypeScript while `format:check` covers
|
|
95
|
+
`src/**`, so a no-op regen used to produce a ~17k-line indentation
|
|
96
|
+
diff and a failing gate.
|
|
97
|
+
- `.prettierignore` no longer lists `src/infrastructure/api/openapi.ts`
|
|
98
|
+
— the generated files moved to `src/shared/api/` long ago, so the
|
|
99
|
+
entry was dead and pointed at the wrong policy.
|
|
100
|
+
|
|
10
101
|
## [0.13.0] - 2026-08-20
|
|
11
102
|
|
|
12
103
|
### Added
|
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ Add to your MCP client config (Cursor: `~/.cursor/mcp.json`; Claude Desktop: `~/
|
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Restart your client. You should see `kaminari-ad` in the MCP servers list with
|
|
66
|
+
Restart your client. You should see `kaminari-ad` in the MCP servers list with 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
|
-
|
|
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** (
|
|
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** (
|
|
127
|
-
- **Alerts** (
|
|
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 `
|
|
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
|
|
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-
|
|
2
|
+
import { NAME, VERSION, err, ok } from './chunk-FQTVDC5D.js';
|
|
3
3
|
import process from 'process';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
@@ -160,10 +160,10 @@ async function main() {
|
|
|
160
160
|
}
|
|
161
161
|
const config = configResult.value;
|
|
162
162
|
if (config.transport === "stdio") {
|
|
163
|
-
const { bootstrapStdio } = await import('./stdio-bootstrap-
|
|
163
|
+
const { bootstrapStdio } = await import('./stdio-bootstrap-XZIU2X6Z.js');
|
|
164
164
|
return bootstrapStdio(config);
|
|
165
165
|
}
|
|
166
|
-
const { bootstrapHttp } = await import('./http-bootstrap-
|
|
166
|
+
const { bootstrapHttp } = await import('./http-bootstrap-AYQ47NTT.js');
|
|
167
167
|
return bootstrapHttp(config);
|
|
168
168
|
}
|
|
169
169
|
main().then(
|