@lionchat/mcp-server 0.4.1 → 0.4.3

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.
@@ -78,18 +78,23 @@ Account (conta/empresa)
78
78
  - `display_id`: número visível na UI da conversa (use este pra falar com humanos)
79
79
  - `source_id`: ID externo da plataforma origem (ex: `wamid.xxx` do WhatsApp)
80
80
 
81
- ### Body wrapper — CRÍTICO
81
+ ### Body wrapper — use raiz por padrão
82
82
 
83
- A maioria dos POST/PUT/PATCH exige o body **wrapped no nome do recurso** (padrão Rails strong params). Sem o wrapper retorna HTTP 500 silencioso.
83
+ O Rails tem `wrap_parameters: [:json]` global. Body JSON raiz é auto-wrapped no nome do recurso, então ambas as formas funcionam na maioria dos endpoints:
84
84
 
85
85
  ```json
86
86
  PUT /api/v1/accounts/43/kanban_config
87
- { "kanban_config": { "win_reasons": [...] } } ✅
88
-
89
- { "win_reasons": [...] } ❌ 500
87
+ { "win_reasons": [...] } ✅ raiz (recomendado)
88
+ { "kanban_config": { "win_reasons": [...] } } ✅ explícito (também OK)
90
89
  ```
91
90
 
92
- Como saber: se o param da tool no MCP tem ponto no nome (`kanban_config.win_reasons`, `funnel.name`), o body precisa do wrapper. Sem ponto (`message`, `phone`), vai raiz.
91
+ **Exceção importante — `/contacts` SÓ aceita raiz.** Com wrapper, o `name`/`email`/`phone_number` somem silenciosamente e o contato é criado vazio:
92
+
93
+ ```json
94
+ POST /api/v1/accounts/43/contacts
95
+ { "name": "Fulano", "phone_number": "+5511..." } ✅
96
+ { "contact": { "name": "Fulano" } } ⚠️ cria contato com name vazio
97
+ ```
93
98
 
94
99
  Detalhes completos em `lionchat://docs/api-conventions` → "Wrapper de body".
95
100
 
@@ -19,62 +19,61 @@ Pra você via MCP: tudo já tá resolvido. O servidor MCP injeta o token correto
19
19
 
20
20
  Tokens são **por usuário**, não por conta. O mesmo token funciona em qualquer conta que o usuário tem acesso. O scoping por conta acontece via `account_id` no path.
21
21
 
22
- ## Wrapper de body — REGRA CRÍTICA
22
+ ## Wrapper de body — como o Rails interpreta
23
23
 
24
- A maioria dos endpoints `POST/PUT/PATCH` espera o body **wrapped no nome do recurso**, herdando o padrão `params.require(:recurso).permit(...)` do Rails strong params.
24
+ O projeto roda com `wrap_parameters format: [:json]` no Rails (config global). Na prática:
25
25
 
26
- ### Padrão geral
26
+ - Bodies JSON raiz são **auto-wrapped** pelo Rails no nome do recurso (derivado do controller).
27
+ - Mandar `{"enabled": true}` em `PUT /kanban_config` é equivalente a mandar `{"kanban_config": {"enabled": true}}`.
28
+ - Ambas as formas funcionam na maioria dos endpoints. Não existe "HTTP 500 silencioso por falta de wrapper" — esse claim era impreciso.
29
+
30
+ ### Padrão recomendado
31
+
32
+ Use **body raiz** (sem wrapper). É mais curto, funciona em todos os endpoints listados abaixo, e evita a pegadinha do `/contacts`.
27
33
 
28
34
  ```http
29
35
  PUT /api/v1/accounts/43/kanban_config
30
36
  Content-Type: application/json
31
37
 
32
- {
33
- "kanban_config": {
34
- "win_reasons": [...],
35
- "enabled": true
36
- }
37
- }
38
+ { "win_reasons": [{"id": "wr-1", "title": "Preço competitivo"}] }
38
39
  ```
39
40
 
40
- **Se você manda `{"win_reasons": [...]}` direto (sem o wrapper) → HTTP 500 silencioso.** Rails levanta `ActionController::ParameterMissing` que vira 500 sem mensagem útil.
41
+ ### Comportamento por endpoint (validado empiricamente)
41
42
 
