@kadam-net/mcp-server
Official# @kadam-net/mcp-server
[](https://www.npmjs.com/package/@kadam-net/mcp-server)
[](https://www.npmjs.com/package/@kadam-net/mcp-server)
[](https://github.com/kadam-official/mcp-server/blob/main/LICENSE)
[](https://nodejs.org)
[](https://registry.modelcontextprotocol.io)
MCP server for [Kadam](https://kadam.net) ad network — manage campaigns, creatives, audiences, sites, and analytics via AI agents.
Built on the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP), the open standard for connecting LLMs to external tools and data.
## Install (one click)
### Cursor
<a href="https://kadam-official.github.io/mcp-server/install.html"><img alt="Install in Cursor" src="https://cursor.com/deeplink/mcp-install-dark.png" height="32" /></a>
Or add manually to `.cursor/mcp.json`:
```json
{
"mcpServers": {
"kadam": {
"command": "npx",
"args": ["-y", "@kadam-net/mcp-server"],
"env": {
"KADAM_ADV_API_KEY": "your-advertiser-api-key",
"KADAM_PUB_API_KEY": "your-publisher-api-key"
}
}
}
}
```
### Claude Code
```bash
claude mcp add kadam -- npx -y @kadam-net/mcp-server
```
Then set the env var: `export KADAM_ADV_API_KEY=your-key`
### Claude Desktop
<a href="https://github.com/kadam-official/mcp-server/releases/latest/download/kadam-mcp-server.mcpb"><img alt="Download for Claude Desktop" src="https://img.shields.io/badge/Claude_Desktop-Download_.mcpb-orange?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0tMSAxNXYtNEg4bDQtNiA0IDZoLTN2NGgtMnoiLz48L3N2Zz4=" height="32" /></a>
Download → double-click → Install. Or add manually to `claude_desktop_config.json` (Settings → Developer → Edit Config):
```json
{
"mcpServers": {
"kadam": {
"command": "npx",
"args": ["-y", "@kadam-net/mcp-server"],
"env": {
"KADAM_ADV_API_KEY": "your-advertiser-api-key",
"KADAM_PUB_API_KEY": "your-publisher-api-key"
}
}
}
}
```
### Any MCP client (universal one-liner)
```bash
npx add-mcp @kadam-net/mcp-server
```
### Docker
```bash
docker run -i --rm \
-e KADAM_ADV_API_KEY=your-key \
kadam/mcp-server:latest
```
### npm global
```bash
npm install -g @kadam-net/mcp-server
KADAM_ADV_API_KEY=your-key kadam-mcp-server
```
## Configuration
| Variable | Required | Description |
| -------------------- | ---------- | ------------------------------------------------------------------------------------------ |
| `KADAM_ADV_API_KEY` | One of two | Advertiser API key from [partners.kadam.net](https://partners.kadam.net) -> Profile -> API |
| `KADAM_PUB_API_KEY` | One of two | Publisher API key from [pub.kadam.net](https://pub.kadam.net) -> Profile -> API |
| `KADAM_ADV_API_BASE` | No | Advertiser API URL (default: `https://partners.kadam.net/api/v1`) |
| `KADAM_PUB_API_BASE` | No | Publisher API URL (default: `https://pub.kadam.net/api`) |
| `LOG_LEVEL` | No | Log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal` (default: `info`) |
At least one API key must be provided in stdio mode. In HTTP mode, tokens are passed per-request via Bearer authentication.
### Role-based catalog
The tool catalog matches the role of the bearer. Once per session (and once per process in stdio mode) the server calls `GET /api/v1/access` on the advertiser API, which answers `{ impersonation: bool }`:
| Bearer | `impersonation` | Catalog |
| ------------------------------------------------------------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Advertiser API key, or a cabinet JWT with `sub` only | `false` | 75 tools; `create_campaign` / `update_campaign` have no manager-only fields |
| Cabinet JWT with `act.sub` (a Kadam manager acting for the client) | `true` | 80 tools (adds the five marked "manager-only" below); campaign writes accept `hasCorrectPostback`, `isDirectTrafficPriority`, `allowMultiAds` |
| Publisher API key | n/a | 17 publisher tools; the publisher API has no impersonation |
This is a catalog hint, not a gate: the Kadam API enforces the same rule on every call (403 / 422 for a client token). If `/access` cannot be reached, the session gets the client catalog — the smaller one is always safe to show.
### HTTP Transport (multi-tenant deployment)
For server-side deployment serving multiple users:
| Variable | Required | Description |
| ------------------ | -------- | ----------------------------------------------------------------- |
| `MCP_TRANSPORT` | No | `"stdio"` (default) or `"http"` |
| `MCP_HTTP_PORT` | No | HTTP port (default: `8080`) |
| `MCP_HTTP_HOST` | No | Bind address (default: `0.0.0.0`) |
| `KADAM_ADV_DOMAIN` | No | Advertiser domain for PRM (default: `https://partners.kadam.net`) |
| `KADAM_PUB_DOMAIN` | No | Publisher domain for PRM (default: `https://pub.kadam.net`) |
```bash
docker run -d --name kadam-mcp \
-e MCP_TRANSPORT=http \
-p 8080:8080 \
kadam/mcp-server:latest
```
### Connecting via HTTP (Cursor / Claude)
```json
{
"mcpServers": {
"kadam-adv": {
"url": "https://partners.kadam.net/mcp",
"headers": {
"Authorization": "Bearer YOUR_ADV_API_KEY"
}
}
}
}
```
### Connecting via OAuth (ChatGPT)
Add `https://partners.kadam.net/mcp` (advertiser) or `https://pub.kadam.net/mcp` (publisher) as Server URL in ChatGPT settings. OAuth discovery, registration, and login happen automatically.
## Tools (80)
### Advertiser Tools (63)
Requires `KADAM_ADV_API_KEY`.
#### Dictionaries
| Tool | Description | Annotations |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `kadam_adv_get_dictionary` | Reference dictionary: campaign types, browsers, platforms, devices, connections, categories, isps, conversion templates, countries, regions, cities | readOnly |
#### Campaigns
| Tool | Description | Annotations |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `kadam_adv_list_campaigns` | List campaigns with filters (folder, status, type, date, search) and pagination | readOnly |
| `kadam_adv_get_campaign` | Get full campaign configuration by ID: landing page URL, bids per country, budgets, targeting, frequency caps, schedule, conversion settings | readOnly |
| `kadam_adv_create_campaign` | Create campaign with full targeting (countries, devices, OS, browsers, age, gender, audiences); `dryRun: true` validates only | — |
| `kadam_adv_update_campaign` | Update any campaign fields by ID | — |
| `kadam_adv_set_campaign_status` | Bulk status change (active/paused/archived/restored) for comma-separated IDs; reports per-ID result | idempotent |
| `kadam_adv_delete_campaigns` | Bulk delete of archived campaigns by comma-separated IDs; requires `confirm: true`, reports per-ID result | destructive |
| `kadam_adv_move_campaigns` | Move campaigns to a campaign group by comma-separated campaign IDs and target group ID | idempotent |
| `kadam_adv_copy_campaign` | Copy a campaign into a campaign group: settings and bids are cloned, the copy starts paused and re-enters moderation. Optional pricing-model switch, push <-> in-page push cross-format copy, autorules and per-site bids | — |
| `kadam_adv_bulk_replace_urls` | Find and replace a landing-URL fragment across campaigns and their creatives. Manager-only (impersonation sessions); supports `dryRun` | destructive |
| `kadam_adv_set_campaign_rss` | Point a native campaign at an RSS feed to import creatives, or stop the import with an empty link | idempotent |
| `kadam_adv_set_easy_start` | Turn Easy Start (curated site set) on or off for a campaign. Manager-only (impersonation sessions) | idempotent |
| `kadam_adv_get_blocked_traffic_sources` | Traffic sources rejecting a campaign, split into category blocks and moderation-tag blocks. Manager-only (impersonation sessions) | readOnly |
| `kadam_adv_get_traffic_forecast` | Bid-to-traffic curve for a targeting set from yesterday's auction. An empty curve means the forecast could not be built, not that traffic is zero | readOnly |
#### Bid Management
| Tool | Description | Annotations |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------- |
| `kadam_adv_update_campaign_bid` | Update bid for a single campaign (lightweight, no full payload). Falls back to current countries if omitted | idempotent |
| `kadam_adv_bulk_update_bids` | Update bids for multiple campaigns at once (all must share the same pricing model) | idempotent |
| `kadam_adv_block_sites` | Stop or resume buying sites (zones) in campaigns | — |
| `kadam_adv_update_site_bids` | Set per-site (zone) bids: static (`0.05`), multiplier (`x1.5`), or remove (`0`) | idempotent |
#### Campaign Folders
| Tool | Description | Annotations |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `kadam_adv_list_campaign_folders` | List folders with campaign counts and budgets | readOnly |
| `kadam_adv_get_campaign_folder` | Get a single folder: name, archived state, budgets | readOnly |
| `kadam_adv_create_campaign_folder` | Create a new folder (name 1-50 chars) | — |
| `kadam_adv_update_campaign_folder` | Partial update: rename and/or change budgets and distribution | — |
| `kadam_adv_set_campaign_folder_status` | Bulk action for comma-separated IDs: activate/pause campaigns, archive or restore folders with their campaigns; returns per-folder result | idempotent |
#### Creatives
| Tool | Description | Annotations |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------- |
| `kadam_adv_list_creatives` | List creatives by campaign, status, or search query | readOnly |
| `kadam_adv_create_creative` | Create creative for a campaign: image, HTML5 archive, video file or VAST tag (goes through moderation) | — |
| `kadam_adv_update_creative` | Update creative fields | — |
| `kadam_adv_set_creative_status` | Bulk status change for creatives: active/paused/archived/restored; returns per-creative result | idempotent |
| `kadam_adv_get_creative_test_conversion` | Signed links to test a creative's conversion tracking (test click + frame preview) | — |
| `kadam_adv_upload_bulk_images` | Stage images for a bulk creative batch (async, returns a job) | — |
| `kadam_adv_create_bulk_creatives` | Turn staged images into creatives in several campaigns at once (async, returns a job) | — |
| `kadam_adv_get_bulk_job` | State of a bulk batch: staged images, refusals, per-row outcome | — |
| `kadam_adv_copy_creatives` | Copy creatives into other campaigns (creatives × targets copies), originals untouched | — |
| `kadam_adv_move_creatives` | Move creatives to another campaign of the same format and pricing model; they lose per-geo bids and return to moderation | — |
| `kadam_adv_get_creative_blocked_sources` | Traffic sources that reject one creative, split by category and moderation tag. Manager-only (impersonation sessions) | — |
| `kadam_adv_set_creative_bids` | Replace the creatives' own per-country bids; countries left out fall back to the campaign bid | — |
| `kadam_adv_delete_creatives` | Bulk delete of archived creatives by comma-separated IDs; requires `confirm: true`, reports per-ID result | destructive |
#### Audiences
| Tool | Description | Annotations |
| ----------------------------------------- | ----------------------------------------------------------------------------------------- | ----------- |
| `kadam_adv_list_audiences` | List audiences with search and sorting | readOnly |
| `kadam_adv_get_audience` | Get detailed audience info by ID | readOnly |
| `kadam_adv_create_audience` | Create audience (pixel, code, fingerprint, or S2S) | — |
| `kadam_adv_update_audience` | Update audience settings | — |
| `kadam_adv_get_filtered_audience_sources` | Audiences usable as sources of a filtered audience. Manager-only (impersonation sessions) | — |
| `kadam_adv_get_audience_params` | Hardcoded parameters a pixel/S2S audience substitutes into its code | — |
| `kadam_adv_set_audience_params` | Replace the hardcoded parameters of a pixel/S2S audience | — |
| `kadam_adv_delete_audience` | Delete audience permanently (requires `confirm: true`) | destructive |
#### Finance & Statistics
| Tool | Description | Annotations |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `kadam_adv_list_finance_operations` | Transaction history (deposits, charges, refunds) | readOnly |
| `kadam_adv_get_stats` | Unified statistics — 3 report types via `reportType` param: `custom` (report builder with dimension/metric mapping), `sites` (per-site breakdown), `postbacks` (conversion logs) | readOnly |
### Publisher Tools (17)
Requires `KADAM_PUB_API_KEY`.
#### Sites (Sources)
| Tool | Description | Annotations |
| ----------------------------- | ------------------------------------------------------ | ----------- |
| `kadam_pub_list_sources` | List publisher sites with stats | readOnly |
| `kadam_pub_create_source` | Add a new site (starts verification flow) | — |
| `kadam_pub_get_source` | Get detailed site info | readOnly |
| `kadam_pub_update_source` | Update site name | — |
| `kadam_pub_set_source_status` | Change site status (active/paused/archived/unarchived) | idempotent |
#### Ad Units
| Tool | Description | Annotations |
| ------------------------------ | ----------------------------------------------------------------------------------- | ----------- |
| `kadam_pub_list_ad_units` | List ad units for a site, filter by format (native/banner/push/popunder/inpagepush) | readOnly |
| `kadam_pub_set_ad_unit_status` | Change ad unit status (active/paused/archived/restored) | idempotent |
#### User & Statistics
| Tool | Description | Annotations |
| ------------------------- | ----------------------------------------------------------------- | ----------- |
| `kadam_pub_get_user_info` | Get publisher account info and balance | readOnly |
| `kadam_pub_get_stats` | Publisher statistics with human-readable dimension/metric mapping | readOnly |
#### Kadam Smart Mediation
Selling an ad unit's traffic through outside ad networks alongside Kadam demand. The API,
the code and the report dimensions call it plain `mediation`. Read
`kadam://reference/mediation` first; split the income in `kadam_pub_get_stats` with
`groupBy=network` and the `kadam_revenue` / `mediation_revenue` metrics.
| Tool | Description | Annotations |
| -------------------------------------------- | ---------------------------------------------------------------------------- | ----------- |
| `kadam_pub_list_mediation_networks` | Catalog of networks, which have an account, and what an ad unit already runs | readOnly |
| `kadam_pub_connect_mediation_network` | Attach a network to an ad unit (resolves account and placement itself) | — |
| `kadam_pub_update_mediation_network` | Change geo, unique cap, proxy policy, tag template, placement or account | — |
| `kadam_pub_set_mediation_network_status` | active / paused for a connection | idempotent |
| `kadam_pub_retest_mediation_network` | Restart the forced test share (1-50%); starts a new measurement epoch | — |
| `kadam_pub_disconnect_mediation_network` | Remove a connection (requires `confirm`) | destructive |
| `kadam_pub_update_mediation_network_account` | Rotate the network API key, rename or disable the account | — |
| `kadam_pub_delete_mediation_network_account` | Remove a network account (requires `confirm`) | destructive |
## Resources (9)
Static reference data the agent can read before calling tools:
| URI | Description |
| ------------------------------------- | -------------------------------------------------------------------------------- |
| `kadam://reference/campaign-types` | All ad format types with IDs, features, pricing, and creative specs |
| `kadam://reference/pricing-models` | CPC, CPM, CPV, CPA Target with IDs and descriptions |
| `kadam://reference/creative-formats` | Creative requirements per campaign type |
| `kadam://reference/ad-unit-types` | Publisher ad unit formats with IDs |
| `kadam://reference/site-states` | Publisher site lifecycle states |
| `kadam://reference/mediation` | Outside ad networks: accounts, placements, connections, test share, income split |
| `kadam://reference/report-dimensions` | Available dimensions and metrics for statistics tools |
| `kadam://reference/api-overview` | General Kadam API capabilities overview |
## Prompts (4)
Pre-built workflow templates that guide the agent through multi-step operations:
| Prompt | Description | Arguments |
| ---------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `kadam_launch_campaign` | Step-by-step campaign creation (check types -> create folder -> create campaign -> add creatives) | `type`, `name`, `url`, `budget` |
| `kadam_campaign_performance` | Campaign performance analysis with optimization recommendations | `campaignId`, `period` |
| `kadam_optimize_sites` | Analyze site performance and suggest blacklist/whitelist changes | `campaignId`, `minClicks`, `maxCPA` |
| `kadam_account_overview` | Full account overview — campaigns, spend, top performers | — |
## Architecture
```
src/
├── index.ts # Entry point, server instructions, transport
├── config.ts # Zod-validated env config with cache
├── errors.ts # AuthError class
├── logger.ts # Pino structured logging (stderr)
├── output-formatter.ts # Text formatting + 50KB truncation
├── middleware/
│ └── tool-wrapper.ts # Auth, error handling, logging middleware
├── api/
│ ├── http-client.ts # Generic HTTP client with retry/429/timeout
│ ├── partners-client.ts # Advertiser API (lazy singleton)
│ └── pub-client.ts # Publisher API (lazy singleton)
├── utils/
│ ├── pagination.ts # Shared pagination extraction
│ ├── cache-once.ts # Generic async cache-once utility
│ └── dimension-mapper.ts # Stats dimension name→ID resolution
├── types/
│ ├── common.ts # Shared types (ApiListResponse, ReportConfig)
│ ├── advertiser.ts # Campaign, Creative, Audience types + maps
│ ├── publisher.ts # Source, AdUnit, PubUser types + maps
│ └── tool-module.ts # ToolModule interface
├── tools/
│ ├── advertiser/ # 63 tools across 14 modules
│ └── publisher/ # 17 tools across 6 modules
├── resources/ # 9 reference resources
└── prompts/ # 4 workflow prompts
```
### Key Design Decisions
- **ToolWrapper middleware** — centralized auth validation, error formatting, and logging for all 80 tools
- **Lazy singleton API clients** — one `HttpClient` instance per product, created on first use
- **Output truncation** — hard 50KB limit per response with `maxResults` (default 25, max 100) to prevent LLM context overflow
- **Human-readable output** — formatted tables, aligned entities, pagination metadata instead of raw JSON
- **Tool annotations** — `readOnlyHint`, `destructiveHint`, `idempotentHint` to guide agent behavior
- **Server instructions** — usage patterns and constraints sent to the LLM on connection
- **Dimension mapping** — stats tools accept human-readable names ("clicks", "spend") and resolve them to API IDs internally
- **Role-shaped catalog** — the bearer is forwarded untouched, and its role is read once per session from `GET /access` (see "Role-based catalog"). Tools declare `requires: "impersonation"` and `ToolWrapper` skips them for client sessions; `campaigns.ts` adds the manager-only campaign fields to its schemas only under impersonation. Nothing is enforced here — the API still answers 403 / 422 to a client token — so the catalog can never be wider than the key's real rights, only narrower when `/access` is unreachable
## Development
### Prerequisites
- Node.js >= 18
- npm
### Setup
```bash
git clone https://github.com/kadam-official/mcp-server.git
cd mcp-server
npm install
cp .env.example .env # Fill in your API keys
```
### Commands
```bash
npm run dev # Watch mode with tsx
npm run build # Production build with Vite
npm run start # Run built server
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run format # Prettier
npm test # Run 82 tests
npm run test:coverage # Tests with V8 coverage
npm run inspect # MCP Inspector (visual debugger)
```
### Testing
202 tests across 23 files using Vitest + MCP SDK InMemoryTransport:
- **Unit tests** — output formatter, config, HTTP client (mocked fetch)
- **Middleware tests** — ToolWrapper auth, error formatting, logging
- **Integration tests** — full server with all 72 tools, 8 resources, 4 prompts via in-memory MCP client
- **Tool handler tests** — each tool module with mocked API clients
```bash
npm test
# Test Files 23 passed (23)
# Tests 202 passed (202)
```
### MCP Inspector
The [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) provides a visual interface for testing:
```bash
npm run build
npm run inspect
```
## Deployment
### Docker
```bash
docker build -t kadam-mcp-server .
docker run -i --rm -e KADAM_ADV_API_KEY=... kadam-mcp-server
```
### CI/CD
The `.gitlab-ci.yml` pipeline includes:
- **lint** — ESLint + TypeScript check
- **test** — Vitest with coverage
- **build** — Vite production build
- **publish** — npm publish + Docker push (manual trigger)
## License
MIT
TDQS
Scored across 58 tools
Tools are mostly differentiated by resource and action, and descriptions explicitly resolve several overlaps (e.g. get_balance vs get_account, update_campaign_bid vs bulk_update_bids). Some clusters around finance, bid optimization, and bulk campaign/creative operations still require careful reading, but boundaries are documented.
All 58 tools use a consistent kadam_adv_ prefix and snake_case verb_noun naming. Verbs like get, list, create, update, delete, set, move, copy, and reset are used predictably with no mixed conventions.
58 tools is far above the typical 3–15 range and makes agent navigation heavy. While many tools map to distinct API operations, the surface could be consolidated across finance, bid optimization, and folder/creative bulk actions.
Core lifecycle coverage is strong across campaigns, creatives, audiences, autorules, bid optimization, finance, and dictionaries. Minor gaps exist (e.g. account profile updates, deposit creation, some reporting consolidation), but agents can work around them.