Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
MCP server for Meta Marketing API
This repo gives your agent a bridge to Meta Ads through an MCP server on Cloudflare Workers. It handles OAuth, workspace-linked token storage, and direct Graph API calls for ad account setup, campaign management, audiences, reporting, and batch helpers.
Builders who want to connect Claude Code, Codex, Cursor, or any MCP-capable agent to Meta Ads data and controls.
You can manage Meta ad accounts and reporting through your agent without building the API integration yourself.
What it does
Meta OAuth flow
Provides `/oauth/meta/start` and `/oauth/meta/callback` routes to connect a Meta account for a workspace.
Multi-tenant JWT protection
Requires an app-issued bearer JWT with `workspaceId` and user identity claims on MCP requests.
Cloudflare storage
Stores workspace connections in D1 and short-lived OAuth state in KV, with token encryption at rest.
Direct Graph API access
Uses `fetch` calls to the Meta Graph API instead of the Meta SDK.
Ads tool surface
Registers tools for account setup, campaigns, ad sets, creatives, audiences, reporting, and batch helpers.
Built-in admin UI
Includes a small `/app` page that can start OAuth and show whether a Meta account is connected.
How to get it
- 1Set the callback URL to
https://<your-worker-host>/oauth/meta/callback
README
Meta Ads MCP Server
A Cloudflare Workers MCP server for Meta Ads account setup, campaign management, ad sets, creatives, audiences, reporting, and batch workflows.
This repo is built on xmcp and exposes a Streamable HTTP MCP endpoint plus browser-facing Meta OAuth routes.
Open Source / Self-Hosted
This repository is intended to be deployed in your own Cloudflare account with your own Meta app credentials.
It does not ship with:
- a hosted control plane
- a shared Meta app
- a built-in end-user dashboard
- a JWT issuer for your users and workspaces
You bring:
- your Cloudflare Worker deployment
- your Meta developer app
- your JWT issuer or auth provider
- your own UI or backend that initiates the OAuth flow
What It Does
- Runs as a Cloudflare Worker
- Uses direct Meta Graph API
fetchcalls instead of the Meta SDK - Stores Meta user connections per workspace in D1
- Stores short-lived OAuth state in KV
- Encrypts stored Meta access tokens
- Protects MCP requests with your app-issued JWTs
Endpoints
GET /healthGET /appPOST /mcpGET /oauth/meta/startGET /oauth/meta/callback
Auth Model
This server is multi-tenant. Every MCP request must include a bearer JWT issued by your app.
If you are open-sourcing this project, the important implication is that consumers must wire it into their own auth system. The server does not know how to identify a user or workspace without that JWT.
Required JWT claims:
suboruserIdworkspaceId- optional
roles
Example payload:
{
"sub": "user_123",
"workspaceId": "workspace_abc",
"roles": ["admin"]
}
Why /oauth/meta/start is not a generic public link:
- the server must know which workspace the Meta account should be attached to
- that workspace context comes from the JWT
- without it, the server cannot safely bind the resulting Meta token
Tool Surface
Implemented tool families:
- Account and setup
- Campaign management
- Ad set management
- Creative and ads
- Audience and targeting
- Reporting and insights
- Batch helpers
The server currently registers 39 tools.
Project Layout
src/toolstool definitions grouped by domainsrc/libauth, storage, OAuth, runtime, and Meta client helperssrc/servicesdomain-specific Meta service logicsrc/middleware.tsOAuth routing and MCP JWT authcloudflare-entry.mjsWorker wrapper entry for Cloudflare-specific route interceptionschema.sqlD1 schematestunit and contract-style tests
Local Development
Install dependencies:
pnpm install
Run local dev:
pnpm dev
Useful scripts:
pnpm build
pnpm test
pnpm deploy
Cloudflare Bindings
Required bindings:
- D1 database bound as
META_DB - KV namespace bound as
META_OAUTH_STATE
Required secrets:
JWT_SECRETorJWT_JWKS_URLMETA_APP_IDMETA_APP_SECRETMETA_TOKEN_ENCRYPTION_KEYAPP_UI_PASSWORDfor the built-in admin page at/app
Optional configuration:
JWT_ISSUERJWT_AUDIENCEAPP_SESSION_SECRETAPP_UI_WORKSPACE_IDAPP_UI_USER_IDMETA_REDIRECT_URIMETA_GRAPH_VERSIONMETA_OAUTH_SCOPESMETA_OAUTH_ALLOWED_RETURN_ORIGINS
Defaults:
META_GRAPH_VERSION=v25.0META_OAUTH_SCOPES=ads_management,business_managementAPP_UI_WORKSPACE_ID=workspace_adminAPP_UI_USER_ID=app_admin
Built-In Admin UI
The Worker now includes a small browser UI at /app.
What it does:
- prompts for an admin password
- starts the existing Meta OAuth flow without requiring you to manually mint a bearer JWT
- shows whether a Meta account is connected for the admin workspace
- loads accessible ad accounts using the same service logic as
get_ad_accounts
Required setup:
- Set
APP_UI_PASSWORDon the Worker. - Make sure
META_REDIRECT_URImatches your public host, for example:
https://meta-mcp.gestalt.xyz/oauth/meta/callback
- Open:
https://meta-mcp.gestalt.xyz/app
Meta App Setup
In your Meta app:
- Add the Marketing API product.
- Add a Website platform.
- Set the Website platform URL to your Worker origin.
- Set
App Domainsto your Worker domain. - Set the callback URL to:
https://<your-worker-host>/oauth/meta/callback
If your app uses Facebook Login or Facebook Login for Business, also add that exact callback URL to the product-specific redirect URI settings.
For a Worker deployed on workers.dev, these fields usually need to match the Worker host exactly.
Database
Apply the D1 schema:
pnpm wrangler d1 execute META_DB --remote --file schema.sql -y
Tables:
meta_connectionsmeta_ad_accounts_cache
Deployment
Deploy the Worker:
pnpm deploy
After deploy:
- note the public Worker URL
- set
META_REDIRECT_URItohttps://<your-worker-host>/oauth/meta/callback - update the same callback in the Meta app settings
If you plan to use a separate frontend or dashboard on another origin, allow that origin for post-OAuth browser redirects:
META_OAUTH_ALLOWED_RETURN_ORIGINS=https://your-ui.example.com,http://localhost:3000
Use your real frontend origin in production.
Manual Test Flow
1. Generate a short-lived JWT
Use the same JWT secret your app uses for the Worker.
export JWT_SECRET="YOUR_JWT_SECRET"
TOKEN=$(node --input-type=module <<'NODE'
import { SignJWT } from 'jose';
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const token = await new SignJWT({ workspaceId: 'workspace_test', roles: ['admin'] })
.setProtectedHeader({ alg: 'HS256' })
.setSubject('user_test')
.setIssuedAt()
.setExpirationTime('10m')
.sign(secret);
console.log(token);
NODE
)
2. Start Meta OAuth
curl -i \
-H "Authorization: Bearer $TOKEN" \
"https://<your-worker-host>/oauth/meta/start?workspace_id=workspace_test"
Copy the Location header into your browser and complete the Meta login flow.
Expected success page:
Meta account connected.
3. Initialize MCP
curl -s https://<your-worker-host>/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":"init-1","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"manual-test","version":"1.0.0"}}}'
4. List Tools
curl -s https://<your-worker-host>/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":"tools-1","method":"tools/list","params":{}}'
5. Call a Real Tool
After OAuth succeeds, this should return the accessible ad accounts for that workspace:
curl -s https://<your-worker-host>/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":"call-1","method":"tools/call","params":{"name":"get_ad_accounts","arguments":{}}}'
If you get a connect/reconnect error, the OAuth flow and the MCP call used different workspaceId values.
Notes
- Cloudflare
workers.devdomains can require extra care in Meta app settings. - The Worker entrypoint explicitly intercepts OAuth routes before delegating to the generated XMCP Worker.
- The Cloudflare Worker build path is not identical to local
xmcp dev, so always verify the deployed routes after OAuth-related changes.
References
Files in the repo
- src
- test
- .gitignore
- cloudflare-entry.mjs
- package.json
- pnpm-lock.yaml
- README.md
- schema.sql
- tsconfig.json
- vite.config.ts
- wrangler.jsonc
- xmcp.config.ts
Discussion (0)
Ask about usage, or say what you built with itSign in to join the discussion.
No comments yet. Be the first to say what this is good for.
More connectors
20 MB lightweight cross-platform database client for 90+ databases, including MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, SQL Server, and Dameng. Built-in AI, MCP Server, CLI, desktop and Docker. | 轻量级跨平台数据库管理工具,支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、达梦等 90+ 数据库,提供桌面端、Docker、CLI、内置 AI 助手和 MCP Server。
The fastest browser for AI agents to run browser automation, built for sharing your logged-in browser state with your AI agents, like Codex or Claude Code, without disturbing you. Zero cost, zero config.
BankMCP™: your AI can now read your bank. Self-hosted, read-only MCP server for your own bank accounts via open banking (Enable Banking). Standard MCP; tested with Claude and Ollama.
Open source alternative to Semrush and Ahrefs
Open-source auth gateway connecting 1400+ SaaS providers to AI agents through SDK, CLI, MCP, HTTP, and OpenAPI.