Skip to main content
Glama
README.md
# Woovi Pix MCP

Servidor MCP local em Go para consultar e, opcionalmente, criar cobranças Pix
Woovi. A ferramenta `pix_create_charge` fica desativada por padrão. Não há Pix
Out, transferências, reembolsos ou cancelamentos financeiros.

Stack: Go 1.27.1, MCP Go SDK v1.8.0, SQLite embutido e Goose v3 para migrações
SQL. Quando escrita está habilitada, o servidor cria o banco privado e aplica
as migrações ao iniciar. Não requer PostgreSQL nem Docker.

CLI local: setup, perfis, stdio, doctor, instalação segura em clientes JSON e
Litestream opcional. Transporte remoto, distribuição pública e operações Pix
Out estão fora do escopo. Validação externa no sandbox Woovi ainda não realizada.

## Licença

Copyright 2026 Lucas Eufrasio e contribuidores.
Licenciado sob a [Apache License 2.0](LICENSE).

Projeto independente, não oficial da Woovi. A licença cobre o código deste
repositório, não concede direitos sobre marcas de terceiros e não substitui
os termos de uso da API Woovi. Dependências mantêm suas próprias licenças.

## Configurar pela CLI

```sh
go build -o ./bin/woovi-pix-mcp ./cmd/woovi-pix-mcp
./bin/woovi-pix-mcp setup --profile sandbox
./bin/woovi-pix-mcp doctor --profile sandbox
```

O assistente pede ambiente, identificador estável da conta e opt-in de criação.
O AppID é digitado sem eco e salvo no cofre de credenciais do sistema. Se o
cofre não estiver disponível, o setup falha: não há fallback silencioso. Use
`setup --profile sandbox --secret-file` somente se aceitar guardar o segredo
sem criptografia em arquivo restrito ao usuário. Permissões POSIX são validadas;
ACLs Windows ainda precisam validação antes de recomendar esse fallback lá.
Perfis existentes não são sobrescritos: use outro nome para nova configuração.

Configure seu cliente com o caminho absoluto do binário e argumentos
`["stdio", "--profile", "sandbox"]`. Não inclua AppID na configuração do cliente.
`doctor` é diagnóstico local; não chama Woovi nem cria cobrança.

`profiles` lista os nomes configurados sem carregar ou exibir credenciais.
O identificador de conta precisa ser estável e corresponder à conta do AppID:
o servidor não consegue verificar isso offline. Dois perfis da mesma conta e
ambiente compartilham o histórico de idempotência, inclusive após trocar AppID.

## Instalar no cliente MCP

Selecione explicitamente cliente e arquivo. O padrão só valida a alteração;
`--apply` salva com backup privado, preservando entradas alheias. Exemplo:

```sh
./bin/woovi-pix-mcp install-mcp --profile sandbox --client opencode --config ~/.config/opencode/opencode.json
./bin/woovi-pix-mcp install-mcp --profile sandbox --client opencode --config ~/.config/opencode/opencode.json --apply
```

Clientes aceitos: `opencode` **V2** (`mcp.servers`), `claude` e `cursor`
(`mcpServers`). O caminho é sempre explícito: não há descoberta ou edição
automática de todos os clientes. JSONC, arquivos públicos ou symlinks são
recusados; revise permissões antes de aplicar (diretório 0700, arquivo 0600).
Uma entrada diferente com o mesmo nome não é sobrescrita. Uma entrada idêntica
não gera nova alteração/backup. Codex/TOML deve ser configurado manualmente.

## Comandos de desenvolvimento

`make help` lista os comandos disponíveis. Exemplos:

Para `make fmt`/`make check`, além de Go, instale golangci-lint 2.14.0, Python
3.12+ e uv. Ruff é resolvido na versão fixa 0.16.10. O lint também verifica
espaçamento entre blocos Go; não basta passar no `gofmt`.

```sh
make test
make check
make migrate-create name=add_charge_metadata
```

Os testes usam arquivos SQLite reais temporários, inclusive processos separados.
As novas migrations são timestamped e criadas pela
Goose CLI. Não há target `migrate-down`/`reset`: a migration Down remove as
tabelas de operação/auditoria e pode apagar evidência de idempotência.

## Processo de release

**Prepare Release** abre um PR de changelog; após revisão/CI e merge, uma tag
`vX.Y.Z` dispara **Release**, que repete os checks e cria um **draft** com binários
Linux/macOS/Windows (amd64/arm64) e checksums SHA-256. Publicar o draft continua
sendo uma decisão manual. Nenhuma release é disparada ao adicionar os workflows.

