DP

dadof3bytes/pinterest-ads-mcp

Developer tools
0 stars 0 forks Quality 83 Trend 20MIT

MCP server for businesses to manager their Pinterest Ads accounts.

Overview

Connect your AI assistant (Cursor, Claude Desktop, etc.) to so it can read performance data, run account audits, and make changes (preview first, then confirm). You do not need to write code. You will copy settings, run a few terminal commands, click through Pinterest login once, and paste a small JSON config into your AI tool. a small web app plus an (Model Context Protocol) bridge. MCP is how compatible AI apps call structured “tools” instead of guessing from screenshots. a multi-brand agency dashboard, billing manager, or delete-anything admin panel. One deployment = one ad account. 1. Clone or download this repository to a folder you will keep (e.g. ~/pinterest-ads-mcp). 2. Open Terminal and go to that folder: 1. Copy the template: 2. Open .env.local in a text editor and fill in values (see below). Generate strong random secrets (run in Terminal, paste output into .env.local): Use values for TOKEN_ENCRYPTION_KEY, MCP_AUTH_TOKEN, and OAUTH_STATE_SECRET.

README

Pinterest Ads MCP — setup guide

Connect your AI assistant (Cursor, Claude Desktop, etc.) to one Pinterest ad account so it can read performance data, run account audits, and make guarded changes (preview first, then confirm).

You do not need to write code. You will copy settings, run a few terminal commands, click through Pinterest login once, and paste a small JSON config into your AI tool.

What this is: a small web app plus an MCP (Model Context Protocol) bridge. MCP is how compatible AI apps call structured “tools” instead of guessing from screenshots.

What this is not: a multi-brand agency dashboard, billing manager, or delete-anything admin panel. One deployment = one ad account.


Before you start — checklist

Gather these before installing:

Requirement Why you need it
Pinterest Business account Ads API access is tied to business identity.
Ad account you will manage This server is locked to a single PINTEREST_AD_ACCOUNT_ID.
Ad account role: Admin or Campaign Manager (Analyst is read-only) Write tools need create/update permission; Analyst → 403 on applies.
Valid ads billing on the account (for live ads) Pinterest may block creation or delivery without billing.
Pinterest developer app OAuth client ID and secret for login.
Node.js 20+ and npm Runs the server locally (nodejs.org).
Terminal access on your Mac/PC For npm install and npm run dev.

Optional later:

Requirement Why
Vercel (or similar) account Host the app on a URL instead of only localhost.
Strong secrets Protect OAuth and MCP endpoints in production.

Step 1 — Get the project on your machine

  1. Clone or download this repository to a folder you will keep (e.g. ~/pinterest-ads-mcp).
  2. Open Terminal and go to that folder:
    cd /path/to/pinterest-ads-mcp
    

Step 2 — Install dependencies

npm install

Step 3 — Create your environment file

  1. Copy the template:
    cp .env.example .env.local
    
  2. Open .env.local in a text editor and fill in values (see Environment variables below).

Generate strong random secrets (run in Terminal, paste output into .env.local):

# 32+ characters — used to encrypt the Pinterest refresh token on disk
openssl rand -base64 32

# 16+ characters — protects the MCP HTTP endpoint
openssl rand -base64 24

# 16+ characters — signs OAuth “state” during login (CSRF protection)
openssl rand -base64 24

Use different values for TOKEN_ENCRYPTION_KEY, MCP_AUTH_TOKEN, and OAUTH_STATE_SECRET.

Step 4 — Create a Pinterest developer app

  1. Go to Pinterest Developers and sign in with the same business identity that owns the ad account.

  2. Create an app (or use an existing one).

  3. Note the App ID → PINTEREST_APP_ID and App secret → PINTEREST_APP_SECRET.

  4. Add an OAuth redirect URI that matches exactly (including http vs https, path, no trailing slash unless you configured one):

    Where you run the app Redirect URI to register
    Local (default) http://localhost:3000/api/oauth/pinterest
    Cloud host https://YOUR-DOMAIN/api/oauth/pinterest

    Mismatch is the most common setup failure (“redirect_uri mismatch”).

  5. Enable / request access for the Ads API scopes your app needs (Pinterest’s developer console labels may vary; the server requests the list below at login).

Step 5 — Required OAuth scopes

When you click Start Pinterest OAuth on /connect, the server asks Pinterest for these scopes (comma-separated in the authorize URL):

Scope Purpose
user_accounts:read Confirm which user authorized the app.
ads:read Read campaigns, ad groups, ads, analytics, audit data.
ads:write Create/update campaigns, ads, keywords (via guarded tools).
pins:read Read Pin metadata linked to ads.
pins:write Create Pins when building new ads.
boards:read Board context for Pins.
boards:write Pin creation may require board access.
catalogs:read Catalog / shopping audit evidence.
catalogs:write Catalog-related writes if your workflows need them.

If Pinterest shows fewer scopes after login, your developer app may not be approved for Ads API yet — fix that in the developer portal before expecting tools to work.

Step 6 — Find your ad account ID

In Pinterest Ads Manager, open the ad account you want this server to use. The account ID is often visible in the URL or account settings (numeric string). Set:

PINTEREST_AD_ACCOUNT_ID=your_numeric_ad_account_id

Use pinterest_list_ad_accounts later to confirm the ID matches the connected user.

Step 7 — Start the server

npm run dev

You should see Next.js running on http://localhost:3000.

Step 8 — Connect Pinterest (OAuth)

  1. In a browser, open http://localhost:3000/connect.
  2. Check the page:
    • Sandbox mode on/off should match PINTEREST_USE_SANDBOX in .env.local (see Sandbox vs live).
    • Ad account id should show your configured ID.
  3. Click Start Pinterest OAuth.
  4. Log in to Pinterest and Allow the requested permissions.
  5. Pinterest sends you back to the app. Success looks like:
    • Green message: “Pinterest connected successfully.”
    • A Stored connection section with connected time and scope list.
    • No refresh token shown on the page (by design).

Where tokens live:

  • After OAuth, an encrypted refresh token is saved in .data/oauth.json on the machine running the server.
  • That file is secret. It is listed in .gitignore — never commit it or paste it into chat.
  • Decryption uses TOKEN_ENCRYPTION_KEY from .env.local. If you lose the key, you must run /connect again after generating a new key (old file becomes unusable).

Alternative (advanced): put a plain PINTEREST_REFRESH_TOKEN or PINTEREST_REFRESH_TOKEN_ENCRYPTED in env instead of using .data/oauth.json — useful for cloud hosts without persistent disk.


Part 2 — Connect your AI tool

Two supported paths. Pick one (HTTP is easier when the app stays running; stdio is fine for all-local setups).

Option A — HTTP MCP (/api/mcp)

The server exposes MCP at:

http://localhost:3000/api/mcp

Authentication:

  • If MCP_AUTH_TOKEN is unset, anyone who can reach the URL can use your tools — only acceptable on a locked-down machine.
  • If MCP_AUTH_TOKEN is set:
    • On localhost, requests without a token are still allowed (convenience for local dev).
    • On a public URL (e.g. Vercel), every request must include
      Authorization: Bearer YOUR_MCP_AUTH_TOKEN or you get 401 Unauthorized.

Cursor mcp.json example (adjust path and token):

{
  "mcpServers": {
    "pinterest-ads": {
      "url": "http://localhost:3000/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_MCP_AUTH_TOKEN"
      }
    }
  }
}

Keep npm run dev running while you use HTTP MCP locally.

For production URL:

"url": "https://YOUR-DOMAIN/api/mcp"

Always use the Authorization header on non-localhost hosts.

Option B — stdio MCP (npm run stdio)

Runs MCP over standard input/output — no browser URL. Your AI app launches the process.

Cursor mcp.json example:

{
  "mcpServers": {
    "pinterest-ads": {
      "command": "npm",
      "args": ["run", "stdio"],
      "cwd": "/absolute/path/to/pinterest-ads-mcp",
      "env": {
        "PINTEREST_APP_ID": "…",
        "PINTEREST_APP_SECRET": "…",
        "PINTEREST_AD_ACCOUNT_ID": "…",
        "TOKEN_ENCRYPTION_KEY": "…",
        "PINTEREST_USE_SANDBOX": "true"
      }
    }
  }
}

stdio does not load .env.local automatically. Either:

  • Put required variables in the "env" block (as above), or
  • Export them in your shell before Cursor starts.

OAuth for stdio: complete npm run dev once and visit /connect, or set PINTEREST_REFRESH_TOKEN in the "env" block after connecting elsewhere.


Part 3 — Optional: deploy to Vercel (or similar)

Use cloud hosting when you want MCP available without keeping your laptop on.

  1. Push the repo to GitHub (do not commit .env.local or .data/).
  2. Import the project in Vercel as a Next.js app.
  3. Set all environment variables from .env.local in the Vercel project settings.
  4. Update:
    • NEXT_PUBLIC_APP_URL → https://YOUR-DOMAIN
    • PINTEREST_REDIRECT_URI → https://YOUR-DOMAIN/api/oauth/pinterest
  5. Add the same redirect URI in the Pinterest developer app.
  6. Deploy, then visit https://YOUR-DOMAIN/connect and complete OAuth on the deployed site.

