@lionchat/mcp-server 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 +313 -0
- package/dist/client.d.ts +27 -0
- package/dist/client.js +264 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/tools.d.ts +4 -0
- package/dist/tools.js +280 -0
- package/dist/tools.js.map +1 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +80 -0
- package/dist/utils.js.map +1 -0
- package/endpoints.json +12655 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LionChat Crm
|
|
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,313 @@
|
|
|
1
|
+
# @lionchat/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server que conecta agentes de IA a plataforma LionChat. Expoe mais de 540 ferramentas para gerenciar contatos, conversas, kanban, funis, mensagens, automacoes e muito mais.
|
|
4
|
+
|
|
5
|
+
## O que e MCP?
|
|
6
|
+
|
|
7
|
+
MCP (Model Context Protocol) e um protocolo aberto que permite agentes de IA interagirem com servicos externos. Com este servidor, qualquer agente compativel (Claude Code, n8n, agentes customizados) pode executar acoes no LionChat por linguagem natural.
|
|
8
|
+
|
|
9
|
+
**Sem MCP:** Voce le a documentacao, copia endpoints, configura manualmente.
|
|
10
|
+
**Com MCP:** Voce diz "crie um funil com 4 etapas" e o agente faz tudo.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Inicio Rapido
|
|
15
|
+
|
|
16
|
+
### 1. Obtenha seu token
|
|
17
|
+
|
|
18
|
+
1. Faca login no LionChat
|
|
19
|
+
2. Va em **Configuracoes > Perfil**
|
|
20
|
+
3. Copie o **Access Token**
|
|
21
|
+
|
|
22
|
+
### 2. Descubra seu Account ID
|
|
23
|
+
|
|
24
|
+
O account_id aparece na URL quando voce esta logado:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
app.lionchat.com.br/app/accounts/ACCOUNT_ID/dashboard
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 3. Conecte ao Claude Code
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
claude mcp add lionchat \
|
|
34
|
+
-e LIONCHAT_API_TOKEN=seu_token \
|
|
35
|
+
-e LIONCHAT_ACCOUNT_ID=123 \
|
|
36
|
+
-- npx @lionchat/mcp-server
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Pronto. Agora voce pode pedir ao Claude Code para fazer qualquer coisa no LionChat.
|
|
40
|
+
|
|
41
|
+
### Instalacao alternativa (variaveis de ambiente)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export LIONCHAT_API_TOKEN=seu_token
|
|
45
|
+
export LIONCHAT_ACCOUNT_ID=123
|
|
46
|
+
npx @lionchat/mcp-server
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Instancia self-hosted / white-label
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export LIONCHAT_BASE_URL=https://sua-instancia.com
|
|
53
|
+
npx @lionchat/mcp-server
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Configuracao
|
|
59
|
+
|
|
60
|
+
| Variavel | CLI | Obrigatorio | Padrao |
|
|
61
|
+
|----------|-----|-------------|--------|
|
|
62
|
+
| `LIONCHAT_API_TOKEN` | `--token` | Sim | -- |
|
|
63
|
+
| `LIONCHAT_ACCOUNT_ID` | `--account` | Sim | -- |
|
|
64
|
+
| `LIONCHAT_BASE_URL` | `--base-url` | Nao | `https://app.lionchat.com.br` |
|
|
65
|
+
| `LIONCHAT_CATEGORIES` | `--categories` | Nao | todas |
|
|
66
|
+
| `LIONCHAT_INCLUDE_PUBLIC_API` | `--include-public-api` | Nao | false |
|
|
67
|
+
|
|
68
|
+
**Prioridade:** argumentos CLI > variaveis de ambiente > valores padrao.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Filtro por Categoria
|
|
73
|
+
|
|
74
|
+
Para melhor performance do agente, carregue apenas as ferramentas que precisa:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx @lionchat/mcp-server --categories=contacts,conversations,kanban_items
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Perfis Sugeridos
|
|
81
|
+
|
|
82
|
+
| Perfil | Categorias | Uso |
|
|
83
|
+
|--------|-----------|-----|
|
|
84
|
+
| **Vendas** | `contacts,conversations,messages,kanban_items,funnels,offers` | CRM e automacao de vendas |
|
|
85
|
+
| **Suporte** | `contacts,conversations,messages,canned_responses,teams,labels` | Atendimento ao cliente |
|
|
86
|
+
| **Marketing** | `contacts,automation_rules,labels,scheduled_messages` | Campanhas e automacao |
|
|
87
|
+
| **Completo** | _(sem filtro)_ | Cobertura total da API (541 ferramentas) |
|
|
88
|
+
|
|
89
|
+
### Categorias Disponiveis
|
|
90
|
+
|
|
91
|
+
| Slug | Descricao | Tools |
|
|
92
|
+
|------|-----------|-------|
|
|
93
|
+
| `account` | Detalhes da conta | 3 |
|
|
94
|
+
| `agents` | Agentes/operadores | 2 |
|
|
95
|
+
| `agent_availability` | Disponibilidade dos agentes | 4 |
|
|
96
|
+
| `announcements` | Anuncios internos | 4 |
|
|
97
|
+
| `assignment_policies` | Politicas de atribuicao | 11 |
|
|
98
|
+
| `automation_rules` | Regras de automacao | 7 |
|
|
99
|
+
| `booking_event_types` | Tipos de evento (agenda) | 8 |
|
|
100
|
+
| `canned_responses` | Respostas rapidas | 6 |
|
|
101
|
+
| `capacity_policies` | Politicas de capacidade | 11 |
|
|
102
|
+
| `captain_assistants` | Assistentes IA | 33 |
|
|
103
|
+
| `captain_documents` | Base de conhecimento IA | 4 |
|
|
104
|
+
| `contacts` | Contatos | 25 |
|
|
105
|
+
| `conversations` | Conversas | 30 |
|
|
106
|
+
| `copilot_prompts` | Prompts salvos do Copilot | 4 |
|
|
107
|
+
| `csat` | Pesquisas de satisfacao | 3 |
|
|
108
|
+
| `csat_template` | Template CSAT por inbox | 2 |
|
|
109
|
+
| `custom_attributes` | Atributos personalizados | 5 |
|
|
110
|
+
| `custom_filters` | Filtros personalizados | 5 |
|
|
111
|
+
| `custom_roles` | Roles personalizados | 5 |
|
|
112
|
+
| `dashboard_apps` | Apps no dashboard | 5 |
|
|
113
|
+
| `ecommerce_webhooks` | Integracoes e-commerce (Guru, Hotmart, Kiwify, Ticto, Eduzz, Monetizze) | 55 |
|
|
114
|
+
| `flows` | FlowBuilder (automacoes visuais) | 10 |
|
|
115
|
+
| `flow_sessions` | Sessoes do FlowBuilder | 3 |
|
|
116
|
+
| `funnels` | Funis de vendas | 10 |
|
|
117
|
+
| `google_calendar` | Google Calendar | 13 |
|
|
118
|
+
| `inbox_members` | Membros das inboxes | 4 |
|
|
119
|
+
| `inbox_migration` | Migracao entre inboxes | 3 |
|
|
120
|
+
| `inboxes` | Caixas de entrada | 4 |
|
|
121
|
+
| `kanban_agents` | Agentes do Kanban | 3 |
|
|
122
|
+
| `kanban_bulk` | Operacoes em massa (Kanban) | 4 |
|
|
123
|
+
| `kanban_checklist` | Checklist dos cards | 5 |
|
|
124
|
+
| `kanban_config` | Configuracao do Kanban | 5 |
|
|
125
|
+
| `kanban_items` | Cards do Kanban | 21 |
|
|
126
|
+
| `kanban_notes` | Notas dos cards | 4 |
|
|
127
|
+
| `kanban_v2` | Kanban V2 (items, funis, etapas, automacoes) | 25 |
|
|
128
|
+
| `labels` | Labels/etiquetas | 5 |
|
|
129
|
+
| `macros` | Macros | 6 |
|
|
130
|
+
| `messages` | Mensagens | 8 |
|
|
131
|
+
| `meta_lead` | Meta Lead Ads (Facebook/Instagram) | 10 |
|
|
132
|
+
| `notification_settings` | Config de notificacoes | 2 |
|
|
133
|
+
| `notifications` | Notificacoes | 8 |
|
|
134
|
+
| `offers` | Ofertas | 6 |
|
|
135
|
+
| `portals` | Portais e base de conhecimento | 20 |
|
|
136
|
+
| `public_booking` | Agendamento publico | 5 |
|
|
137
|
+
| `public_contacts` | API publica: contatos | 3 |
|
|
138
|
+
| `public_conversations` | API publica: conversas | 5 |
|
|
139
|
+
| `public_csat` | API publica: CSAT | 1 |
|
|
140
|
+
| `public_messages` | API publica: mensagens | 3 |
|
|
141
|
+
| `reports` | Relatorios V2 | 19 |
|
|
142
|
+
| `scheduled_messages` | Mensagens agendadas | 8 |
|
|
143
|
+
| `search` | Busca global | 4 |
|
|
144
|
+
| `sla` | SLA (Service Level Agreement) | 8 |
|
|
145
|
+
| `support_access` | Acesso de suporte | 3 |
|
|
146
|
+
| `tasks` | Agenda / Tarefas | 14 |
|
|
147
|
+
| `teams` | Times | 7 |
|
|
148
|
+
| `upload` | Upload de arquivos | 1 |
|
|
149
|
+
| `voip_calls` | Ligacoes VoIP (Zenvia) | 20 |
|
|
150
|
+
| `waha_groups` | Grupos WhatsApp | 14 |
|
|
151
|
+
| `webhooks` | Webhooks | 4 |
|
|
152
|
+
| `whatsapp_templates` | Templates WhatsApp | 5 |
|
|
153
|
+
| `account_variables` | Variaveis da conta | 4 |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Exemplos de Uso
|
|
158
|
+
|
|
159
|
+
### No Claude Code
|
|
160
|
+
|
|
161
|
+
Depois de conectar, basta pedir em linguagem natural:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
"Liste meus ultimos 10 contatos"
|
|
165
|
+
→ lionchat_contacts_list
|
|
166
|
+
|
|
167
|
+
"Crie um funil chamado 'Vendas Q2' com etapas Prospeccao, Qualificacao, Proposta, Fechado"
|
|
168
|
+
→ lionchat_funnels_create
|
|
169
|
+
|
|
170
|
+
"Mova o card 42 para a etapa 'Proposta'"
|
|
171
|
+
→ lionchat_kanban_items_move_to_stage
|
|
172
|
+
|
|
173
|
+
"Envie 'Ola!' na conversa 15"
|
|
174
|
+
→ lionchat_conversations_messages_create
|
|
175
|
+
|
|
176
|
+
"Quais conversas estao abertas no time de suporte?"
|
|
177
|
+
→ lionchat_conversations_list (com filtro por time)
|
|
178
|
+
|
|
179
|
+
"Crie uma regra de automacao: quando criar conversa com label VIP, atribuir ao time Premium"
|
|
180
|
+
→ lionchat_automation_rules_create
|
|
181
|
+
|
|
182
|
+
"Qual o saldo do meu VoIP?"
|
|
183
|
+
→ lionchat_voip_calls_balance
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### No n8n
|
|
187
|
+
|
|
188
|
+
O n8n suporta MCP de duas formas:
|
|
189
|
+
|
|
190
|
+
#### Opcao 1: MCP Client + AI Agent (recomendado para decisoes inteligentes)
|
|
191
|
+
|
|
192
|
+
1. Adicione um node **"AI Agent"** ao seu workflow (Claude, GPT, ou outro LLM)
|
|
193
|
+
2. Adicione um node **"MCP Client"** como ferramenta do agente
|
|
194
|
+
3. Configure o MCP Client:
|
|
195
|
+
- **Transport:** stdio
|
|
196
|
+
- **Command:** `npx`
|
|
197
|
+
- **Arguments:** `@lionchat/mcp-server`
|
|
198
|
+
- **Environment Variables:**
|
|
199
|
+
- `LIONCHAT_API_TOKEN` = seu token
|
|
200
|
+
- `LIONCHAT_ACCOUNT_ID` = seu account ID
|
|
201
|
+
- `LIONCHAT_CATEGORIES` = categorias desejadas (opcional)
|
|
202
|
+
4. Conecte o MCP Client ao AI Agent
|
|
203
|
+
5. O agente de IA decide automaticamente quais ferramentas usar
|
|
204
|
+
|
|
205
|
+
**Exemplo de prompt no AI Agent:**
|
|
206
|
+
> "Analise os contatos criados ontem. Se algum tem empresa no nome, crie um card no funil 'Vendas B2B' na etapa 'Prospeccao'."
|
|
207
|
+
|
|
208
|
+
O agente vai chamar `lionchat_contacts_list`, analisar os resultados, e chamar `lionchat_kanban_items_create` para cada contato relevante.
|
|
209
|
+
|
|
210
|
+
#### Opcao 2: HTTP Request (para automacoes fixas)
|
|
211
|
+
|
|
212
|
+
Para automacoes tipo "se X entao Y" sem necessidade de IA, use o node **HTTP Request** do n8n chamando a API do LionChat diretamente:
|
|
213
|
+
|
|
214
|
+
- **URL:** `https://app.lionchat.com.br/api/v1/accounts/{account_id}/contacts`
|
|
215
|
+
- **Header:** `api_access_token: seu_token`
|
|
216
|
+
- **Method:** GET, POST, etc.
|
|
217
|
+
|
|
218
|
+
#### Quando usar cada opcao
|
|
219
|
+
|
|
220
|
+
| Abordagem | Quando usar |
|
|
221
|
+
|-----------|-------------|
|
|
222
|
+
| **MCP + AI Agent** | O fluxo precisa de decisao inteligente ("analise e decida") |
|
|
223
|
+
| **HTTP Request** | Automacao fixa ("quando receber webhook, criar contato") |
|
|
224
|
+
|
|
225
|
+
### Em scripts ou agentes customizados
|
|
226
|
+
|
|
227
|
+
O MCP server funciona com qualquer cliente MCP via transporte stdio. Execute como subprocesso e comunique via stdin/stdout (JSON-RPC).
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Ferramentas Meta
|
|
232
|
+
|
|
233
|
+
Sempre disponiveis, independente do filtro de categorias:
|
|
234
|
+
|
|
235
|
+
| Ferramenta | Descricao |
|
|
236
|
+
|------------|-----------|
|
|
237
|
+
| `lionchat_ping` | Testa conectividade e valida o token |
|
|
238
|
+
| `lionchat_list_categories` | Lista categorias disponiveis com contagem de ferramentas |
|
|
239
|
+
|
|
240
|
+
Use `lionchat_ping` para verificar se a conexao esta funcionando antes de executar outras acoes.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## API Publica
|
|
245
|
+
|
|
246
|
+
Os 12 endpoints da API publica (widget, contatos publicos, CSAT) sao **ocultos por padrao** por seguranca. Para habilitar:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
npx @lionchat/mcp-server --include-public-api
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Esses endpoints usam `inbox_identifier` e `contact_identifier` em vez de token. Habilite apenas se seu agente precisa criar contatos ou conversas em nome de usuarios finais.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Upload de Arquivos
|
|
257
|
+
|
|
258
|
+
Endpoints que requerem upload de arquivo (importacao CSV, avatar, anexos) estao incluidos mas retornam uma mensagem orientando a usar a interface web ou chamadas API diretas. Upload via MCP sera suportado em versoes futuras.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Seguranca
|
|
263
|
+
|
|
264
|
+
- Token armazenado em variavel de ambiente, nunca logado ou incluido em respostas
|
|
265
|
+
- Transporte stdio -- comunicacao local entre processos, sem porta de rede aberta
|
|
266
|
+
- Stateless -- nada salvo em disco
|
|
267
|
+
- Timeout de 30 segundos por requisicao
|
|
268
|
+
- Retry automatico com backoff exponencial para rate limiting (429)
|
|
269
|
+
- Cada requisicao usa o token do cliente -- permissoes do LionChat sao respeitadas no servidor
|
|
270
|
+
|
|
271
|
+
**Recomendacao:** Use um token com permissoes minimas para seu caso de uso. Evite tokens de administrador em agentes automatizados.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Requisitos
|
|
276
|
+
|
|
277
|
+
- Node.js 18 ou superior
|
|
278
|
+
- Conta no LionChat com access token
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Desenvolvimento
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Clonar o repositorio
|
|
286
|
+
git clone https://github.com/LionChatCRM/lionchat-mcp-server-.git
|
|
287
|
+
cd lionchat-mcp-server-
|
|
288
|
+
|
|
289
|
+
# Instalar dependencias
|
|
290
|
+
npm install
|
|
291
|
+
|
|
292
|
+
# Build
|
|
293
|
+
npm run build
|
|
294
|
+
|
|
295
|
+
# Testar localmente
|
|
296
|
+
LIONCHAT_API_TOKEN=seu_token LIONCHAT_ACCOUNT_ID=123 node dist/index.js
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Atualizar endpoints
|
|
300
|
+
|
|
301
|
+
Quando a API do LionChat mudar:
|
|
302
|
+
|
|
303
|
+
1. Atualize o HTML de documentacao da API
|
|
304
|
+
2. Execute `npm run extract -- caminho/para/api-docs.html`
|
|
305
|
+
3. Verifique o `endpoints.json` gerado
|
|
306
|
+
4. Incremente a versao no `package.json`
|
|
307
|
+
5. `npm publish`
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Licenca
|
|
312
|
+
|
|
313
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Config } from './config.js';
|
|
2
|
+
export interface RequestOptions {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
body?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
export interface PaginatedResponse {
|
|
8
|
+
data: unknown[];
|
|
9
|
+
pagination?: {
|
|
10
|
+
current_page: number;
|
|
11
|
+
total_pages: number;
|
|
12
|
+
total_count: number;
|
|
13
|
+
has_more: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare class LionChatApiError extends Error {
|
|
17
|
+
readonly status: number;
|
|
18
|
+
readonly responseBody: unknown;
|
|
19
|
+
constructor(message: string, status: number, responseBody?: unknown);
|
|
20
|
+
}
|
|
21
|
+
export declare class LionChatClient {
|
|
22
|
+
private config;
|
|
23
|
+
constructor(config: Config);
|
|
24
|
+
request(options: RequestOptions): Promise<unknown>;
|
|
25
|
+
private buildUrl;
|
|
26
|
+
private executeWithRetry;
|
|
27
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// AIDEV-NOTE: HTTP client for LionChat API
|
|
2
|
+
// Uses native fetch (Node 18+), zero external dependencies
|
|
3
|
+
// Handles auth, retry (429/5xx/network), timeout, and response normalization
|
|
4
|
+
// AIDEV-NOTE: Custom error class with HTTP status for callers to inspect
|
|
5
|
+
export class LionChatApiError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
responseBody;
|
|
8
|
+
constructor(message, status, responseBody) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'LionChatApiError';
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.responseBody = responseBody;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const TIMEOUT_MS = 30_000;
|
|
16
|
+
const MAX_RETRIES_429 = 3;
|
|
17
|
+
const MAX_RETRIES_5XX = 1;
|
|
18
|
+
const MAX_RETRIES_NETWORK = 1;
|
|
19
|
+
// AIDEV-NOTE: Actionable error messages per HTTP status
|
|
20
|
+
function getErrorMessage(status, body) {
|
|
21
|
+
switch (status) {
|
|
22
|
+
case 401:
|
|
23
|
+
return 'Authentication failed. Check your token at Login > Profile Settings';
|
|
24
|
+
case 403:
|
|
25
|
+
return 'Permission denied. Your token may not have access to this resource';
|
|
26
|
+
case 404:
|
|
27
|
+
return 'Resource not found. Check the ID and account_id';
|
|
28
|
+
case 422: {
|
|
29
|
+
const details = extractValidationDetails(body);
|
|
30
|
+
return `Validation error: ${details}`;
|
|
31
|
+
}
|
|
32
|
+
case 429:
|
|
33
|
+
return 'Rate limited. Wait and retry';
|
|
34
|
+
default:
|
|
35
|
+
if (status >= 500) {
|
|
36
|
+
return 'LionChat server error. Try again later';
|
|
37
|
+
}
|
|
38
|
+
return `HTTP error ${status}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// AIDEV-NOTE: Extract validation details from 422 response body
|
|
42
|
+
function extractValidationDetails(body) {
|
|
43
|
+
if (body && typeof body === 'object') {
|
|
44
|
+
const obj = body;
|
|
45
|
+
// Chatwoot returns errors in various formats
|
|
46
|
+
if (obj.message && typeof obj.message === 'string') {
|
|
47
|
+
return obj.message;
|
|
48
|
+
}
|
|
49
|
+
if (obj.error && typeof obj.error === 'string') {
|
|
50
|
+
return obj.error;
|
|
51
|
+
}
|
|
52
|
+
if (obj.errors && Array.isArray(obj.errors)) {
|
|
53
|
+
return obj.errors.join(', ');
|
|
54
|
+
}
|
|
55
|
+
if (obj.errors && typeof obj.errors === 'object') {
|
|
56
|
+
return JSON.stringify(obj.errors);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(body);
|
|
60
|
+
}
|
|
61
|
+
// AIDEV-NOTE: Check if an error is a network/timeout error worth retrying
|
|
62
|
+
function isNetworkError(err) {
|
|
63
|
+
if (err instanceof Error) {
|
|
64
|
+
const msg = err.message.toLowerCase();
|
|
65
|
+
return (msg.includes('econnrefused') ||
|
|
66
|
+
msg.includes('econnreset') ||
|
|
67
|
+
msg.includes('etimedout') ||
|
|
68
|
+
msg.includes('fetch failed') ||
|
|
69
|
+
msg.includes('abort') ||
|
|
70
|
+
err.name === 'AbortError');
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// AIDEV-NOTE: Sleep utility for retry backoff
|
|
75
|
+
function sleep(ms) {
|
|
76
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
77
|
+
}
|
|
78
|
+
// AIDEV-NOTE: Normalize LionChat API responses to a consistent format
|
|
79
|
+
// - Array with meta/payload pagination -> PaginatedResponse
|
|
80
|
+
// - Plain array -> { data: [...] }
|
|
81
|
+
// - Single object -> returned as-is
|
|
82
|
+
function normalizeResponse(body) {
|
|
83
|
+
if (body && typeof body === 'object' && !Array.isArray(body)) {
|
|
84
|
+
const obj = body;
|
|
85
|
+
// AIDEV-NOTE: Chatwoot pagination format: { payload: [...], meta: { all_count, current_page } }
|
|
86
|
+
if (Array.isArray(obj.payload) && obj.meta && typeof obj.meta === 'object') {
|
|
87
|
+
const meta = obj.meta;
|
|
88
|
+
const allCount = typeof meta.all_count === 'number' ? meta.all_count : 0;
|
|
89
|
+
const currentPage = typeof meta.current_page === 'number' ? meta.current_page : 1;
|
|
90
|
+
// AIDEV-NOTE: Chatwoot uses 25 items per page by default. Don't use payload.length
|
|
91
|
+
// as it's wrong on the last page (e.g. 3 items on last page → wrong totalPages calc)
|
|
92
|
+
const perPage = 25;
|
|
93
|
+
const totalPages = perPage > 0 ? Math.ceil(allCount / perPage) : 1;
|
|
94
|
+
return {
|
|
95
|
+
data: obj.payload,
|
|
96
|
+
pagination: {
|
|
97
|
+
current_page: currentPage,
|
|
98
|
+
total_pages: totalPages,
|
|
99
|
+
total_count: allCount,
|
|
100
|
+
has_more: currentPage < totalPages,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// AIDEV-NOTE: Some endpoints return { data: [...], meta: {...} } format
|
|
105
|
+
if (Array.isArray(obj.data) && obj.meta && typeof obj.meta === 'object') {
|
|
106
|
+
const meta = obj.meta;
|
|
107
|
+
const totalCount = typeof meta.total_count === 'number'
|
|
108
|
+
? meta.total_count
|
|
109
|
+
: typeof meta.all_count === 'number'
|
|
110
|
+
? meta.all_count
|
|
111
|
+
: obj.data.length;
|
|
112
|
+
const currentPage = typeof meta.current_page === 'number' ? meta.current_page : 1;
|
|
113
|
+
const totalPages = typeof meta.total_pages === 'number'
|
|
114
|
+
? meta.total_pages
|
|
115
|
+
: Math.ceil(totalCount / Math.max(obj.data.length, 1));
|
|
116
|
+
return {
|
|
117
|
+
data: obj.data,
|
|
118
|
+
pagination: {
|
|
119
|
+
current_page: currentPage,
|
|
120
|
+
total_pages: totalPages,
|
|
121
|
+
total_count: totalCount,
|
|
122
|
+
has_more: currentPage < totalPages,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// AIDEV-NOTE: Single object response — return as-is
|
|
127
|
+
return body;
|
|
128
|
+
}
|
|
129
|
+
// AIDEV-NOTE: Flat array response — wrap in { data: [...] }
|
|
130
|
+
if (Array.isArray(body)) {
|
|
131
|
+
return { data: body };
|
|
132
|
+
}
|
|
133
|
+
return body;
|
|
134
|
+
}
|
|
135
|
+
export class LionChatClient {
|
|
136
|
+
config;
|
|
137
|
+
constructor(config) {
|
|
138
|
+
this.config = config;
|
|
139
|
+
}
|
|
140
|
+
// AIDEV-NOTE: Main request method with auth, timeout, retry, and normalization
|
|
141
|
+
async request(options) {
|
|
142
|
+
const { method, path, body } = options;
|
|
143
|
+
// AIDEV-NOTE: Build full URL (query params already embedded in path by tools.ts)
|
|
144
|
+
const url = this.buildUrl(path);
|
|
145
|
+
// AIDEV-NOTE: Build headers — api_access_token on all requests
|
|
146
|
+
const headers = {
|
|
147
|
+
api_access_token: this.config.apiToken,
|
|
148
|
+
};
|
|
149
|
+
const hasBody = body && ['POST', 'PATCH', 'PUT', 'DELETE'].includes(method.toUpperCase());
|
|
150
|
+
if (hasBody) {
|
|
151
|
+
headers['Content-Type'] = 'application/json';
|
|
152
|
+
}
|
|
153
|
+
const fetchOptions = {
|
|
154
|
+
method: method.toUpperCase(),
|
|
155
|
+
headers,
|
|
156
|
+
};
|
|
157
|
+
if (hasBody) {
|
|
158
|
+
fetchOptions.body = JSON.stringify(body);
|
|
159
|
+
}
|
|
160
|
+
// AIDEV-NOTE: Execute with retry logic
|
|
161
|
+
return this.executeWithRetry(url, fetchOptions);
|
|
162
|
+
}
|
|
163
|
+
// AIDEV-NOTE: Build URL from base + path (query params already in path string)
|
|
164
|
+
buildUrl(path) {
|
|
165
|
+
const base = this.config.baseUrl.replace(/\/+$/, '');
|
|
166
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
167
|
+
return `${base}${cleanPath}`;
|
|
168
|
+
}
|
|
169
|
+
// AIDEV-NOTE: Retry logic: 429 (exponential backoff, 3x), 5xx (once, 2s), network (once, 2s)
|
|
170
|
+
async executeWithRetry(url, fetchOptions) {
|
|
171
|
+
let lastError;
|
|
172
|
+
let retries429 = 0;
|
|
173
|
+
let retries5xx = 0;
|
|
174
|
+
let retriesNetwork = 0;
|
|
175
|
+
// AIDEV-NOTE: Max possible attempts = 1 initial + 3 (429) + 1 (5xx) + 1 (network)
|
|
176
|
+
// In practice, only one retry category applies per request
|
|
177
|
+
const maxAttempts = 1 + MAX_RETRIES_429 + MAX_RETRIES_5XX + MAX_RETRIES_NETWORK;
|
|
178
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
179
|
+
try {
|
|
180
|
+
// AIDEV-NOTE: AbortController for 30s timeout
|
|
181
|
+
const controller = new AbortController();
|
|
182
|
+
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
183
|
+
let response;
|
|
184
|
+
try {
|
|
185
|
+
response = await fetch(url, {
|
|
186
|
+
...fetchOptions,
|
|
187
|
+
signal: controller.signal,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
clearTimeout(timeoutId);
|
|
192
|
+
}
|
|
193
|
+
// AIDEV-NOTE: Parse response body
|
|
194
|
+
let responseBody;
|
|
195
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
196
|
+
if (contentType.includes('application/json')) {
|
|
197
|
+
responseBody = await response.json();
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
const text = await response.text();
|
|
201
|
+
// AIDEV-NOTE: Try parsing as JSON even without content-type header
|
|
202
|
+
try {
|
|
203
|
+
responseBody = JSON.parse(text);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
responseBody = text;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// AIDEV-NOTE: Success — normalize and return
|
|
210
|
+
if (response.ok) {
|
|
211
|
+
return normalizeResponse(responseBody);
|
|
212
|
+
}
|
|
213
|
+
// AIDEV-NOTE: 429 — exponential backoff with Retry-After header support
|
|
214
|
+
if (response.status === 429 && retries429 < MAX_RETRIES_429) {
|
|
215
|
+
retries429++;
|
|
216
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
217
|
+
let delayMs;
|
|
218
|
+
if (retryAfter) {
|
|
219
|
+
const seconds = parseInt(retryAfter, 10);
|
|
220
|
+
// AIDEV-NOTE: Retry-After can be seconds (integer) or HTTP-date. parseInt on
|
|
221
|
+
// a date string returns NaN, so fall back to exponential backoff.
|
|
222
|
+
delayMs = Number.isNaN(seconds) ? 1000 * Math.pow(2, retries429 - 1) : seconds * 1000;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
delayMs = 1000 * Math.pow(2, retries429 - 1); // 1s, 2s, 4s
|
|
226
|
+
}
|
|
227
|
+
await sleep(delayMs);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// AIDEV-NOTE: 5xx — retry once after 2s
|
|
231
|
+
if (response.status >= 500 && retries5xx < MAX_RETRIES_5XX) {
|
|
232
|
+
retries5xx++;
|
|
233
|
+
await sleep(2000);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
// AIDEV-NOTE: Non-retryable error — throw with actionable message
|
|
237
|
+
const message = getErrorMessage(response.status, responseBody);
|
|
238
|
+
throw new LionChatApiError(message, response.status, responseBody);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
// AIDEV-NOTE: Re-throw our own errors (already handled above)
|
|
242
|
+
if (err instanceof LionChatApiError) {
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
245
|
+
// AIDEV-NOTE: Network/timeout errors — retry once after 2s
|
|
246
|
+
if (isNetworkError(err) && retriesNetwork < MAX_RETRIES_NETWORK) {
|
|
247
|
+
retriesNetwork++;
|
|
248
|
+
lastError = err;
|
|
249
|
+
await sleep(2000);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
// AIDEV-NOTE: Unrecoverable network error
|
|
253
|
+
if (isNetworkError(err)) {
|
|
254
|
+
throw new LionChatApiError(`Network error: Could not connect to ${this.config.baseUrl}. Check your base URL and network connection.`, 0, undefined);
|
|
255
|
+
}
|
|
256
|
+
// AIDEV-NOTE: Unexpected error — rethrow
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// AIDEV-NOTE: Should not reach here, but safety fallback
|
|
261
|
+
throw lastError ?? new Error('Request failed after all retries');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|