@kaminari-ad/mcp 0.1.5 → 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,108 @@ 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
+
10
112
  ## [0.1.5] - 2026-05-17
11
113
 
12
114
  Re-release of v0.1.4 — the Corepack-based npm upgrade in v0.1.4
@@ -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