42
- ### Recursos que exigem wrapper
43
+ | Endpoint | Raiz | Com wrapper | Observação |
44
+ |---|---|---|---|
45
+ | `POST/PUT /kanban_config` | ✅ | ✅ | qualquer um |
46
+ | `POST/PATCH /funnels` | ✅ | ✅ | qualquer um |
47
+ | `POST/PUT /kanban_items` | ✅ | ✅ | controller usa `require(:kanban_item)` mas auto-wrap resolve |
48
+ | `POST/PATCH /labels` | ✅ | ✅ | qualquer um |
49
+ | `POST/PATCH /custom_attribute_definitions` | ✅ | ✅ | qualquer um |
50
+ | `POST/PATCH /contacts` | ✅ | ⚠️ **drop silencioso** | Controller usa `params.permit(...)` na raiz sem `require(:contact)`. Com wrapper, o auto-wrap não duplica e os campos do body raiz ficam ausentes — o contato é criado com `name` vazio. **USE RAIZ.** |
51
+ | `POST/PATCH /conversations` | ✅ | varia | conversa aceita raiz |
52
+ | `POST /captain/copilot_threads` | ✅ | n/a | só raiz (`message`, `assistant_id`, `conversation_id`) |
53
+ | `POST /voip/settings/create_child_account` | ✅ | n/a | só raiz (`nome`, `login`, `senha`) |
43
54
 
44
- | Endpoint | Wrapper |
45
- |---|---|
46
- | `POST/PUT /kanban_config` | `kanban_config: {...}` |
47
- | `POST/PATCH /funnels` | `funnel: {...}` |
48
- | `POST/PUT /kanban_items` | `kanban_item: {...}` |
49
- | `POST/PATCH /contacts` | `contact: {...}` |
50
- | `POST/PATCH /conversations` | `conversation: {...}` ou direto (varia) |
51
- | `POST/PATCH /custom_attribute_definitions` | sem wrapper (params raiz) |
52
- | `POST/PATCH /labels` | sem wrapper |
53
- | `POST /captain/copilot_threads` | sem wrapper (params raiz: `message`, `assistant_id`) |
54
- | `POST /voip/settings/create_child_account` | sem wrapper (raiz: `nome`, `login`, `senha`) |
55
+ ### Quando WRAPPER é obrigatório
56
+
57
+ Praticamente nunca — exceto se você está testando legacy/curl e quer ser explícito. As tools do MCP geram params com nome `recurso.campo` (ex: `kanban_config.win_reasons`) só pra organizar a UI; no body final qualquer formato roda.
55
58
 
56
- ### Como saber se precisa wrapper?
59
+ ### Listas substituem, não fazem merge
57
60
 
58
- 1. **Olha o nome do tool no MCP** — params nomeados `recurso.campo` (com ponto) significa que precisa wrapper.
59
- 2. Params nomeados sem ponto (`message`, `phone`, `title`) ficam direto no body raiz.
60
- 3. Em caso de dúvida: tentar **com wrapper primeiro** (mais comum). Se 500, tentar sem.
61
+ `win_reasons`, `loss_reasons`, `stages`, `linked_conversations`, etc. — quando você manda a lista, ela **substitui** a anterior por inteiro. Pra preservar, leia com GET primeiro e mande a lista completa.
61
62
 
62
- ### Exemplo de listas dentro de wrapper
63
+ ### Estrutura interna das listas
63
64
 
64
- `kanban_config.win_reasons` espera array de **objetos** `{id, title}`, NÃO strings:
65
+ `win_reasons` / `loss_reasons` aceitam array de **objetos** `{id, title}`, NÃO strings:
65
66
 
66
67
  ```json
67
68
  {
68
- "kanban_config": {
69
- "win_reasons": [
70
- {"id": "wr-1", "title": "Preço competitivo"},
71
- {"id": "wr-2", "title": "Indicação forte"}
72
- ]
73
- }
69
+ "win_reasons": [
70
+ {"id": "wr-1", "title": "Preço competitivo"},
71
+ {"id": "wr-2", "title": "Indicação forte"}
72
+ ]
74
73
  }
75
74
  ```
76
75
 
77
- Quando você envia uma lista, ela **substitui** a anterior por inteiro (não faz merge). Pra preservar, leia primeiro com GET.
76
+ Strings simples (`"win_reasons": ["motivo"]`) causam erro de serialização.
78
77
 
79
78
  ## Restrições de formato em campos comuns
80
79
 
@@ -75,36 +75,30 @@ NÃO retry imediato — só piora.
75
75
  ### 500 Internal Server Error