Veja [docs/releases.md](docs/releases.md) para configuração, preparação,
tagueamento, verificação de artefatos e limitações de plataforma. Para validar
o empacotamento local sem publicar: `make release-snapshot` (GoReleaser 2.18.2).

## Executar

```sh
./bin/woovi-pix-mcp stdio --profile sandbox
```

Para compilar um binário local sem instalá-lo globalmente:

```sh
go build -o ./bin/woovi-pix-mcp ./cmd/woovi-pix-mcp
```

Execute o binário com `stdio --profile sandbox`. O diretório
`bin/` é apenas uma saída local e não deve ser versionado.

O AppID fica no processo servidor e nunca é um argumento ou resultado MCP. Logs
operacionais vão para stderr; stdout fica reservado ao protocolo stdio. O host
Woovi de produção é `https://api.woovi.com`, mas o servidor aceita apenas HTTPS
para hosts remotos.

Criação é uma capacidade explicitamente opt-in. Requer identificador fixo da
conta autorizada e limite máximo de R$ 100.000 por cobrança, sem banco externo:

No `setup`, responda `yes` somente se quiser habilitar criação. Para automação,
o modo legado sem subcomando ainda aceita `WOOVI_API_BASE_URL`, `WOOVI_APP_ID`,
`WOOVI_ACCOUNT_ID` e `WOOVI_ENABLE_CHARGE_CREATION`; injete o segredo pelo cofre
da automação, nunca em argv, histórico do shell ou configuração dos clientes.

Cada referência é usada como `correlationID` Woovi e chave idempotente local.
Repetir o mesmo payload retorna resultado já persistido; outra carga para a
mesma referência conflita. Uma chamada com resultado incerto permanece `UNKNOWN`
e não é reenviada automaticamente: a ferramenta primeiro consulta a Woovi pela
referência e só fecha a operação quando referência e valor batem. Se não for
encontrada, permanece `UNKNOWN`. O valor explícito está limitado a R$ 100.000 e
expiração de 300 a 2.592.000 segundos. Tentativas e resultados são auditados no
SQLite sem persistir AppID ou conteúdo do QR no log de auditoria. Esta fatia
não implementa approval workflow.

O arquivo SQLite é criado sob o diretório de configuração do usuário, em
`woovi-pix-mcp/state/<escopo>/operations.db`; o escopo separa URL/ambiente e conta.
`WOOVI_DATABASE_PATH` permite indicar outro arquivo privado. Nunca apague o
arquivo para resolver um erro: ele guarda a evidência das tentativas anteriores.
SQLite usa WAL, synchronous FULL e espera limitada para escritores concorrentes.
Não há DATABASE_URL nem importação PostgreSQL; esse MCP não teve usuários legados.

## Teste rápido local — sem chamar a Woovi

Execute os comandos na raiz deste repositório, com Go 1.27.1 instalado. Este
roteiro usa somente dados fictícios e não precisa de conta Woovi ou Docker.

### 1. Terminal 1: subir o simulador

```sh
go run ./cmd/woovi-simulator
```

Deixe esse terminal aberto. O simulador escuta em `127.0.0.1:8081`.

### 2. Terminal 2: compilar e configurar

```sh
go build -o ./bin/woovi-pix-mcp ./cmd/woovi-pix-mcp
./bin/woovi-pix-mcp setup --profile teste
```

Responda ao assistente:

- Ambiente: `simulator`
- Conta: `conta-teste`
- Habilitar criação: `no`
- AppID: `simulator` (a entrada fica oculta)

Se o cofre do sistema estiver indisponível, repita o setup com o fallback
explícito abaixo. Ele guarda o AppID sem criptografia em arquivo privado 0600:

```sh
./bin/woovi-pix-mcp setup --profile teste --secret-file
```

Se o perfil já existir, reutilize-o ou escolha outro nome; não há sobrescrita.

### 3. Conferir a configuração

```sh
./bin/woovi-pix-mcp doctor --profile teste
```

**Esperado:** ambiente `simulator`, credencial disponível sem exibir AppID e
criação desabilitada. O banco pode aparecer como não inicializado: isso é normal
em modo somente leitura. `doctor` não consulta nem mesmo o simulador.

### 4. Conectar ao OpenCode V2

Primeiro valide a alteração (preview), depois aplique:

```sh
./bin/woovi-pix-mcp install-mcp --profile teste \
  --client opencode --config ~/.config/opencode/opencode.json

./bin/woovi-pix-mcp install-mcp --profile teste \
  --client opencode --config ~/.config/opencode/opencode.json --apply
```

