Skip to main content
Glama
lil-j

meta-ads-mcp

by lil-j

Meta Ads MCP

This repository contains two deliberately separate local MCP servers:

  • meta-ads-mcp is the default read-only reporting server. It uses ads_read and cannot change Meta.

  • meta-ads-writer-mcp is an optional, guarded creation server. It can upload reviewed images and create only new PAUSED ads from a validated manifest.

Both run over stdio. The writer reuses the same private credential store, but it must be registered as a separate MCP server so enabling it never expands the read-only server's tool set.

It also includes the meta-ads-upload-qa Codex skill under skills/meta-ads-upload-qa. The skill coordinates the two servers, requires explicit write authorization, renders every requested placement preview, and keeps ads paused when visual QA does not pass.

What it can do

Tool

Purpose

health_check

Validate authorization and inspect granted permissions

list_ad_accounts

List accessible ad accounts

get_ad_account

Read account configuration and status

list_campaigns

Read campaign budgets, objectives, schedules, and delivery

list_ad_sets

Read targeting, optimization, attribution, and budgets

list_ads

Read ads and attached creative details

list_ad_creatives

Read creative copy, images, calls to action, and asset feeds

get_ad_previews

Generate Meta-rendered placement previews for visual QA

get_insights

Query paginated performance data and breakdowns

compare_performance

Compare matched entities across equal-length periods

export_insights

Write paginated CSV or JSON reports locally

compare_performance calculates spend, delivery, CTR, CPC, CPM, conversions, cost per conversion, conversion value, and ROAS deltas. export_insights flattens Meta action arrays by action_type, safely quotes CSV values, and protects spreadsheet programs from formula injection.

Related MCP server: Meta Ads MCP Server

Safety model

  • Meta access is read-only. The server never creates, updates, pauses, resumes, or deletes campaigns, ad sets, ads, or creatives.

  • OAuth requests only ads_read.

  • The app secret and access token live outside the repository under ~/.config/meta-ads-mcp, with owner-only file permissions.

  • Graph API requests send the token in the Authorization header.

  • Meta pagination URLs are never returned because they can contain credentials.

  • get_ad_previews returns Meta's ephemeral preview URLs and, by default, renders each successful placement to a labeled PNG image directly in the MCP response. This lets an agent inspect placement overlays without controlling the user's browser. Set render_images to false when only URLs are needed, or include_iframe_html to true when raw embed HTML is useful for debugging. Rendering uses an isolated temporary Chrome/Chromium profile and deletes its local files after the response. It does not edit the ad.

  • The renderer discovers Chrome/Chromium automatically. Set META_ADS_PREVIEW_BROWSER_PATH to an executable path to override discovery.

  • export_insights creates a new local file and never overwrites an existing export. It cannot modify Meta.

  • The server never logs tokens or app secrets.

Guarded writer

The optional writer exposes five tools:

Tool

Purpose

plan_ad_upload

Validate local 4:5/9:16 files, hash them, and save a plan

validate_ad_upload

Read and verify the account, campaign, ad set, Page, and IG ID

upload_images

Upload only the files fingerprinted in the validated plan

upload_assets

Upload fingerprinted images and MP4 videos in a validated plan

create_ads_paused

Create placement or carousel ads hard-coded PAUSED

It has no edit, publish, pause, resume, archive, or delete tools. Both write phases require a specific confirmation string after the plan is reviewed. Progress is saved after every successful Meta response, so the same plan can be retried without intentionally duplicating completed items. A partial failure is never rolled back or cleaned up automatically. Before each real creative or ad creation, the writer sends the exact payload through Meta's validate_only execution option.

The manifest is JSON and uses absolute media paths. Existing placement-image entries remain backward compatible:

{
  "version": 1,
  "account_id": "123",
  "campaign_id": "456",
  "campaign_name": "Main campaign",
  "adset_id": "789",
  "adset_name": "Testing",
  "page_id": "101",
  "instagram_user_id": "202",
  "link": "https://example.com/landing",
  "display_link": "https://apps.apple.com/",
  "call_to_action_type": "DOWNLOAD",
  "ads": [
    {
      "name": "Concept 01",
      "title": "Headline ✨",
      "body": "Primary text 🧡",
      "description": "Description 📲",
      "feed_image_path": "/absolute/path/concept_F45.png",
      "vertical_image_path": "/absolute/path/concept_V916.png"
    }
  ]
}

