New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

zapcore-mcp

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zapcore-mcp

Servidor MCP (stdio) sobre a API REST v2 do zapCore — read-only por padrão, envio atrás de flag explícito + allowlist

latest
npmnpm
Version
0.1.1
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
 
Created
Source

zapcore-mcp

Servidor MCP (stdio) sobre a API REST v2 do zapCore. Zero linha de Go — é um cliente HTTP; o motor não muda e não corre risco.

Desenho completo: docs/specs/2026-08-29-mcp-server-design.md.

O que ele expõe

8 tools de leitura (sempre): zap_session, zap_user_lookup, zap_contacts, zap_groups, zap_labels, zap_history, zap_message_status, zap_media_download.

4 tools de escrita (só com o flag): zap_send, zap_send_interactive, zap_react, zap_chat_action.

Doze tools cobrem ~30 rotas porque o discriminador mora no argumento, não no nome. Cada tool a mais pesa em TODA requisição da sessão — medido: leitura sozinha = 3,9 KB (~1k tokens); com escrita = 8,0 KB (~2k tokens). Uma tool por rota custaria mais de 10× isso e o servidor viraria algo que se desliga.

Requisitos

  • Node.js ≥ 20 (para instalar via npm) ou Docker — não precisa dos dois.
  • Um token de instância zapCore (ZAPCORE_TOKEN) e a URL da API (ZAPCORE_URL).
  • Se a base estiver atrás do Cloudflare Access: um service token do app zapcore-mcp.

Escrita é opt-in, e o padrão não sobe

ZAPCORE_MCP_WRITE=1   sem   ZAPCORE_MCP_ALLOW   →  o servidor NÃO INICIA

Sem ZAPCORE_MCP_WRITE=1 as tools de escrita não são registradas — não é recusa no handler, é ausência no tools/list. O modelo não tem como tentar enviar.

Com o flag ligado, ZAPCORE_MCP_ALLOW é o raio de alcance: mensagem só sai para os números/JIDs listados. As duas travas são independentes de propósito — uma é o interruptor, a outra é o alcance. A allowlist mora no env, fora do alcance da conversa: o modelo não consegue se autorizar.

Config (variáveis de ambiente)

EnvObrigatórioDefaultPara quê
ZAPCORE_TOKENsimheader token da instância
ZAPCORE_URLnãohttps://zapcore.barberai.onlinebase da API — exige https://; http:// só em localhost/127.0.0.1/[::1]
ZAPCORE_MCP_WRITEnão(desligado)1/true/yes liga as 4 tools de envio
ZAPCORE_MCP_ALLOWse WRITEdestinos permitidos, E.164 separados por vírgula
ZAPCORE_MCP_TIMEOUT_MSnão30000timeout por chamada
ZAPCORE_MCP_DOWNLOAD_DIRnão<TEMP>/zapcore-mcponde zap_media_download grava
ZAPCORE_CF_ACCESS_CLIENT_IDse atrás do Accessservice token do Cloudflare Access
ZAPCORE_CF_ACCESS_CLIENT_SECRETse atrás do Accesspar do anterior — os dois ou nenhum

Config ausente/errada falha alto no stderr, com mensagem em português, e o processo sai com código 1 — nunca sobe "meio ligado".

Três formas de rodar

O pacote é publicado no npm como zapcore-mcp. O código-fonte é proprietário e o repositório é privado: o que vai ao registro é o JavaScript compilado, e mais nada. Use uma destas três:

(a) npx, sem instalar nada

npx -y zapcore-mcp

É a forma usada nos exemplos de configuração abaixo. Para fixar a versão: npx -y zapcore-mcp@0.1.0.

(b) Instalar global

npm install -g zapcore-mcp

Registra o binário zapcore-mcp no PATH global.

(c) Docker — forma canônica para distribuir sem expor código-fonte

docker run -i --rm \
  -e ZAPCORE_URL=https://zapcore.barberai.online \
  -e ZAPCORE_TOKEN=... \
  ghcr.io/adeiltonpessini/zapcore-mcp

-i é obrigatório: o servidor fala MCP por stdio, não abre porta nenhuma (sem -p). A imagem em ghcr.io/adeiltonpessini/zapcore-mcp ainda não foi publicada — o Dockerfile builda a partir do fonte, que é privado. Enquanto ela não existir, use (a) ou (b).

