DeuxOrders MCP
README.md
# DeuxOrders MCP
Camada MCP do DeuxOrders. Expõe as operações do sistema como capabilities de domínio para agentes de IA.
```text
Claude Desktop ──(stdio)──┐
├──> DeuxOrders MCP ──> DeuxOrders Backend ──> Services / Database
Hermes ──(HTTP + bearer)──┘
```
Todo cliente consome exatamente as mesmas capabilities — não existe implementação por canal.
O MCP **não** reimplementa regra de negócio. Toda validação de domínio, persistência e geração de documento continua no backend.
Construído com [Invokta](https://github.com/vinilana/invokta): a mesma capability é publicada por MCP HTTP, MCP stdio, CLI e chamada direta, sem código duplicado por canal.
## Instalação
```sh
npm install
cp .env.example .env # preencha os valores
npm run check # typecheck + testes + build + conformance MCP
```
Requer Node.js 22.20 ou superior.
## Configuração
| Variável | Obrigatória | Descrição |
|---|---|---|
| `BACKEND_URL` | sim | `https://deux-erp.deuxcerie.com.br/api/v1` (o sufixo `/api/v1` é opcional) |
| `BACKEND_SERVICE_EMAIL` | sim | usuário de serviço do MCP no backend |
| `BACKEND_SERVICE_PASSWORD` | sim | senha desse usuário |
| `MCP_AUTH_TOKEN` | sim | Bearer exigido no canal HTTP (mín. 32 caracteres) |
| `BACKEND_TIMEOUT_MS` | não | padrão `15000` |
| `BACKEND_RETRIES` | não | padrão `2`, só para leituras |
| `BACKEND_MAX_EXPORT_BYTES` | não | padrão `4194304`, teto do arquivo de exportação |
| `INVOKTA_HTTP_HOST` | não | padrão `127.0.0.1` |
| `INVOKTA_HTTP_PORT` | não | padrão `3000` |
| `INVOKTA_HTTP_ALLOWED_HOSTS` | condicional | obrigatória quando o bind não é loopback |
| `INVOKTA_HTTP_ALLOWED_ORIGINS` | não | allowlist de origem para clientes de browser |
Gere o token do MCP com `openssl rand -base64 48`. `.env` não é versionado.
## Autenticação
São duas, distintas e independentes:
**Cliente → MCP.** Todo `POST /mcp` exige `Authorization: Bearer <MCP_AUTH_TOKEN>`. A comparação é em tempo constante; sem token válido, 401. É o canal do Hermes.
O Claude Desktop conecta por **stdio**, onde não há bearer: quem inicia o processo já provou ser o dono da máquina.
ChatGPT web não é suportado — conectores remotos do ChatGPT exigem OAuth e o Authorization Server não foi construído. É aditivo depois, sem mexer em capability nenhuma.
**MCP → Backend.** O connector faz login com o usuário de serviço, guarda o JWT em memória e o renova sozinho. A credencial do backend nunca aparece em prompt, description de tool, resposta ou log — há um teste que verifica isso.
Full access no MVP: cliente autenticado alcança todas as capabilities. Não há RBAC por capability.
## Executando
```sh
npm run mcp:stdio # Claude Desktop (ou npm run mcp:install para registrar)
npm run mcp:http # Hermes
npm run devtools # UI de desenvolvimento: invoca capabilities por qualquer canal
npm run smoke # varredura de leitura contra o backend real
```
O passo a passo de lançamento está em [`DEPLOY.md`](./DEPLOY.md).
### Testando sem agente nenhum
O CLI executa pelo mesmo pipeline (`engine.invoke`) que o MCP — mesma validação, mesma autorização, mesmo connector:
```sh
npm run cli -- list
npm run cli -- describe orders.search
npm run cli -- run clients.search --input '{"search":"maria"}'
```
## Capabilities
### Datas, receita e Hermes
Pedidos, métricas do dashboard, rankings e exportações recebem `from`/`to` como
dias inclusivos (`AAAA-MM-DD`, fuso `America/Sao_Paulo`) e `dateField`:
`DeliveryDate` é o padrão; `CreatedAt` seleciona a criação quando solicitada.
Os filtros opcionais `clientId`, `status` e `isPaid` podem ser combinados.
`orders.search` também aceita `productId` para pedidos que contêm um item ativo
do produto. `products.stats` aceita `month` e `dateField`.
Para períodos relativos, envie `period` sem `from/to`: `today`, `yesterday`,
`this_week`, `last_week`, `this_month`, `last_month`, `this_year`, `last_year` ou
`last_n_days` com `days`. O MCP calcula as datas pelo relógio de São Paulo em
cada chamada e devolve `resolvedPeriod` com o intervalo aplicado. Semana atual
vai de segunda até hoje; mês/ano atual começa no primeiro dia e termina hoje.
Semana/mês/ano anterior inclui o período anterior inteiro.
Receita segue `totalRevenue`, após descontos e sem pedidos cancelados; omitir
`isPaid` inclui pedidos pagos e não pagos. Caixa mantém a data de competência
dos lançamentos. As regras de escrita e os cálculos continuam no backend.
O prompt do bot está em [HERMES_SOUL.md](./HERMES_SOUL.md). No Hermes, use o
provedor nativo `gemini`, modelo `gemini-3.5-flash-lite`, raciocínio `low` e
endpoint `https://generativelanguage.googleapis.com/v1beta`. A chave pertence ao
`.env` do Hermes (`GOOGLE_API_KEY`), não ao código ou ao prompt.
Publique primeiro o backend com suporte a `from/to/dateField`, depois reconstrua
e reinicie MCP e Hermes. Os antigos campos de tools `createdAtFrom/To` e
`deliveryFrom/To` são rejeitados para evitar consultas sem o filtro pretendido.
O backend preserva os parâmetros `deliveryDateFrom/To` usados pelo SaaS.
53 capabilities em 7 domínios. O mapeamento completo com os endpoints de origem está em [`MCP_CAPABILITY_MAP.md`](./MCP_CAPABILITY_MAP.md).
| Domínio | Capabilities |
|---|---|
| Clientes | `search`, `list-options`, `get`, `stats`, `list-orders`, `create`, `update`, `set-status`, `delete` |
| CRM | `list` |
| Pedidos | `search`, `get`, `create`, `update`, `complete`, `cancel`, `cancel-item`, `adjust-item-quantity`, `mark-paid`, `reverse-payment`, `remove-reference` |
| Produtos | `search`, `list-options`, `get`, `stats`, `create`, `update`, `set-status`, `delete`, `get-recipe`, `set-recipe`, `list-recipe-options`, `set-recipe-option`, `list-order-options` |
| Estoque | `search`, `list-options`, `get`, `create`, `update`, `restock`, `set-status` |
| Caixa | `search`, `get`, `summary`, `create`, `update`, `delete`, `audit` |
| Dashboard | `summary`, `revenue-over-time`, `top-products`, `top-clients`, `export-orders` |
Um cliente MCP vê cada uma como uma tool (`orders.create` → `orders_create`). Não existe tool genérica de HTTP ou de banco: o agente só alcança o backend pelas operações declaradas aqui.
Tarefas compostas são composição de primitivas. "Cria um pedido igual ao último da cliente X" é `clients.search` → `clients.list-orders` → `orders.get` → `orders.create`.
## Arquitetura
```text
Capability (src/capabilities/) contrato de domínio, schemas de entrada e saída
↓
BackendGateway (src/application/) port
↓
connector (src/infrastructure/) BACKEND_URL, login, headers, timeout, retry, erros
↓
DeuxOrders Backend
```
O connector é o único lugar que conhece o `BACKEND_URL` e a credencial. Retries acontecem só em `GET`, e só para falha de rede, 429 ou 5xx — escritas nunca são repetidas.
Erros do backend chegam ao agente traduzidos: uma violação de regra de negócio vira `INPUT_INVALID` com a mensagem original, que o agente consegue corrigir; um 5xx vira uma mensagem genérica que não vaza nada.
## Auditoria
Toda invocação emite em stderr, como JSON de uma linha: timestamp, `requestId`, capability, canal (`mcp-http`, `mcp-stdio`, `cli`, `direct`), identidade do chamador, duração e código de erro. Segredos nunca são registrados.
## Testes
```sh
npm test
```
Cobrem operação válida, entrada inválida, recurso inexistente, erro do backend, backend indisponível, resposta inesperada, retry, exportação grande demais, não vazamento de credencial e a ausência de tool genérica. O MCP é testável sem Hermes nem Claude.
`npm test` roda contra dublês. `npm run smoke` roda contra o backend de produção e é o que valida os contratos de saída contra respostas reais — rode antes de cada lançamento.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues