Pular para o conteúdo

Servidor MCP

Conecte um assistente de IA ao seu workspace — como autorizar, o que ele pode fazer e todas as ferramentas que recebe.

O Pingo Notify fala MCP (Model Context Protocol), então um assistente de IA pode operar um workspace direto: listar conexões, enviar mensagens de WhatsApp, ler uma conversa, gerenciar webhooks. Você aprova o que ele pode fazer uma vez, numa tela de consentimento, e ele nunca manuseia um token de API.

Conectar

Adicione uma URL ao cliente, como conector customizado ou remoto:

https://api.pingonotify.com/v3/mcp
text

Tudo depois disso é o protocolo fazendo o trabalho dele:

O cliente descobre o servidor

A primeira chamada dele chega sem credencial e recebe um 401 com WWW-Authenticate: Bearer …, resource_metadata="…/.well-known/oauth-protected-resource/v3/mcp". Esse documento diz qual é o servidor de autorização e, de propósito, apenas os escopos que este servidor realmente usa.

Ele se registra sozinho

POST /v3/oauth/register. O registro não tem burocracia: um conector de IA se registra como cliente público — sem segredo, com PKCE no lugar.

Você autoriza

O cliente abre /v3/oauth/authorize. Você entra na conta, escolhe o workspace e aprova os escopos que ele pediu. Este é o único passo com uma pessoa dentro.

Ele recebe um token

POST /v3/oauth/token troca o código, e daí em diante toda chamada leva Authorization: Bearer ….

Nada disso é específico de um produto. É o fluxo padrão de MCP remoto — OAuth 2.1 com registro dinâmico de cliente (RFC 7591), PKCE e tokens restritos por audiência (RFC 8707) — então qualquer cliente conforme conecta do mesmo jeito.

Só um Owner ou um Admin pode autorizar uma aplicação para um workspace. A tela de consentimento oferece apenas os workspaces em que o seu papel permite; um Manager ou um Agent não consegue conectar um assistente ao workspace do time. Autorizar sem escolher workspace amarra o token à sua conta pessoal.

Conectar não exige plano nenhum, e continua funcionando com assinatura expirada — mas enviar não. Veja Limites.

O que o assistente recebe

Um workspace, fixado no consentimento

Tudo que o assistente enxerga pertence ao workspace que você escolheu ao autorizar. Não há ferramenta para trocar de workspace nem header que ele possa mandar para mudar isso: uma requisição que tentar recebe 403 — This access token is bound to a different workspace; re-authorize to switch. Um segundo workspace significa uma segunda autorização.

Escopo é teto, nunca concessão

A permissão efetiva de um token é a interseção entre os escopos que você aprovou e o que você pode fazer naquele workspace. Aprovar messages:send não deixa um assistente enviar se o seu próprio papel não pode; escopo só estreita.

EscopoO que o assistente alcança
profileQuem autorizou, e em qual workspace
connections:readSuas conexões e o status delas, conversas, templates, consulta de número
messages:readHistórico de mensagens e a mídia de uma mensagem recebida
messages:sendEnvio — todos os tipos de mensagem abaixo, e o indicador de "digitando"
webhooks:readSeus webhooks, o catálogo de eventos, as tentativas de entrega
webhooks:writeCriar, editar e excluir webhooks

Você pode sempre aprovar menos do que foi pedido. Reaprovar com menos escopos revoga os tokens já emitidos para aquela integração, para que a tela e a realidade nunca discordem.

connections:write pode aparecer na tela de consentimento também, porque ele igualmente satisfaz "ler uma conexão". Nenhuma ferramenta deste catálogo precisa dele — aprovar só connections:read basta.

Ele conhece o próprio limite antes de tentar

O handshake carrega um parágrafo montado para aquele token: as ferramentas que este acesso pode usar e, pelo nome, as que não pode. Um assistente que já sabe que lhe falta webhooks:write avisa você em vez de gastar um turno descobrindo.

