Skip to main content
Glama
wallacymenezes

mcp-meta-ads

mcp-1nort-meta

Servidor MCP que expõe a Meta Marketing API ao Claude: relatórios de desempenho de anúncios e ajustes operacionais (pausar/reativar, alterar orçamento), para as contas dos clientes da 1nort.

Roda local, por stdio, em TypeScript sobre Node 20+.

O token configurado é de produção. Ele é de um usuário de sistema do Business Manager e alcança as contas de todos os clientes da agência. A allowlist de contas é o que restringe este servidor a um subconjunto — leia docs/seguranca-mutacoes.md antes de liberar escrita.

Começando

npm ci
cp .env.example .env      # e preencha
npm run check             # typecheck + testes
npm run build

O .env precisa ter META_AD_ACCOUNT_ALLOWLIST preenchida. Sem ela o servidor não inicia — de propósito: vazio nunca significa "liberar todas as contas".

Rodar e inspecionar

npm run inspector    # abre o MCP Inspector apontando para o servidor
npm run dev          # roda direto do TypeScript, sem build
npm start            # roda o build de dist/

Registrar no Claude Code

O .mcp.json na raiz já registra dois servidores (só caminhos, nenhum segredo):

  • meta-ads — respeita o .env.

  • meta-ads-somente-leitura — força META_READ_ONLY=true. Como o ambiente do processo tem precedência sobre o --env-file, este é somente-leitura por construção, independente do que o .env diga. Use este como padrão e deixe o outro para sessões deliberadas de alteração.

Ou pela linha de comando:

claude mcp add --scope project meta-ads -- node --env-file=.env dist/index.js

No Claude Desktop, o claude_desktop_config.json precisa de caminhos absolutos (Desktop não tem raiz de projeto).

Related MCP server: Marketing MCP

Ferramentas

Leitura (sempre disponíveis)

Ferramenta

O que faz

meta_server_status

Guard-rails em vigor: contas permitidas, modo, limites. Zero chamadas à Meta. Consulte antes de propor qualquer alteração.

meta_list_ad_accounts

As contas da allowlist, com moeda, fuso e orçamento mínimo.

meta_get_insights

Métricas por conta/campanha/conjunto/anúncio, uma linha por dia por padrão.

meta_list_campaigns

Campanhas com status, objetivo e orçamento.

meta_list_ad_sets

Conjuntos, opcionalmente de uma campanha específica.

meta_list_ads

Anúncios com miniatura em 512px; include_media resolve a URL do vídeo.

Banco (só com DB_FERRAMENTAS_MCP=true)

Leem o banco da 1nort com um usuário SELECT-only. Histórico já coletado, sem consumir cota da Meta.

Ferramenta

O que faz

db_scope_summary

Quantas contas o escopo alcança e até que dia o banco tem dado.

db_list_clients

Nome e segmento do cliente de cada conta — traduz "cliente X" em id de conta.

db_account_daily

Série diária de uma conta a partir do histórico coletado.

db_collector_health

Se a coleta de uma conta está bloqueada e até onde chegou.

db_top_ads

Ranking de anúncios do período.

Nenhuma delas aceita SQL, e nenhuma aceita e-mail. SQL livre não tem como ser seguro aqui: o usuário somente-leitura enxerga tb_credenciais (com token_kommo, token_ghl, pixel_token) e a própria tb_meta_ads tem pixel_token; um agente que lê nome de campanha escrito por cliente é superfície de injeção. Filtrar SQL na aplicação não resolve (CTE, subquery, pg_catalog, ::regclass), e a restrição correta — REVOKE no Postgres — depende de um direito que não temos. E como nenhuma ferramenta aceita e-mail, o modelo não consegue trocar o próprio escopo: isso é ação do humano na tela.

Escrita (só com META_READ_ONLY=false)

Ferramenta

O que faz

meta_update_status

Pausa ou reativa campanha, conjunto ou anúncio.

meta_update_budget

Altera orçamento diário ou total de campanha ou conjunto.

meta_create_campaign

Cria campanha. Nasce pausada.

meta_create_ad_set

Cria conjunto numa campanha existente. Nasce pausado.

meta_create_ad

Cria anúncio a partir de publicação, criativo salvo ou URL de imagem.

meta_create_audience

Cria público personalizado (Instagram, página, site ou formulário).

E três de leitura que existem para o modelo não inventar id: meta_list_creatives, meta_list_publications e meta_list_audiences.

Tudo nasce pausado, e isso não é parâmetro. Nenhuma ferramenta de criação aceita status: um objeto pausado não gasta, então errar custa zero, e ativar continua sendo meta_update_status — auditado e com confirmação do host.

special_ad_categories é constante [], fora do schema. A 1nort declara que não anuncia crédito, emprego, moradia nem tema social. Se isso mudar — financiamento solar seria CREDIT —, o valor está em SEM_CATEGORIA_ESPECIAL, num lugar só, e alterá-lo é decisão de um humano. Declarar categoria especial errada é violação de política da Meta, não erro de campanha.