76
76
  Erro do servidor LionChat.
77
77
 
78
- **Causa mais comum (90% dos casos):** body sem o wrapper do recurso.
79
-
80
- ```http
81
- PUT /api/v1/accounts/43/kanban_config
82
- {"win_reasons": [...]} // ❌ 500 silencioso
83
- ```
84
-
85
- Rails levanta `ActionController::ParameterMissing` que vira 500 sem mensagem útil. A request precisa ser:
86
-
87
- ```http
88
- PUT /api/v1/accounts/43/kanban_config
89
- {"kanban_config": {"win_reasons": [...]}} // ✅
90
- ```
91
-
92
- Veja `api-conventions.md → Wrapper de body` pra tabela completa de quais endpoints exigem wrapper.
93
-
94
- **Outras causas:**
95
- - Estrutura do array errada (ex: `win_reasons: ["str"]` em vez de `[{id, title}]`)
96
- - Validação de modelo falha (cheque se o controller tem `validate :*` que rejeita o input)
78
+ **Causas comuns:**
79
+ - Estrutura interna de lista errada (ex: `win_reasons: ["str"]` em vez de `[{id, title}]`)
80
+ - Tipos errados em campos (string onde espera int, hash onde espera array, etc)
81
+ - Validação de modelo lança exceção não tratada
97
82
  - Bug genuíno do servidor
98
83
 
99
84
  **Ação:**
100
- - Primeiro: revisar wrapper e estrutura do payload
101
- - Se OK: retry 1x após 2-5 segundos
85
+ - Revisar tipos e estrutura do payload contra `api-conventions.md` e `data-model.md`
86
+ - Retry 1x após 2-5 segundos
102
87
  - Se persiste: reportar pro usuário com timestamp/request ID
103
88
 
89
+ > Nota: o Rails do projeto roda com `wrap_parameters: [:json]` global, então body raiz é auto-wrapped no nome do recurso. Falta de wrapper **não** gera 500 silencioso na maioria dos endpoints — descarte essa hipótese e foque em tipos/estrutura. Detalhes em `api-conventions.md → Wrapper de body`.
90
+
104
91
  ### "param is missing or the value is empty: <recurso>"
105
- Status: 400 (às vezes)
92
+ Status: 400 (raro)
93
+
94
+ Aparece em endpoints que rodam `params.require(:algo)` com nome não-padrão (fora da convenção REST que o auto-wrap deduz). Solução: enviar wrapper explícito `{"<recurso>": {...}}`.
95
+
96
+ ### Contato criado com `name`/`phone` vazios após POST /contacts
97
+ Não é 5xx — request volta 200, mas o contato fica sem os dados que você mandou.
98
+
99
+ **Causa:** body foi enviado com wrapper (`{"contact": {"name": "..."}}`). O `ContactsController` usa `params.permit(:name, ...)` direto na raiz, sem `require(:contact)`. Como o auto-wrap não duplica quando o wrapper já está presente, os campos ficam só dentro de `params[:contact]` e o `permit` na raiz não pega.
106
100
 
107
- Exatamente o erro do wrapper faltando. Mesma solução: wrapar o body em `{<recurso>: {...}}`.
101
+ **Solução:** mandar body raiz: `{"name": "Fulano", "phone_number": "+5511..."}`.
108
102
 
109
103
  ### 502 Bad Gateway / 503 Service Unavailable
110
104
  Indisponibilidade temporária (deploy, manutenção, sobrecarga).
package/dist/resources.js CHANGED
@@ -67,9 +67,12 @@ export const RESOURCES = [
67
67
  },
68
68
  ];
69
69
  function findDocsPath(filename) {
70
+ // __dirname e dist/ quando publicado (dist/docs/resources/) ou dist/ em dev.
71
+ // Tenta multiplos layouts pra cobrir build local, npm/npx e source.
70
72
  const candidates = [
71
- join(__dirname, '../docs/resources', filename),
72
- join(__dirname, '../../src/docs/resources', filename),
73
+ join(__dirname, 'docs/resources', filename), // dist/docs/resources (publicado)
74
+ join(__dirname, '../docs/resources', filename), // legacy / monorepo
75
+ join(__dirname, '../../src/docs/resources', filename), // source dir em dev
73
76
  ];
74
77
  for (const p of candidates) {
75
78
  if (existsSync(p))
@@ -1 +1 @@
1
- {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}