Skip to main content
Glama
LuanV1udes

mcp-pdv-demo

by LuanV1udes
README.md
# mcp-pdv-demo

Servidor **MCP (Model Context Protocol)** que conecta um assistente de IA ao banco de um PDV — ponto de venda de varejo e food service.

Em vez de expor um `executar_sql` genérico, ele expõe **perguntas de negócio**: qual foi o faturamento, o que dá mais margem, o que está encalhado no estoque, se o caixa fechou certo. O modelo escolhe a ferramenta; o SQL fica do lado de cá, escrito e testado.

> Banco 100% sintético, gerado por seed determinístico. Nenhum dado real de cliente.

```
Claude / Cursor / qualquer host MCP
        │  stdio
        ▼
   mcp-pdv-demo  ──►  Prisma  ──►  SQLite
   (6 tools de leitura + 1 de escrita, sob flag)
```

## Rodando

```bash
npm install
npm run setup     # cria o banco e gera ~96 produtos, 90 dias, ~3.700 vendas
npm run dev       # sobe o servidor MCP no stdio
```

### Conectando no Claude Code

```bash
claude mcp add pdv-demo -- npx tsx /caminho/para/mcp-pdv-demo/src/server.ts
```

### Conectando no Claude Desktop

Em `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "pdv-demo": {
      "command": "npx",
      "args": ["tsx", "/caminho/para/mcp-pdv-demo/src/server.ts"],
      "env": { "DATABASE_URL": "file:./pdv.db" }
    }
  }
}
```

## As ferramentas

| Tool | Responde |
|---|---|
| `consultar_produto` | "quanto custa X", "tem X em estoque", "qual o código de X" |
| `resumo_de_vendas` | "quanto vendi em maio", "qual meu ticket médio", "quanto entrou no pix" |
| `produtos_mais_vendidos` | "o que mais vendeu", "top 10 do mês", "meus carros-chefe" |
| `margem_por_categoria` | "o que dá mais lucro" — que não é a mesma pergunta que "o que mais vende" |
| `produtos_encalhados` | "o que está parado", "quanto de capital tem preso no estoque" |
| `fechamento_de_caixa` | "fecha o caixa de ontem", "teve diferença", "sobrou ou faltou dinheiro" |
| `ajustar_estoque` | escrita — só existe se `PDV_MCP_READONLY=false` |

### Saída real

`resumo_de_vendas`:

```
**Vendas de 2026-08-01 a 2026-08-31**

- Faturamento: **R$ 136.517,91**
- Vendas concluidas: 1345
- Ticket medio: R$ 101,50
- Descontos concedidos: R$ 1.484,56
- Vendas canceladas: 37

| Forma    | Vendas | Valor        | % do total |
| -------- | ------ | ------------ | ---------- |
| PIX      | 459    | R$ 49.385,27 | 36.2%      |
| DEBITO   | 360    | R$ 36.350,09 | 26.6%      |
| CREDITO  | 336    | R$ 32.355,06 | 23.7%      |
| DINHEIRO | 190    | R$ 18.427,49 | 13.5%      |
```

`produtos_encalhados`:

```
5 produto(s) sem vender ha 30+ dias. Capital parado: **R$ 8.690,14**.

| Produto           | Categoria | Estoque | Custo un. | Capital parado |
| ----------------- | --------- | ------- | --------- | -------------- |
| Polenta Frita ... | Porcoes   | 100     | R$ 28,21  | R$ 2.821,00    |
| Misto Quente ...  | Lanches   | 92      | R$ 30,21  | R$ 2.779,32    |
| Creme Dental      | Higiene   | 96      | R$ 18,31  | R$ 1.757,76    |
```

## Decisões de projeto

**Dinheiro é `Int` em centavos, nunca `Float`.** `0.1 + 0.2 !== 0.3` — num PDV isso vira quebra de caixa. A conversão para reais acontece só na formatação da saída.

**Read-only por padrão.** As tools de escrita não são apenas bloqueadas: elas não são *registradas*. Se `PDV_MCP_READONLY` não for explicitamente `"false"`, `ajustar_estoque` não aparece no `listTools` — o modelo não pode chamar o que não sabe que existe. A decisão fica no ambiente, fora do alcance do prompt.

**Preço e custo são congelados no item da venda.** `ItemVenda` guarda `precoUnitario` e `custoUnitario` do momento da venda. Sem isso, mudar o preço de um produto reescreveria o histórico de margem — um relatório de março passaria a mentir porque o custo subiu em agosto.

**A saída é markdown, não JSON.** O consumidor é um modelo de linguagem. Tabela formatada com R$ e percentual gasta menos contexto e produz menos erro de leitura do que um array de objetos com valores em centavos.

**Nada de `executar_sql`.** Uma tool que aceita SQL arbitrário transfere para o modelo a responsabilidade de entender o schema, e transfere para o usuário o risco de um `DELETE`. Cada tool aqui é uma pergunta fechada com uma query revisada atrás.

**Log vai para stderr.** No transporte stdio, o stdout *é* o canal do protocolo — um `console.log` perdido corrompe a sessão inteira.

## Testes

```bash
npm test
```

18 testes que sobem um cliente MCP real ligado ao servidor por transporte em memória — exercitam o protocolo inteiro, não as funções por dentro. Cobrem o contrato (toda tool tem descrição; a de escrita não aparece em modo leitura), a corretude dos números contra o banco, parsing de data em dois formatos, e os casos vazios.

## Stack

TypeScript · [MCP SDK](https://github.com/modelcontextprotocol/typescript-sdk) · Prisma · SQLite · Zod · Vitest

## Licença

MIT