Important for cloud (Vercel):

  • Vercel cannot write .data/oauth.json under /var/task. After OAuth succeeds, /connect shows a yellow One more step for Vercel panel with PINTEREST_REFRESH_TOKEN_ENCRYPTED. Copy it into Vercel → Settings → Environment Variables, then redeploy.
  • Use the same TOKEN_ENCRYPTION_KEY in Vercel that was set when you completed OAuth (the encrypted blob only decrypts with that key).
  • Do not rely on .data/ on serverless; tokens must live in env vars.
  • Set a strong MCP_AUTH_TOKEN and always send the Bearer header from your AI client.
  • Do not share the public MCP URL publicly without that token.

Build/start commands (if your host asks):

Script Command
Install npm install
Build npm run build
Start npm run start

Environment variables

Variable Required when What it is If missing / wrong
PINTEREST_APP_ID OAuth + API Developer app ID Cannot start OAuth or call API.
PINTEREST_APP_SECRET OAuth + API Developer app secret Token exchange fails.
PINTEREST_REDIRECT_URI OAuth Must match Pinterest app setting Redirect or token exchange errors.
PINTEREST_AD_ACCOUNT_ID Most tools Single ad account for this deployment Tools error on account resolution.
PINTEREST_USE_SANDBOX Always set explicitly true / false — picks sandbox vs production API host Wrong environment, confusing empty data or auth errors.
TOKEN_ENCRYPTION_KEY Storing token in .data/oauth.json 32+ char secret; encrypts refresh token on disk Cannot save or read OAuth file.
OAUTH_STATE_SECRET OAuth login 16+ char HMAC secret for login CSRF /connect shows “Set … OAUTH_STATE_SECRET”.
MCP_AUTH_TOKEN Recommended 16+ char Bearer token for /api/mcp Open MCP on public URLs; localhost still works without header.
DRAFT_SIGNING_SECRET Optional Signs write “draft” tokens (defaults to TOKEN_ENCRYPTION_KEY) Write previews fail if neither is set.
NEXT_PUBLIC_APP_URL Links / redirects Base URL of this app (e.g. http://localhost:3000) Wrong redirect targets in some flows.
PINTEREST_REFRESH_TOKEN Optional dev Plain refresh token in env Bypasses .data/oauth.json; dev-only risk.
PINTEREST_REFRESH_TOKEN_ENCRYPTED Optional cloud Encrypted token blob + TOKEN_ENCRYPTION_KEY Used instead of file store.

Sandbox vs live (PINTEREST_USE_SANDBOX)

Setting Behavior
PINTEREST_USE_SANDBOX=true API calls go to api-sandbox.pinterest.com. Use for testing without touching live spend.
PINTEREST_USE_SANDBOX=false Production API and real ad account data.

On /connect, you can toggle sandbox OAuth vs production OAuth via the link on the page. The token exchange uses that choice. Keep PINTEREST_USE_SANDBOX aligned with how you connected; otherwise refresh and API hosts may disagree.

Default in .env.example is sandbox (true) — switch to false only when you intend to manage live ads.


What your AI can do — tools by use case

Account & structure (read)

Tool Use
pinterest_get_account Account details, user summary, terms-of-service state.
pinterest_list_ad_accounts Confirm which ad accounts the login can see vs your configured ID.
pinterest_list_campaigns / pinterest_get_campaign Campaign list and detail.
pinterest_list_ad_groups Ad groups, optionally filtered by campaign.
pinterest_list_ads Ads in the account.
pinterest_list_keywords Keywords for ad groups.

Reporting & analytics

Tool Use
pinterest_get_analytics Quick sync stats for short date ranges (≤31 days), ≤100 entities, not hourly.
pinterest_create_report Start a large or slow report (custom body, or template).
pinterest_get_report Poll report job status and download link.
pinterest_list_metrics Valid metric column names for reports.
pinterest_metrics_ready Whether conversion metrics are ready for a date.

Tip: If sync analytics refuses the request or times out, use pinterest_create_report then pinterest_get_report.

Audit & compliance evidence

Tool Use
pinterest_audit_snapshot Read-only bundle for ads-pinterest PN-* style controls (tags, catalogs, campaigns, etc.). Observations only — no “health score.”

Targeting research

Tool Use
pinterest_get_targeting_options Interests, locations, geos, audiences, etc. for planning ad groups.

Create & change ads (guarded writes)

Nothing deletes campaigns/ads via API here. New entities default to PAUSED.

Workflow: always draft → review preview → apply with confirm: true. Draft tokens expire in about 5 minutes.

Draft tool Apply tool What it does
pinterest_draft_campaign pinterest_apply_campaign Create/update campaigns. New campaigns: objectives SALES or LEADS only.
pinterest_draft_ad_group pinterest_apply_ad_group Create/update ad groups.
pinterest_draft_pin pinterest_apply_pin Create a Pin (image URL or media id).
pinterest_draft_ad pinterest_apply_ad Create/update ads.
pinterest_draft_keyword pinterest_apply_keyword Keywords including negatives.
pinterest_draft_entity_status pinterest_apply_entity_status Set ACTIVE / PAUSED / ARCHIVED on campaign, ad group, or ad.

Money: tool inputs/outputs use major currency (e.g. dollars); the server converts to Pinterest’s micro-currency internally.

Objectives: Pinterest is migrating to simplified objectives. You may still see legacy types (CATALOG_SALES, WEB_CONVERSION, AWARENESS, etc.) when reading old campaigns. Creating new campaigns through this server only allows SALES and LEADS. Ask your AI to map legacy setups to the new model when planning creates.


Quick verification after setup

In your AI chat (with MCP connected), ask it to run these in order:

  1. List tools — you should see about 25+ pinterest_* tools (read, report, audit, draft/apply).
  2. pinterest_get_account — returns your ad account payload without “No Pinterest refresh token”.
  3. pinterest_audit_snapshot — returns a JSON evidence bundle (may take a few seconds).

If all three succeed, reporting and draft tools are ready to use.


Troubleshooting

Symptom Likely cause What to do
redirect_uri mismatch Pinterest app redirect ≠ PINTEREST_REDIRECT_URI Copy the URI character-for-character into the developer app; redeploy if you changed domain.
401 / 403 on ads tools Missing scopes, app not approved for Ads API, or user lacks ad account role Re-check scopes table; confirm you’re admin on PINTEREST_AD_ACCOUNT_ID; re-authorize at /connect.
No Pinterest refresh token / tools say not connected Never finished OAuth, deleted .data/oauth.json, missing PINTEREST_REFRESH_TOKEN_ENCRYPTED on Vercel, or wrong TOKEN_ENCRYPTION_KEY Visit /connect and complete OAuth again; on Vercel, paste encrypted token from setup panel into env and redeploy.
ENOENT mkdir /var/task/.data Hosted on Vercel without env token storage Redeploy with this fix, run OAuth again, copy PINTEREST_REFRESH_TOKEN_ENCRYPTED from /connect into Vercel env.
Token refresh failed Revoked app, expired refresh, sandbox/live mismatch Re-run /connect; align PINTEREST_USE_SANDBOX with connection mode.
401 Unauthorized MCP request Missing/wrong Bearer on a public URL, or wrong token on localhost Add Authorization: Bearer matching MCP_AUTH_TOKEN. On localhost you can omit the header, but a wrong Bearer still fails.
sync_request_too_large / analytics timeout Range too long, too many IDs, or hourly granularity Use pinterest_create_report + pinterest_get_report.
Invalid create objective Draft used CATALOG_SALES, WEB_CONVERSION, etc. Use SALES or LEADS for new campaigns only.
Apply failed / confirm required Skipped draft step or expired draft Run pinterest_draft_* again, then apply within ~5 minutes with confirm: true.
Set PINTEREST_APP_ID… on /connect Incomplete .env.local Fill app ID, secret, redirect URI, OAUTH_STATE_SECRET; restart npm run dev.
stdio tools fail immediately Env vars not visible to subprocess Add "env" in mcp.json or export variables before launching Cursor.

Security notes (please read)

  • .env.local holds app secrets. Do not email it, commit it to GitHub, or paste into Slack/ChatGPT.
  • .data/oauth.json is as sensitive as a password. Back up only encrypted, store offline, never in git.
  • MCP HTTP URL on the internet is a remote control for your ad account if MCP_AUTH_TOKEN is weak or missing. Use a long random token and HTTPS.
  • Do not paste refresh tokens, MCP_AUTH_TOKEN, or app secrets into AI chats — even “private” threads.
  • Who can run writes: anyone who can call your MCP server can run apply tools after a draft. Lock down network access and tokens.
  • This project intentionally excludes agency/business-access APIs, billing APIs, and delete tools.

NPM scripts reference

Command Purpose
npm run dev Local server + /connect + HTTP MCP at /api/mcp
npm run stdio MCP over stdio for AI apps that launch a subprocess
npm run build Production build (cloud deploy)
npm run start Run production build
npm test Automated tests (optional)

Out of scope

Multi-tenant token vaults, Pinterest Business Access / partner APIs, billing management, entity delete tools, and automated “account health scores.”

For technical architecture (OpenAPI version, libraries), see comments in the repository source.

View this README on GitHub

Install

This server does not publish a one-line install command.

Open the repository installation guide

Configuration

{ "mcpServers": { "pinterest-ads": { "url": "http://localhost:3000/api/mcp", "headers": { "Authorization": "Bearer YOUR_MCP_AUTH_TOKEN" } } } }