Configuração por cliente

Em todos os exemplos abaixo, troque ZAPCORE_TOKEN pelo token real e, se a base estiver atrás do Cloudflare Access, acrescente ZAPCORE_CF_ACCESS_CLIENT_ID e ZAPCORE_CF_ACCESS_CLIENT_SECRET (ver seção Access). Nunca cole o token na conversa com o modelo — ele vai para o env do processo, não para o prompt.

Claude Desktop

claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "zapcore": {
      "command": "node",
      "args": ["/caminho/absoluto/para/zapcore/mcp/dist/bin.js"],
      "env": {
        "ZAPCORE_TOKEN": "...",
        "ZAPCORE_URL": "https://zapcore.barberai.online"
      }
    }
  }
}

Ou via Docker, sem precisar de Node instalado na máquina:

{
  "mcpServers": {
    "zapcore": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "ZAPCORE_URL", "-e", "ZAPCORE_TOKEN",
        "ghcr.io/adeiltonpessini/zapcore-mcp"
      ],
      "env": {
        "ZAPCORE_TOKEN": "...",
        "ZAPCORE_URL": "https://zapcore.barberai.online"
      }
    }
  }
}

Claude Code

CLI (grava em ~/.claude.json do projeto atual):

claude mcp add zapcore -e ZAPCORE_TOKEN=... -e ZAPCORE_URL=https://zapcore.barberai.online \
  -- node /caminho/absoluto/para/zapcore/mcp/dist/bin.js

Ou .mcp.json na raiz do projeto (compartilhável no git sem o token — prefira um wrapper que injete o segredo, como mcp/scripts/zapcore-mcp.ps1 faz para o time interno lendo do DPAPI):

{
  "mcpServers": {
    "zapcore": {
      "command": "node",
      "args": ["/caminho/absoluto/para/zapcore/mcp/dist/bin.js"],
      "env": { "ZAPCORE_TOKEN": "...", "ZAPCORE_URL": "https://zapcore.barberai.online" }
    }
  }
}

No repo interno, registre pelo wrapper — ele decifra o token do DPAPI na hora e nunca escreve segredo em disco:

claude mcp add zapcore -- powershell -NoProfile -ExecutionPolicy Bypass \
  -File f:/zapcore/mcp/scripts/zapcore-mcp.ps1

Cursor

.cursor/mcp.json (no projeto) ou ~/.cursor/mcp.json (global):

{
  "mcpServers": {
    "zapcore": {
      "command": "node",
      "args": ["/caminho/absoluto/para/zapcore/mcp/dist/bin.js"],
      "env": {
        "ZAPCORE_TOKEN": "...",
        "ZAPCORE_URL": "https://zapcore.barberai.online"
      }
    }
  }
}

VS Code (GitHub Copilot Chat)

.vscode/mcp.json, usando inputs para não deixar o token em texto puro no arquivo versionado — o VS Code pergunta uma vez e guarda no cofre de segredos:

{
  "inputs": [
    {
      "type": "promptString",
      "id": "zapcore-token",
      "description": "Token da instância zapCore",
      "password": true
    }
  ],
  "servers": {
    "zapcore": {
      "type": "stdio",
      "command": "node",
      "args": ["/caminho/absoluto/para/zapcore/mcp/dist/bin.js"],
      "env": {
        "ZAPCORE_TOKEN": "${input:zapcore-token}",
        "ZAPCORE_URL": "https://zapcore.barberai.online"
      }
    }
  }
}

Windsurf

~/.codeium/windsurf/mcp_config.json (Windsurf Settings → Cascade → MCP Servers → View raw config):

{
  "mcpServers": {
    "zapcore": {
      "command": "node",
      "args": ["/caminho/absoluto/para/zapcore/mcp/dist/bin.js"],
      "env": {
        "ZAPCORE_TOKEN": "...",
        "ZAPCORE_URL": "https://zapcore.barberai.online"
      }
    }
  }
}

Ligar a escrita

Em qualquer cliente acima, acrescente ao env:

"ZAPCORE_MCP_WRITE": "1",
"ZAPCORE_MCP_ALLOW": "5527997491287"