Ferramenta fora de escopo continua listada de propósito. Esconder trocaria uma recusa que diz qual escopo pedir por um inútil "ferramenta desconhecida".

As ferramentas

Vinte e três ferramentas. O corte é por domínio, não por rota — o helpdesk está fora por decisão de produto.

Orientação

FerramentaEscopoConexão
get_workspaceprofilequalquer
list_connectionsconnections:readqualquer
check_whatsapp_numberconnections:readsó QR
list_chatsconnections:readsó QR
list_message_templatesconnections:readsó Oficial
get_message_templateconnections:readsó Oficial

Comece por list_connections, sempre. Toda outra ferramenta precisa de um connectionId, e é ela que revela o tipo da conexão — que decide se uma ferramenta de envio funciona. Ela também informa canSend, verdadeiro só enquanto a conexão está open.

check_whatsapp_number aceita até 50 números por chamada e devolve o id canônico de cada um. get_workspace conta ao assistente quais escopos ele de fato tem.

Mensageria

FerramentaEscopoConexão
send_whatsapp_messagemessages:sendqualquercusta dinheiro
send_whatsapp_mediamessages:sendqualquercusta dinheiro
send_whatsapp_voice_notemessages:sendqualquercusta dinheiro
send_whatsapp_stickermessages:sendsó QRcusta dinheiro
send_whatsapp_templatemessages:sendsó Oficialcusta dinheiro
send_whatsapp_listmessages:sendsó QRcusta dinheiro
send_whatsapp_buttonsmessages:sendsó QRcusta dinheiro
set_typing_presencemessages:sendsó QRplano pago
list_chat_historymessages:readqualquer
download_message_mediamessages:readqualquer
prepare_media_uploadmessages:sendqualquerabre um envio

Há uma ferramenta por forma de mensagem, em vez de uma ferramenta com um campo type. Um único schema com sete ramos opcionais deixa o modelo montar uma combinação que o WhatsApp recusa — e esse erro chega depois de o envio ter sido cobrado.

Webhooks

FerramentaEscopo
list_webhook_event_typeswebhooks:reado catálogo, com uma nota de confiabilidade por evento
list_webhookswebhooks:read
list_webhook_deliverieswebhooks:reado que o Pingo de fato tentou entregar
create_webhookwebhooks:write
update_webhookwebhooks:writedestrutivo: a lista de eventos substitui, não soma
delete_webhookwebhooks:writedestrutivo

Regras que falham em silêncio

Vale ler antes de soltar um assistente: ignorar estas não produz erro nenhum, só um resultado que ninguém recebe.

