Skip to main content
Glama
netty-linux

Meta Ad Library MCP

by netty-linux
README.md
# Meta Ad Library MCP

Servidor [MCP](https://modelcontextprotocol.io/) de produção para consultar a **Meta Ad Library API oficial** (`/ads_archive`) — sem scraping.

Construído com **FastMCP**, **httpx**, **Pydantic v2** e cache **SQLite** com TTL (preparado para migrar para Redis).

## Ferramentas (v1)

| Ferramenta | Descrição |
|---|---|
| `search_meta_ads` | Busca anúncios por palavras-chave + país |
| `get_ads_from_page` | Lista anúncios de uma página (`page_id`) |
| `health_check` | Verifica token e saúde do servidor |

## Limitações reais da API (2026)

A Meta Ad Library API é **oficial**, mas tem restrições importantes:

1. **UE e Reino Unido** — A API retorna a maior parte dos anúncios comerciais entregues nessas regiões, incluindo dados de transparência (alcance, targeting, beneficiários, etc.).

2. **Fora da UE/UK (ex.: EUA, Brasil)** — A cobertura de **anúncios comerciais** é **muito limitada**. Em geral, a API retorna principalmente:
   - Anúncios de **temas sociais, eleições e política** (`POLITICAL_AND_ISSUE_ADS`)
   - Anúncios que **atingiram audiência na UE**, mesmo que a campanha seja global

3. **Sem tradução automática** — `search_terms` devem estar no **idioma dos anúncios** que você busca.

4. **Campos condicionais** — `spend`, `impressions`, `demographic_distribution` e similares só aparecem em categorias/regiões específicas (principalmente política e UE).

5. **Rate limits** — A Graph API aplica limites; o cliente faz retry automático em erros 429/5xx.

6. **Não é substituto do site** — O site [facebook.com/ads/library](https://www.facebook.com/ads/library) pode exibir mais anúncios do que a API em alguns cenários fora da UE.

> **Resumo para agentes de IA:** Para pesquisa competitiva comercial no Brasil ou EUA, espere resultados parciais. Para compliance/transparência na UE/UK ou ads políticos globais, a API é confiável.

## Pré-requisitos

- Python **3.12+**
- Conta [Meta for Developers](https://developers.facebook.com/)
- **User Access Token** com permissão `ads_read`

## Como obter o User Access Token

1. Acesse [developers.facebook.com](https://developers.facebook.com/) e crie um app (tipo **Business** ou **Other**).
2. No app, vá em **Tools → Graph API Explorer**.
3. Selecione seu app e adicione a permissão **`ads_read`**.
4. Gere um **User Access Token** (não Page Token).
5. Para produção, prefira um **token de longa duração** via [Access Token Debugger](https://developers.facebook.com/tools/debug/accesstoken/) → *Extend Access Token*.
6. Copie o token para `META_ACCESS_TOKEN`.

Documentação oficial: [Ad Library API](https://www.facebook.com/ads/library/api/)

## Setup local

```bash
cd meta-ad-library-mcp
python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS/Linux
source .venv/bin/activate

pip install -r requirements.txt
cp .env.example .env
# Edite .env e defina META_ACCESS_TOKEN
```

### Executar

```bash
python server.py
```

Por padrão usa transporte **stdio** (ideal para Claude Desktop, Cursor, etc.).

### Modo HTTP (remoto / Railway)

```bash
# .env
MCP_TRANSPORT=http
MCP_PORT=8000
```

```bash
python server.py
# Endpoint MCP: http://localhost:8000/mcp
```

## Conectar clientes MCP

### Cursor

Em **Settings → MCP**, adicione:

```json
{
  "mcpServers": {
    "meta-ad-library": {
      "command": "python",
      "args": ["D:/caminho/absoluto/para/meta-ad-library-mcp/server.py"],
      "env": {
        "META_ACCESS_TOKEN": "seu_token_aqui"
      }
    }
  }
}
```

> Use o caminho absoluto do `server.py` e o Python do seu venv se preferir.

### Claude Desktop

Em `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "meta-ad-library": {
      "command": "python",
      "args": ["/caminho/absoluto/para/meta-ad-library-mcp/server.py"],
      "env": {
        "META_ACCESS_TOKEN": "seu_token_aqui"
      }
    }
  }
}
```

### Cliente HTTP (Ollama, scripts, etc.)

Com `MCP_TRANSPORT=http`:

```python
import asyncio
from fastmcp import Client

async def main():
    async with Client("http://localhost:8000/mcp") as client:
        result = await client.call_tool(
            "search_meta_ads",
            {"search_terms": "fitness", "country": "GB", "limit": 10},
        )
        print(result)

asyncio.run(main())
```

## Deploy no Railway

### 1. Preparar o repositório

Faça push desta pasta para um repositório Git (GitHub, GitLab, etc.).

### 2. Criar projeto no Railway

1. [railway.app](https://railway.app/) → **New Project** → **Deploy from GitHub repo**
2. Selecione o repositório

### 3. Variáveis de ambiente

| Variável | Valor |
|---|---|
| `META_ACCESS_TOKEN` | Seu token Meta |
| `MCP_TRANSPORT` | `http` |
| `MCP_HOST` | `0.0.0.0` |
| `MCP_PORT` | `${{PORT}}` (Railway injeta a porta) |
| `CACHE_DB_PATH` | `/data/cache.db` |

### 4. Volume persistente (cache SQLite)

1. No serviço Railway → **Volumes** → **Add Volume**
2. Mount path: `/data`
3. Garanta `CACHE_DB_PATH=/data/cache.db`

Sem volume, o cache é perdido a cada redeploy.

### 5. Dockerfile

O projeto já inclui um `Dockerfile` otimizado. Railway detecta e usa automaticamente.

### 6. Verificar deploy

```bash
curl https://seu-app.up.railway.app/mcp
```

Use a URL pública no cliente MCP com transporte HTTP.

## Variáveis de ambiente

| Variável | Padrão | Descrição |
|---|---|---|
| `META_ACCESS_TOKEN` | — | **Obrigatório.** Token com `ads_read` |
| `META_API_VERSION` | `v25.0` | Versão da Graph API |
| `CACHE_TTL_SECONDS` | `3600` | TTL do cache em segundos |
| `CACHE_DB_PATH` | `./data/cache.db` | Caminho do SQLite |
| `MCP_TRANSPORT` | `stdio` | `stdio` ou `http` |
| `MCP_HOST` | `0.0.0.0` | Host HTTP |
| `MCP_PORT` | `8000` | Porta HTTP |
| `LOG_LEVEL` | `INFO` | Nível de log |
| `REQUEST_TIMEOUT_SECONDS` | `30` | Timeout httpx |
| `MAX_RETRIES` | `3` | Retries em 429/5xx |

## Estrutura do projeto

```
meta-ad-library-mcp/
├── server.py                 # FastMCP server + tools
├── config.py                 # Settings via env vars
├── clients/
│   └── meta_api_client.py    # httpx client + pagination/errors
├── models/
│   └── ad_models.py          # Pydantic v2 models
├── cache/
│   ├── base.py               # CacheBackend protocol (Redis-ready)
│   └── sqlite_cache.py       # SQLite implementation
├── data/                     # SQLite cache (gitignored)
├── requirements.txt
├── Dockerfile
└── README.md
```

## Migrar cache para Redis

Implemente `CacheBackend` em `cache/redis_cache.py` com os mesmos métodos de `SqliteCache`, injete no `MetaApiClient` em `server.py`, e troque a instância — nenhuma tool precisa mudar.

## Exemplos de uso (tools)

**Busca por keyword (Reino Unido — boa cobertura comercial):**

```json
{
  "search_terms": "protein powder",
  "country": "GB",
  "limit": 25
}
```

**Anúncios de uma página:**

```json
{
  "page_id": "123456789012345",
  "country": "US",
  "ad_active_status": "ALL",
  "limit": 50
}
```

**Paginação:**

Passe `after` com o valor de `paging.next_cursor` da resposta anterior.

## Licença

MIT — use livremente em projetos pessoais e comerciais.