Não existe exclusão nem arquivamento. DELETED e ARCHIVED não estão no schema, então não há caminho de código que chegue a eles — objeto criado por engano se apaga à mão no Gerenciador.

Duas regras de negócio que não podem ser afrouxadas

1. Leads de formulário e de mensagem nunca se somam.

leads_form     ← onsite_conversion.lead_grouped
leads_message  ← onsite_conversion.messaging_conversation_started_7d

O action type genérico lead é ignorado — conta formulário por outro critério e diverge de lead_grouped por ordem de grandeza. Ausência devolve null, nunca 0: "não medido" e "medido e deu zero" são fatos diferentes. Regra portada de ExtratorAcoesMeta.java, do projeto Citrino, para que os relatórios dos dois sistemas não divirjam para o mesmo cliente no mesmo dia.

2. Orçamento é informado na unidade MAIOR da moeda.

amount: 250.50 significa R$ 250,50. A Meta armazena em unidade menor (25050) e a conversão é do servidor. Mandar centavos aqui definiria um orçamento cem vezes maior — por isso a resposta sempre mostra as duas unidades lado a lado, para o erro saltar aos olhos antes da confirmação.

Segurança

Resumo; o contrato completo está em docs/seguranca-mutacoes.md.

  • Allowlist de contas, com comparação exata e tipo branded que o compilador cobra.

  • Vínculo objeto → conta antes de toda escrita: POST /{id} não menciona conta nenhuma, então sem essa releitura a allowlist não valeria nada na escrita.

  • Duas chaves para aplicar: META_WRITE_DRY_RUN=false e confirm: true. Prévia e aplicação percorrem o mesmo código; muda só o validate_only, que é validado pela própria Meta.

  • Limites de orçamento com falha fechada; moeda fora da tabela é recusada, nunca chutada.

  • Auditoria de toda tentativa, inclusive recusas, em stderr.

  • Token só no header Authorization; appsecret_proof em toda chamada.

Variáveis de ambiente

Ver .env.example. As que mais importam:

Variável

Padrão

Efeito

META_AD_ACCOUNT_ALLOWLIST

(nenhum)

Obrigatória. Sem ela o servidor não inicia.

META_READ_ONLY

true

Ferramentas de escrita nem são registradas.

META_WRITE_DRY_RUN

true

Toda escrita vira validate_only.

META_MAX_BUDGET_CHANGE_PCT

50

Variação máxima num ajuste.

META_MAX_DAILY_BUDGET

1000

Teto diário, unidade maior.

META_ALLOWLIST_FONTE

env

banco faz a allowlist vir do que META_ESCOPO_EMAILo usuário somente-leitura enxerga.

DB_URL / DB_USER / DB_PASSWORD

(nenhum)

Ausentes = recurso de banco desligado.

DB_FERRAMENTAS_MCP

false

Registra as cinco ferramentas db_*.

Interface local

npm run ui         # constrói tudo e sobe em http://127.0.0.1:8787

Ou, do Claude Code: /painel — ou /painel email@do-usuario para abrir já escopado.

O painel lê do banco: uma consulta traz o histórico de todas as contas do escopo. Um gestor com 67 contas carrega em menos de 2 s com zero chamadas à Meta. O que só a Graph sabe — teto de gastos, status da conta, moeda, fuso, campanhas ativas — chega ao expandir uma linha, uma conta por vez. Antes eram quatro chamadas por conta na carga da tela, o que a 67 contas seriam 268 chamadas contra um limite de ~15/min.

O chat responde interpretando os números, com as duas famílias de ferramenta. A conversa é gravada em .conversas/ (fora do git): sobrevive a trocar de aba, a F5, a fechar o navegador e a reiniciar o servidor. Uma resposta cortada no meio volta marcada como interrompida — e nunca é retomada sozinha, o que recobraria tokens e poderia reexecutar consultas.

Escopo por usuário

Ativar um e-mail resolve users → tb_usuarios_clientes → tb_clientes → tb_meta_ads e constrói uma Allowlist nova com essas contas. ROLE_ADMINo usuário somente-leitura enxerga todos os clientes ativos, porque a plataforma não enumera vínculo de administrador. Contas sem cliente vinculado não entram no escopo de ninguém — conta sem dono não é de todo mundo —, mas a contagem aparece no aviso.

Ativar é ação de operador, pelo controle da tela ou por ?ativar=. Não é um padrão que o sistema reconheça dentro de uma mensagem: o agente lê texto escrito por cliente, e uma campanha chamada [ativar:alguem@1nort...] viraria escalonamento de escopo por um campo que o cliente controla. O atalho de digitação existe, mas é tratado só no navegador e removido antes do envio.

De onde vem cada número

Dado

Fonte

gasto, impressões, cliques, leads, série diária

banco (tb_meta_anuncios_metricas)

cliente, segmento, saúde da coleta

banco

teto de gastos, status da conta, moeda, fuso

Meta, sob demanda

campanhas ativas, dados de hoje

Meta, sob demanda

