Skip to main content
Glama
cesar-carlos

Se7e MCP Server

by cesar-carlos
README.md
# Se7e MCP Server

Servidor MCP remoto (Streamable HTTP) que conecta um Client já existente no `plug-server` ao ERP. O MCP é **cofre + base de conhecimento**: guarda e-mail/senha (só autenticação — **não** particiona o catálogo), `agentId` e `client_token`, emite **um token MCP opaco por acesso**, e dá à IA o pacote da skill publicada **daquele acesso**. **1 `client_token` = 1 persona = 1 catálogo isolado = 1 Bearer.** Mesmo e-mail/`agentId` + outro token (`adicionar_acesso` / `registrar_acesso`) começa vazio e ganha outro Bearer. Tools omitem `acessoId`. Resource `skill://{acessoId}/{slug}`. Cache `mcp:query:acesso:{acessoId}:`. Hub SQL continua `agentId` + `client_token` daquele acesso. A **base comum** de todo consumidor: SQL no plug_server, dialeto do acesso, resources (`guia://`, `skill://`, `persona://`) e estrutura pelas skills publicadas (consultas dinâmicas no pacote, fail-closed). Sem embeddings. Persona no acesso oriente tom/uso e **não** recorta skills **neste acesso** (outro token = outro catálogo) nem licencia SQL. O domínio (atendimento, pagamentos, KPI/gestão, etc.) é o que o usuário treinou e publicou neste acesso, mais o chapéu da persona.

Não há login próprio, Authorization Server, catálogo pronto com seed, nem Client de serviço no `.env`.

## Requisitos

- Node.js 24.19.0+ (LTS Krypton; `.nvmrc`)
- PostgreSQL (produção). Testes unitários usam repositórios in-memory. `npm run db:migrate` exige privilégio `CREATE EXTENSION` para `unaccent`, `btree_gin` e `pg_trgm` (FTS).
- Redis opcional (rate limit + cache de policy)

## Setup

### Produção neste servidor (PM2)

Postgres e Redis ficam no Docker. O processo Node é gerenciado pelo PM2 (mesmo daemon de `plug_server` / Chatwoot), em `fork` com 1 instância — sessões MCP são in-memory e não suportam cluster.

```bash
nvm use
npm install
npm run build
docker compose up -d postgres redis
pm2 start ecosystem.config.cjs
pm2 save
```

O Nginx em `mcp.se7esistemassinop.com.br` faz proxy para `127.0.0.1:3333`. Para o container Node em vez do PM2: `docker compose --profile container up --build -d mcp`.

### Local (Node + Postgres no Docker)

```bash
cp .env.example .env
nvm use
docker compose up -d postgres redis
npm install
npm run db:migrate
npm run dev
```

O Compose publica o Postgres na porta `5433` do host (para não colidir com um Postgres local na `5432`). Ajuste `DATABASE_URL` no `.env` para essa porta.

Não há script de seed. O grafo nasce vazio; o treino com SQL modelo deve fechar numa skill publicada — é ela que a IA usa na consulta.

- Health: `GET http://127.0.0.1:3333/health` (`version`, `sha` via `GIT_SHA`/`SOURCE_COMMIT`/`GITHUB_SHA`, `buildTime`, `uptimeSec`). Após deploy, reconecte o cliente MCP para atualizar `tools/list`.
- Matriz de erros: `GET http://127.0.0.1:3333/docs/mcp/error-mapping.md` (mesmo path de `error.documentationUrl`).
- Ready: `GET http://127.0.0.1:3333/ready` (`database: ok|skipped|error`; 503 se o banco falhar)
- MCP: `POST http://127.0.0.1:3333/mcp`
- Token MCP (one-shot): `GET http://127.0.0.1:3333/setup/{code}`

## Bootstrap

Consulta ao ERP: `consultar_dados` com skill publicada. Sem `sql`, executa a consulta exemplo; com `sql` ou `consultaSemantica`, o SELECT precisa ficar no escopo. Stub `kind: anexo` em `consultar_dados`: use `exportar_anexo`. `buscar_contexto` não devolve SQL — use `obter_skill`. Skill em treino que cobre a pergunta: `blockingReason SKILL_NOT_PUBLISHED`. Sem skill capaz: `SKILL_GAP` (a busca por termos não prova ausência — `listar_skills`). Token MCP pode expirar (`MCP_TOKEN_TTL_DAYS`). `MCP_ALLOWED_ORIGINS` não vazio recusa Origin estranho com 403. Rate limit por tool além do HTTP em `/mcp`. Flags novas (default ligado): `MCP_INSPECTION_ENABLED`, `MCP_DISCOVERY_QUERY_ENABLED`, `MCP_SEMANTIC_QUERY_ENABLED`, `MCP_SCHEMA_DRIFT_ENABLED`. `MCP_SKILL_TOOLS_ENABLED=true` liga tools `skill_*` (default desligado).

