{"_id":"@channel47/pinterest-ads-mcp","name":"@channel47/pinterest-ads-mcp","dist-tags":{"beta":"0.1.0","latest":"0.1.0"},"versions":{"0.1.0":{"name":"@channel47/pinterest-ads-mcp","version":"0.1.0","description":"Pinterest Ads MCP Server - Query and mutate Pinterest advertising data via REST API v5","main":"server/index.js","bin":{"pinterest-ads-mcp":"server/index.js"},"type":"module","scripts":{"start":"node server/index.js","test":"node --test *.test.js test/*.test.js","prepublishOnly":"npm test"},"keywords":["mcp","model-context-protocol","pinterest-ads","pinterest","analytics","advertising","claude"],"author":{"name":"channel47"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/channel47/mcps.git","directory":"pinterest-ads"},"bugs":{"url":"https://github.com/channel47/mcps/issues"},"homepage":"https://github.com/channel47/mcps/tree/main/pinterest-ads#readme","dependencies":{"@modelcontextprotocol/sdk":"^1.29.0"},"engines":{"node":">=18.0.0"},"_id":"@channel47/pinterest-ads-mcp@0.1.0","gitHead":"b40ed5fec586628f184bd1fecd8838760b4e67e9","_nodeVersion":"24.1.0","_npmVersion":"11.3.0","dist":{"integrity":"sha512-ySezsGHW/uLWCyfexoe9F4J5+zDUvnqOJyOkedekSk3DAG63LkLxg9SkJ/w9DWKJnvOIDtUvGSbN+qtKnuBydg==","shasum":"120505969e12e6d2b2914bee2f632d7c454338ad","tarball":"https://registry.npmjs.org/@channel47/pinterest-ads-mcp/-/pinterest-ads-mcp-0.1.0.tgz","fileCount":20,"unpackedSize":69918,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCDDWZnYZVTFFLXUiRqg8VAjgUWQ52ZxfDZxgTpas1JsAIhALrbOKXqsnbHRMbQGpT6BkvQU47bEiGawGqlO/LjGUEl"}]},"_npmUser":{"name":"ctrlswing","email":"jacksondean.me@gmail.com"},"directories":{},"maintainers":[{"name":"ctrlswing","email":"jacksondean.me@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pinterest-ads-mcp_0.1.0_1783745136359_0.15479574613116287"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-11T04:45:36.150Z","0.1.0":"2026-07-11T04:45:36.523Z","modified":"2026-07-11T04:45:36.866Z"},"maintainers":[{"name":"ctrlswing","email":"jacksondean.me@gmail.com"}],"description":"Pinterest Ads MCP Server - Query and mutate Pinterest advertising data via REST API v5","homepage":"https://github.com/channel47/mcps/tree/main/pinterest-ads#readme","keywords":["mcp","model-context-protocol","pinterest-ads","pinterest","analytics","advertising","claude"],"repository":{"type":"git","url":"git+https://github.com/channel47/mcps.git","directory":"pinterest-ads"},"author":{"name":"channel47"},"bugs":{"url":"https://github.com/channel47/mcps/issues"},"license":"MIT","readme":"# @channel47/pinterest-ads-mcp\n\nMCP server for Pinterest Ads using the Pinterest REST API (`v5`).\n\nThis server exposes four tools expected by channel47 Pinterest workflows:\n\n- `list_accounts`\n- `query`\n- `analytics`\n- `mutate`\n\n## Installation\n\n### Standalone\n\n```bash\nnpx @channel47/pinterest-ads-mcp@latest\n```\n\n### Monorepo Development\n\n```bash\ncd mcps\nnpm install\nnpm run test\n```\n\n## Configuration\n\n### Getting Credentials\n\n1. Create an app at [developers.pinterest.com/apps](https://developers.pinterest.com/apps/) and request Standard access for the `ads:read` (and `ads:write` for mutations) scopes.\n2. Complete the OAuth authorization-code flow to obtain an access token and refresh token — see [Set up authentication and authorization](https://developers.pinterest.com/docs/getting-started/set-up-authentication-and-authorization/).\n3. Either paste a valid access token into `PINTEREST_ADS_ACCESS_TOKEN`, or configure the client credentials + refresh token and let this server mint access tokens itself.\n\n### Required (one of the two options)\n\n| Variable | Description |\n|----------|-------------|\n| `PINTEREST_ADS_ACCESS_TOKEN` | Pinterest OAuth access token (sent as `Authorization: Bearer`) |\n\nor all three of:\n\n| Variable | Description |\n|----------|-------------|\n| `PINTEREST_ADS_CLIENT_ID` | Pinterest app ID |\n| `PINTEREST_ADS_CLIENT_SECRET` | Pinterest app secret |\n| `PINTEREST_ADS_REFRESH_TOKEN` | OAuth refresh token used to mint access tokens via `POST /v5/oauth/token` |\n\nWhen both are configured, the refresh-token flow wins. Access tokens are cached in\nmemory and refreshed about 5 minutes before expiry. Pinterest uses continuous\nrefresh tokens and may rotate the refresh token on use — the server logs a stderr\nwarning when that happens because it cannot persist the new value; update\n`PINTEREST_ADS_REFRESH_TOKEN` yourself when you see the warning.\n\n### Optional\n\n| Variable | Description |\n|----------|-------------|\n| `PINTEREST_ADS_AD_ACCOUNT_ID` | Default ad account ID used when `ad_account_id` is omitted |\n| `PINTEREST_ADS_READ_ONLY` | Set to `true` to disable live mutations |\n| `PINTEREST_ADS_REQUEST_TIMEOUT_MS` | HTTP request timeout in milliseconds (default `30000`) |\n\n### Claude Code\n\n```bash\nclaude mcp add pinterest-ads \\\n  --env PINTEREST_ADS_ACCESS_TOKEN=<token> \\\n  --env PINTEREST_ADS_AD_ACCOUNT_ID=<ad-account-id> \\\n  -- npx @channel47/pinterest-ads-mcp@latest\n```\n\nOr as JSON config:\n\n```json\n{\n  \"mcpServers\": {\n    \"pinterest-ads\": {\n      \"command\": \"npx\",\n      \"args\": [\"@channel47/pinterest-ads-mcp@latest\"],\n      \"env\": {\n        \"PINTEREST_ADS_ACCESS_TOKEN\": \"<token>\",\n        \"PINTEREST_ADS_AD_ACCOUNT_ID\": \"<ad-account-id>\"\n      }\n    }\n  }\n}\n```\n\n## Tool Reference\n\n### `list_accounts`\n\nList accessible ad accounts from `GET /ad_accounts` with bookmark pagination.\n\n**Params:**\n- `include_shared_accounts` (optional): include accounts shared with you (API default `true`)\n\n**Notes:**\n- Returns `id`, `name`, `currency`, `country`, `owner_username`, `time_zone`, `permissions`\n- Follows bookmark pagination to return all accessible accounts\n\n### `query`\n\nStructured entity reads over:\n\n- `campaigns` — `GET /ad_accounts/{id}/campaigns`\n- `ad_groups` — `GET /ad_accounts/{id}/ad_groups`\n- `ads` — `GET /ad_accounts/{id}/ads`\n\n**Params:**\n- `entity` (required)\n- `ad_account_id` (optional if `PINTEREST_ADS_AD_ACCOUNT_ID` exists)\n- `entity_statuses`: `ACTIVE`, `PAUSED`, `ARCHIVED`, `DRAFT`, `DELETED_DRAFT` — the API defaults to `ACTIVE, PAUSED`, so pass this to see archived entities\n- `campaign_ids` / `ad_group_ids` / `ad_ids`: id filters (each only where the endpoint supports it)\n- `order`: `ASCENDING` | `DESCENDING` by ID\n- `limit`: rows to return across pages (default `100`, max `1000`; API pages are max `250`)\n\n### `analytics`\n\nMetrics via the v5 analytics endpoints:\n\n- `account` — `GET /ad_accounts/{id}/analytics`\n- `campaign` — `GET /ad_accounts/{id}/campaigns/analytics` (requires `campaign_ids`)\n- `ad_group` — `GET /ad_accounts/{id}/ad_groups/analytics` (requires `ad_group_ids`)\n- `ad` — `GET /ad_accounts/{id}/ads/analytics` (requires `ad_ids`)\n\n**Params:**\n- `start_date` / `end_date` (required, `YYYY-MM-DD`): at most 90 days back and a 90-day range\n- `level`: `account` (default), `campaign`, `ad_group`, `ad`\n- `columns`: defaults to `SPEND_IN_DOLLAR, IMPRESSION_2, CLICKTHROUGH_2, CTR_2, TOTAL_CONVERSIONS` (see the `pinterestads://analytics-columns` resource)\n- `granularity`: `TOTAL` (default), `DAY`, `WEEK`, `MONTH`, `HOUR` (`HOUR` no longer returns conversion metrics)\n- `click_window_days` / `engagement_window_days` / `view_window_days`: `0, 1, 7, 14, 30, 60`\n- `conversion_report_time`: `TIME_OF_AD_ACTION` | `TIME_OF_CONVERSION`\n- `reporting_timezone`: `PINTEREST_TIME_ZONE` | `AD_ACCOUNT_TIME_ZONE`\n\n### `mutate`\n\nMutation tool with dry-run safety by default.\n\n**Operation format:**\n\n```json\n{\n  \"entity\": \"campaign\",\n  \"action\": \"update\",\n  \"id\": \"626735565838\",\n  \"params\": {\n    \"daily_spend_cap\": 25000000\n  }\n}\n```\n\n**Supported entities:** `campaign`, `ad_group`, `ad`\n\n**Supported actions:** `create`, `update`, `pause`, `enable`, `archive`\n\n**Top-level params:**\n- `operations` (required)\n- `dry_run` (default `true`)\n- `partial_failure` (default `true`)\n\n**Notes:**\n- Pinterest v5 uses bulk-array bodies: creates are `POST /ad_accounts/{id}/{campaigns|ad_groups|ads}` with an array of creation objects; updates and status changes are `PATCH` with an array of `{ id, ...changes }`\n- **There is no delete.** `archive` sets `status: ARCHIVED`, which is terminal — archived entities cannot be reactivated\n- Pinterest has no server-side validate-only mode, so `dry_run` performs local validation and returns a preview of the exact requests (method, path, body) without calling the API\n- Creates default to `status: PAUSED` (pass an explicit `status` to override)\n- Required create fields — campaign: `name`, `objective_type`; ad_group: `name`, `campaign_id`, `billable_event`; ad: `ad_group_id`, `pin_id`, `creative_type`\n- Money fields (spend caps, budgets, bids) are in micro-currency: `25000000` = 25.00 in the account currency\n\n## Read-Only Mode\n\nSet `PINTEREST_ADS_READ_ONLY=true` to remove the `mutate` tool from the tool list\nand block mutate calls entirely. Recommended for reporting-only setups.\n\n## Behavior Notes\n\n- Auth is sent via `Authorization: Bearer <token>` request header\n- Retries once on HTTP `429`, using `Retry-After` when available (fallback `60s`)\n- Requests abort on timeout (default `30000ms`, configurable via `PINTEREST_ADS_REQUEST_TIMEOUT_MS`)\n- Errors surface as `Pinterest Ads API request failed (<status>): <message> [code <n>]`\n- Pagination uses `page_size` (max 250) + `bookmark` cursors; responses are `{ items, bookmark }`\n\n## Development Commands\n\n```bash\ncd pinterest-ads\nnpm test\nnode server/index.js\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-1ca58a62c5570197cd6c7189491a9fdb"}