Use o caminho do arquivo realmente usado pelo seu cliente. Precisa ser JSON,
não JSONC; diretório 0700 e arquivo 0600, sem symlinks. Se o instalador recusar,
revise o motivo antes de alterar permissões ou use configuração manual. Para
Claude/Cursor, selecione `--client claude`/`--client cursor` e o respectivo arquivo.
`examples/mcp-client.json` ilustra a configuração manual: binário e perfil,
**nunca AppID**. O binário precisa permanecer no caminho usado na instalação.

### 5. Consultar a cobrança fictícia

Reabra o OpenCode para carregar o servidor `woovi-pix-teste` e peça:

> Use o servidor woovi-pix-teste para consultar a cobrança `demo-charge` com
> `pix_get_charge`. Não use outros servidores nem crie cobranças.

**Esperado:** ID `demo-charge`, referência `demo-order-001`, estado `ACTIVE` e
valor `1250` centavos (**R$ 12,50**), com código Pix fictício. Nenhuma chamada
Woovi ou operação financeira real é feita. Termine o simulador com Ctrl+C.

**O simulador manual só suporta consulta.** Criação/idempotência são exercitadas
pelo simulador específico dos testes automatizados, não por esse comando.
Para rodar a suíte e as verificações do projeto:

```sh
make check
```

## Teste com o sandbox da própria Woovi

Este roteiro usa a API de teste externa da Woovi, **não o simulador local**.
Não é necessário executar `woovi-simulator`. Execute apenas com autorização
para usar essa conta; os passos abaixo não foram executados pela implementação
ou pela CI. Não use credenciais de produção nem faça pagamentos reais.

### 1. Preparar a conta e uma cobrança de teste

