Meta Ads MCP by ScaleForge — control Facebook/Instagram campaigns from Claude, ChatGPT, Cursor, or any MCP agent. Direct Meta Graph API v24.0, 32 tools, no backend required.
Strong foundation with 21 well-named tools, comprehensive descriptions (avg 180 chars), and explicit input schemas. All tools follow verb_noun naming (list_, get_, create_, update_, delete_, search_). Descriptions clearly state WHAT the tool does and WHEN to use it (e.g., 'PRE-FLIGHT CHECK' for get_ads_volume). Parameters have types and descriptions. However, output schemas are not documented, LLMs cannot predict response structure. Error handling is minimal (generic error wrapping). Some parameter descriptions lack format/constraint details (e.g., targeting object is 'JSON' but no schema). Tool composition is excellent: batch tools (pause_campaigns_batch, activate_campaigns_batch, update_bids_batch) reduce sequential calls; chaining IDs are returned (campaign_id in list_campaigns enables get_campaign). Security: token injection via env var is correct; no secrets in params.
WRITE (BULK): Activate many campaigns in one Batch API call. BEFORE activation we run a per-Page ads_volume pre-flight for every distinct ad account — warnings are returned in the response so the agent / user can abort if a Page is over capacity. Agents MUST confirm with the user before calling this (activation starts spend immediately).
WRITE: Create an ad set under a campaign. Default status is PAUSED. `targeting` is a Meta targeting spec object (geo_locations, age_min, age_max, interests, etc.). `bid_amount` is in account currency minor units (cents). For multi-text / dynamic creative ads you MUST set is_dynamic_creative=true — otherwise asset_feed_spec ads will be rejected.
WRITE: Create a new campaign. Default status is PAUSED (recommended — set ACTIVE only after creating ad sets and ads). For CBO, pass daily_budget or lifetime_budget at this level; for ABO leave budget off and set it on the ad set. `special_ad_categories` is required (empty array [] is fine for normal advertising).
WRITE: Hard-delete an ad. Prefer update_ad status=ARCHIVED to keep history.
WRITE: Hard-delete an ad set (and its ads). Prefer update_adset status=ARCHIVED to keep history.
Output schemas not documented. LLMs cannot predict response structure (fields, types, pagination format). Responses are returned as raw JSON strings without schema guidance.
Complex object parameters (targeting, promoted_object, creative) lack schema definitions. Descriptions say 'JSON' or 'Meta spec object' but do not document required/optional fields, nested structure, or valid values.
Error handling is generic. Tool handlers catch errors and return 'Error: <message>' without categorizing as retryable, user-fixable, or fatal. No recovery guidance (e.g., 'Try search_users() first').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 65 | 2026-07-28+ | v2 |
WRITE: Hard-delete a campaign (and its ad sets / ads). Prefer update_campaign with status=ARCHIVED if you want to keep historical data.
Get a single ad by ID. Returns full creative + issues + preview link.
Get detailed info for a single Ad Account: status, spend cap, balance, funding source, business, timezone, disable_reason. Returns the full configuration record — use this for deep inspection of one account.
PRE-FLIGHT CHECK: Get per-Page ads-running-or-in-review counts and limits for an ad account. Meta caps active ads per Page (default 250) and this limit is SHARED across every account using the same Page. Always call this before a bulk activation to avoid silent review failures. Returns one row per Page actor.
Get a single ad set by ID. Returns default fields plus anything in `fields`.
Get a single campaign by ID. Returns all default fields plus anything in `fields`. Use this for deep inspection of one campaign.
List Meta Ad Accounts accessible to the current access token. Returns id (act_XXX), name, account_status, currency, business_name, spend_cap, timezone_name. Use this first to discover what you can work with.
List ads. Pass either `ad_account_id` (all ads in account), `adset_id` (ads in one ad set), or `campaign_id` (ads in a campaign). Returns id, name, adset_id, creative, status.
List ad sets. Pass either `ad_account_id` (lists all adsets in account) OR `campaign_id` (lists adsets of one campaign). Returns id, name, campaign_id, status, daily_budget, bid_amount, billing_event, optimization_goal, targeting, is_dynamic_creative.
List campaigns in an ad account. Returns id, name, status, objective, daily_budget, lifetime_budget, bid_strategy, created_time. Paginated via `limit` + `after` cursor.
WRITE (BULK): Pause many campaigns in a single Meta Batch API call (up to 50/request; arrays bigger than 50 are chunked automatically with a 2s delay between chunks to sidestep rate-limit code 17). Returns {results: Array<{code, body}>} — one entry per campaign. `code: 200` = success.
Search Meta's public Ad Library for competitive research. Returns active and inactive ads matching `search_terms` OR `search_page_ids` in the selected countries. Default country = ['US']. Returns ad_snapshot_url (preview), creative bodies/titles, page_id, delivery times, publisher_platforms. Note: only ads in categories subject to public transparency (political / housing / employment / credit) return full metadata; other categories return lighter data.
WRITE: Update an ad's name, status, or swap its creative. To replace the creative pass `creative: {creative_id: 'XXX'}`.
WRITE: Update any mutable field on an ad set (status, bid_amount, daily_budget, targeting, name, etc.). Pass only the fields you want to change.
WRITE (BULK): Update `bid_amount` on many ad sets in one Batch API call. Input is an array of {adset_id, bid_amount_cents} pairs. Bid values are in minor currency units (cents). Chunks of 50 automatically.
WRITE: Update any mutable field on a campaign (name, status, daily_budget, lifetime_budget, bid_strategy). Pass only the fields you want to change.
Destructive operations (delete_campaign, delete_adset, delete_ad) lack confirmation or dry-run support. Agents can permanently delete resources without a safety gate.
Parameter descriptions for numeric fields lack min/max bounds. E.g., limit, bid_amount, daily_budget, lifetime_budget have no stated ranges, allowing LLMs to pass invalid values.