O tipo da conexão decide o que é possível. Uma conexão QR envia figurinha, lista, botões e o indicador de "digitando", confere se um número existe no WhatsApp, e é a única que alimenta webhook com o conjunto completo de eventos. Uma conexão Oficial (Cloud API da Meta) envia template aprovado — o único jeito de iniciar conversa fora da janela de 24 horas — e não faz nada da lista acima. Uma lista ou um conjunto de botões enviados pelo canal oficial chegam como texto puro, sem os botões, e ninguém recebe erro.

  • Conexão oficial alimenta menos o webhook. Mensagens recebidas (messages.upsert) e atualizações de entrega (messages.update) chegam sim; send.message, messages.delete, messages.edited, presence.update e connection.update nunca chegam. Inscrever-se nesses numa conexão oficial produz silêncio, não recusa.
  • Dois eventos são aceitos no cadastro e nunca entregues em conexão nenhuma: connection.update e presence.update. O list_webhook_event_types carrega essa verdade por evento, e é por isso que o assistente deve chamá-lo antes de criar uma inscrição. Para status de conexão, faça polling em list_connections.
  • Webhook sem conexão vinculada nunca dispara. O create_webhook exige pelo menos um id de conexão — passe todos se você quer o workspace inteiro. O webhook nasce ativo e o list_webhook_deliveries volta vazio, o que se lê exatamente como "evento errado".
  • Seu endpoint vai ver o mesmo evento mais de uma vez. A entrega estoura em 10 segundos e é retentada até três vezes, com uma linha registrada por tentativa. Faça o endpoint idempotente; um endpoint que responde em mais de 10 segundos garante duplicatas.
  • Telefone vai no formato internacional, só dígitos5511999998888, sem +, sem espaço, sem pontuação. Para um número que você nunca mandou mensagem, rode check_whatsapp_number primeiro e envie o id canônico que ele devolve, não o que a pessoa digitou.
  • Mídia já hospedada vai por URL pública https; arquivo local vai por upload. Base64 no argumento não é aceito, e não é capricho: quando a imagem chegou ao assistente como imagem, ela não é mais uma string que ele possa recuperar — e quando os bytes existem, escrevê-los consumiria a resposta inteira (1 MB de arquivo são ~1,4 milhão de caracteres). Para um arquivo na máquina do assistente, prepare_media_upload devolve uma URL de upload de uso único: ele sobe o arquivo com um curl -F 'file=@caminho' e o envio acontece nesse mesmo request, sem os bytes passarem pelo contexto. Sem um terminal disponível, a mesma URL serve para você subir o arquivo à mão.
  • O tipo da mídia é uma escolha, e o contato vê coisas diferentes. Uma imagem aparece na bolha com a legenda embaixo; um vídeo vira player com miniatura e toca dentro da conversa; um áudio é anexo, com botão de play e o nome do arquivo ao lado; um áudio de voz é a bolha redonda com forma de onda e velocidade 1×/1,5×/2×, que é o que as pessoas querem dizer com "manda um áudio"; um documento é o card com ícone, nome e tamanho, que o contato baixa para abrir. Tudo que não é imagem, vídeo ou áudio só pode ser documento. O assistente declara esse tipo ao pedir a URL de upload (ou no mediatype, quando manda por URL) — ele não é adivinhado pela extensão do arquivo, justamente para um vídeo não chegar como card de arquivo.
  • Enviar custa dinheiro do workspace e é irreversível. As instruções do servidor pedem que o assistente confirme com você antes de enviar, a menos que você já tenha pedido aquele envio na conversa.
  • download_message_media desiste em silêncio com arquivo grande. Acima de uns 750 KB ele responde sem conteúdo e com uma nota mandando abrir o arquivo no Pingo — um sucesso, não um erro. Um cliente que só checa falha segue adiante de mãos vazias.
  • send_whatsapp_template preenche só variáveis posicionais de texto. Um template com parameterFormat: NAMED, com header de mídia ou com botão dinâmico não tem como ser preenchido por esta ferramenta — esse caso é a API HTTP. A Meta recusa o envio inteiro quando nome, idioma ou número de parâmetros não batem.
  • delay é em milissegundos e para em 20000. A espera é bloqueante do lado do WhatsApp; valores mais altos estouram a requisição por timeout depois de a mensagem ter sido aceita — erro para o assistente, mensagem entregue para o contato.

Limites

TransporteStreamable HTTP, stateless, só POST
Rate limit do endpoint MCP300 requisições por minuto, chaveadas pela credencial, não pelo IP
Rate limit de /v3/oauth/token120 por minuto, por IP
Rate limit de /v3/oauth/authorize e /register15 por minuto, por IP
Access tokenvale 1 hora
Refresh tokenvale 30 dias, rotacionado a cada uso
Código de autorizaçãovale 60 segundos, uso único
Cota de mensagens e tamanho de textoo que o seu plano permitir — os mesmos limites que o painel aplica
Tamanho de mídiapor URL, o teto do próprio WhatsApp — o Pingo não tem bytes para medir e não aplica o limite do plano. Por upload (prepare_media_upload), vale o limite do seu plano por tipo de mídia
Exige plano pagoset_typing_presence
URL de uploaduso único, vale 10 minutos

O balde do MCP é chaveado por credencial de propósito. Um cliente MCP remoto não é chamado pelo seu navegador: quem chama é a infraestrutura que hospeda o modelo, de um punhado de IPs de saída. Por IP, um workspace derrubaria o outro.

