Skip to main content
Glama
mlreymendez

MCP Meta Ads

by mlreymendez

MCP Meta Ads

Servidor MCP para preparar, validar y crear campañas de Meta Ads desde un brief en castellano. El contrato está congelado en CAMPAIGN_SPEC.md: ese documento manda, este README solo cuenta cómo está implementado.

Estado: núcleo completo con la suite de tests de la sección 5 en verde. Nunca se corrió contra la Marketing API real — la sección 6 del spec (log de errores de Meta) sigue vacía justamente por eso.

Las 6 reglas invariantes, y dónde viven en el código

Regla

Dónde se hace cumplir

1. Todo se crea en PAUSED

payloads.py fuerza status=PAUSED en los 4 payloads; validate.py recorre el spec entero (_statuses_recursivos) y rechaza cualquier otro status antes de tocar la API

2. ad_account_id nunca se infiere

profiles.ProfileStore.resolver(): con 2+ cuentas y sin alias, AmbiguousAccountError con la lista de cuentas

3. El token no vive en el repo

El perfil guarda token_ref (nombre de la variable de entorno); AccountProfile.token() la lee en el momento y no la cachea. Un test recorre el repo buscando algo con forma de token

4. Desarrollo va contra sandbox

AccountProfile.exigir_sandbox() corre antes de cada llamada; solo META_ADS_ALLOW_PRODUCTION=1 la levanta, y habilitarla no es tarea de un agente

5. Presupuestos enteros en unidades menores

validate.py rechaza floats y strings; payloads.py manda str(int(...))

6. preparar nunca escribe en Meta

spec.py no importa el cliente HTTP; un test rompe urllib.request.urlopen mientras corre preparar

Related MCP server: meta-ads-manager-mcp

Estructura

meta_ads/
  errors.py        errores con `code` estable + mensaje accionable
  enums.py         objetivos, compatibilidades, CTAs, mínimos por moneda
  profiles.py      perfil de cuentas, resolución de cuenta, token, sandbox
  brief.py         brief en castellano -> hechos (nada que el brief no diga)
  spec.py          armado del Campaign Spec + provenance + warnings
  validate.py      validación local (2 severidades) + validate_only contra Meta
  payloads.py      spec -> payloads de la Marketing API (un solo lugar)
  meta_client.py   cliente HTTP (solo stdlib, urllib)
  create.py        creación de los 4 objetos + rollback
  server.py        adaptador MCP (mcp 1.x y 2.x)
tests/             80 tests, sin red, stdlib

El núcleo no tiene dependencias: pip install -r requirements.txt solo hace falta para exponerlo como servidor MCP.

Uso

cp profiles.example.json profiles.json     # completar ids reales (sandbox)
cp .env.example .env                       # y exportar SECRET_META_TOKEN_OWN
export META_ADS_PROFILES=$PWD/profiles.json
python3 -m meta_ads.server                 # stdio
./correr_tests.sh                          # la suite entera, sin red

Los tools se registran con nombre ASCII (preparar_campana, validar_campana, crear_campana) porque MCP no admite ñ en los identificadores; son los preparar_campaña / validar_campaña / crear_campaña del documento. Hay un cuarto tool, listar_cuentas, que no toca Meta: sirve para que el agente pueda preguntar "¿cuál de estas?" cuando la regla 2 lo obliga a preguntar.

El copy no lo inventa el servidor

preparar_campana(brief, account_alias?, creatives?) acepta creatives como lista de dicts escritos por el agente. Si no vienen, el spec sale con primary_text / headline en null, provenance pending y un warning; validar y crear los exigen completos. Es la división honesta: el spec resuelve estructura, el agente escribe texto. Lo mismo con los assets: image_hash / video_id entran ya resueltos, el spec no sube nada.

Desviaciones del spec congelado

Dos campos aditivos y opcionales (un spec sin ellos sigue siendo válido):

  • preset (raíz): nombre del preset aplicado. Sin esto, las reglas 11-13 de la sección 5 no son verificables. Si falta, validate.py deduce el preset comparando objective + optimization_goal + destination_type contra los presets del perfil.

  • ad_set.destination_type: los presets ya lo definen y la API lo pide.