Placement image and video ads can use two to five primary-text options in one creative by replacing the singular "body" field with a "bodies" array:

{
  "name": "Concept 01",
  "title": "Headline ✨",
  "bodies": [
    "Primary text option A 🧡",
    "Primary text option B 🧡"
  ],
  "description": "Description 📲",
  "feed_image_path": "/absolute/path/concept_F45.png",
  "vertical_image_path": "/absolute/path/concept_V916.png"
}

Placement videos use "format": "video" with feed_video_path (1:1 or 4:5) and vertical_video_path (9:16). Carousels use "format": "carousel" and two to ten square PNG/JPEG card objects under cards, each with an image_path and optional card title, description, or HTTPS link.

Story-only image ads use "format": "story_image" with one vertical_image_path (9:16):

{
  "name": "Story concept",
  "format": "story_image",
  "title": "One Dinner. Two Plates. 🧡",
  "body": "Family recipes with a baby-friendly version 🍽️",
  "description": "7-day free trial • Then $29/year 🧡",
  "vertical_image_path": "/absolute/path/concept_V916.png"
}

The writer validates the target ad set before upload and rejects story_image plans unless every explicitly configured placement is a Facebook, Instagram, or Messenger Story. Automatic placements, feeds, Reels, Search, Audience Network, and Threads are rejected. This prevents a single-asset Story creative from silently delivering outside Stories.

The campaign and ad-set names are checked as well as their IDs. Images must be PNG or JPEG and no larger than 30 MB. MP4 videos are limited to 1 GB by the writer. A plan is invalidated if any fingerprinted file changes. Campaigns and ad sets must already exist in the Marketing API; unpublished Ads Manager drafts cannot be targeted.

Requirements

  • Node.js 20 or newer

  • A Meta developer app with access to the Marketing API

  • A Meta user who can access the required ad accounts

Install

git clone https://github.com/lil-j/meta-ads-mcp.git
cd meta-ads-mcp
npm install
npm run build

Authorize Meta

In the Meta app dashboard, enable a Facebook Login web flow and add this exact Valid OAuth Redirect URI:

http://localhost:53682/callback

Start the local setup flow:

META_APP_ID=your_app_id npm run auth -- setup

The helper opens a local browser page. Enter the app secret there, then authorize Meta. The form posts only to 127.0.0.1; credentials are not sent to the MCP client or this repository.

Meta user tokens are time-limited. Reauthorize when health_check reports an expired token:

npm run auth -- login

Connect an MCP client

For Codex CLI, register the built server using its absolute path:

codex mcp add meta-ads -- node /absolute/path/to/meta-ads-mcp/dist/index.js

For clients that use a JSON MCP configuration:

{
  "mcpServers": {
    "meta-ads": {
      "command": "node",
      "args": ["/absolute/path/to/meta-ads-mcp/dist/index.js"]
    }
  }
}

Restart or refresh the MCP client after changing its configuration.

Register the writer separately only when paused-ad creation is needed:

codex mcp add meta-ads-writer -- node /absolute/path/to/meta-ads-mcp/dist/writer-index.js

The configured token must grant ads_management for the writer. The included authorization helper intentionally requests only ads_read; it does not widen the default server's access.

Install the Codex skill

Copy the included skill into your personal Codex skills directory:

mkdir -p ~/.codex/skills
cp -R skills/meta-ads-upload-qa ~/.codex/skills/

Restart or refresh Codex after installing or updating the skill.

Development

npm run typecheck
npm run build
npm test

The test suite uses fake credentials and local mock transports. It does not call Meta or require access to an ad account.

Exports

Exports are written to ~/Downloads/Meta Ads by default. Set META_ADS_EXPORT_DIR to use a different directory. Files are created with owner-only permissions and receive a numeric suffix if a name already exists.

Environment overrides

  • META_ADS_CONFIG_DIR

  • META_OAUTH_CLIENT_FILE

  • META_ACCESS_TOKEN_FILE

  • META_ACCESS_TOKEN

  • META_APP_SECRET

  • META_GRAPH_API_VERSION (defaults to v25.0)

  • META_GRAPH_BASE_URL

  • META_ADS_EXPORT_DIR

  • META_ADS_WRITER_PLAN_DIR

  • META_OAUTH_PORT

Security

Never commit Meta credentials or exported ad data. See SECURITY.md for reporting instructions and additional guidance. CI runs Gitleaks on every push and pull request as an additional guard against committed credentials.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lil-j/meta-ads-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server