O endpoint MCP responde só a POST. GET e DELETE devolvem 405 com Allow: POST. O servidor é stateless, então não existe sessão para um GET em fluxo receber; um cliente que abrisse um ficaria com um socket que nunca recebe mensagem.

O que ele não faz, de propósito

ForaPor quê
Ler ou rotacionar o segredo de assinatura de um webhookEsse segredo é o que prova que um evento veio do Pingo. Numa ferramenta, ele vira a capacidade de forjar eventos assinados contra o seu endpoint. Rotacionar quebra a verificação em produção no instante seguinte, e nada numa conversa consegue atualizar o outro lado.
Criar, editar ou excluir um template de mensagemÉ configuração da conexão, não mensageria, e um template recusado conta contra a qualidade da sua conta do WhatsApp Business. Ler templates continua disponível — sem isso, enviar um é impossível.
Qualquer coisa do helpdeskFora de escopo por decisão de produto.

Os dois primeiros continuam no painel, onde há uma pessoa.

Quando algo dá errado

Uma ferramenta que falha volta como conteúdo legível, não como exceção de protocolo — então o assistente vê o motivo e consegue corrigir o rumo: pedir outro escopo, avisar você, tentar outra ferramenta. Falhas inesperadas nunca vazam detalhe; a stack e qualquer mensagem de banco ficam no log.

SintomaO que significa
"This integration was not granted permission to use <tool>"Falta um escopo ao token. A recusa diz quais; reautorize o conector e aprove-os. Se repetir, verifique se o seu próprio papel no workspace tem aquela permissão — o token não excede você.
"This workspace has no active plan, so it cannot send messages"Leitura continua funcionando; envio não.
"…reached its plan limit of N messages for the period"É a cota, não a requisição. Repetir não resolve.
403 em toda chamada, citando outro workspaceO token está preso ao workspace escolhido no consentimento. Reautorize para trocar.
401 com error="invalid_token"Expirado, revogado, ou emitido para outro recurso. O header WWW-Authenticate diz qual caso é e aponta o documento de metadata para começar de novo.
Meu webhook nunca disparalist_webhook_deliveries responde se o Pingo chegou a tentar. Lista vazia é problema de inscrição — evento errado, ou conexão oficial. Lista com 4xx/5xx é o seu endpoint.

Escrevendo o seu próprio cliente

Se você está escrevendo o cliente em vez de usar um pronto, quatro detalhes poupam uma tarde:

  • Peça o grant refresh_token no registro. grant_types omitido significa só authorization_code, e aí nenhum refresh token é emitido — o seu conector para de funcionar uma hora depois e precisa de gente de novo.
  • Mande token_endpoint_auth_method: "none" para ser cliente público. Omitir o campo cria um cliente confidencial, que recebe um client_secret mostrado uma única vez e tem de enviá-lo em toda chamada de token.
  • A primeira requisição precisa de Accept: application/json, text/event-stream. O transporte do MCP exige, mesmo que este servidor sempre responda JSON.
  • O parâmetro resource prende o token a um caminho. Um token emitido para https://api.pingonotify.com/v3/mcp é aceito sob /v3/mcp e recusado com 401 em todo o resto da API. Peça o recurso que você realmente pretende chamar.

prompt=consent força a tela mesmo quando já existe um consentimento vivo que cobre o pedido; prompt=none nunca mostra a tela e responde consent_required ou login_required. Fora disso, uma reautorização pode se completar em silêncio — mas só quando existe exatamente um consentimento que cobre os escopos pedidos e o seu papel ainda permite.

O servidor de autorização se descreve em /.well-known/oauth-authorization-server, e a metadata deste endpoint — com a lista exata dos escopos que ele usa — em /.well-known/oauth-protected-resource/v3/mcp. Os dois são cacheados por cinco minutos e não pedem credencial. Para o modelo de credenciais do resto da API, veja Autenticação.

© 2026 Pingo Notify. Todos os direitos reservados.

pingonotify.com ·Feito com Nuxt e Scalar