Skip to main content
Glama
gabrielbssantos

Pluggy Finance MCP

README.md
# Pluggy Finance MCP

Servidor MCP pessoal em Python para múltiplas conexões Pluggy em uma única instância.
Implementa consultas, agregações financeiras e sincronização explícita sob demanda,
com isolamento por Item em cada chamada, paginação e proteção de credenciais.

Implementação independente voltada a consultas financeiras pessoais no Hermes; não é um
produto oficial da Pluggy. Existem também o [MCP da Pluggy](https://github.com/pluggyai/pluggy-mcp)
e o [mcp-pluggy comunitário](https://github.com/lefranchi/mcp-pluggy).

**O uso local não precisa de Docker.** O Hermes inicia diretamente o processo Python por
`stdio`. O container é opcional e serve para implantação remota em Cloud Run ou plataforma
equivalente.

## Execução local

Requisitos: Python 3.12+ e [uv](https://docs.astral.sh/uv/getting-started/installation/).

```bash
uv sync --frozen
cp .env.example .env
```

Preencha apenas `PLUGGY_CLIENT_ID` e `PLUGGY_CLIENT_SECRET` em `.env`.
A autorização inicial no MeuPluggy deve ter sido feita previamente. O servidor não cria Items.

```bash
uv run --env-file .env python -m pluggy_finance_mcp
```

O processo aguarda mensagens MCP em stdin; não abre interface gráfica ou porta de rede.
`.env` só é carregado quando solicitado explicitamente ao `uv`. A aplicação lê o ambiente.
Depois da instalação, `.venv/bin/python -m pluggy_finance_mcp` também funciona com as variáveis
já exportadas. Veja a [configuração do Hermes](docs/OPERACAO_LOCAL.md).

## Tools

Todas as 21 tools exigem `item_id` (UUID), além dos parâmetros abaixo.

| Conexão e sincronização | Parâmetros adicionais |
|---|---|
| `get_item` | nenhum; valida existência e consulta estado/freshness |
| `get_sync_status` | nenhum; consulta andamento, sem PATCH |
| `sync_item` | `wait=true`, `timeout_seconds?` (1–3600) |

| Consultas básicas | Parâmetros |
|---|---|
| `get_connection_status` | nenhum |
| `list_accounts` | `type?`, `subtype?` (filtro local) |
| `get_account` | `account_id` |
| `get_account_balance` | `account_id` |
| `list_account_statements` | `account_id` |
| `list_transactions` | `account_id`, `date_from?`, `date_to?`, `cursor?` |
| `get_transaction` | `transaction_id` |
| `list_credit_card_bills` | `account_id` |
| `get_credit_card_bill` | `bill_id` |
| `list_investments` | `type?`, `page=1`, `page_size=100` |
| `get_investment` | `investment_id` |
| `list_investment_transactions` | `investment_id`, `page=1`, `page_size=100` |

| Agregações | Parâmetros |
|---|---|
| `get_total_balance` | `account_ids?` |
| `get_monthly_expenses` | `month?` **ou** `date_from` e `date_to`; `account_ids?` |
| `get_expenses_by_category` | mesmos parâmetros de gastos mensais |
| `get_credit_card_summary` | `account_ids?` |
| `get_investment_portfolio` | `investment_ids?` |
| `get_net_worth` | `account_ids?`, `investment_ids?` |

Nenhuma tool aceita credenciais, URL ou método HTTP. Identity não está implementada.
Datas de intervalo usam `YYYY-MM-DD`; mês usa `YYYY-MM`. O padrão das agregações de gastos é
mês atual em `America/Sao_Paulo`, pelo lançamento de cada transação.

As respostas têm `ok`, `tool`, `data`, `pagination`, `meta`, `warnings` e `error`.
Totais calculados com Decimal são strings decimais; campos brutos mantêm seus nomes e valores
numéricos JSON. Verifique `meta.complete` e `warnings` antes de interpretar uma agregação.
As [regras de cálculo](docs/AGREGACOES.md) descrevem cobertura, ambiguidades e limites.

## Pluggy authentication

`PLUGGY_CLIENT_ID` + `PLUGGY_CLIENT_SECRET` → `POST /auth` → API Key temporária.
O gerenciador compartilha a chave entre todas as conexões, somente em memória, e a renova
15 minutos antes da expiração de duas horas. Um HTTP 401 permite uma renovação e repetição;
um segundo 401 encerra a chamada. Não configure `PLUGGY_API_KEY` nem persista chaves em disco.

## Item IDs

Cada conexão tem seu próprio `itemId`. Forneça-o ao Hermes no primeiro uso e peça que guarde
a associação, por exemplo `pluggy.itau_pessoal → <UUID>`. O Hermes pode validar com `get_item`.
Nas próximas sessões ele resolve o nome na memória e fornece o UUID em cada chamada.
Uma única instância e as mesmas credenciais atendem Itaú, Nubank, Inter e outras conexões.

O MCP não lê nem escreve `MEMORY.md`, não mantém registry, banco local ou aliases, e não
depende da memória do Hermes para funcionar. Não há Item em variável de ambiente.
Migração: as antigas tools agora também exigem `item_id`; o vínculo de contas, transações
e investimentos continua sendo validado contra o Item recebido naquela chamada.

## Manual synchronization

Consultas nunca chamam PATCH. “Quanto gastei hoje?” consulta os dados já disponíveis.
“Atualize meu Itaú e veja quanto gastei hoje” exige `sync_item` antes da consulta.

`sync_item(item_id, wait=true)` valida o Item, reutiliza as credenciais guardadas pela Pluggy
com `PATCH /items/{id}` e corpo `{}`, e acompanha o estado por polling. Um Item já atualizando
é acompanhado sem novo PATCH. `wait=false` retorna imediatamente após validação/envio.
`get_sync_status(item_id)` e `get_item(item_id)` permitem verificar depois, incluindo `lastUpdatedAt`.

Configurações opcionais: `PLUGGY_SYNC_POLL_INTERVAL_SECONDS=3` e
`PLUGGY_SYNC_TIMEOUT_SECONDS=120`. O prazo inclui validação, envio, retries e polling.
Timeout local com execução iniciada retorna `data.inProgress=true`; não significa falha do banco.
Falha de rede após envio pode deixar o resultado incerto; consulte o status antes de reenviar.

O envelope `ok=true` indica que a tool produziu um resultado, não que o banco sincronizou:
verifique `data.success`, `data.inProgress`, `data.requiresUserAction` e `data.error`.
Sucesso parcial é sinalizado por `partialSuccess=true`, sem afirmar atualização completa.
MFA, login inválido e renovação de parâmetros exigem intervenção via Pluggy Connect;
o MCP não pede senha ou token. `OUTDATED` pode ser atualizado mediante pedido explícito.

Restrições de plano/frequência e connector offline preservam códigos específicos e mensagens
seguras; não causam repetição de PATCH. HTTP 403 não é repetido. HTTP 429 respeita Retry-After
com até duas repetições; se a espera exceder o limite HTTP, devolve o erro e a espera indicada.
Mensagens livres da API não são expostas, pois podem conter credenciais. A frequência mínima
é retornada quando existe em campo numérico estruturado; a próxima data só é calculada com
timestamp e fuso conhecidos. Não se infere frequência de texto livre.

Não há scheduler, cron, sync_all ou descoberta de Items. A Pluggy pode executar auto-sync
por configuração própria; este MCP não altera essa configuração. Confira o
[ciclo do Item](https://docs.pluggy.ai/docs/item-lifecycle) e as
[restrições de atualização](https://docs.pluggy.ai/reference/items).

## Example

Primeiro uso: “Meu Itaú pessoal possui itemId <uuid>. Lembre disso.”
Depois: “Verifique quando meu Itaú foi atualizado.” → memória Hermes → `get_item(item_id)`.
Depois: “Atualize meu Itaú.” → memória Hermes → `sync_item(item_id)` → Pluggy.
Para atualizar dois bancos, o Hermes chama `sync_item` individualmente, inicialmente em sequência.

## Teste manual com uma conexão real

1. Inicie o MCP por stdio e forneça ao Hermes um UUID real.
2. Chame `get_item` e confira connector, status e `lastUpdatedAt`.
3. Solicite uma única atualização com `sync_item`; se ainda estiver em andamento, use `get_sync_status`.
4. Consulte `get_item` novamente e compare o timestamp. Consulte contas/transações normalmente.
5. Em uma nova sessão Hermes, peça o estado pelo nome do banco e confirme que a memória resolve o UUID.

Os testes automatizados não executam essas etapas com contas reais. Sem um Item real fornecido
para teste, a validação de plano, instituição, MFA e memória entre sessões permanece manual.

## Validação e desenvolvimento

```bash
make check     # lint, formato, tipos, contratos e testes; não precisa de Docker
make audit     # auditoria de dependências
make openapi-check  # compara o snapshot com a API pública, sem credenciais
```

Os testes usam dados sintéticos e HTTP simulado para a Pluggy. Testes MCP exercitam subprocessos
`stdio` e HTTP real em loopback; não consultam dados financeiros reais.

## HTTP remoto com OAuth

O mesmo registro de tools atende `/mcp` por Streamable HTTP. O modo remoto exige OAuth 2.1/OIDC
e access tokens JWT; bearer estático não é aceito. O servidor atua somente como resource server:
o login e a emissão de tokens pertencem a um provedor como Auth0, Keycloak ou Zitadel.

Configuração mínima:

```dotenv
MCP_TRANSPORT=streamable-http
MCP_AUTH_MODE=oauth
MCP_PUBLIC_URL=https://finance.example.com/mcp
MCP_ALLOWED_HOSTS=finance.example.com
MCP_OAUTH_ISSUER_URL=https://identity.example.com
MCP_OAUTH_ALLOWED_SUBJECT=provider-user-id-estavel
MCP_OAUTH_ALLOWED_CLIENT_IDS=gpt-client,claude-client,hermes-client
MCP_OAUTH_SCOPE=pluggy:access
MCP_OAUTH_SIGNING_ALGORITHM=RS256
```

O MCP publica Protected Resource Metadata e responde com o desafio OAuth padrão. O token precisa
ter assinatura válida, issuer e audience exatos, prazo vigente, o subject autorizado, um client ID
da allowlist e o escopo `pluggy:access`. `/healthz` e `/readyz` permanecem públicos e não consultam
a Pluggy. Credenciais Pluggy ficam somente no servidor.

Veja o [guia de implantação remota](docs/DEPLOY_REMOTE.md) para configurar o provedor e os clientes.
O [exemplo Cloud Run](docs/DEPLOY_CLOUD_RUN.md) cobre a plataforma Google. Nenhum deploy ou recurso
de infraestrutura é criado automaticamente.

## Documentação

- [Operação local e Hermes](docs/OPERACAO_LOCAL.md)
- [Regras das agregações](docs/AGREGACOES.md)
- [Segurança e contratos](docs/SEGURANCA.md)
- [Resultados da validação](docs/VALIDACAO.md)
- [Implantação remota e OAuth](docs/DEPLOY_REMOTE.md)
- [Cloud Run opcional](docs/DEPLOY_CLOUD_RUN.md)
- [Inventário OpenAPI gerado](docs/ENDPOINTS_PLUGGY.md)
- [Especificação original e decisões da implementação](docs/ESPECIFICACAO_TECNICA.md)

O SDK oficial MCP está fixado na linha 1.x mantida (`mcp>=1.28,<2`, lock em 1.30.0).
A atualização para 2.x requer migração explícita e os mesmos testes de transporte.

TDQS

B3/5.0

Scored across 18 tools

Disambiguation4/5

Most tools are clearly separated by resource and action, with list_* for collections and get_* for single items. A few aggregate tools like get_total_balance, get_net_worth, and get_investment_portfolio could be confused, but their descriptions clarify their distinct scopes.

Naming Consistency5/5

Tool names follow a consistent list_/get_ + noun pattern with snake_case throughout. The verb prefix reliably indicates whether the tool returns a collection or a single resource, making the naming predictable and easy to navigate.

Tool Count4/5

18 tools is slightly above the ideal compact range, but the count is justified by the breadth of finance domains covered: accounts, transactions, credit cards, investments, expenses, and net worth. No tool feels redundant, though a few could potentially be consolidated.

Completeness4/5

The surface covers the main read-only finance data needs well: accounts, balances, transactions, statements, credit cards, investments, expenses, and net worth. Minor gaps exist, such as no connection refresh or broader account-level filtering, but core workflows are not blocked.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive