@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 +102 -0
- package/MIGRATION_0_2.md +139 -0
- package/README.md +8 -6
- package/dist/bin.js +181 -43
- package/dist/bin.js.map +1 -1
- package/package.json +3 -2
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
|
package/MIGRATION_0_2.md
ADDED
|
@@ -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": "
|
|
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
|
|
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
|
|
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
|
-
|
|
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** (
|
|
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,
|
|
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
|
|