- Acesse [app.woovi-sandbox.com](https://app.woovi-sandbox.com/). O sandbox tem
  cadastro separado: dados de produção não funcionam nele.
- No painel **do sandbox**, obtenha um AppID da sua conta. A documentação de
  autenticação indica `Admin Panel > Permissions > APIs` para gerar a chave,
  com permissão de administrador. Use os escopos necessários para consulta.
- Tenha uma cobrança criada no sandbox pelo painel e copie seu ID ou
  `correlationID`. `demo-charge` é exclusivo do simulador e não deve ser usado aqui.

Referências oficiais: [ambiente de teste](https://developers.woovi.com/docs/test-environment)
e [criação da chave API](https://developers.woovi.com/en/docs/apis/api-getting-started).

### 2. Compilar e configurar o perfil sandbox

```sh
go build -o ./bin/woovi-pix-mcp ./cmd/woovi-pix-mcp
./bin/woovi-pix-mcp setup --profile sandbox
```

Responda:

- Ambiente: `sandbox` (seleciona `https://api.woovi-sandbox.com`)
- Conta: um identificador estável dessa conta, por exemplo `minha-conta-sandbox`
- Habilitar criação: `no`, para começar somente com consulta
- AppID: **o AppID do sandbox**, no prompt oculto; nunca cole no chat ou no cliente

A conta é o escopo local de idempotência, não outro segredo; mantenha o mesmo
identificador ao criar perfis para essa conta. Se o cofre estiver indisponível e
você aceitar o arquivo privado sem criptografia, use explicitamente:

```sh
./bin/woovi-pix-mcp setup --profile sandbox --secret-file
```

### 3. Conferir e conectar ao cliente

```sh
./bin/woovi-pix-mcp doctor --profile sandbox

./bin/woovi-pix-mcp install-mcp --profile sandbox \
  --client opencode --config ~/.config/opencode/opencode.json

./bin/woovi-pix-mcp install-mcp --profile sandbox \
  --client opencode --config ~/.config/opencode/opencode.json --apply
```

Valem os requisitos de JSON e permissões do roteiro local. **Esperado no doctor:**
ambiente `sandbox`, credencial disponível e criação desabilitada. Isso valida
apenas a configuração local, **não autenticação ou conectividade com a Woovi**.

### 4. Consultar uma cobrança do sandbox

Reabra o cliente e substitua `<ID_OU_CORRELATION_ID>` pelo identificador copiado
do painel. Se houver outros perfis instalados, selecione `woovi-pix-sandbox`:

> Use somente o servidor woovi-pix-sandbox para chamar `pix_get_charge` com
> `id` igual a `<ID_OU_CORRELATION_ID>`. Não crie nem pague cobranças.

**Esperado:** a cobrança da sua conta sandbox, com ID/referência, estado e valor
em centavos coerentes com o painel, sem AppID ou dados do pagador. Estado e
valor dependem da cobrança escolhida; não há resultado fixo de R$ 12,50 aqui.
Essa consulta é a verificação efetiva de API, credencial e escopo da conta.

### 5. Opcional: testar criação e idempotência no sandbox

Somente se quiser testar escrita e tiver autorização, siga o roteiro detalhado
em [docs/sandbox.md](docs/sandbox.md#criação-e-idempotência-opcionais). Ele cria
outro perfil com opt-in explícito e explica como repetir o mesmo payload e
verificar conflito. Não pague o QR gerado, não use produção e não invente outra
referência para contornar um timeout ou resultado incerto.

## Contrato Woovi verificado

- Autenticação por `Authorization: <AppID>`; API retorna/aceita JSON e requer
  HTTPS. Limite documentado: 10 requisições por segundo.
- Consulta: `GET /api/v1/charge/{id}`; `id` aceita identificador da cobrança ou
  `correlationID`.
- O retorno MCP é minimizado a identificador, referência, estado, centavos BRL,
  expiração e código Pix; dados do pagador e payloads brutos são descartados.
- O cliente aplica limiter local conservador de 10 req/s (sem rajada) para
  respeitar o limite publicado pela Woovi.
- Referências: [Autenticação e limites](https://developers.woovi.com/en/docs/apis/api-getting-started),
  [API Redoc](https://developers.woovi.com/en/api-redoc),
  [Correlation ID/idempotência](https://developers.woovi.com/en/docs/concepts/correlation-id).

O simulador de teste também suporta POST e mantém cobranças idempotentes pela
referência em memória; nunca use credencial ou host de produção nos testes.

Todos os testes de persistência SQLite executam automaticamente em `make test`,
sem credenciais ou infraestrutura externa.

## Litestream opcional e recuperação

Instale separadamente [Litestream 0.5.12+ (série 0.5.x)](https://litestream.io/install/), validado
com 0.5.17. Não é iniciado automaticamente e o MCP funciona sem ele.

```sh
./bin/woovi-pix-mcp replica-setup --profile sandbox --replica-url file:///absolute/private/backup/sandbox
# Alternativa: S3 ou compatível; access key e secret key entram em prompts sem eco.
./bin/woovi-pix-mcp replica-setup --profile sandbox --replica-url s3://my-bucket/account-specific-prefix --region us-east-1
./bin/woovi-pix-mcp replica-run --profile sandbox
```

Use apenas um dos destinos; configuração existente não é sobrescrita. Para S3
compatível use `--endpoint https://...`. Credenciais usam cofre do sistema ou
`--secret-file` explicitamente; não coloque credenciais na URL. O processo filho
recebe somente as credenciais da réplica, não o AppID. Prefira discos privados e
criptografia/controle de acesso do bucket: a réplica contém dados de cobranças.

Inicie stdio uma vez para inicializar o banco antes da primeira réplica.
`replica-run` fica em foreground; Ctrl+C o encerra. Um lock impede duas réplicas
gerenciadas para o mesmo banco. `replica-status` verifica o estado local via
Litestream; **não atesta sincronização remota nem atraso zero**. Saída bruta do
processo externo é suprimida para evitar vazamento em mensagens de erro.

Para recuperar: pare todos os processos MCP e réplica; arquive manualmente o
banco e seus sidecars, sem excluí-los. O destino e sidecars precisam estar
ausentes; não existe opção force/overwrite nesta CLI.

```sh
./bin/woovi-pix-mcp replica-restore --profile sandbox --acknowledge-stale-state
./bin/woovi-pix-mcp doctor --profile sandbox
```

A restauração faz integrity check completo e cria `operations.db.recovered`,
que bloqueia inicialização com criação habilitada. **Não remova esse marcador
antes de conferir o histórico da Woovi e reconciliar operações possivelmente
ausentes na cópia**. Se não conseguir reconciliar, permaneça em leitura. Não há
importação automática de histórico PSP nem botão de desbloqueio automático.
Uma falha de restore pode deixar arquivos parciais: arquive-os e investigue,
não reinicie com estado vazio. O marcador também permanece em caso de falha.

Litestream replica de forma assíncrona: RPO depende do último envio confirmado;
RTO depende da recuperação manual. Não oferece HA automática ou multiwriter.
Uma cópia antiga pode esquecer uma criação já efetivada no provedor; restaurar
o banco não autoriza repetir pedidos financeiros com novas referências.

Integração real de réplica em arquivo e armazenamento S3 local (SeaweedFS):

```sh
TEST_LITESTREAM_BINARY=/absolute/path/litestream make test-litestream
```

Esse target precisa Docker somente para os testes opcionais de réplica; uso
normal continua sem Docker. CI executa ambos os backends e valida o checksum
do binário fixado. Não chama Woovi nem usa credenciais reais.

Roteiro e troubleshooting: [docs/sandbox.md](docs/sandbox.md).