@kaminari-ad/mcp 0.1.0 → 0.2.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,198 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2026-05-17
11
+
12
+ Comprehensive parser-drift sweep across all `/api/v1/*` list
13
+ endpoints, a new tool for slim campaign selection, an MCP-client
14
+ interop fix, and small docs polish. Semver-minor because three list
15
+ tools changed output shape (breaking) and one new tool was added.
16
+
17
+ The MCP audit covered every `/api/v1/*` list endpoint. The MCP
18
+ already wrapped 21 of them — 18 were correctly wired, 3 had drift
19
+ (now fixed: `list_run_scans`, `list_custom_rules`, `list_policy_sets`).
20
+ One additional list endpoint (`/campaigns/picker`) had no MCP wrapper
21
+ and was added as the new `list_campaigns_picker` tool. The detail
22
+ endpoint `get_tag_definition` was also fixed to surface
23
+ `linked_rules`. See [MIGRATION_0_2.md](https://github.com/kaminari-ad/mcp/blob/main/MIGRATION_0_2.md)
24
+ for the agent-side migration notes.
25
+
26
+ ### Breaking
27
+
28
+ - **`list_run_scans` output element type:** `ScanBriefResponse` →
29
+ `ScanTileResponse`. The slim DTO drops `url` / `created_at` /
30
+ `labels` / `campaign_id` / `campaign_name` / `is_ad_tag` and adds
31
+ `error`. The tool no longer fails on prod data with `malformed
32
+ scans page: items.0.url: Required`; agents needing full scan
33
+ details should call `get_scan` per tile.
34
+ - **`list_custom_rules` output shape:** `readonly CustomRuleResponse[]`
35
+ → `PaginatedResponse<CustomRuleResponse>`. New optional
36
+ `page` / `limit` inputs (defaults `1` / `50`). Previously the tool
37
+ silently dropped pagination metadata; orgs with > 50 rules
38
+ appeared to have exactly 50.
39
+ - **`list_policy_sets` output shape:** `readonly PolicySetListItemResponse[]`
40
+ → `PaginatedResponse<PolicySetListItemResponse>`. New optional
41
+ `page` / `limit` inputs (defaults `1` / `50`). Same pagination
42
+ drop as `list_custom_rules`.
43
+
44
+ ### Fixed
45
+
46
+ - **`list_run_scans` failed with `malformed scans page:
47
+ items.0.url: Required`.** Parser wired the wrong DTO
48
+ (`ScanBriefResponse`) for an endpoint that returns
49
+ `ScanTileResponse`. Same drift class as `list_policy_sets` v0.1.1.
50
+ - **`list_custom_rules` / `list_policy_sets` silently dropped
51
+ pagination metadata.** Now expose `total` / `page` / `limit` so
52
+ agents iterate correctly past the default page size.
53
+ - **`get_tag_definition` now returns `linked_rules`** (the custom
54
+ rules currently producing this tag — `id` / `name` / `is_active`).
55
+ Previously silently dropped because the parser reused the list-row
56
+ schema. Agents no longer need to grep `list_custom_rules` by
57
+ `tag_slug` themselves.
58
+ - **`invalid-input` (HTTP 400 / 422) error responses now preserve
59
+ the API's machine-readable `code` field.** Forward-compat for
60
+ `delete_policy_set` growing `code: "policies.in_use"` — agents
61
+ can branch programmatically as soon as the API ships the code,
62
+ no MCP release required.
63
+
64
+ ### Added
65
+
66
+ - **`list_campaigns_picker`** (83rd tool — was 82, now 83) — slim
67
+ per-row campaign list for selection UIs (id, name, group_id,
68
+ is_archived). Cheaper than `list_campaigns` for orgs with thousands
69
+ of campaigns. Use `get_campaign(id)` after a selection.
70
+ - **Server now declares empty `resources` and `prompts` capabilities
71
+ with handlers returning `[]`.** Cursor / Claude Desktop / Cline
72
+ probe these at session start; previously the SDK responded with
73
+ `-32601 Method not found`, which Cursor's client mistranslated
74
+ as a misleading `"MCP error -32000: Connection closed"` warning.
75
+ Functional impact was zero (all tools remained callable) but
76
+ downstream agent logs filled with false positives once per session
77
+ per server. Now clean.
78
+
79
+ ### Docs
80
+
81
+ - **README:** replaced the fictional `kad_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
82
+ API-key placeholder with `<your-kaminari-ad-api-key>` + a note
83
+ that keys are opaque (no required prefix or fixed length).
84
+ - **`create_custom_rule` / `update_custom_rule` / `test_custom_rule`:**
85
+ the `target` parameter description no longer claims a stale
86
+ `page | offer_url | html` enum (OpenAPI declares it as a freeform
87
+ string; `html` may not be currently valid). Defers to API docs.
88
+ - **`delete_policy_set`:** description now explicitly mentions the
89
+ API returns `HTTP 400` if campaigns are bound, and suggests the
90
+ unbind-via-`update_campaign` workflow before retry.
91
+
92
+ ### Internal
93
+
94
+ - Comprehensive parser-drift audit across all `/api/v1/*` list
95
+ endpoints (run via parallel `explore` subagents covering API + MCP
96
+ sides). 21 endpoints already had MCP wrappers — 18 clean, 3 with
97
+ drift (now fixed); 1 additional list endpoint had no wrapper and
98
+ was added as a new tool. No remaining drift.
99
+ - New parser modules: `parse-run-scan-page.ts`, `parse-custom-rule-page.ts`,
100
+ `parse-policy-set-page.ts`, `parse-campaign-picker.ts`. The old
101
+ defensive bare-or-envelope helpers (`parseCustomRuleArray`,
102
+ `parsePolicySetList`) are gone; their files now expose only the
103
+ per-entity parsers. `parseTagDetail` moved out of `parse-generic.ts`
104
+ into `parse-tag.ts` because it now needs the detail schema's
105
+ `linked_rules` field which the list schema doesn't have.
106
+ - New shared helper `presentation/shared/declare-empty-caps.ts`
107
+ used by both stdio and HTTP bootstraps. Unit test against an
108
+ in-memory `Client` + `InMemoryTransport.createLinkedPair()` pair,
109
+ plus an end-to-end probe in the CLI smoke that hits a real HTTP
110
+ RPC after the `initialize` handshake.
111
+
112
+ ## [0.1.5] - 2026-05-17
113
+
114
+ Re-release of v0.1.4 — the Corepack-based npm upgrade in v0.1.4
115
+ reported `Preparing npm@^11 for immediate activation...` but the
116
+ shell still showed bundled `npm 10.9.7`, because Corepack shims
117
+ were not in PATH priority. Publish failed at the same point (404).
118
+
119
+ ### Fixed
120
+
121
+ - **Release workflow pinned to Node 24** (instead of .nvmrc Node 22)
122
+ so the bundled npm is 11.x, which has built-in Trusted Publisher
123
+ OIDC publish support. CI and local dev keep .nvmrc Node 22 — only
124
+ the release job overrides. Added an explicit `npm --version` gate
125
+ that fails the build if npm < 11.5.1 (defense against future Node
126
+ releases bundling older npm).
127
+
128
+ ## [0.1.4] - 2026-05-17
129
+
130
+ Re-release of v0.1.3 — that release's `npm install -g npm@^11`
131
+ step broke mid-upgrade with `Cannot find module 'promise-retry'`
132
+ (npm 10 → 11 in-place self-upgrade leaves a broken state because
133
+ newer arborist references modules the old bundled tree never had).
134
+
135
+ ### Fixed
136
+
137
+ - **Upgrade npm via Corepack instead of in-place self-upgrade.**
138
+ Corepack (bundled with Node 22+) manages the npm install via a
139
+ separate symlink layer and avoids the self-upgrade race. The
140
+ publish step now reliably runs against npm 11.x with full OIDC
141
+ Trusted Publisher support.
142
+
143
+ ## [0.1.3] - 2026-05-17
144
+
145
+ Re-release of 0.1.1 / 0.1.2 (both failed at the npm publish step).
146
+ The 0.1.2 OIDC debug step revealed every JWT claim matches the
147
+ Trusted Publisher config perfectly — the failure was elsewhere.
148
+
149
+ ### Fixed
150
+
151
+ - **npm CLI version too old for OIDC publishing.** GitHub Actions
152
+ Node 22 LTS runner ships with **npm 10.x bundled**, but npm
153
+ Trusted Publisher OIDC publish was introduced in **npm 11.5.1**
154
+ (Aug 2025). Without it, `npm publish --provenance` signs the
155
+ sigstore attestation BUT sends the actual PUT to the registry
156
+ unauthenticated → npm replies HTTP 404 "not found" (security
157
+ obscurity for "no auth"). Workflow now runs
158
+ `npm install -g npm@^11` before `npm ci` / publish, pinning the
159
+ latest npm 11.x line.
160
+
161
+ ## [0.1.2] - 2026-05-17
162
+
163
+ Re-release of v0.1.1 — that tag's release pipeline failed at the
164
+ npm publish step (HTTP 404 — npm Trusted Publisher OIDC subject
165
+ mismatch we couldn't pre-diagnose). No 0.1.1 tarball ever landed
166
+ on the registry; users skip straight from 0.1.0 to 0.1.2. Bin
167
+ alias fix and OIDC switch from 0.1.1 carry forward; this release
168
+ adds a diagnostic step that prints the GitHub OIDC subject claim
169
+ before the publish attempt, so any future Trusted Publisher
170
+ mismatch is visible in workflow logs (not silently 404'd).
171
+
172
+ ### Added
173
+
174
+ - `release.yml` "Debug OIDC subject claim" step — fetches the
175
+ `npm:registry.npmjs.org`-audience OIDC token, decodes the JWT
176
+ payload, prints the `sub` / `repository` / `workflow` /
177
+ `environment` claims. Sensitive data is the signed token itself
178
+ (logged claims are public metadata).
179
+
180
+ ## [0.1.1] - 2026-05-17
181
+
182
+ ### Fixed
183
+
184
+ - **`npx -y @kaminari-ad/mcp` failed with `command not found`.** npx
185
+ defaults the bin name to the **basename of the package** (`mcp` for
186
+ a scoped `@kaminari-ad/mcp`), but v0.1.0 declared bin
187
+ `kaminari-ad-mcp` only — so the documented Cursor / Claude Desktop
188
+ config in README literally did not start the server. Added `"mcp":
189
+ "./dist/bin.js"` as a second bin entry; both names now work
190
+ (`npx -y @kaminari-ad/mcp` and `npx -y -p @kaminari-ad/mcp
191
+ kaminari-ad-mcp`). No other changes — pure ergonomics.
192
+
193
+ ### Changed
194
+
195
+ - **npm publish now uses GitHub Actions OIDC + provenance** instead
196
+ of a static `NPM_TOKEN`. Restored `publishConfig.provenance: true`
197
+ and `--provenance` flag in `release.yml`; the `npm-publish`
198
+ GitHub Environment + the Trusted Publisher registered on the npm
199
+ package settings page produce signed sigstore attestations
200
+ reachable from the npm package page.
201
+
10
202
  ## [0.1.0] - 2026-05-17
11
203
 
12
204
  First public release of `@kaminari-ad/mcp`.
@@ -379,5 +571,10 @@ Initial public release. The first version that ships to npm under
379
571
  need them.
380
572
  - Invoice PDF fetcher — same reason.
381
573
 
382
- [Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.1.0...HEAD
574
+ [Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.1.5...HEAD
575
+ [0.1.5]: https://github.com/kaminari-ad/mcp/compare/v0.1.0...v0.1.5
576
+ [0.1.4]: https://github.com/kaminari-ad/mcp/compare/v0.1.0...v0.1.4
577
+ [0.1.3]: https://github.com/kaminari-ad/mcp/compare/v0.1.0...v0.1.3
578
+ [0.1.2]: https://github.com/kaminari-ad/mcp/compare/v0.1.0...v0.1.2
579
+ [0.1.1]: https://github.com/kaminari-ad/mcp/compare/v0.1.0...v0.1.1
383
580
  [0.1.0]: https://github.com/kaminari-ad/mcp/releases/tag/v0.1.0
@@ -0,0 +1,139 @@
1
+ # Migrating from `@kaminari-ad/mcp` 0.1.x to 0.2.0
2
+
3
+ Two tools changed output shape and one tool changed its element type.
4
+ Everything else is wire-compatible.
5
+
6
+ If your agent calls **only** the unchanged tools, **no action is
7
+ required** — drop in v0.2.0 and continue.
8
+
9
+ ## Breaking changes
10
+
11
+ ### 1. `list_custom_rules` — bare array → paginated envelope
12
+
13
+ Before (≤ 0.1.5):
14
+
15
+ ```jsonc
16
+ [
17
+ { "id": "…", "name": "…", "tag_slug": "…", "rule_type": "…", "config": {...},
18
+ "target": "…", "is_active": true, "organization_id": "…", "created_at": "…" },
19
+ /* … */
20
+ ]
21
+ ```
22
+
23
+ After (≥ 0.2.0):
24
+
25
+ ```jsonc
26
+ {
27
+ "items": [
28
+ { "id": "…", "name": "…", "tag_slug": "…", "rule_type": "…", "config": {...},
29
+ "target": "…", "is_active": true, "organization_id": "…", "created_at": "…" }
30
+ ],
31
+ "total": 67,
32
+ "page": 1,
33
+ "limit": 50
34
+ }
35
+ ```
36
+
37
+ **Why:** the API has always returned a `PaginatedResponse[CustomRuleResponse]`
38
+ envelope; v0.1.x silently dropped `total` / `page` / `limit`. Orgs with
39
+
40
+ > 50 rules saw exactly 50 with no signal there was more.
41
+
42
+ **Migration:** wherever you read `result.length` on a `list_custom_rules`
43
+ output, replace with `result.items.length` and check `result.total >
44
+ result.items.length` to know if you need to call the tool again with
45
+ `page: 2`. New optional inputs:
46
+
47
+ ```jsonc
48
+ { "page": 1, "limit": 50 }
49
+
50
+
51
+ // defaults; both optional
52
+ ```
53
+
54
+ ### 2. `list_policy_sets` — bare array → paginated envelope
55
+
56
+ Same shape change as `list_custom_rules`, same migration. The items
57
+ themselves are unchanged (slim — no `entries`; call `get_policy_set`
58
+ for those).
59
+
60
+ ### 3. `list_run_scans` — `ScanBriefResponse` → `ScanTileResponse` per item
61
+
62
+ Before (≤ 0.1.5) — crashed in production with `malformed scans page:
63
+ items.0.url: Required`:
64
+
65
+ ```jsonc
66
+ { "items": [{ "id": "…", "url": "…", "country_code": "…", "status": "…",
67
+ "offer_url": "…", "screenshot_url": "…", "labels": {…},
68
+ "elapsed_ms": 1234, "campaign_id": "…", "campaign_name": "…",
69
+ "is_ad_tag": false, "created_at": "…" }], … }
70
+ ```
71
+
72
+ After (≥ 0.2.0):
73
+
74
+ ```jsonc
75
+ { "items": [{ "id": "…", "country_code": "…", "status": "…",
76
+ "offer_url": "…", "screenshot_url": "…",
77
+ "elapsed_ms": 1234, "error": "" }], … }
78
+ ```
79
+
80
+ **Why:** the API endpoint returns `ScanTileResponse` (designed for
81
+ the run-detail UI's tile grid) — `url` / `created_at` / `labels` /
82
+ `campaign_*` / `is_ad_tag` are intentionally omitted because the
83
+ caller already knows the run's campaign and the tiles don't need to
84
+ re-render input URLs. `error` (the scan-failure reason) is **added**.
85
+
86
+ **Migration:** if your agent reads any of the removed fields off
87
+ `list_run_scans` items, call `get_scan(id)` per tile to fetch the
88
+ full `ScanResponse` (which has them all).
89
+
90
+ ## Additions (non-breaking)
91
+
92
+ ### `list_campaigns_picker` — new tool
93
+
94
+ Slim per-row campaign list for selection UIs:
95
+
96
+ ```jsonc
97
+ { "id": "…", "name": "…", "group_id": "…", "is_archived": false }
98
+ ```
99
+
100
+ Cheaper than `list_campaigns` for orgs with thousands of campaigns —
101
+ the API endpoint is non-paginated and intentionally omits heavy
102
+ fields. Use `get_campaign(id)` after a selection to fetch full
103
+ details.
104
+
105
+ ### `get_tag_definition` — now returns `linked_rules`
106
+
107
+ The detail endpoint always returned `linked_rules` per OpenAPI; the
108
+ v0.1.x parser silently dropped them. v0.2.0 surfaces them:
109
+
110
+ ```jsonc
111
+ {
112
+ "slug": "malware",
113
+ "category": "security",
114
+ /* … all existing fields … */
115
+ "linked_rules": [{ "id": "…", "name": "ad-detector", "is_active": true }],
116
+ }
117
+ ```
118
+
119
+ No input change. Agents that need rules linked to a tag no longer
120
+ need to grep `list_custom_rules` by `tag_slug` themselves.
121
+
122
+ ### `resources/list` and `prompts/list` now return `[]`
123
+
124
+ Most MCP clients (Cursor, Claude Desktop, Cline) probe these methods
125
+ at session start. v0.1.x returned the JSON-RPC standard `-32601
126
+ Method not found` (spec-compliant), but Cursor's client mistranslated
127
+ that into a misleading `"Connection closed"` warning. v0.2.0 declares
128
+ empty capabilities + handlers so the probe is silent.
129
+
130
+ Functional impact for callers: zero. No tool surface changed; the
131
+ fix is purely about client-side noise.
132
+
133
+ ## Internal: forward-compat for `policies.in_use`
134
+
135
+ `invalid-input` MCP errors (HTTP 400 / 422) now preserve the API's
136
+ machine-readable `code` field when present. The API does not emit
137
+ `code` on `delete_policy_set` today, but `policies.in_use` is a known
138
+ candidate. When the API ships it, agents can branch on the code
139
+ without waiting for an MCP release.
package/README.md CHANGED
@@ -19,6 +19,8 @@ Lets AI agents (Cursor, Claude Desktop, Cline, and any MCP-compatible client) la
19
19
  2. Once signed in, go to **Settings → API Keys** and generate a new key, OR have an existing AI assistant (with a temporary login) call the [`create_api_key`](#tools) tool — both paths produce the same result.
20
20
  3. The key is shown **once**. Copy it. The full key is hashed server-side immediately.
21
21
 
22
+ > Keys are opaque random strings — no required prefix or fixed length. Treat the whole value as a raw secret and paste it verbatim into your client config.
23
+
22
24
  > Tip for evaluators / Anthropic Software Directory reviewers: ask the team at [hello@kaminari.ad](mailto:hello@kaminari.ad) for a sandboxed test account with seeded sample scans, campaigns, and alerts.
23
25
 
24
26
  ### 2a. Local install (stdio transport)
@@ -32,14 +34,14 @@ Add to your MCP client config (Cursor: `~/.cursor/mcp.json`; Claude Desktop: `~/
32
34
  "command": "npx",