1. Cliente MCP chama `initialize` / `tools/list` **sem** Bearer. Só `registrar_acesso` está disponível.
2. `registrar_acesso` recebe e-mail/senha do Client, `agentId`, dialeto e `client_token`. **Não devolve o token MCP.**
3. A tool devolve `setupCode` + `setupUrl`. O usuário abre a URL, copia o token e cola em `Authorization: Bearer`.
4. Demais tools exigem Bearer. Novos acessos: `adicionar_acesso` (sem senha de novo; emite outro Bearer via `setupUrl` e **não** troca esta sessão).

## Scripts

| Script                       | Função                                                     |
| ---------------------------- | ---------------------------------------------------------- |
| `npm run dev`                | `tsx watch`                                                |
| `npm test`                   | Vitest in-memory                                           |
| `npm run test:live`          | plug-server real (`E2E_*`)                                 |
| `npm run lint` / `format`    | ESLint + Prettier                                          |
| `npm run release:check`      | Gate local: lint, formatação, tipos, testes e build        |
| `npm run db:migrate`         | Aplica `drizzle/*.sql`                                     |
| `npm run test:migrations`    | Certifica banco limpo e upgrade `0023` em DB efêmero de CI |
| `npm run worker:operacoes`   | Processa SLO, revisões e outbox de webhook (requer banco)  |
| `npm run db:backfill-escopo` | Preenche `skill.escopo` vazio a partir do `sql_modelo`     |

Docker: `Dockerfile` multi-stage (Alpine 3.24 + Node 24.19.0 musl, sem npm no runtime) + `docker-compose.yml` (Postgres, Redis, MCP opcional). CI: `.github/workflows/ci.yml` lê `.nvmrc`.

### Contratos e consulta inteligente

`consultaSemantica` v2 separa agregação (múltiplas métricas) de listagem (dimensões) e mantém v1 compatível. `validar_consulta` aplica o mesmo preflight de `consultar_dados` e só executa envelope vazio. `publicar_skill` funciona em preview/diff + `confirmacaoHash` antes da confirmação efetiva. Anotações podem ter data/cadência de revisão; a fila de `listar_anotacoes` apenas prioriza manutenção do conhecimento e nunca licencia SQL. `listar_metricas_agente.painel` resume tendência, erro, cache e truncamento sem conteúdo sensível. Timings do hub são solicitados por amostragem com `PLUG_SERVER_TIMINGS_SAMPLE_PERCENT` (0..100, padrão 10). O contrato REST versionado é gerado no repositório irmão pelo script `contract:generate`, protegido por baseline que rastreia todos os campos públicos e verificado na CI.

Operação proativa é separada do servidor HTTP: `npm run worker:operacoes` calcula SLO e revisões por `acessoId`, grava somente IDs/contagens/taxas e entrega alertas a uma caixa MCP. Webhook é opcional por acesso, exige confirmação, HTTPS público sem query/credenciais e segredo cifrado; eventos são assinados e entregues pelo menos uma vez. Nenhuma dessas funções é conhecimento, RAG ou licença de SQL.

## Testes live contra o plug-server real

`npm run test:live` roda `tests/live/`, que autentica com uma conta de teste dedicada no plug-server (nunca uma conta de produção) e chama a API real. Requer as variáveis `E2E_AGENT_ID`, `E2E_CLIENT_TOKEN`, `E2E_CLIENT_EMAIL`, `E2E_CLIENT_PASSWORD` e `E2E_DIALETO` no `.env` (ver `.env.example`). Sem essas variáveis, a suíte se pula sozinha — nunca falha por falta de credenciais, e nunca roda como parte de `npm test`.

## Conectar um cliente

Ver [docs/clients/connecting-clients.md](docs/clients/connecting-clients.md).

## Documentação

1. Norte — [docs/product/objective.md](docs/product/objective.md)
2. Tools e erros — [docs/mcp/tools.md](docs/mcp/tools.md), [docs/mcp/error-mapping.md](docs/mcp/error-mapping.md)
3. Hub REST — [docs/plug-server/communication.md](docs/plug-server/communication.md) (adapter: [rest-integration.md](docs/plug-server/rest-integration.md))
4. Modelo e FTS — [docs/data/data-model.md](docs/data/data-model.md)
5. Índice — [`docs/README.md`](docs/README.md). Changelog — [`CHANGELOG.md`](CHANGELOG.md). Histórico das três camadas — [docs/proposta-arquitetura-mcp-se7e.md](docs/proposta-arquitetura-mcp-se7e.md)