@botozap/mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/client.d.ts +27 -0
- package/dist/client.js +26 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/register.d.ts +19 -0
- package/dist/register.js +42 -0
- package/dist/register.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.js +59 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/contacts.d.ts +2 -0
- package/dist/tools/contacts.js +46 -0
- package/dist/tools/contacts.js.map +1 -0
- package/dist/tools/conversations.d.ts +2 -0
- package/dist/tools/conversations.js +24 -0
- package/dist/tools/conversations.js.map +1 -0
- package/dist/tools/customers.d.ts +2 -0
- package/dist/tools/customers.js +65 -0
- package/dist/tools/customers.js.map +1 -0
- package/dist/tools/media.d.ts +2 -0
- package/dist/tools/media.js +17 -0
- package/dist/tools/media.js.map +1 -0
- package/dist/tools/messages.d.ts +110 -0
- package/dist/tools/messages.js +125 -0
- package/dist/tools/messages.js.map +1 -0
- package/dist/tools/misc.d.ts +2 -0
- package/dist/tools/misc.js +18 -0
- package/dist/tools/misc.js.map +1 -0
- package/dist/tools/phone-numbers.d.ts +2 -0
- package/dist/tools/phone-numbers.js +12 -0
- package/dist/tools/phone-numbers.js.map +1 -0
- package/dist/tools/templates.d.ts +2 -0
- package/dist/tools/templates.js +26 -0
- package/dist/tools/templates.js.map +1 -0
- package/dist/tools/webhooks.d.ts +2 -0
- package/dist/tools/webhooks.js +40 -0
- package/dist/tools/webhooks.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bytecraft Consultoria em Tecnologia LTDA
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# @botozap/mcp
|
|
2
|
+
|
|
3
|
+
Servidor **MCP (Model Context Protocol)** para a API pública do **BotoZap** — a
|
|
4
|
+
plataforma dev-first sobre a WhatsApp Cloud API oficial (multi-tenant, a "Kapso
|
|
5
|
+
brasileira").
|
|
6
|
+
|
|
7
|
+
Ele expõe as operações da plataforma (`/api/v1`) como **ferramentas MCP**, para
|
|
8
|
+
que assistentes como **Claude Code**, **Cursor** e **Codex** consigam enviar
|
|
9
|
+
mensagens, gerenciar contatos/clientes/números, criar templates e webhooks etc.
|
|
10
|
+
|
|
11
|
+
Cada ferramenta MCP mapeia para uma operação do SDK oficial **`@botozap/sdk`**
|
|
12
|
+
(que autentica com a sua chave `bz_...`, monta a requisição e trata o envelope de
|
|
13
|
+
erro). É uma ponte fina: valida os argumentos (zod), delega ao SDK e devolve o
|
|
14
|
+
JSON da API.
|
|
15
|
+
|
|
16
|
+
> **Status: em preparação — ainda NÃO publicado no npm.** Use a partir do
|
|
17
|
+
> monorepo (build local), como abaixo.
|
|
18
|
+
|
|
19
|
+
## Requisitos
|
|
20
|
+
|
|
21
|
+
- **Node.js ≥ 20** (ESM).
|
|
22
|
+
- Uma chave de API do BotoZap (gere em **/chaves** no painel).
|
|
23
|
+
- **pnpm** (este monorepo usa pnpm exclusivamente).
|
|
24
|
+
|
|
25
|
+
## Build
|
|
26
|
+
|
|
27
|
+
Este pacote vive no monorepo `botozap-js` e depende de `@botozap/sdk`
|
|
28
|
+
(`workspace:*`). Instale as dependências na **raiz** do monorepo e então compile:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# na raiz do monorepo botozap-js
|
|
32
|
+
pnpm install
|
|
33
|
+
pnpm --filter @botozap/mcp build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> Não use `--ignore-workspace`: o pacote precisa resolver `@botozap/sdk` pelo
|
|
37
|
+
> workspace.
|
|
38
|
+
|
|
39
|
+
Smoke test (monta o servidor e lista as ferramentas, **sem** chamar a API real):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm --filter @botozap/mcp smoke
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Testes (vitest, sem rede):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pnpm --filter @botozap/mcp test
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Configuração (variáveis de ambiente)
|
|
52
|
+
|
|
53
|
+
| Variável | Obrigatória | Default | Descrição |
|
|
54
|
+
| ------------------- | ----------- | ------------------------------------ | ------------------------------------------ |
|
|
55
|
+
| `BOTOZAP_API_KEY` | **Sim** | — | Sua chave `bz_...` (header `Authorization: Bearer`). |
|
|
56
|
+
| `BOTOZAP_API_URL` | Não | `https://botozap.com.br/api/v1` | Base da API (útil para staging/local). |
|
|
57
|
+
|
|
58
|
+
Sem `BOTOZAP_API_KEY` o servidor falha imediatamente com uma mensagem clara.
|
|
59
|
+
|
|
60
|
+
## Usar com Claude Code
|
|
61
|
+
|
|
62
|
+
Via CLI (caminho **absoluto** para o `dist/index.js` compilado):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
claude mcp add botozap \
|
|
66
|
+
--env BOTOZAP_API_KEY=bz_live_suachaveaqui \
|
|
67
|
+
-- node /caminho/absoluto/para/botozap-js/packages/mcp/dist/index.js
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Ou no JSON do MCP (`.mcp.json` do projeto ou config do usuário):
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"botozap": {
|
|
76
|
+
"command": "node",
|
|
77
|
+
"args": ["/caminho/absoluto/para/botozap-js/packages/mcp/dist/index.js"],
|
|
78
|
+
"env": {
|
|
79
|
+
"BOTOZAP_API_KEY": "bz_live_suachaveaqui"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usar com Cursor
|
|
87
|
+
|
|
88
|
+
Edite `~/.cursor/mcp.json` (global) ou `.cursor/mcp.json` (no projeto):
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"botozap": {
|
|
94
|
+
"command": "node",
|
|
95
|
+
"args": ["/caminho/absoluto/para/botozap-js/packages/mcp/dist/index.js"],
|
|
96
|
+
"env": {
|
|
97
|
+
"BOTOZAP_API_KEY": "bz_live_suachaveaqui"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Ferramentas disponíveis
|
|
105
|
+
|
|
106
|
+
Transporte: **stdio**. Nomes em inglês (snake_case, melhor para tool-calling);
|
|
107
|
+
descrições em PT-BR.
|
|
108
|
+
|
|
109
|
+
**Mensagens** — `send_message`, `list_messages`, `get_message`
|
|
110
|
+
**Conversas** — `list_conversations`, `get_conversation`, `update_conversation`
|
|
111
|
+
**Contatos** — `list_contacts`, `get_contact`, `create_contact`, `update_contact`, `delete_contact`
|
|
112
|
+
**Mídia** — `ingest_media`
|
|
113
|
+
**Clientes** — `list_customers`, `get_customer`, `create_customer`, `update_customer`, `delete_customer`
|
|
114
|
+
**Links de setup** — `list_setup_links`, `create_setup_link`, `update_setup_link`
|
|
115
|
+
**Números** — `list_phone_numbers`, `get_phone_number`, `phone_number_health`
|
|
116
|
+
**Templates** — `list_templates`, `get_template`, `create_template`
|
|
117
|
+
**Webhooks** — `list_webhooks`, `get_webhook`, `create_webhook`, `update_webhook`, `delete_webhook`, `test_webhook`
|
|
118
|
+
**Entregas de webhook** — `list_webhook_deliveries`
|
|
119
|
+
**Logs** — `list_api_logs`
|
|
120
|
+
**Usuários** — `list_users`
|
|
121
|
+
|
|
122
|
+
Em caso de erro da API, a ferramenta devolve um resultado de erro (`isError`) com
|
|
123
|
+
a mensagem PT-BR do envelope `{ error: { code, message } }` no formato
|
|
124
|
+
`Erro [code]: message`.
|
|
125
|
+
|
|
126
|
+
## Chaves de sandbox
|
|
127
|
+
|
|
128
|
+
Uma chave de **sandbox** (prefixo `bz_sandbox_`) só habilita as ferramentas de
|
|
129
|
+
mensagem: `send_message`, `list_messages` e `get_message`. Qualquer outra
|
|
130
|
+
ferramenta responde `403 sandbox_forbidden` (vindo da API) — útil para testar a
|
|
131
|
+
integração de envio sem tocar dados reais.
|
|
132
|
+
|
|
133
|
+
**Números mágicos** (destinatários simulados, sem custo, sem número real):
|
|
134
|
+
|
|
135
|
+
| `to` | Comportamento simulado |
|
|
136
|
+
| ---------------- | ------------------------------------------------------- |
|
|
137
|
+
| `+5500000000001` | happy path: `sent → delivered → read`, janela sempre aberta |
|
|
138
|
+
| `+5500000000002` | falha: `sent → failed` (erro Meta `131026`) |
|
|
139
|
+
| `+5500000000003` | `sent → delivered` + resposta inbound simulada (abre a janela de 24h) |
|
|
140
|
+
|
|
141
|
+
### Smokes de sandbox
|
|
142
|
+
|
|
143
|
+
Há dois smokes, com propósitos distintos:
|
|
144
|
+
|
|
145
|
+
- **Hermético (roda no `test`, CI):** `tests/sandbox-smoke.test.ts` usa um
|
|
146
|
+
`fetch` stub fiel ao contrato do sandbox — não precisa de credencial nem rede.
|
|
147
|
+
Verifica que o texto (acento + emoji) sobrevive intacto, que a chave vira
|
|
148
|
+
`Authorization: Bearer`, que a resposta (`sandbox: true` + `wamid.sandbox.<...>`)
|
|
149
|
+
chega ao agente, e que **a chave nunca aparece** no resultado.
|
|
150
|
+
|
|
151
|
+
- **Externo (manual, exige credencial):** `pnpm --filter @botozap/mcp smoke:sandbox`
|
|
152
|
+
bate na API real. Exige `dist/` compilado (`pnpm --filter @botozap/mcp build`
|
|
153
|
+
antes) e as variáveis `BOTOZAP_API_KEY` (uma chave **`bz_sandbox_`**) e
|
|
154
|
+
`BOTOZAP_API_URL`. Recusa qualquer chave que não seja sandbox e **nunca imprime
|
|
155
|
+
a chave** (redação defensiva em toda saída, inclusive erros). **Nunca** entra no
|
|
156
|
+
`test` — não roda em PR/fork, que não têm credencial.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pnpm --filter @botozap/mcp build
|
|
160
|
+
BOTOZAP_API_KEY=bz_sandbox_suachave BOTOZAP_API_URL=https://.../api/v1 \
|
|
161
|
+
pnpm --filter @botozap/mcp smoke:sandbox
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Segurança
|
|
165
|
+
|
|
166
|
+
A conta é sempre derivada da chave de API no servidor do BotoZap (multi-tenant,
|
|
167
|
+
IDOR-safe). A chave é um segredo — não a comite nem a logue. O servidor MCP só
|
|
168
|
+
escreve logs em **stderr** (stdout é reservado para o protocolo MCP); a chave
|
|
169
|
+
nunca aparece em resultado de ferramenta nem em mensagem de erro.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ponte fina para o SDK oficial `@botozap/sdk`.
|
|
3
|
+
*
|
|
4
|
+
* O servidor MCP NÃO fala HTTP diretamente: cada ferramenta chama um método do
|
|
5
|
+
* `BotoZap` (o cliente do SDK), que cuida de auth (`Authorization: Bearer`),
|
|
6
|
+
* query string, envelope de erro e desempacote de itens. Este módulo só expõe:
|
|
7
|
+
* - `createClient` — instancia o `BotoZap` a partir de `{ apiKey, baseUrl }`;
|
|
8
|
+
* - `Client` — o tipo do cliente, usado por `register`/tools;
|
|
9
|
+
* - `BotoZapError` — re-exportado para o `register` adaptar o `isError`.
|
|
10
|
+
*/
|
|
11
|
+
import { BotoZap, BotoZapError } from "@botozap/sdk";
|
|
12
|
+
export { BotoZapError };
|
|
13
|
+
/** Tipo do cliente do SDK usado pelas ferramentas. */
|
|
14
|
+
export type Client = BotoZap;
|
|
15
|
+
/**
|
|
16
|
+
* Base padrão da API pública. O SDK já usa o mesmo default; mantemos a constante
|
|
17
|
+
* aqui só para a config por ambiente (`BOTOZAP_API_URL`) ser explícita.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_API_URL = "https://botozap.com.br/api/v1";
|
|
20
|
+
export interface ClientOptions {
|
|
21
|
+
apiKey: string;
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
/** Injeta um fetch (usado pelos testes de integração). Prod → global do SDK. */
|
|
24
|
+
fetch?: typeof fetch;
|
|
25
|
+
}
|
|
26
|
+
/** Instancia o cliente do SDK. `baseUrl` indefinido → default do SDK. */
|
|
27
|
+
export declare function createClient(options: ClientOptions): Client;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ponte fina para o SDK oficial `@botozap/sdk`.
|
|
3
|
+
*
|
|
4
|
+
* O servidor MCP NÃO fala HTTP diretamente: cada ferramenta chama um método do
|
|
5
|
+
* `BotoZap` (o cliente do SDK), que cuida de auth (`Authorization: Bearer`),
|
|
6
|
+
* query string, envelope de erro e desempacote de itens. Este módulo só expõe:
|
|
7
|
+
* - `createClient` — instancia o `BotoZap` a partir de `{ apiKey, baseUrl }`;
|
|
8
|
+
* - `Client` — o tipo do cliente, usado por `register`/tools;
|
|
9
|
+
* - `BotoZapError` — re-exportado para o `register` adaptar o `isError`.
|
|
10
|
+
*/
|
|
11
|
+
import { BotoZap, BotoZapError } from "@botozap/sdk";
|
|
12
|
+
export { BotoZapError };
|
|
13
|
+
/**
|
|
14
|
+
* Base padrão da API pública. O SDK já usa o mesmo default; mantemos a constante
|
|
15
|
+
* aqui só para a config por ambiente (`BOTOZAP_API_URL`) ser explícita.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_API_URL = "https://botozap.com.br/api/v1";
|
|
18
|
+
/** Instancia o cliente do SDK. `baseUrl` indefinido → default do SDK. */
|
|
19
|
+
export function createClient(options) {
|
|
20
|
+
return new BotoZap({
|
|
21
|
+
apiKey: options.apiKey,
|
|
22
|
+
baseUrl: options.baseUrl,
|
|
23
|
+
fetch: options.fetch,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,CAAC;AAKxB;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,+BAA+B,CAAC;AAS/D,yEAAyE;AACzE,MAAM,UAAU,YAAY,CAAC,OAAsB;IACjD,OAAO,IAAI,OAAO,CAAC;QACjB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bootstrap do servidor MCP do BotoZap (transporte stdio).
|
|
4
|
+
*
|
|
5
|
+
* Uso: defina `BOTOZAP_API_KEY` (e opcionalmente `BOTOZAP_API_URL`) e rode o
|
|
6
|
+
* binário `botozap-mcp`. O servidor fala MCP por stdin/stdout — não imprima
|
|
7
|
+
* nada em stdout fora do protocolo; logs vão para stderr.
|
|
8
|
+
*/
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
import { buildServer, configFromEnv } from "./server.js";
|
|
11
|
+
async function main() {
|
|
12
|
+
const config = configFromEnv();
|
|
13
|
+
const server = buildServer(config);
|
|
14
|
+
const transport = new StdioServerTransport();
|
|
15
|
+
await server.connect(transport);
|
|
16
|
+
console.error("[botozap-mcp] servidor MCP iniciado (stdio).");
|
|
17
|
+
}
|
|
18
|
+
main().catch((err) => {
|
|
19
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20
|
+
console.error(`[botozap-mcp] erro fatal: ${message}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEzD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;AAChE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,KAAK,CAAC,6BAA6B,OAAO,EAAE,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper de registro de ferramentas: encapsula o padrão comum de
|
|
3
|
+
* 1. validar args (zod, feito pelo SDK a partir do `inputSchema`),
|
|
4
|
+
* 2. chamar a API via cliente do `@botozap/sdk`,
|
|
5
|
+
* 3. devolver o JSON cru como conteúdo de texto (JSON pretty),
|
|
6
|
+
* 4. converter `BotoZapError`/exceções em resultado `isError` com mensagem PT-BR.
|
|
7
|
+
*/
|
|
8
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import type { ZodRawShape } from "zod";
|
|
10
|
+
import { type Client } from "./client.js";
|
|
11
|
+
/** Assinatura do handler de uma ferramenta: recebe o client + args validados. */
|
|
12
|
+
export type ToolHandler<Args> = (client: Client, args: Args) => Promise<unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Fábrica que devolve um `register(...)` ligado a um server + client.
|
|
15
|
+
* `inputSchema` é um *raw shape* zod (objeto de schemas), como o
|
|
16
|
+
* `registerTool` do SDK espera.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createRegister(server: McpServer, client: Client): (name: string, description: string, inputSchema: ZodRawShape, handler: ToolHandler<Record<string, unknown>>) => void;
|
|
19
|
+
export type Register = ReturnType<typeof createRegister>;
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper de registro de ferramentas: encapsula o padrão comum de
|
|
3
|
+
* 1. validar args (zod, feito pelo SDK a partir do `inputSchema`),
|
|
4
|
+
* 2. chamar a API via cliente do `@botozap/sdk`,
|
|
5
|
+
* 3. devolver o JSON cru como conteúdo de texto (JSON pretty),
|
|
6
|
+
* 4. converter `BotoZapError`/exceções em resultado `isError` com mensagem PT-BR.
|
|
7
|
+
*/
|
|
8
|
+
import { BotoZapError } from "./client.js";
|
|
9
|
+
/**
|
|
10
|
+
* Fábrica que devolve um `register(...)` ligado a um server + client.
|
|
11
|
+
* `inputSchema` é um *raw shape* zod (objeto de schemas), como o
|
|
12
|
+
* `registerTool` do SDK espera.
|
|
13
|
+
*/
|
|
14
|
+
export function createRegister(server, client) {
|
|
15
|
+
return function register(name, description, inputSchema, handler) {
|
|
16
|
+
server.registerTool(name, { description, inputSchema }, async (args) => {
|
|
17
|
+
try {
|
|
18
|
+
const data = await handler(client, (args ?? {}));
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
let message;
|
|
25
|
+
if (err instanceof BotoZapError) {
|
|
26
|
+
message = `Erro [${err.code}]: ${err.message}`;
|
|
27
|
+
}
|
|
28
|
+
else if (err instanceof Error) {
|
|
29
|
+
message = `Erro: ${err.message}`;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
message = `Erro: ${String(err)}`;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: message }],
|
|
36
|
+
isError: true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAQxD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAiB,EAAE,MAAc;IAC9D,OAAO,SAAS,QAAQ,CACtB,IAAY,EACZ,WAAmB,EACnB,WAAwB,EACxB,OAA6C;QAE7C,MAAM,CAAC,YAAY,CACjB,IAAI,EACJ,EAAE,WAAW,EAAE,WAAW,EAAE,EAC5B,KAAK,EAAE,IAAI,EAA2B,EAAE;YACtC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC,CAAC;gBAC5E,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;iBACjE,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,OAAe,CAAC;gBACpB,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;oBAChC,OAAO,GAAG,SAAS,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;gBACjD,CAAC;qBAAM,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;oBAChC,OAAO,GAAG,SAAS,GAAG,CAAC,OAAO,EAAE,CAAC;gBACnC,CAAC;qBAAM,CAAC;oBACN,OAAO,GAAG,SAAS,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnC,CAAC;gBACD,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBAC1C,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export interface BuildServerOptions {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
/** Injeta um fetch (testes de integração). Prod → global do SDK. */
|
|
6
|
+
fetch?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
/** Monta um `McpServer` com todas as ferramentas registradas. */
|
|
9
|
+
export declare function buildServer(options: BuildServerOptions): McpServer;
|
|
10
|
+
/**
|
|
11
|
+
* Lê a config do ambiente e valida a presença da chave (falha rápida com
|
|
12
|
+
* mensagem PT-BR clara). Usado pelo bootstrap stdio.
|
|
13
|
+
*/
|
|
14
|
+
export declare function configFromEnv(): BuildServerOptions;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Construção do servidor MCP: cria o `McpServer`, instancia o cliente do SDK
|
|
3
|
+
* e registra todos os grupos de ferramentas. Extraído de `index.ts` para que o
|
|
4
|
+
* smoke test possa montar o servidor sem abrir o transporte stdio.
|
|
5
|
+
*/
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { createClient, DEFAULT_API_URL } from "./client.js";
|
|
9
|
+
import { createRegister } from "./register.js";
|
|
10
|
+
import { registerMessageTools } from "./tools/messages.js";
|
|
11
|
+
import { registerConversationTools } from "./tools/conversations.js";
|
|
12
|
+
import { registerContactTools } from "./tools/contacts.js";
|
|
13
|
+
import { registerMediaTools } from "./tools/media.js";
|
|
14
|
+
import { registerCustomerTools } from "./tools/customers.js";
|
|
15
|
+
import { registerPhoneNumberTools } from "./tools/phone-numbers.js";
|
|
16
|
+
import { registerTemplateTools } from "./tools/templates.js";
|
|
17
|
+
import { registerWebhookTools } from "./tools/webhooks.js";
|
|
18
|
+
import { registerMiscTools } from "./tools/misc.js";
|
|
19
|
+
const { version: VERSION } = createRequire(import.meta.url)("../package.json");
|
|
20
|
+
/** Monta um `McpServer` com todas as ferramentas registradas. */
|
|
21
|
+
export function buildServer(options) {
|
|
22
|
+
const server = new McpServer({
|
|
23
|
+
name: "botozap-mcp",
|
|
24
|
+
version: VERSION,
|
|
25
|
+
});
|
|
26
|
+
const client = createClient({
|
|
27
|
+
apiKey: options.apiKey,
|
|
28
|
+
baseUrl: options.baseUrl,
|
|
29
|
+
fetch: options.fetch,
|
|
30
|
+
});
|
|
31
|
+
const register = createRegister(server, client);
|
|
32
|
+
registerMessageTools(register);
|
|
33
|
+
registerConversationTools(register);
|
|
34
|
+
registerContactTools(register);
|
|
35
|
+
registerMediaTools(register);
|
|
36
|
+
registerCustomerTools(register);
|
|
37
|
+
registerPhoneNumberTools(register);
|
|
38
|
+
registerTemplateTools(register);
|
|
39
|
+
registerWebhookTools(register);
|
|
40
|
+
registerMiscTools(register);
|
|
41
|
+
return server;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Lê a config do ambiente e valida a presença da chave (falha rápida com
|
|
45
|
+
* mensagem PT-BR clara). Usado pelo bootstrap stdio.
|
|
46
|
+
*/
|
|
47
|
+
export function configFromEnv() {
|
|
48
|
+
const apiKey = process.env.BOTOZAP_API_KEY?.trim();
|
|
49
|
+
if (!apiKey) {
|
|
50
|
+
throw new Error("BOTOZAP_API_KEY não definida. Gere uma chave (bz_live_...) no painel " +
|
|
51
|
+
"do BotoZap em /chaves e exporte-a como variável de ambiente " +
|
|
52
|
+
"BOTOZAP_API_KEY antes de iniciar o servidor MCP.");
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
apiKey,
|
|
56
|
+
baseUrl: process.env.BOTOZAP_API_URL?.trim() || DEFAULT_API_URL,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAE5E,CAAC;AASF,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,YAAY,CAAC;QAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC/B,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACpC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC/B,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC7B,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChC,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACnC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC/B,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAE5B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IACnD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,uEAAuE;YACrE,8DAA8D;YAC9D,kDAAkD,CACrD,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM;QACN,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,eAAe;KAChE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Ferramentas de contatos (end users do WhatsApp): CRUD. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerContactTools(register) {
|
|
4
|
+
register("list_contacts", "Lista contatos da conta (paginação por cursor: { data, paging }). Filtros opcionais por cliente, presença de cliente, busca parcial em nome/wa_id e janela de criação.", {
|
|
5
|
+
customer_id: z.string().optional().describe("Filtra pelos números do cliente (derivado)."),
|
|
6
|
+
has_customer: z
|
|
7
|
+
.boolean()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("Nesta base todo contato tem cliente: false → vazio, true → no-op."),
|
|
10
|
+
profile_name_contains: z.string().optional().describe("Busca parcial (ilike) no nome de perfil."),
|
|
11
|
+
wa_id_contains: z.string().optional().describe("Busca parcial (ilike) no wa_id."),
|
|
12
|
+
created_after: z.string().optional().describe("Criados em ou após esta data (ISO 8601)."),
|
|
13
|
+
created_before: z.string().optional().describe("Criados em ou antes desta data (ISO 8601)."),
|
|
14
|
+
limit: z.number().int().positive().optional(),
|
|
15
|
+
after: z.string().optional(),
|
|
16
|
+
before: z.string().optional(),
|
|
17
|
+
}, (client, args) => client.contacts.list(args));
|
|
18
|
+
register("get_contact", "Busca um contato pelo id (uuid interno). Retorna { data }.", { id: z.string().describe("ID do contato (uuid interno).") }, async (client, args) => ({ data: await client.contacts.get(String(args.id)) }));
|
|
19
|
+
register("create_contact", "Cria um contato. O contato é sempre atado a um phone_number e único por (phone_number_id, wa_id). `wa_id` (BSUID ou E.164) é a chave canônica. Informe phone_number_id (Meta) OU customer_id para resolver o número.", {
|
|
20
|
+
wa_id: z.string().describe("Identidade canônica: BSUID (BR.1A2B...) ou dígitos E.164."),
|
|
21
|
+
phone_number_id: z.string().optional().describe("phone_number_id (Meta) ao qual atar."),
|
|
22
|
+
customer_id: z.string().optional().describe("Cliente cujo número será usado."),
|
|
23
|
+
profile_name: z.string().optional(),
|
|
24
|
+
phone: z.string().optional(),
|
|
25
|
+
user_id: z.string().optional().describe("BSUID do usuário."),
|
|
26
|
+
username: z.string().optional(),
|
|
27
|
+
parent_user_id: z.string().optional(),
|
|
28
|
+
}, async (client, args) => ({
|
|
29
|
+
data: await client.contacts.create(args),
|
|
30
|
+
}));
|
|
31
|
+
register("update_contact", "Atualiza um contato (campos editáveis: profile_name, username). Retorna { data }.", {
|
|
32
|
+
id: z.string().describe("ID do contato (uuid interno)."),
|
|
33
|
+
profile_name: z.string().optional(),
|
|
34
|
+
username: z.string().optional(),
|
|
35
|
+
}, async (client, args) => {
|
|
36
|
+
const { id, ...body } = args;
|
|
37
|
+
return { data: await client.contacts.update(String(id), body) };
|
|
38
|
+
});
|
|
39
|
+
register("delete_contact", "Exclui um contato pelo id (uuid interno).", { id: z.string().describe("ID do contato (uuid interno).") }, async (client, args) => {
|
|
40
|
+
// A rota responde 204 (sem corpo); preservamos o retorno `null` do MCP
|
|
41
|
+
// legado (JSON.stringify(null) === "null").
|
|
42
|
+
await client.contacts.delete(String(args.id));
|
|
43
|
+
return null;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=contacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contacts.js","sourceRoot":"","sources":["../../src/tools/contacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Ferramentas de conversas: listar, ler e atualizar status. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerConversationTools(register) {
|
|
4
|
+
register("list_conversations", "Lista conversas da conta (paginação por cursor: { data, paging }). Filtros opcionais por número e por busca textual de contato.", {
|
|
5
|
+
phone_number_id: z.string().optional().describe("Filtra pelo phone_number_id (Meta)."),
|
|
6
|
+
status: z.string().optional().describe("Filtra por status (active|ended)."),
|
|
7
|
+
// A rota (/v1/conversations) casa por `contact`/`phone_number` (busca parcial
|
|
8
|
+
// em nome/username/telefone/wa_id, INNER join em contacts). Não existe filtro
|
|
9
|
+
// `contact_id` — por isso ele não é exposto aqui.
|
|
10
|
+
contact: z.string().optional().describe("Busca parcial: nome, @username, telefone ou wa_id do contato."),
|
|
11
|
+
phone_number: z.string().optional().describe("Alias de busca por contato (usado quando 'contact' não é informado)."),
|
|
12
|
+
limit: z.number().int().positive().optional(),
|
|
13
|
+
after: z.string().optional(),
|
|
14
|
+
before: z.string().optional(),
|
|
15
|
+
}, (client, args) => client.conversations.list(args));
|
|
16
|
+
register("get_conversation", "Busca uma conversa pelo id (uuid interno). Retorna { data }.", { id: z.string().describe("ID da conversa (uuid interno).") }, async (client, args) => ({ data: await client.conversations.get(String(args.id)) }));
|
|
17
|
+
register("update_conversation", "Atualiza o status de uma conversa. status='ended' fecha a janela de 24h; status='active' é no-op idempotente (a janela só reabre com novo inbound do contato).", {
|
|
18
|
+
id: z.string().describe("ID da conversa (uuid interno)."),
|
|
19
|
+
status: z.enum(["active", "ended"]).describe("Novo status da conversa."),
|
|
20
|
+
}, async (client, args) => ({
|
|
21
|
+
data: await client.conversations.update(String(args.id), { status: args.status }),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../src/tools/conversations.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,yBAAyB,CAAC,QAAkB;IAC1D,QAAQ,CACN,oBAAoB,EACpB,iIAAiI,EACjI;QACE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QACtF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;QAC3E,8EAA8E;QAC9E,8EAA8E;QAC9E,kDAAkD;QAClD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;QACxG,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sEAAsE,CAAC;QACpH,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;QAC7C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,EACD,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAA+B,CAAC,CAC7E,CAAC;IAEF,QAAQ,CACN,kBAAkB,EAClB,8DAA8D,EAC9D,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC7D,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CACpF,CAAC;IAEF,QAAQ,CACN,qBAAqB,EACrB,gKAAgK,EAChK;QACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QACzD,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,0BAA0B,CAAC;KACzE,EACD,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,IAAI,EAAE,MAAM,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;KAClF,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Ferramentas de clientes (customers do dev) e seus links de setup. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerCustomerTools(register) {
|
|
4
|
+
register("list_customers", "Lista os clientes (customers) da conta (paginação offset: { data, meta }).", {
|
|
5
|
+
// A rota (/v1/customers) só lê `page`/`per_page` (parseOffsetParams).
|
|
6
|
+
page: z.number().int().positive().optional().describe("Página (1-based)."),
|
|
7
|
+
per_page: z.number().int().positive().optional().describe("Itens por página (máx. 100)."),
|
|
8
|
+
}, (client, args) => client.customers.list(args));
|
|
9
|
+
register("get_customer", "Busca um cliente pelo id (uuid interno). Retorna { data }.", { id: z.string().describe("ID do cliente (uuid interno).") }, async (client, args) => ({ data: await client.customers.get(String(args.id)) }));
|
|
10
|
+
register("create_customer", "Cria um cliente. Informe ao menos `name`. `external_customer_id` é opcional (sua referência externa). Retorna { data }.", {
|
|
11
|
+
name: z.string().describe("Nome do cliente."),
|
|
12
|
+
external_customer_id: z.string().optional().describe("Referência externa opcional."),
|
|
13
|
+
}, async (client, args) => ({
|
|
14
|
+
data: await client.customers.create(args),
|
|
15
|
+
}));
|
|
16
|
+
register("update_customer", "Atualiza um cliente (name, external_customer_id). Retorna { data }.", {
|
|
17
|
+
id: z.string().describe("ID do cliente (uuid interno)."),
|
|
18
|
+
name: z.string().optional(),
|
|
19
|
+
external_customer_id: z.string().optional(),
|
|
20
|
+
}, async (client, args) => {
|
|
21
|
+
const { id, ...body } = args;
|
|
22
|
+
return { data: await client.customers.update(String(id), body) };
|
|
23
|
+
});
|
|
24
|
+
register("delete_customer", "Exclui um cliente pelo id (uuid interno).", { id: z.string().describe("ID do cliente (uuid interno).") }, async (client, args) => {
|
|
25
|
+
await client.customers.delete(String(args.id));
|
|
26
|
+
return null;
|
|
27
|
+
});
|
|
28
|
+
// ---- Links de setup (Embedded Signup) ----
|
|
29
|
+
register("list_setup_links", "Lista os links de setup de um cliente (paginação offset: { data, meta }).", {
|
|
30
|
+
customer_id: z.string().describe("ID do cliente (uuid interno)."),
|
|
31
|
+
page: z.number().int().positive().optional().describe("Página (1-based)."),
|
|
32
|
+
per_page: z.number().int().positive().optional().describe("Itens por página (máx. 100)."),
|
|
33
|
+
}, (client, args) => {
|
|
34
|
+
const { customer_id, ...query } = args;
|
|
35
|
+
return client.customers.listSetupLinks(String(customer_id), query);
|
|
36
|
+
});
|
|
37
|
+
register("create_setup_link", "Cria um link de setup para um cliente. Opções: allowed_connection_types ('dedicated'|'coexistence'), provision_phone_number, language e redirects. Retorna { data }.", {
|
|
38
|
+
customer_id: z.string().describe("ID do cliente (uuid interno)."),
|
|
39
|
+
allowed_connection_types: z
|
|
40
|
+
.array(z.enum(["dedicated", "coexistence"]))
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Modos de conexão permitidos."),
|
|
43
|
+
provision_phone_number: z.boolean().optional(),
|
|
44
|
+
language: z.string().optional().describe("Idioma (ou 'auto')."),
|
|
45
|
+
success_redirect_url: z.string().optional(),
|
|
46
|
+
failure_redirect_url: z.string().optional(),
|
|
47
|
+
}, async (client, args) => {
|
|
48
|
+
const { customer_id, ...body } = args;
|
|
49
|
+
return {
|
|
50
|
+
data: await client.customers.createSetupLink(String(customer_id), body),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
register("update_setup_link", "Atualiza um link de setup (status, expires_at). Retorna { data }.", {
|
|
54
|
+
customer_id: z.string().describe("ID do cliente (uuid interno)."),
|
|
55
|
+
link_id: z.string().describe("ID do link de setup."),
|
|
56
|
+
status: z.string().optional().describe("Novo status do link."),
|
|
57
|
+
expires_at: z.string().optional().describe("Nova expiração (ISO 8601)."),
|
|
58
|
+
}, async (client, args) => {
|
|
59
|
+
const { customer_id, link_id, ...body } = args;
|
|
60
|
+
return {
|
|
61
|
+
data: await client.customers.updateSetupLink(String(customer_id), String(link_id), body),
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=customers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customers.js","sourceRoot":"","sources":["../../src/tools/customers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Ferramenta de ingest de mídia a partir de uma URL. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerMediaTools(register) {
|
|
4
|
+
register("ingest_media", "Faz o ingest de uma mídia a partir de uma URL para a Meta. delivery='meta_media' (default) retorna um media_id usável em mensagens; delivery='meta_resumable_asset' retorna um handle reutilizável (ex.: header de template). Retorna { data }.", {
|
|
5
|
+
phone_number_id: z.string().describe("phone_number_id (Meta) dono da mídia."),
|
|
6
|
+
source: z.string().describe("URL de origem da mídia (https)."),
|
|
7
|
+
filename: z.string().optional(),
|
|
8
|
+
mime_type: z.string().optional().describe("Force o mime (ex.: image/png); senão é inferido."),
|
|
9
|
+
delivery: z
|
|
10
|
+
.enum(["meta_media", "meta_resumable_asset"])
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Forma de entrega à Meta (default meta_media)."),
|
|
13
|
+
}, async (client, args) => ({
|
|
14
|
+
data: await client.media.upload(args),
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/tools/media.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,QAAQ,CACN,cAAc,EACd,iPAAiP,EACjP;QACE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;QAC7E,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;QAC9D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;QAC7F,QAAQ,EAAE,CAAC;aACR,IAAI,CAAC,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAC;aAC5C,QAAQ,EAAE;aACV,QAAQ,CAAC,+CAA+C,CAAC;KAC7D,EACD,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,IAAI,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAoC,CAAC;KACtE,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Ferramentas de mensagens: envio, listagem e leitura. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { Register } from "../register.js";
|
|
4
|
+
/**
|
|
5
|
+
* Schema refinado que amarra `type` ao payload (o shape base sozinho aceitaria
|
|
6
|
+
* type='text' sem `text`, type='template' sem `template`, ou os dois juntos).
|
|
7
|
+
* Exportado pros testes e usado no handler pra validar ANTES de chamar a API —
|
|
8
|
+
* a validação do próprio SDK roda contra o shape base (sem esta regra cruzada).
|
|
9
|
+
*/
|
|
10
|
+
export declare const sendMessageSchema: z.ZodEffects<z.ZodObject<{
|
|
11
|
+
readonly to: z.ZodString;
|
|
12
|
+
readonly type: z.ZodEnum<["text", "template"]>;
|
|
13
|
+
readonly text: z.ZodOptional<z.ZodObject<{
|
|
14
|
+
body: z.ZodString;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
body: string;
|
|
17
|
+
}, {
|
|
18
|
+
body: string;
|
|
19
|
+
}>>;
|
|
20
|
+
readonly template: z.ZodOptional<z.ZodObject<{
|
|
21
|
+
name: z.ZodString;
|
|
22
|
+
language: z.ZodObject<{
|
|
23
|
+
code: z.ZodString;
|
|
24
|
+
policy: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
code: string;
|
|
27
|
+
policy?: string | undefined;
|
|
28
|
+
}, {
|
|
29
|
+
code: string;
|
|
30
|
+
policy?: string | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
components: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">>;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
name: string;
|
|
35
|
+
language: {
|
|
36
|
+
code: string;
|
|
37
|
+
policy?: string | undefined;
|
|
38
|
+
};
|
|
39
|
+
components?: Record<string, unknown>[] | undefined;
|
|
40
|
+
}, {
|
|
41
|
+
name: string;
|
|
42
|
+
language: {
|
|
43
|
+
code: string;
|
|
44
|
+
policy?: string | undefined;
|
|
45
|
+
};
|
|
46
|
+
components?: Record<string, unknown>[] | undefined;
|
|
47
|
+
}>>;
|
|
48
|
+
readonly from: z.ZodOptional<z.ZodString>;
|
|
49
|
+
}, "strip", z.ZodTypeAny, {
|
|
50
|
+
type: "text" | "template";
|
|
51
|
+
to: string;
|
|
52
|
+
text?: {
|
|
53
|
+
body: string;
|
|
54
|
+
} | undefined;
|
|
55
|
+
template?: {
|
|
56
|
+
name: string;
|
|
57
|
+
language: {
|
|
58
|
+
code: string;
|
|
59
|
+
policy?: string | undefined;
|
|
60
|
+
};
|
|
61
|
+
components?: Record<string, unknown>[] | undefined;
|
|
62
|
+
} | undefined;
|
|
63
|
+
from?: string | undefined;
|
|
64
|
+
}, {
|
|
65
|
+
type: "text" | "template";
|
|
66
|
+
to: string;
|
|
67
|
+
text?: {
|
|
68
|
+
body: string;
|
|
69
|
+
} | undefined;
|
|
70
|
+
template?: {
|
|
71
|
+
name: string;
|
|
72
|
+
language: {
|
|
73
|
+
code: string;
|
|
74
|
+
policy?: string | undefined;
|
|
75
|
+
};
|
|
76
|
+
components?: Record<string, unknown>[] | undefined;
|
|
77
|
+
} | undefined;
|
|
78
|
+
from?: string | undefined;
|
|
79
|
+
}>, {
|
|
80
|
+
type: "text" | "template";
|
|
81
|
+
to: string;
|
|
82
|
+
text?: {
|
|
83
|
+
body: string;
|
|
84
|
+
} | undefined;
|
|
85
|
+
template?: {
|
|
86
|
+
name: string;
|
|
87
|
+
language: {
|
|
88
|
+
code: string;
|
|
89
|
+
policy?: string | undefined;
|
|
90
|
+
};
|
|
91
|
+
components?: Record<string, unknown>[] | undefined;
|
|
92
|
+
} | undefined;
|
|
93
|
+
from?: string | undefined;
|
|
94
|
+
}, {
|
|
95
|
+
type: "text" | "template";
|
|
96
|
+
to: string;
|
|
97
|
+
text?: {
|
|
98
|
+
body: string;
|
|
99
|
+
} | undefined;
|
|
100
|
+
template?: {
|
|
101
|
+
name: string;
|
|
102
|
+
language: {
|
|
103
|
+
code: string;
|
|
104
|
+
policy?: string | undefined;
|
|
105
|
+
};
|
|
106
|
+
components?: Record<string, unknown>[] | undefined;
|
|
107
|
+
} | undefined;
|
|
108
|
+
from?: string | undefined;
|
|
109
|
+
}>;
|
|
110
|
+
export declare function registerMessageTools(register: Register): void;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/** Ferramentas de mensagens: envio, listagem e leitura. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Shape (raw) de `send_message`. Fica como *raw shape* de propósito: é ele que
|
|
5
|
+
* vai pro `registerTool` do MCP SDK, que só gera JSON Schema (com as descrições
|
|
6
|
+
* dos campos, que o AGENTE lê) a partir de um ZodObject/raw shape. Um schema
|
|
7
|
+
* refinado (`superRefine`) ou uma discriminated union viram ZodEffects/ZodUnion
|
|
8
|
+
* SEM `.shape` — o `normalizeObjectSchema` do SDK não os reconhece e cai no
|
|
9
|
+
* `EMPTY_OBJECT_JSON_SCHEMA`, apagando todas as descrições. Por isso: shape base
|
|
10
|
+
* pro JSON Schema; a regra cruzada (type ↔ payload) mora no `sendMessageSchema`.
|
|
11
|
+
*/
|
|
12
|
+
const sendMessageShape = {
|
|
13
|
+
to: z.string().describe("Destinatário: telefone E.164 (ex.: 5511999999999) ou wa_id."),
|
|
14
|
+
type: z.enum(["text", "template"]).describe("Tipo da mensagem."),
|
|
15
|
+
text: z
|
|
16
|
+
.object({ body: z.string() })
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Conteúdo de texto quando type='text'."),
|
|
19
|
+
// Schema restritivo de propósito: o MCP é consumido por AGENTES, então o
|
|
20
|
+
// objeto do template precisa espelhar o contrato da rota (e do SDK:
|
|
21
|
+
// TemplatePayload) — não aceitar "qualquer objeto". A rota exige `name` e o
|
|
22
|
+
// objeto `language: { code }` (string solta é rejeitada). `components` fica
|
|
23
|
+
// como array de objetos livres porque é genuinamente o formato aberto da
|
|
24
|
+
// Cloud API (parâmetros de header/body/button). Contraste com create_template
|
|
25
|
+
// em tools/templates.ts, onde `components` também é livre pelo mesmo motivo.
|
|
26
|
+
template: z
|
|
27
|
+
.object({
|
|
28
|
+
name: z.string().describe("Nome do template APROVADO na Meta."),
|
|
29
|
+
language: z
|
|
30
|
+
.object({
|
|
31
|
+
code: z.string().describe("Código de idioma, ex.: pt_BR."),
|
|
32
|
+
// Zod sem .strict() faz STRIP de chaves extras — sem `policy`
|
|
33
|
+
// declarado, um `language.policy` legítimo da Cloud API seria
|
|
34
|
+
// descartado em silêncio. Declarado pra preservar.
|
|
35
|
+
policy: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Política de idioma da Cloud API (ex.: deterministic)."),
|
|
39
|
+
})
|
|
40
|
+
.describe("Idioma no formato da Cloud API: { code, policy? }."),
|
|
41
|
+
components: z
|
|
42
|
+
.array(z.record(z.string(), z.unknown()))
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Componentes com parâmetros (header/body/button) no formato da Cloud API."),
|
|
45
|
+
})
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Objeto do template quando type='template'. Exige name e language.code."),
|
|
48
|
+
from: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("phone_number_id de origem (obrigatório se a conta tem >1 número)."),
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Schema refinado que amarra `type` ao payload (o shape base sozinho aceitaria
|
|
55
|
+
* type='text' sem `text`, type='template' sem `template`, ou os dois juntos).
|
|
56
|
+
* Exportado pros testes e usado no handler pra validar ANTES de chamar a API —
|
|
57
|
+
* a validação do próprio SDK roda contra o shape base (sem esta regra cruzada).
|
|
58
|
+
*/
|
|
59
|
+
export const sendMessageSchema = z
|
|
60
|
+
.object(sendMessageShape)
|
|
61
|
+
.superRefine((val, ctx) => {
|
|
62
|
+
if (val.type === "text") {
|
|
63
|
+
if (!val.text) {
|
|
64
|
+
ctx.addIssue({
|
|
65
|
+
code: z.ZodIssueCode.custom,
|
|
66
|
+
path: ["text"],
|
|
67
|
+
message: "type='text' exige o campo 'text' com { body }.",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (val.template) {
|
|
71
|
+
ctx.addIssue({
|
|
72
|
+
code: z.ZodIssueCode.custom,
|
|
73
|
+
path: ["template"],
|
|
74
|
+
message: "type='text' não aceita 'template' junto — envie só 'text' (ou use type='template').",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (val.type === "template") {
|
|
79
|
+
if (!val.template) {
|
|
80
|
+
ctx.addIssue({
|
|
81
|
+
code: z.ZodIssueCode.custom,
|
|
82
|
+
path: ["template"],
|
|
83
|
+
message: "type='template' exige o campo 'template' com { name, language.code }.",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (val.text) {
|
|
87
|
+
ctx.addIssue({
|
|
88
|
+
code: z.ZodIssueCode.custom,
|
|
89
|
+
path: ["text"],
|
|
90
|
+
message: "type='template' não aceita 'text' junto — envie só 'template' (ou use type='text').",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
export function registerMessageTools(register) {
|
|
96
|
+
register("send_message", "Envia uma mensagem de WhatsApp (texto ou template) via API do BotoZap. `to` é o número E.164 ou wa_id do destinatário. Para texto: type='text' e text={ body }. Para template: type='template' e template={ name, language, components? }. `from` (phone_number_id) é obrigatório se a conta tem mais de um número. Retorna { id, wamid, to, status }.", sendMessageShape, (client, args) => {
|
|
97
|
+
// O SDK do MCP já validou `args` contra o shape base; aqui aplicamos a
|
|
98
|
+
// regra cruzada type ↔ payload (que o shape base não expressa) com mensagem
|
|
99
|
+
// PT-BR clara pro agente, ANTES de gastar um round-trip com a API.
|
|
100
|
+
const parsed = sendMessageSchema.safeParse(args);
|
|
101
|
+
if (!parsed.success) {
|
|
102
|
+
throw new Error(parsed.error.issues.map((i) => i.message).join(" "));
|
|
103
|
+
}
|
|
104
|
+
const { to, type, text, template, from } = parsed.data;
|
|
105
|
+
// Despacha pelo `type` — o superRefine já garantiu o payload correspondente.
|
|
106
|
+
// Resposta direta (sem envelope), igual ao POST /messages.
|
|
107
|
+
if (type === "text") {
|
|
108
|
+
return client.messages.send({ to, text: text.body, from });
|
|
109
|
+
}
|
|
110
|
+
return client.messages.sendTemplate({ to, template: template, from });
|
|
111
|
+
});
|
|
112
|
+
register("list_messages", "Lista mensagens da conta (paginação por cursor: { data, paging }). Filtros opcionais por número, conversa, direção, status, tipo e presença de mídia.", {
|
|
113
|
+
phone_number_id: z.string().optional().describe("Filtra pelo phone_number_id (Meta)."),
|
|
114
|
+
conversation_id: z.string().optional().describe("Filtra pela conversa (uuid interno)."),
|
|
115
|
+
direction: z.enum(["inbound", "outbound"]).optional(),
|
|
116
|
+
status: z.string().optional().describe("Status da mensagem (enum message_status)."),
|
|
117
|
+
message_type: z.string().optional().describe("Tipo da mensagem (kind: text, image, ...)."),
|
|
118
|
+
has_media: z.boolean().optional().describe("true para apenas mensagens com mídia."),
|
|
119
|
+
limit: z.number().int().positive().optional().describe("Tamanho da página."),
|
|
120
|
+
after: z.string().optional().describe("Cursor: itens após este created_at."),
|
|
121
|
+
before: z.string().optional().describe("Cursor: itens antes deste created_at."),
|
|
122
|
+
}, (client, args) => client.messages.list(args));
|
|
123
|
+
register("get_message", "Busca uma mensagem específica pelo id (uuid interno). Retorna { data }.", { id: z.string().describe("ID da mensagem (uuid interno).") }, async (client, args) => ({ data: await client.messages.get(String(args.id)) }));
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/tools/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Ferramentas diversas: logs de API e usuários da conta. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerMiscTools(register) {
|
|
4
|
+
register("list_api_logs", "Lista os logs de requisições à API pública da conta (auditoria). Paginação por cursor: { data, paging }.", {
|
|
5
|
+
// Filtros reais da rota (/v1/api_logs): source, method e status_code.
|
|
6
|
+
source: z.string().optional().describe("Origem do log (ex.: api)."),
|
|
7
|
+
method: z.string().optional().describe("Método HTTP (normalizado p/ maiúsculas)."),
|
|
8
|
+
status_code: z.number().int().optional().describe("Código HTTP exato (inteiro)."),
|
|
9
|
+
limit: z.number().int().positive().optional(),
|
|
10
|
+
after: z.string().optional(),
|
|
11
|
+
before: z.string().optional(),
|
|
12
|
+
}, (client, args) => client.apiLogs.list(args));
|
|
13
|
+
register("list_users", "Lista os usuários (membros) da conta. Paginação offset: { data, meta }.", {
|
|
14
|
+
page: z.number().int().positive().optional().describe("Página (1-based)."),
|
|
15
|
+
per_page: z.number().int().positive().optional().describe("Itens por página (máx. 100)."),
|
|
16
|
+
}, (client, args) => client.users.list(args));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=misc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misc.js","sourceRoot":"","sources":["../../src/tools/misc.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,MAAM,UAAU,iBAAiB,CAAC,QAAkB;IAClD,QAAQ,CACN,eAAe,EACf,0GAA0G,EAC1G;QACE,sEAAsE;QACtE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;QACnE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;QAClF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QACjF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;QAC7C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,EACD,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAyB,CAAC,CACjE,CAAC;IAEF,QAAQ,CACN,YAAY,EACZ,yEAAyE,EACzE;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAC1E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KAC1F,EACD,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAoB,CAAC,CAC1D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Ferramentas de números de telefone (WABA): listar, ler e saúde. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerPhoneNumberTools(register) {
|
|
4
|
+
register("list_phone_numbers", "Lista os números de telefone conectados da conta (paginação offset: { data, meta }). Filtro opcional por cliente.", {
|
|
5
|
+
customer_id: z.string().optional().describe("Filtra pelos números de um cliente."),
|
|
6
|
+
page: z.number().int().positive().optional().describe("Página (1-based)."),
|
|
7
|
+
per_page: z.number().int().positive().optional().describe("Itens por página (máx. 100)."),
|
|
8
|
+
}, (client, args) => client.phoneNumbers.list(args));
|
|
9
|
+
register("get_phone_number", "Busca um número pelo id (uuid interno). Retorna { data }.", { id: z.string().describe("ID do número (uuid interno).") }, async (client, args) => ({ data: await client.phoneNumbers.get(String(args.id)) }));
|
|
10
|
+
register("phone_number_health", "Retorna a saúde/qualidade de um número (quality rating, status de verificação, limites). Retorna { data }.", { id: z.string().describe("ID do número (uuid interno).") }, async (client, args) => ({ data: await client.phoneNumbers.health(String(args.id)) }));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=phone-numbers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phone-numbers.js","sourceRoot":"","sources":["../../src/tools/phone-numbers.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,wBAAwB,CAAC,QAAkB;IACzD,QAAQ,CACN,oBAAoB,EACpB,mHAAmH,EACnH;QACE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QAClF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAC1E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KAC1F,EACD,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAA8B,CAAC,CAC3E,CAAC;IAEF,QAAQ,CACN,kBAAkB,EAClB,2DAA2D,EAC3D,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,EAC3D,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CACnF,CAAC;IAEF,QAAQ,CACN,qBAAqB,EACrB,4GAA4G,EAC5G,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,EAC3D,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CACtF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Ferramentas de templates de mensagem. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerTemplateTools(register) {
|
|
4
|
+
register("list_templates", "Lista templates da conta (paginação offset: { data, meta }). Filtros opcionais por status, categoria e conexão WABA.", {
|
|
5
|
+
status: z.string().optional().describe("Filtra por status (ex.: APPROVED, PENDING)."),
|
|
6
|
+
category: z.string().optional().describe("Filtra por categoria (case-insensitive)."),
|
|
7
|
+
waba_connection_id: z.string().optional(),
|
|
8
|
+
phone_number_id: z.string().optional().describe("Resolve a conexão do número (Meta)."),
|
|
9
|
+
page: z.number().int().positive().optional().describe("Página (1-based)."),
|
|
10
|
+
per_page: z.number().int().positive().optional().describe("Itens por página (máx. 100)."),
|
|
11
|
+
}, (client, args) => client.templates.list(args));
|
|
12
|
+
register("get_template", "Busca um template pelo id (uuid interno). Retorna { data }.", { id: z.string().describe("ID do template (uuid interno).") }, async (client, args) => ({ data: await client.templates.get(String(args.id)) }));
|
|
13
|
+
register("create_template", "Cria um template e o submete à Meta. Informe name, language, category e components (array no formato da Cloud API). Aponte a conexão via waba_connection_id OU phone_number_id. Retorna { data }.", {
|
|
14
|
+
name: z.string().describe("Nome do template (snake_case)."),
|
|
15
|
+
language: z.string().describe("Código de idioma (ex.: pt_BR)."),
|
|
16
|
+
category: z.string().describe("Categoria (MARKETING|UTILITY|AUTHENTICATION)."),
|
|
17
|
+
components: z
|
|
18
|
+
.array(z.record(z.string(), z.unknown()))
|
|
19
|
+
.describe("Componentes do template (header/body/footer/buttons)."),
|
|
20
|
+
waba_connection_id: z.string().optional(),
|
|
21
|
+
phone_number_id: z.string().optional().describe("Resolve a conexão do número (Meta)."),
|
|
22
|
+
}, async (client, args) => ({
|
|
23
|
+
data: await client.templates.create(args),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/tools/templates.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,qBAAqB,CAAC,QAAkB;IACtD,QAAQ,CACN,gBAAgB,EAChB,sHAAsH,EACtH;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;QACrF,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;QACpF,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACzC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QACtF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAC1E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KAC1F,EACD,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAA2B,CAAC,CACrE,CAAC;IAEF,QAAQ,CACN,cAAc,EACd,6DAA6D,EAC7D,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC7D,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAChF,CAAC;IAEF,QAAQ,CACN,iBAAiB,EACjB,mMAAmM,EACnM;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QAC3D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QAC/D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;QAC9E,UAAU,EAAE,CAAC;aACV,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;aACxC,QAAQ,CAAC,uDAAuD,CAAC;QACpE,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACzC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;KACvF,EACD,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,IAAI,EAAE,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAuC,CAAC;KAC7E,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Ferramentas de webhooks (assinaturas de eventos de saída) e entregas. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export function registerWebhookTools(register) {
|
|
4
|
+
register("list_webhooks", "Lista os endpoints de webhook da conta (paginação por cursor: { data, paging }).", {
|
|
5
|
+
limit: z.number().int().positive().optional(),
|
|
6
|
+
after: z.string().optional(),
|
|
7
|
+
before: z.string().optional(),
|
|
8
|
+
}, (client, args) => client.webhooks.list(args));
|
|
9
|
+
register("get_webhook", "Busca um webhook pelo id (uuid interno). Retorna { data }.", { id: z.string().describe("ID do webhook (uuid interno).") }, async (client, args) => ({ data: await client.webhooks.get(String(args.id)) }));
|
|
10
|
+
register("create_webhook", "Cria um endpoint de webhook. `url` precisa ser https. `events` é a lista de tipos de evento assinados (ao menos um). Retorna { data }.", {
|
|
11
|
+
url: z.string().describe("URL https do endpoint."),
|
|
12
|
+
events: z.array(z.string()).describe("Tipos de evento assinados."),
|
|
13
|
+
active: z.boolean().optional().describe("Ativo (default true)."),
|
|
14
|
+
}, async (client, args) => ({
|
|
15
|
+
data: await client.webhooks.create(args),
|
|
16
|
+
}));
|
|
17
|
+
register("update_webhook", "Atualiza um webhook (url, events, active). Retorna { data }.", {
|
|
18
|
+
id: z.string().describe("ID do webhook (uuid interno)."),
|
|
19
|
+
url: z.string().optional(),
|
|
20
|
+
events: z.array(z.string()).optional(),
|
|
21
|
+
active: z.boolean().optional(),
|
|
22
|
+
}, async (client, args) => {
|
|
23
|
+
const { id, ...body } = args;
|
|
24
|
+
return { data: await client.webhooks.update(String(id), body) };
|
|
25
|
+
});
|
|
26
|
+
register("delete_webhook", "Exclui um webhook pelo id (uuid interno).", { id: z.string().describe("ID do webhook (uuid interno).") }, async (client, args) => {
|
|
27
|
+
await client.webhooks.delete(String(args.id));
|
|
28
|
+
return null;
|
|
29
|
+
});
|
|
30
|
+
register("test_webhook", "Dispara um evento de teste assinado para o endpoint de webhook informado. Retorna { data }.", { id: z.string().describe("ID do webhook (uuid interno).") }, async (client, args) => ({ data: await client.webhooks.test(String(args.id)) }));
|
|
31
|
+
register("list_webhook_deliveries", "Lista as entregas de webhook (tentativas de POST aos endpoints), com status e resposta. Paginação por cursor: { data, paging }.", {
|
|
32
|
+
webhook_id: z.string().optional().describe("Filtra por endpoint (alias de endpoint_id na rota)."),
|
|
33
|
+
status: z.string().optional().describe("Filtra por status da entrega (pending|success|failed|exhausted)."),
|
|
34
|
+
event_type: z.string().optional().describe("Filtra pelo tipo de evento da entrega."),
|
|
35
|
+
limit: z.number().int().positive().optional(),
|
|
36
|
+
after: z.string().optional(),
|
|
37
|
+
before: z.string().optional(),
|
|
38
|
+
}, (client, args) => client.webhookDeliveries.list(args));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/tools/webhooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@botozap/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Servidor MCP (Model Context Protocol) para a API pública do BotoZap — expõe as operações da plataforma WhatsApp Cloud API como ferramentas MCP para Claude Code, Cursor e Codex.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"homepage": "https://botozap.com.br",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/bytecraft-fernando/botozap-js.git",
|
|
11
|
+
"directory": "packages/mcp"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/bytecraft-fernando/botozap-js/issues"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"botozap-mcp": "dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20.19"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
32
|
+
"zod": "^3.25.76",
|
|
33
|
+
"@botozap/sdk": "0.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^20.19.0",
|
|
37
|
+
"typescript": "^5.6.0",
|
|
38
|
+
"vitest": "^3.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json",
|
|
42
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
43
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"start": "node dist/index.js",
|
|
46
|
+
"smoke": "node scripts/smoke.ts",
|
|
47
|
+
"smoke:sandbox": "node scripts/smoke-sandbox.ts"
|
|
48
|
+
}
|
|
49
|
+
}
|