Meta Ads MCP
MCP server for the Meta Marketing API with 118 typed tools across ads, insights, pixels/CAPI, pages, Instagram, WhatsApp, catalogs, audiences, leads, and billing, featuring secure token storage and a confirmation gate on destructive calls.
README
<div align="center">
Meta Ads MCP
An MCP server for the Meta Marketing API — built for people running real ad spend.
118 typed tools across Ads, Insights, Pixels/CAPI, Pages, Instagram, WhatsApp Business, Catalogs, Audiences, Leads and Billing — with the token kept out of your config files and a confirmation gate on every destructive call.
</div>
Why this one
There are several Meta Ads MCP servers out there. This one is built around a specific assumption: the person using it is spending real money, possibly on behalf of clients, and a mis-fired tool call costs more than an apology.
That shapes three decisions the code makes differently:
| How most setups do it | How this one does it | |
|---|---|---|
| Token storage | Pasted into a JSON config or .env that sits in plaintext on disk |
OS credential store first (Windows Credential Manager / macOS Keychain via keyring), .env only as fallback |
| Destructive calls | The model calls delete_campaign and it deletes |
confirm=True required — the refusal names the operation and the target, so the intent shows up in the transcript before anything happens |
| Errors | Meta's opaque code=3 / subcode=1487348 reaches you raw |
Decoded into a message that says what to actually do |
Plus a global MCP_READ_ONLY kill switch — useful when the server is reachable from a phone.
Quick start
Requirements: Python 3.11+, a Meta System User token, and an MCP client (Claude Code, Claude Desktop, Cursor…).
<details open> <summary><b>Windows</b></summary>
git clone https://github.com/AbnerZx/meta-ads-mcp.git
cd meta-ads-mcp
powershell -ExecutionPolicy Bypass -File .\install.ps1
The installer checks your Python, creates .venv, installs dependencies, prompts for your token (masked input, stored in Credential Manager, never written to disk), and prints the exact registration command to run next.
</details>
<details> <summary><b>macOS / Linux</b></summary>
git clone https://github.com/AbnerZx/meta-ads-mcp.git
cd meta-ads-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
python -m keyring set meta-ads-mcp access_token # paste token when prompted
</details>
Then register it with your client:
claude mcp add meta-ads --scope user -- /path/to/.venv/bin/python /path/to/src/server.py
Restart the client. Tools appear as mcp__meta-ads__*.
Verify it works — ask your assistant:
"List my Meta ad accounts."
What's in it
118 tools across 12 modules:
| Module | Tools | What it covers |
|---|---|---|
ads |
29 | Campaigns, ad sets, ads — create, update, duplicate, pause, activate, delete, schedules, previews |
catalog |
12 | Product catalogs, feeds, product sets, diagnostics |
instagram |
12 | Media, insights, publishing (image/video), comment moderation |
pages |
11 | Posts, insights, publishing, comment moderation |
creatives |
10 | Link & carousel creatives, image/video upload, previews |
whatsapp |
9 | Business accounts, phone numbers, message templates, sending |
audiences |
8 | Custom & saved audiences, applying them to ad sets |
pixels |
7 | Pixel management, stats, sharing, Conversions API events |
insights |
6 | Reports, period comparison, async report lifecycle |
billing |
6 | Spend, limits, funding source, invoices, cross-account summary |
leads |
5 | Lead forms and lead retrieval |
graph |
3 | graph_call escape hatch, pagination, token debugging |
graph_callmatters more than it looks. The Meta Marketing API is enormous and changes often. Rather than pretend 118 tools cover it, there's a typed escape hatch for any endpoint that isn't wrapped yet — so you're never blocked waiting on this repo.
Conventions
Worth knowing before your first call:
- Ad account IDs are auto-prefixed with
act_— pass123456oract_123456, both work. - Budgets are integers in account-currency cents.
R$ 10,00→1000. This is Meta's convention, not ours. - Destructive ops need
confirm=True. Without it you get a blocked-operation error naming the target. - The token never travels through tool arguments. It's loaded once at startup from the credential store.
Security model
The threats this is designed against, and what handles each:
| Asset | Risk if exposed | Control |
|---|---|---|
| Meta access token | Full control of every ad account the token can reach | OS credential store (encrypted at rest); never logged, never serialized, never returned by a tool |
| Server logs | Token leaking through a stack trace | Regex scrubbing in client.py; response bodies are not logged |
| Docker image | Secrets baked into build layers | .dockerignore excludes .env, .git, secrets |
| Public endpoint | Brute force, DNS rebinding | Bearer auth with constant-time compare, global rate limit, MCP_PUBLIC_HOSTS allowlist |
| Destructive calls | A campaign paused or deleted by mistake | confirm=True per call + MCP_READ_ONLY global switch |
| CAPI payloads | PII sent in the clear | SHA-256 hashing applied client-side before transmission |
Found a vulnerability? See SECURITY.md — please don't open a public issue.
Getting a token
Use a System User token, not a personal one. It doesn't expire, it's revocable independently of your own account, and Meta gives you a per-user audit trail.
Business Manager → Business settings → Users → System users → Add → assign only the ad accounts and Pages you actually need → Generate new token.
Typical scopes: ads_management, ads_read, business_management, read_insights. Add pages_*, instagram_*, leads_retrieval, whatsapp_business_* or catalog_management only if you use those modules. Meta shows the token exactly once.
Configuration
All optional except the token. See .env.example.
| Variable | Default | Purpose |
|---|---|---|
META_ADS_TOKEN |
— | Token, if you're not using the credential store |
META_DEFAULT_BUSINESS_ID |
— | Fallback Business Manager ID |
META_API_VERSION |
v21.0 |
Graph API version |
META_REQUEST_TIMEOUT |
60 |
Read timeout, seconds — Meta's validation paths can sit 20–40s |
META_RATE_LIMIT_RPS |
5 |
Local rate limiter |
MCP_READ_ONLY |
false |
true blocks every write, regardless of confirm |
MCP_PUBLIC_HOSTS |
— | Host allowlist — required for public HTTP deploys |
MCP_RATE_LIMIT_RPM |
60 |
Per-minute cap on the HTTP transport |
Remote deployment
The server speaks two transports: stdio for local clients, streamable-http for remote ones. The HTTP mode lets you reach your ad accounts from Claude.ai on mobile via a Custom Connector.
→ Full deployment guide (DEPLOY.md) — Fly.io walkthrough, bearer secret generation, secret rotation, and an incident-response checklist.
Short version:
fly launch --no-deploy --copy-config # edit fly.toml first: app name + MCP_PUBLIC_HOSTS
fly secrets set MCP_BEARER_SECRET="..." META_ADS_TOKEN="..."
fly deploy
fly.toml ships with MCP_READ_ONLY = 'true'. Leave it that way until you've confirmed the deployment works.
Project layout
src/
├── server.py # stdio entrypoint
├── http_server.py # streamable-http transport, bearer auth, rate limiting
├── mcp_instance.py # shared FastMCP instance + transport security
├── client.py # Graph API client — retries, backoff, log scrubbing
├── auth.py # token loading (credential store → env fallback)
├── safety.py # confirmation gate, ID normalization, input validation
└── tools/ # 12 modules, 118 tools
Contributing
Issues and pull requests are welcome — see CONTRIBUTING.md. Especially useful: Graph API error subcodes you've hit that aren't decoded yet, and coverage gaps you're currently working around with graph_call.
License
MIT © Abner Ferreira
<sub>Not affiliated with or endorsed by Meta Platforms, Inc. "Meta", "Facebook", "Instagram" and "WhatsApp" are trademarks of Meta Platforms, Inc.</sub>
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.