Skip to main content
Glama
MCP WhatsApp (uazapi)

MCP WhatsApp (uazapi)

README.md
# MCP WhatsApp (uazapi)

Servidor **MCP** que dá ao Claude acesso ao WhatsApp da empresa através da API
[uazapi](https://uazapi.com). Feito para rodar em VPS via **EasyPanel** e ser conectado
ao Claude como conector remoto.

Com ele o Claude consegue, por exemplo:

- responder clientes lendo antes o histórico da conversa;
- levantar atendimentos em aberto, conversas paradas e clientes por etiqueta;
- mandar catálogo em PDF, foto de produto, localização da loja;
- agendar campanhas de divulgação com intervalo seguro entre mensagens;
- manter o CRM em dia (status do lead, tags, responsável, notas internas).

---

## Índice

1. [Como funciona](#como-funciona)
2. [Pré-requisitos](#pré-requisitos)
3. [Deploy no EasyPanel](#deploy-no-easypanel)
4. [Variáveis de ambiente](#variáveis-de-ambiente)
5. [Conectar ao Claude](#conectar-ao-claude)
   - [Conectar ao Codex](#conectar-ao-codex)
6. [Ferramentas disponíveis](#ferramentas-disponíveis)
7. [Segurança](#segurança)
8. [Desenvolvimento local](#desenvolvimento-local)
9. [Solução de problemas](#solução-de-problemas)

---

## Como funciona

```
Claude  ──HTTPS (POST /mcp)──▶  este servidor MCP  ──HTTPS──▶  uazapi  ──▶  WhatsApp
          Authorization: Bearer        (na sua VPS)              header: token
```

Há duas URLs de conexão:

| URL | Alcance |
| --- | --- |
| `POST /mcp` | Todos os números da conta; o Claude escolhe pelo parâmetro `instance`. |
| `POST /mcp/<instancia>` | **Somente aquele número.** O parâmetro `instance` deixa de existir. |

O servidor opera em **modo stateless**: cada requisição é independente, então o
container pode reiniciar ou escalar sem derrubar a conexão com o Claude.

---

## Pré-requisitos

1. **Conta uazapi** — anote a **URL do servidor** (ex.: `https://seusubdominio.uazapi.com`)
   e o **token de administrador** (ou o token de uma instância específica).
2. **VPS com EasyPanel** instalado.
3. **Um subdomínio** apontando para a VPS (ex.: `whatsapp-mcp.suaempresa.com.br`).
4. Use **WhatsApp Business**, não o WhatsApp comum — a própria uazapi recomenda,
   porque a conta pessoal desconecta e trava com mais frequência em integrações.

---

## Deploy no EasyPanel

### 1. Criar o serviço

No projeto do EasyPanel: **+ Service → App**, nome `mcp-whatsapp`.

### 2. Apontar para o repositório

Na aba **Source**:

| Campo | Valor |
| --- | --- |
| Tipo | GitHub (ou Git) |
| Repositório | este repositório |
| Branch | `main` |
| Build Path | `/` |

Na aba **Build**, escolha **Dockerfile** (o `Dockerfile` na raiz já está pronto).

### 3. Configurar as variáveis

Na aba **Environment**, cole e ajuste:

```env
UAZAPI_URL=https://seusubdominio.uazapi.com
UAZAPI_ADMIN_TOKEN=seu-token-de-administrador
MCP_AUTH_TOKEN=cole-aqui-um-segredo-longo
PORT=8080
SEND_ENABLED=false
```

Com `UAZAPI_ADMIN_TOKEN`, o servidor descobre sozinho **todos os números da conta** —
inclusive os que você criar depois. Não precisa cadastrar loja por loja.

Gere o `MCP_AUTH_TOKEN` com:

```bash
openssl rand -hex 32
```

> Comece com `SEND_ENABLED=false`. Assim o Claude consegue **ler** o WhatsApp mas
> não envia nada, e você valida tudo sem risco. Depois mude para `true`.

### 4. Expor a porta e o domínio

Na aba **Domains**: **Add Domain**.

| Campo | Valor |
| --- | --- |
| Host | `whatsapp-mcp.suaempresa.com.br` (ou o domínio automático do EasyPanel) |
| Caminho | `/` |
| Protocolo (destino) | `HTTP` |
| **Porta (destino)** | **`8080`** — o padrão `80` não funciona, o container escuta na 8080 |
| HTTPS | ligado (Let's Encrypt) |

### 5. Deploy e verificação

Clique em **Deploy**. Quando terminar, teste:

```bash
curl "https://whatsapp-mcp.suaempresa.com.br/health?token=SEU_MCP_AUTH_TOKEN"
```

Resposta esperada:

```json
{
  "status": "ok",
  "server": "whatsapp-uazapi",
  "version": "1.1.0",
  "instances": ["loja-centro", "loja-shopping"],
  "autoDiscovery": true,
  "sendEnabled": false,
  "authRequired": true
}
```

Confira três coisas:

- **`instances`** deve listar seus números (a lista só aparece com o token; sem ele, o
  `/health` mostra apenas status, versão e a quantidade em `instancias`). Vazio significa que a descoberta falhou —
  cheque `UAZAPI_ADMIN_TOKEN` e os logs do container.
- **`authRequired`** deve ser `true`. Se vier `false`, o `MCP_AUTH_TOKEN` não foi aplicado
  e o endereço está aberto na internet — corrija antes de prosseguir.
- **`status`** vindo como `"atencao"` traz um campo `avisos` explicando o que falta.

---

## Variáveis de ambiente

### Conexão com a uazapi

`UAZAPI_URL` é sempre obrigatória. Depois, escolha **uma** das formas:

| Variável | Descrição |
| --- | --- |
| `UAZAPI_URL` | URL do seu servidor uazapi. |
| `UAZAPI_ADMIN_TOKEN` | **Forma A (recomendada).** Token de administrador; o servidor descobre todas as instâncias da conta sozinho. |
| `UAZAPI_TOKEN` | **Forma B.** Token de uma instância só. |
| `UAZAPI_INSTANCES` | **Forma C.** Lista fixa em JSON. |
| `UAZAPI_DEFAULT_INSTANCE` | Número usado quando o Claude não especifica qual. |
| `UAZAPI_DISCOVERY_TTL_SECONDS` | Cache da lista descoberta, em segundos (padrão `300`). |

As formas podem ser combinadas: uma instância declarada à mão tem prioridade sobre a
descoberta automática de mesmo nome.

### Segurança

| Variável | Padrão | Descrição |
| --- | --- | --- |
| `MCP_AUTH_TOKEN` | — | Segredo que abre qualquer rota. **Defina sempre em produção.** Aceito em `Authorization: Bearer`, nos cabeçalhos `x-token`/`token`/`x-api-token`/`api-token`/`x-auth-token`/`x-access-token`/`x-mcp-token`/`x-api-key`, ou como `?token=`. |
| `MCP_INSTANCE_TOKENS` | — | JSON `{"instancia":"token"}`; cada token abre só a rota `/mcp/<instancia>`. |
| `SEND_ENABLED` | `true` | `false` bloqueia todas as ferramentas de envio (modo somente leitura). |
| `ALLOWED_NUMBERS` | vazio | Prefixos autorizados a receber mensagens, separados por vírgula. Vazio libera todos. |
| `BULK_MAX_RECIPIENTS` | `500` | Teto de destinatários por campanha. |
| `BULK_MIN_DELAY_SECONDS` | `5` | Intervalo mínimo entre mensagens de campanha. |

### Operação

| Variável | Padrão | Descrição |
| --- | --- | --- |
| `PORT` | `8080` | Porta HTTP. |
| `HOST` | `0.0.0.0` | Interface de escuta. |
| `MCP_TRANSPORT` | `http` | `http` para servidor remoto, `stdio` para rodar local. |
| `DEFAULT_COUNTRY_CODE` | `55` | DDI aplicado a números informados sem código de país. |
| `REQUEST_TIMEOUT_MS` | `60000` | Tempo máximo de espera por resposta da uazapi. |
| `PUBLIC_BASE_URL` | — | Endereço público do servidor usado nos links de upload. Se vazio, vem do próprio pedido (funciona atrás do EasyPanel). |
| `OPENAI_API_KEY` | — | Chave da OpenAI usada para transcrever áudios (`transcribe=true`). Sem ela, a uazapi devolve só o arquivo. |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` ou `silent`. |
| `MCP_JSON_RESPONSE` | `true` | `false` usa streaming SSE em vez de JSON puro. |

### Conectar a um número só (URL travada)

Quando a conta tem muitas instâncias — ou quando algumas pertencem a clientes — o mais
seguro é conectar o Claude a uma URL travada num número:

```
https://SEU-DOMINIO/mcp/loja-centro
```

Nessa conexão:

- as ferramentas **não têm** o parâmetro `instance`: não existe como pedir outro número;
- `whatsapp_list_instances` mostra só aquele;
- se o modelo mandar `instance` assim mesmo, o campo é descartado, não redireciona;
- as instruções de abertura avisam que a conexão é restrita, então o Claude explica isso
  ao usuário em vez de tentar contornar.

Nomes com espaço ou maiúsculas funcionam (`/mcp/Loja%20Norte`); a comparação ignora
maiúsculas. `GET /health?token=SEU_TOKEN` lista a URL de cada instância em `endpoints.porInstancia`
(sem o token, o `/health` só informa status e versão, para não expor os nomes dos clientes).

#### Uma senha por loja

Para que cada URL tenha a própria credencial, use `MCP_INSTANCE_TOKENS`:

```env
MCP_AUTH_TOKEN=segredo-geral-da-administracao
MCP_INSTANCE_TOKENS={"loja-centro":"segredo-da-loja-centro","loja-shopping":"segredo-do-shopping"}
```

Regras:

- o token **geral** abre qualquer rota, inclusive `/mcp`;
- o token de uma loja abre **apenas** `/mcp/<aquela loja>` — em `/mcp` ou na rota de outra
  loja ele recebe `401`.

Assim você entrega a URL e a senha da Loja Centro a quem cuida dela, sem dar acesso ao
resto da conta.

#### Números privados

Para um número que não deve aparecer na conexão geral (um WhatsApp pessoal, por exemplo),
liste-o em `MCP_PRIVATE_INSTANCES` e dê a ele um token próprio:

```env
MCP_PRIVATE_INSTANCES=Pessoal Fulano
MCP_INSTANCE_TOKENS={"Pessoal Fulano":"senha-so-do-pessoal"}
```

Com isso:

- o número some de `/mcp`, de `whatsapp_list_instances` e do `/health`; nenhuma conexão
  geral consegue operar nele, nem pelo nome nem pelo nome do perfil;
- ele só abre em `/mcp/Pessoal%20Fulano`, e **só com o token próprio**: o `MCP_AUTH_TOKEN`
  geral é recusado nessa rota;
- sem token próprio em `MCP_INSTANCE_TOKENS`, a rota fica fechada para todos.

### Vários números (multi-loja)

Toda ferramenta aceita o parâmetro opcional `instance`, e o Claude enxerga os nomes reais
dos seus números na descrição desse parâmetro. Basta pedir em português:

> *"manda pelo WhatsApp da loja do shopping"*

O nome de cada instância vem do campo **Nome** no painel da uazapi. Nomes descritivos
(`loja-centro`, `loja-shopping`) funcionam muito melhor que `instancia1`, `instancia2`.

Quando o Claude não informa `instance`, o número é escolhido nesta ordem:

1. `UAZAPI_DEFAULT_INSTANCE`, se definida;
2. a única instância existente, se houver só uma;
3. a única instância **conectada**, se houver só uma conectada;
4. caso contrário, o Claude pergunta a você qual usar.

Criou uma instância nova na uazapi? Peça *"atualiza a lista de números do WhatsApp"* —
o Claude chama `whatsapp_list_instances` com `refresh=true` e ela aparece na hora,
sem redeploy.

---

## Conectar ao Claude

### Claude Code (recomendado — permite enviar o header de autenticação)

```bash
claude mcp add --transport http whatsapp \
  https://whatsapp-mcp.suaempresa.com.br/mcp \
  --header "Authorization: Bearer SEU_MCP_AUTH_TOKEN"
```

Confira com `/mcp` dentro do Claude Code: deve aparecer `whatsapp` como conectado.

### Claude Desktop / claude.ai (conector personalizado)

Em **Configurações → Conectores → Adicionar conector personalizado**:

1. **Nome**: o rótulo que você quiser.
2. **URL do servidor MCP**: `https://SEU-DOMINIO/mcp` (ou `/mcp/<instancia>` para travar
   num número só).
3. **Autenticação**: escolha **"Sem login"**. Este servidor usa chave de API, não OAuth.
4. **Cabeçalhos de requisição**: adicione um cabeçalho `x-token` com o valor do
   `MCP_AUTH_TOKEN`. A lista suspensa também oferece `token`, `x-api-token`, `api-token`,
   `x-auth-token` e `x-access-token` — todos funcionam.

Se a opção "Entrar agora (Detectado)" aparecer marcada, é porque a primeira tentativa
recebeu `401` — normalmente token errado ou ausente. Selecione "Sem login" e confira o
cabeçalho.

Alternativa sem cabeçalho, para clientes que não os suportam:

```
https://SEU-DOMINIO/mcp?token=SEU_MCP_AUTH_TOKEN
```

Funciona igual, mas a URL passa a **ser** o segredo: trate como senha, não compartilhe e
troque o `MCP_AUTH_TOKEN` se ela vazar. Prefira o cabeçalho quando houver a opção — o
valor fica guardado pelo cliente em vez de viajar na URL.

### Claude Desktop via arquivo de configuração (modo local `stdio`)

Em `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "whatsapp": {
      "command": "node",
      "args": ["/caminho/para/mcp-whatsapp-uazapi/dist/index.js"],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "UAZAPI_URL": "https://seusubdominio.uazapi.com",
        "UAZAPI_TOKEN": "token-da-sua-instancia"
      }
    }
  }
}
```

### Conectar ao Codex

Crie um bloco por número em `~/.codex/config.toml`, usando a URL travada `/mcp/<instancia>`:

```toml
[mcp_servers.whatsapp_loja_centro]
url = "https://SEU-DOMINIO/mcp/loja-centro"
bearer_token_env_var = "MCP_WHATSAPP_TOKEN"

[mcp_servers.whatsapp_loja_norte]
url = "https://SEU-DOMINIO/mcp/Loja%20Norte"
bearer_token_env_var = "MCP_WHATSAPP_TOKEN"
```

Antes de abrir o Codex, guarde o token numa variável (`MCP_AUTH_TOKEN` ou o token da
loja em `MCP_INSTANCE_TOKENS`):

```bash
export MCP_WHATSAPP_TOKEN="seu-token"
```

Se o Codex mostrar "user cancelled" em consultas grandes, aumente o tempo de espera de
cada bloco com `tool_timeout_sec = 120` e use um `limit` menor.

A lista com a URL de cada número fica em `https://SEU-DOMINIO/health?token=SEU_TOKEN`
(campo `endpoints.porInstancia`). Nomes com espaço usam `%20`.

---

## Ferramentas disponíveis

São 38 ferramentas e 4 fluxos guiados. As mais usadas no dia a dia estão em **negrito**.

### Instância

| Ferramenta | O que faz |
| --- | --- |
| **`whatsapp_list_instances`** | Lista os números/lojas com status; `refresh=true` relê da uazapi. |
| **`whatsapp_instance_status`** | Estado da conexão (conectado, desconectado…). |
| `whatsapp_instance_connect` | Gera QR Code ou código de pareamento. |
| `whatsapp_instance_disconnect` | Encerra a sessão do WhatsApp. |

### Envio

| Ferramenta | O que faz |
| --- | --- |
| **`whatsapp_send_text`** | Mensagem de texto, com preview de link opcional. |
| **`whatsapp_send_media`** | Envia um arquivo de verdade (PDF, imagem, planilha, vídeo, áudio). Basta o link: o servidor baixa o arquivo (links de compartilhamento do Google Drive e Dropbox incluídos), recusa link que abre página em vez de arquivo e deduz tipo e nome. Até `MAX_SEND_FILE_BYTES` (padrão 25 MB). |
| `whatsapp_create_upload_link` | Para arquivos do computador: devolve um link de upload (curl) e o `file_url` para usar no envio, sem base64. Arquivos ficam na memória por 1 hora. |
| `whatsapp_send_menu` | Botões, lista, enquete ou carrossel. |
| `whatsapp_send_location` | Localização da loja no mapa. |
| `whatsapp_send_contact` | Cartão de contato (vCard). |
| `whatsapp_send_presence` | "Digitando…" / "Gravando áudio…". |

### Mensagens

| Ferramenta | O que faz |
| --- | --- |
| **`whatsapp_find_messages`** | Histórico de uma conversa. Filtra por período (`start_time`/`end_time`) no servidor e pagina com `next_cursor`, sem repetir mensagens quando chegam novas. Com `id`, devolve a mensagem inteira. |
| `whatsapp_download_message` | Baixa a mídia de uma mensagem; transcreve áudios e informa `transcription_status`. Com `return_base64=true`, devolve o arquivo inteiro num bloco de imagem ou de arquivo (até `MAX_INLINE_FILE_BYTES`, padrão 3 MB por resposta). Arquivos maiores saem em partes com `part=1, 2, ...` e um `sha256` para conferir o arquivo montado. |
| `whatsapp_read_spreadsheet` | Abre planilhas recebidas (xlsx, xlsb, xlsm, xls, ods, csv) no servidor e devolve as linhas como texto, aba por aba e em páginas. Até `MAX_SPREADSHEET_BYTES` (padrão 40 MB). |
| `whatsapp_react_message` | Reage com emoji. |
| `whatsapp_edit_message` | Edita uma mensagem já enviada. |
| `whatsapp_delete_message` | Apaga para todos. |
| `whatsapp_mark_messages_read` | Marca mensagens como lidas. |

Erros chegam ao modelo como `Erro [categoria]: ...` — `tempo_esgotado`, `falha_de_rede`,
`sessao_desconectada`, `limite_de_requisicoes`, `nao_autorizado`, `nao_encontrado`,
`requisicao_invalida`, `falha_no_servidor_uazapi` ou `bloqueado_pela_configuracao`. Um erro
sem categoria (como "user cancelled") foi gerado pelo cliente MCP, não pelo servidor.

### Conversas, contatos e CRM

| Ferramenta | O que faz |
| --- | --- |
| **`whatsapp_find_chats`** | Busca conversas por nome, etiqueta, status do lead, ticket aberto… |
| `whatsapp_chat_details` | Dados completos de um contato ou grupo. |
| **`whatsapp_check_numbers`** | Confere quais números existem no WhatsApp. |
| `whatsapp_list_contacts` | Lista os contatos da instância. |
| `whatsapp_add_contact` | Salva um número na agenda. |
| `whatsapp_update_chat_state` | Arquiva, fixa, silencia ou marca como lido. |
| `whatsapp_block_contact` | Bloqueia ou desbloqueia. |
| `whatsapp_chat_notes` | Lê e grava notas internas (invisíveis ao cliente). |
| **`whatsapp_edit_lead`** | Atualiza status do funil, tags, atendente, CPF/CNPJ, e-mail. |

### Etiquetas

| Ferramenta | O que faz |
| --- | --- |
| `whatsapp_list_labels` | Lista etiquetas com seus IDs. |
| `whatsapp_set_chat_labels` | Aplica ou remove etiquetas de uma conversa. |
| `whatsapp_edit_label` | Cria, renomeia ou exclui etiqueta. |

### Grupos

| Ferramenta | O que faz |
| --- | --- |
| `whatsapp_list_groups` | Lista os grupos da instância. |
| `whatsapp_group_info` | Detalhes, participantes e link de convite. |
| `whatsapp_create_group` | Cria grupo. |
| `whatsapp_group_participants` | Adiciona, remove, promove ou rebaixa membros. |

### Campanhas

| Ferramenta | O que faz |
| --- | --- |
| **`whatsapp_create_campaign`** | Agenda disparo em massa com intervalo entre mensagens. |
| `whatsapp_list_campaigns` | Acompanha campanhas e progresso. |
| `whatsapp_campaign_messages` | Auditoria; filtre por `Failed` para ver o que não entregou. |
| `whatsapp_campaign_control` | Pausa, retoma ou exclui campanha. |

### Fluxos prontos (prompts)

Além das ferramentas, o servidor oferece quatro fluxos guiados que aparecem no menu de
prompts do Claude. Eles já trazem a sequência correta de passos e as travas de segurança:

| Prompt | Para quê |
| --- | --- |
| `atender_cliente` | Lê o histórico da conversa (transcrevendo áudios) e propõe a resposta. |
| `resumo_atendimentos` | Levanta os atendimentos em aberto e o que está parado. |
| `campanha_whatsapp` | Monta um disparo com validação de números e aprovação antes de enviar. |
| `diagnostico_whatsapp` | Confere quais números estão conectados e o que fazer com os que caíram. |

### Exemplos de uso no Claude

> "Quais conversas estão com atendimento em aberto há mais de 2 dias?"

> "Manda o catálogo `https://.../catalogo-primavera.pdf` para a Maria do (19) 99999-1234."

> "Confere se esses 80 números têm WhatsApp e me diz quantos são válidos."

> "Cria uma campanha para as clientes com etiqueta VIP avisando da promoção de brincos,
> com intervalo de 20 a 40 segundos entre as mensagens."

> "Lê a conversa com o (19) 98888-7777 e me resume o que a cliente pediu."

> "Quais números de WhatsApp estão conectados agora?"

---

## Segurança

O que este servidor faz por você:

- **Autenticação obrigatória** quando `MCP_AUTH_TOKEN` está definido; a comparação do
  token é feita em tempo constante.
- **Isolamento por instância**: a rota `/mcp/<instancia>` remove o parâmetro de escolha de
  número do schema das ferramentas, e `MCP_INSTANCE_TOKENS` dá a cada loja sua credencial.
- **Modo somente leitura** com `SEND_ENABLED=false`.
- **Lista de números autorizados** com `ALLOWED_NUMBERS`, ótima para testar.
- **Travas de campanha**: teto de destinatários, intervalo mínimo entre mensagens e
  campo `confirm` obrigatório — o Claude não dispara em massa por engano.
- **Respostas enxutas**: mídias em base64 e listas gigantes são truncadas antes de
  chegar ao modelo.
- **Token de administrador usado só para leitura**: serve apenas para descobrir as
  instâncias. Criar, apagar ou reconfigurar instâncias **não** está exposto como ferramenta.
- **Container sem root** e sem dependências de desenvolvimento na imagem final.

O que **você** precisa fazer:

- Nunca deixe a URL pública sem `MCP_AUTH_TOKEN`.
- Mantenha o HTTPS ligado no EasyPanel.
- Trate `UAZAPI_ADMIN_TOKEN`, `UAZAPI_TOKEN` e `MCP_AUTH_TOKEN` como senhas: só nas
  variáveis de ambiente, nunca no código, em prints ou em conversas.
- Se um token vazar, rotacione: o admin em `POST /admin/token/rotate` (permitido uma vez
  a cada 24 h), os de instância no painel da uazapi.
- Disparos em massa podem levar o número ao **banimento pelo WhatsApp**. Envie para
  quem optou por receber, use intervalos generosos e varie o texto.

---

## Desenvolvimento local

```bash
npm install
cp .env.example .env     # preencha UAZAPI_URL e UAZAPI_TOKEN

npm run dev              # recarrega ao salvar
npm run build            # compila para dist/
npm test                 # compila e roda os testes
npm run typecheck        # checagem de tipos
```

Os testes sobem uma uazapi falsa e exercitam o servidor MCP de verdade — nenhuma
mensagem real é enviada.

Testar uma ferramenta por linha de comando:

```bash
curl -s https://whatsapp-mcp.suaempresa.com.br/mcp \
  -H "Authorization: Bearer SEU_MCP_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"whatsapp_instance_status","arguments":{}}}'
```

---

## Solução de problemas

| Sintoma | Causa provável | O que fazer |
| --- | --- | --- |
| `401` em toda chamada | Token errado ou ausente | Confira o cabeçalho (ou `?token=`) e o valor de `MCP_AUTH_TOKEN`. |
| Conector do Claude pede login OAuth | A primeira tentativa levou `401` | Marque "Sem login" e corrija o token: o servidor usa chave de API, não OAuth. |
| `Erro de configuração` no start | Falta `UAZAPI_URL` ou `UAZAPI_TOKEN` | A mensagem no log diz qual variável falta. |
| Ferramentas de envio recusam | `SEND_ENABLED=false` | Mude para `true` e faça o redeploy. |
| `instances: []` no `/health` | Token admin inválido ou URL errada | Confira `UAZAPI_ADMIN_TOKEN` e `UAZAPI_URL`; os logs mostram o HTTP devolvido. |
| Claude pergunta de qual número enviar | Mais de uma instância e nenhuma padrão | Defina `UAZAPI_DEFAULT_INSTANCE`, use a URL `/mcp/<instancia>` ou diga a loja no pedido. |
| `404` numa rota `/mcp/<nome>` | Nome não confere | A resposta lista os nomes válidos; confira em `GET /health?token=SEU_TOKEN`. |
| Instância nova não aparece | Cache de 5 minutos | Peça "atualiza a lista de números" (`refresh=true`). |
| `uazapi respondeu 401` | Token da instância inválido | Copie novamente o token no painel da uazapi. |
| `uazapi respondeu 429` | Limite de instâncias/plano atingido | Verifique o plano na uazapi. |
| Envios falham com a instância "disconnected" | Sessão caiu | Rode `whatsapp_instance_connect` e escaneie o QR Code. |
| Claude não lista as ferramentas | URL sem `/mcp` no final | A URL correta termina em `/mcp`. |
| Resposta truncada | Resultado grande demais | Reduza `limit` ou use filtros mais específicos. |

Logs do container: aba **Logs** do serviço no EasyPanel. Suba `LOG_LEVEL=debug` para ver
cada chamada feita à uazapi.

---

## Licença

MIT