ZAPCORE_MCP_ALLOW aceita mais de um destino separado por vírgula. Sem ele o processo recusa a subir — não existe "escrita aberta por esquecimento".

Catálogo de tools

Leitura (sempre disponíveis)

ToolParâmetros principaisPergunta de exemplo
zap_session(nenhum)"A sessão do WhatsApp está conectada?"
zap_user_lookupphones[], fields[] (check/info/avatar/lid), preview?"Esse número tem WhatsApp? Qual o nome do perfil?"
zap_contactslimit?, offset?, search?"Procura na agenda um contato chamado Marcos"
zap_groupsaction (list/info/invitelink), groupJID?"Lista os grupos da instância"
zap_labelsaction (list/targets), labelId?, jid?"Quais conversas estão marcadas com a etiqueta VIP?"
zap_historychatJid (ou "index"), limit?, cursor?"Mostra as últimas mensagens dessa conversa"
zap_message_statusmessageId"Essa mensagem já foi lida?"
zap_media_downloadkind, mediaKey, fileEncSHA256, fileSHA256, url?/directPath?"Baixa a imagem que esse cliente mandou"

Escrita (só com ZAPCORE_MCP_WRITE=1)

ToolParâmetros principaisPergunta de exemplo
zap_sendto, kind (text/image/audio/video/document/sticker/location/contact/ptv), body?, media? (data:), fileName?"Manda um texto de confirmação para esse cliente"
zap_send_interactiveto, kind (buttons/list/poll/carousel), buttons?/sections?/options?/cards?, degrade?"Manda uma enquete perguntando o horário preferido"
zap_reactto, messageId, emoji ("" remove)"Reage com 👍 na última mensagem dele"
zap_chat_actionto, action (markread/archive/pin/mute/star/unread/delete), messageId?, value?"Marca essa conversa como lida"

Detalhe completo de cada schema está em src/tools/read.ts e src/tools/write.ts (comentado, é a fonte de verdade — este catálogo é o resumo).

Segurança

  • O token nunca deve estar no prompt da conversa — só no env do processo MCP. Um cliente que só aceita colar variáveis no chat não é seguro para isto.
  • Read-only por padrão: instalar sem ZAPCORE_MCP_WRITE=1 é criptograficamente incapaz de enviar mensagem — as tools de escrita não existem no tools/list, não é uma checagem que pode falhar.
  • A allowlist (ZAPCORE_MCP_ALLOW) mora fora do alcance do modelo: mesmo com escrita ligada, o modelo não pode se autoconceder um novo destino — só quem controla o processo (o env) decide isso.
  • Em máquina de pessoa, prefira um wrapper que leia o token de um cofre (DPAPI, Keychain, pass) e injete no processo filho, em vez de gravar em .mcp.json/claude_desktop_config.json em texto puro — é o que mcp/scripts/zapcore-mcp.ps1 faz para o time interno.

Cloudflare Access: o hostname público é protegido

zapcore.barberai.online fica atrás do Access. Sem credencial a chamada nem chega no zapcore: a borda devolve 302 para a tela de login e o corpo é HTML. Por isso o cliente usa redirect: 'manual' e traduz esse 302 em access_login_required, com a instrução — em vez do genérico "resposta não-JSON", que não diz o que fazer.

Para rodar o MCP fora do servidor, use um service token do Access autorizado no app zapcore (policy zapcore-mcp (service token), decisão non_identity):

"env": {
  "ZAPCORE_TOKEN": "...",
  "ZAPCORE_CF_ACCESS_CLIENT_ID": "....access",
  "ZAPCORE_CF_ACCESS_CLIENT_SECRET": "..."
}