Y dos valores de provenance que el documento no enumera: pending (copy que falta escribir) y brief aplicado a account.resolved_by cuando el propio brief nombra la cuenta.

Lo que todavía no está probado contra Meta

Esto es lo que hay que mirar primero cuando haya token de sandbox:

  1. validate_only del ad set. Meta exige campaign_id para validar un ad set, y en preparar la campaña todavía no existe. Hoy se valida la campaña contra la API y el resto solo localmente; si el spec trae existing.campaign_id, también se valida el ad set. Ver validar_campana.

  2. Click-to-WhatsApp. payloads.creative_payload arma call_to_action.value.app_destination = "WHATSAPP" con link = https://api.whatsapp.com/send. Es la forma más documentada, no está confirmada contra la cuenta.

  3. Mínimos por moneda (enums.MIN_DIARIO_POR_MONEDA_MINOR): solo el USD sale de documentación. El resto son valores conservadores. El mínimo efectivo es max(mínimo de moneda, limits.daily_budget_min_minor), así que el techo real lo pone el perfil.

  4. Nombres de enum en general: la tabla COMPATIBILIDAD es una foto y validate_only es el árbitro. Cada Invalid parameter que aparezca se corrige en enums.py y se anota en la sección 6 del CAMPAIGN_SPEC.md.

Mapa de los casos de test de la sección 5

Caso

Test

1. Brief mínimo, una cuenta

test_preparar.Caso1BriefMinimo

2. Brief que sobreescribe defaults

test_preparar.Caso2BriefSobreescribeDefaults

3. Spec válido → validar ok sin crear

test_validar.Caso3SpecValido

4. crear → 4 objetos en PAUSED

test_crear.Caso4CreacionCompleta

5. Sin cuenta habiendo dos

test_preparar.Caso5CuentaAmbigua

6. Presupuesto fuera de límites

test_preparar.Caso6PresupuestoFueraDeLimites, test_validar.Caso6PresupuestoEnElSpec

7. Cualquier status: ACTIVE

test_validar.Caso7StatusActive

8. Combinación objective/goal/billing inválida

test_validar.Caso8CombinacionInvalida

9. creative_ref inexistente

test_validar.Caso9CreativeRefInexistente

10. Fallo en el POST del ad → rollback

test_crear.Caso10RollbackAlFallarElAd

11. wsp_leads con link_url

test_presets.Caso11WspConLinkUrl

12. web_conversions sin pixel / evento

test_validar.Caso12PromotedObjectIncompleto

13. wsp_leads con cta que no es WhatsApp

test_presets.Caso13WspConCtaEquivocada

14. wsp_leads de punta a punta

test_presets.Caso14PresetWspLeads

15. web_conversions de punta a punta

test_presets.Caso15PresetWebConversions

test_invariantes.py cubre las 6 reglas de la sección 1 y el cliente HTTP; test_server.py, el contrato de los handlers MCP.

Los casos que "deben fallar" se verifican en las dos puertas: preparar los rechaza al armar el spec y validar/crear los rechazan también si alguien edita el spec a mano — con la comprobación extra de que la API no se llamó.

Related MCP Connectors

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables AI assistants to manage Facebook and Instagram advertising campaigns through the Meta Marketing API. Supports full campaign lifecycle management, performance analytics, audience targeting, and creative optimization.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables users to create, manage, and monitor Meta (Facebook/Instagram) ad campaigns directly from Claude using natural language.
    34 PyPI
    11
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A Model Context Protocol server that lets AI assistants run your Meta Ads end to end — launch campaigns, upload creatives, update budgets, and dig into performance through natural conversation. Works across Facebook, Instagram, and other Meta surfaces.
    Business Source 1.1
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables full read/write management of Facebook ad campaigns, ad sets, ads, and creatives via the Meta Marketing API through natural language, with AI creative generation, performance analytics, and PDF reporting.
    MIT