A escolha da tabela foi medida, não suposta (npm run diag:comparar). Em julho/2026, contra a Graph: tb_dados_trafego perdia 67 leads de mensagem numa conta, 54 de formulário noutra, e não tinha linha nenhuma para uma terceira que gastou R$ 4.850 no mês. tb_meta_anuncios_metricas bateu nas três, dentro de 0,2%, e ainda preserva NULL para "não medido" — coisa que o agregado legado não faz (13.264 linhas, 8.875 zeros onde deveria haver nulo).

Período pedido além da cobertura é recortado e avisado, nunca completado em silêncio: somar um dia do banco com um dia da Graph produz um total que não reconcilia com fonte nenhuma.

Desenvolvimento

npm test           # 325 testes
npm run typecheck
npm run fmt
npm run diag:banco     # sonda o banco: cobertura, semântica de nulo, escopo de um e-mail
npm run diag:comparar -- act_123 2026-07-01 2026-07-31   # banco × Graph, mesma conta e período

Testes injetam fetch em vez de mockar o cliente Graph, então exercitam o que de fato quebra: montagem de URL, a string fields, encoding do time_range, cursor de paginação e parsing de número-string. test/invariantes.test.ts varre todas as URLs emitidas atrás do token, confere que ele está no Authorization de toda chamada, e falha se alguma coisa escrever em stdout.

Pendências conhecidas

  1. Conferir os números contra o Gerenciador de Anúncios antes de confiar neles. A Meta interpreta datas no fuso da conta, e a janela de atribuição padrão molda a contagem de leads. Toda resposta de meta_get_insights traz account_timezone e o período resolvido justamente para permitir essa conferência. Pode ser necessário fixar action_attribution_windows explicitamente.

  2. Criar uma campanha sacrificial antes do primeiro teste de escrita real — campanha nova, pausada, orçamento mínimo, sem anúncios. Senão a primeira escrita cai num cliente pagante.

  3. Ligar Require App Secret nas configurações do app Meta. O appsecret_proof já é enviado em toda chamada, mas o benefício (token roubado deixa de servir sozinho) só vale depois de ativar a exigência do lado da Meta.

  4. Token com escopo menor. A allowlist é controle de aplicação sobre uma credencial sem limite técnico. A correção estrutural é um segundo usuário de sistema do BM com acesso só às contas permitidas.

  5. v21.0 expira em 2027-01-21. Agendar o bump, de preferência junto com o Citrino, para o conhecimento compartilhado de erros e action types continuar valendo.

  6. Rate limit (~15 req/min por conta). Há teto de páginas e de linhas, e o resultado sempre informa truncated. Falta decidir se vale acrescentar pausa entre páginas.

  7. node_modules/ e dist/ dentro do OneDrive sofrem varredura de sincronização contínua. Recomendo excluir os dois do sync, ou mover o projeto para fora do OneDrive.

  8. O agregado tb_dados_trafego está errado onde tb_meta_anuncios_metricas está certo. Vale investigar o coletor ou aposentar a tabela — hoje ela só alimenta vw_desempenho_conta_diario, que também expõe um total_leads somado e deveria sair de cena junto. Ver docs/proposta-tabelas.sql.

  9. .conversas/ dentro do OneDrive significa transcript com dado de cliente (gasto, nome de campanha, id de conta) sincronizando para uma nuvem pessoal. Mesmo problema do item 7, com conteúdo mais sensível.

  10. Postgres de produção sem TLS na internet pública. A senha e todas as linhas viajam em texto claro. Não é bug deste projeto, mas vale levar a quem cuida do db02.

F
license - not found
-
quality - not tested
B
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.

Related MCP Servers

  • A
    license
    -
    quality
    D
    maintenance
    An MCP server for programmatic management of Meta (Facebook/Instagram) advertising campaigns through AI assistants. It enables campaign creation, ad set management, creative upload, analytics, audience management, and conversion tracking.
    Last updated
    169
    4
    MIT
  • A
    license
    -
    quality
    B
    maintenance
    A unified MCP server for marketing analytics and management across Google Ads, Meta Ads, GA4, and keyword research, supporting multi-client rollups and platform-conditional credential requirements.
    Last updated
    MIT
  • A
    license
    -
    quality
    D
    maintenance
    MCP server for Meta/Facebook Marketing API allowing you to view and manage ad accounts, campaigns, ad sets, ads, and creatives, as well as fetch insights and upload ad images.
    Last updated
    MIT
  • A
    license
    -
    quality
    D
    maintenance
    Read-only MCP server for Meta Ads that lists and reads ad accounts, campaigns, ad sets, ads, ad images, creatives, and fetches insights at various levels.
    Last updated
    MIT

View all related MCP servers

Related MCP Connectors

  • Managed LinkedIn MCP server for AI agents: search, connect, message and enrich on accounts you own.

  • Hosted Amazon Seller Central and Amazon Ads MCP server for Claude, ChatGPT, Cursor, and agents.

  • A paid remote MCP for CLI tool MCP, built to return verdicts, receipts, usage logs, and audit-ready

View all MCP Connectors

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/wallacymenezes/mcp-meta-ads'

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