Os dois andam juntos: com só um, loadConfig recusa a subir — metade da credencial vira um 302 confuso lá na frente. Dentro do servidor (netns do container, ZAPCORE_URL= http://127.0.0.1:8080) não passa pela borda e nenhum dos dois é necessário.

Na máquina do dono os segredos ficam em DPAPI, nunca em .txt: zapcore-instance-token, zapcore-mcp-cf-client-id, zapcore-mcp-cf-client-secret (leitura: ~/.secrets/ler-segredo.ps1 <nome>).

As quatro travas que vieram de erro medido em produção

Cada uma é código, não texto de prompt — o modelo não contorna.

  • Mídia só por data:. O container do zapcore não tem egress: imagem por URL externa falha lá dentro. O wrapper recusa a URL com a instrução, em vez de deixar o servidor devolver um erro obscuro.
  • Carrossel exige imagem em todo card. Card sem image quebra a bolha INTEIRA no WhatsApp Desktop/Web e abre normal no celular (carousel.go:23) — metade dos destinatários vê lixo, o que é pior que falhar. Use degrade:"text" para cair em texto simples de propósito.
  • Enquete usa group, nunca Phone — inclusive para número individual. O servidor ainda erra com o typo herdado do projeto de origem (missing Grouop in payload), que manda procurar um campo inexistente; o wrapper traduz.
  • POST de envio não é repetido automaticamente. Timeout de rede não prova que a mensagem não saiu; reenviar duplica no aparelho de quem recebe. Só GET tem retry.

Fora de escopo, de propósito

/status/send/* (story vai para a lista de contatos inteira — allowlist de destino não protege), criação/remoção de grupo, gestão de usuários da instância, e o /session/events (stream). Cada um é raio de explosão que uma tool de agente não deve ter.

Verificação

npm run typecheck && npm run build && npm test

44 testes: as guardas, o mapeamento de erro da v2, o payload que sai na rede em cada kind, e a asserção central — sem ZAPCORE_MCP_WRITE=1 nenhuma tool de escrita é registrada.

Verificação em produção (o que build verde não prova)

A borda pública (zapcore.barberai.online) está atrás do Cloudflare Access — a máquina de dev leva 302 para o login, então não dá para testar de fora. E o serviço não publica porta: no Swarm, Endpoint.Ports = null. O jeito de exercitar o cliente de verdade é rodar dentro do netns do container, onde a API é http://127.0.0.1:8080:

# no servidor, com dist/ + node_modules/zod em /tmp/zapcore-mcp
PG=$(docker ps -q -f name=postgres | head -1)
TOKEN=$(docker exec $PG psql -qtAX -U zapcore -d zapcore \
  -c "select token from users where connected=1 limit 1")
CID=$(docker ps -q -f name=zapcore_zapcore)

docker run --rm --network container:$CID -v /tmp/zapcore-mcp:/app \
  -e ZAPCORE_URL=http://127.0.0.1:8080 -e ZAPCORE_TOKEN="$TOKEN" \
  -e ZAPCORE_MCP_WRITE=1 -e ZAPCORE_MCP_ALLOW=5527997491287 \
  node:22-alpine node /app/scripts/verifica-producao.mjs

scripts/verifica-producao.mjs roda os handlers do MCP, não curl: sessão, lookup, texto, imagem base64, enquete (prova o campo group) e carrossel de 2 cards com imagem — mais as três guardas, que precisam recusar antes da rede. Executado em 30/08/2026 contra produção: 9/9 OK, mensagens entregues no 5527997491287.

Publicar (procedimento)

O pacote npm é a distribuição oficial. package.json já traz files (só .js compilado), bin, engines, prepack (roda o build) e publishConfig: { access: public, provenance: false }proveniência fica desligada: ela exige repositório público no GitHub, e ligar faria todo publish falhar com uma mensagem que convida a abrir o código.

homepage e bugs apontam para https://zapcore.app, não para o repositório: todo link da página do npm é público, e o repo é privado. repository foi removido pelo mesmo motivo. O test/publicacao.test.ts falha se algum voltar a apontar para o GitHub.

cd mcp
npm login                 # conta do dono; 2FA pede OTP no publish
npm run build && npm test
npm pack --dry-run        # confira o conteúdo: só dist/*.js, README, server.json
npm publish               # access/provenance já vêm do publishConfig

Depois que a versão existir no npm, o registro oficial do MCP (mcp/server.json, já no formato do MCP Registry):

mcp-publisher login github     # namespace io.github.adeiltonpessini
mcp-publisher publish

A versão de server.json (topo e dentro de packages[].version) tem que ser a mesma já publicada no npm — o registro valida que o pacote existe.

Para a imagem Docker: o Dockerfile builda a partir do fonte privado; falta um workflow que faça docker build + docker push ghcr.io/adeiltonpessini/zapcore-mcp.

Keywords

mcp

FAQs

Package last updated on 14 Sep 2026

Related posts