33
35
  "args": ["-y", "@kaminari-ad/mcp"],
34
36
  "env": {
35
- "KAMINARI_AD_API_KEY": "kad_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
37
+ "KAMINARI_AD_API_KEY": "<your-kaminari-ad-api-key>",
36
38
  },
37
39
  },
38
40
  },
39
41
  }
40
42
  ```
41
43
 
42
- Restart your client. You should see `kaminari-ad` in the MCP servers list with 82 tools exposed.
44
+ Restart your client. You should see `kaminari-ad` in the MCP servers list with 83 tools exposed.
43
45
 
44
46
  ### 2b. Hosted HTTP transport (no install)
45
47
 
@@ -51,7 +53,7 @@ For cloud agents or clients without a local Node runtime, point at the hosted en
51
53
  "kaminari-ad": {
52
54
  "url": "https://mcp.kaminari.ad/mcp",
53
55
  "headers": {
54
- "Authorization": "Bearer kad_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
56
+ "Authorization": "Bearer <your-kaminari-ad-api-key>",
55
57
  },
56
58
  },
57
59
  },
@@ -62,11 +64,11 @@ For cloud agents or clients without a local Node runtime, point at the hosted en
62
64
 
63
65
  ## Tools
64
66
 
65
- 82 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. Highlights:
67
+ 83 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. Highlights:
66
68
 
67
69
  - **Account** (11) — `get_account`, `update_org`, `list_org_users`, `invite_user`, `update_user_role`, `remove_user`, `transfer_ownership`, `list_org_roles`, `list_api_keys`, `create_api_key`, `revoke_api_key`
68
70
  - **Scans** (7) — `list_scans`, `get_scan`, `create_scan`, `create_bulk_scans`, `recheck_scans`, `cancel_scan`, `list_scan_tags`
69
- - **Campaigns** (9) — `list_campaigns`, `get_campaign`, `create_campaign`, `update_campaign`, `archive_campaign`, `unarchive_campaign`, `cancel_campaign`, `run_campaign`, `list_campaign_runs`
71
+ - **Campaigns** (10) — `list_campaigns`, `list_campaigns_picker`, `get_campaign`, `create_campaign`, `update_campaign`, `archive_campaign`, `unarchive_campaign`, `cancel_campaign`, `run_campaign`, `list_campaign_runs`
70
72
  - **Campaign groups** (10) — list/get/create/update/run/cancel/archive/unarchive + `pause_campaign_group_schedule`, `resume_campaign_group_schedule`
71
73
  - **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)
72
74
  - **Tags** (4) — `list_tags`, `get_tag_definition`, `update_tag_definition`, `delete_tag_definition`
@@ -79,7 +81,7 @@ For cloud agents or clients without a local Node runtime, point at the hosted en
79
81
  - **Alert notifications** (5) — `list_alert_destinations`, `delete_alert_destination`, `set_alert_destination_version`, `get_campaign_alert_overrides`, `set_campaign_alert_overrides`
80
82
  - **Reference data** (2) — `list_geos`, `list_emulators`
81
83
 
82
- Not exposed (intentionally): binary scan-screenshot fetchers, invoice PDF, the UI-only campaign picker, and the public marketing forms (`/contact`, `/demo-inquiries`). Open an issue if you need one of those.
84
+ Not exposed (intentionally): binary scan-screenshot fetchers, invoice PDF, and the public marketing forms (`/contact`, `/demo-inquiries`). Open an issue if you need one of those.
83
85
 
84
86
  ## Example agent prompts
85
87