fin-app-mcp 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +71 -42
  2. package/docs/guia.md +54 -0
  3. package/index.mjs +132 -10
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -14,13 +14,31 @@ MCP server que expõe as operações do FIN (finanças pessoais) para qualquer L
14
14
  - Uma instância do FIN rodando (local ou na nuvem)
15
15
  - Service token configurado no FIN (endpoint `/api/genius/*`)
16
16
 
17
- ## Setup
17
+ ## Setup (recomendado) — com API Key
18
18
 
19
- ### Opção A: via npx (recomendado zero instalação)
19
+ Cada usuário gera sua própria chave no app FIN. Zero fricção, nada de tokens compartilhados.
20
20
 
21
- Não precisa clonar nem instalar nada. O `npx` baixa e executa automaticamente.
21
+ ### Passo 1: Gerar uma API Key
22
22
 
23
- #### Claude Code (settings.json)
23
+ 1. Acesse o app FIN (web ou mobile)
24
+ 2. Vá em **Configurações → Chaves de API**
25
+ 3. Clique em **"Nova chave"**
26
+ 4. Dê um nome (ex: "Claude Code", "Personal Genius")
27
+ 5. Copie a chave gerada (`fin_live_xxx`) — **ela só é mostrada uma vez**
28
+
29
+ ### Passo 2: Configurar o Claude Code
30
+
31
+ Um único comando:
32
+
33
+ ```bash
34
+ claude mcp add fin-app \
35
+ -s user \
36
+ -e FIN_BASE_URL=https://fin-app-wine.vercel.app \
37
+ -e FIN_API_KEY=fin_live_xxx \
38
+ -- npx -y fin-app-mcp
39
+ ```
40
+
41
+ Ou edite manualmente o `settings.json` do Claude Code:
24
42
 
25
43
  ```json
26
44
  {
@@ -29,40 +47,74 @@ Não precisa clonar nem instalar nada. O `npx` baixa e executa automaticamente.
29
47
  "command": "npx",
30
48
  "args": ["-y", "fin-app-mcp"],
31
49
  "env": {
32
- "FIN_BASE_URL": "https://fin.example.com",
33
- "FIN_SERVICE_TOKEN": "seu-token-aqui",
34
- "FIN_USER_ID": "seu-user-id-aqui"
50
+ "FIN_BASE_URL": "https://fin-app-wine.vercel.app",
51
+ "FIN_API_KEY": "fin_live_xxx"
35
52
  }
36
53
  }
37
54
  }
38
55
  }
39
56
  ```
40
57
 
41
- Reinicie o client MCP. O server deve aparecer com as tools disponíveis.
58
+ ### Passo 3: Testar
42
59
 
43
- ### Opção B: instalação global
60
+ Reinicie o Claude Code. Rode `claude mcp list` — o `fin-app` deve aparecer como conectado. As tools do FIN ficam disponíveis automaticamente.
44
61
 
45
- ```bash
46
- npm install -g fin-app-mcp
47
- ```
62
+ ### Variáveis de ambiente
63
+
64
+ | Variável | Descrição |
65
+ |----------|-----------|
66
+ | `FIN_BASE_URL` | URL base da instância do FIN (ex: `https://fin-app-wine.vercel.app`) |
67
+ | `FIN_API_KEY` | API key pessoal (`fin_live_xxx`) gerada em `/settings/api-keys` |
68
+
69
+ ---
70
+
71
+ ## Setup legado — com Service Token (backward-compat)
72
+
73
+ Modo antigo, ainda suportado. Requer conhecer o `FIN_SERVICE_TOKEN` (env var do deploy) e o UUID do usuário no Supabase.
48
74
 
49
- Claude Code (settings.json):
50
75
  ```json
51
76
  {
52
77
  "mcpServers": {
53
78
  "fin-app": {
54
- "command": "fin-app-mcp",
79
+ "command": "npx",
80
+ "args": ["-y", "fin-app-mcp"],
55
81
  "env": {
56
- "FIN_BASE_URL": "https://fin.example.com",
57
- "FIN_SERVICE_TOKEN": "seu-token-aqui",
58
- "FIN_USER_ID": "seu-user-id-aqui"
82
+ "FIN_BASE_URL": "https://fin-app-wine.vercel.app",
83
+ "FIN_SERVICE_TOKEN": "shared-secret",
84
+ "FIN_USER_ID": "uuid-do-usuario"
59
85
  }
60
86
  }
61
87
  }
62
88
  }
63
89
  ```
64
90
 
65
- ### Opção C: clone local (para desenvolvimento)
91
+ Variáveis:
92
+
93
+ | Variável | Descrição |
94
+ |----------|-----------|
95
+ | `FIN_BASE_URL` | URL base da instância do FIN |
96
+ | `FIN_SERVICE_TOKEN` | Service token compartilhado |
97
+ | `FIN_USER_ID` | UUID do usuário no Supabase |
98
+
99
+ Se `FIN_API_KEY` e as variáveis legadas estiverem setadas juntas, `FIN_API_KEY` tem precedência.
100
+
101
+ ---
102
+
103
+ ## Opções de instalação
104
+
105
+ ### Via npx (padrão)
106
+
107
+ O `npx -y fin-app-mcp` baixa e executa automaticamente. Updates automáticos na próxima invocação.
108
+
109
+ ### Instalação global
110
+
111
+ ```bash
112
+ npm install -g fin-app-mcp
113
+ ```
114
+
115
+ Depois use `"command": "fin-app-mcp"` na config (sem `npx`).
116
+
117
+ ### Clone local (desenvolvimento)
66
118
 
67
119
  ```bash
68
120
  git clone https://github.com/pe-menezes/fin-app.git
@@ -70,30 +122,7 @@ cd fin-app/mcp-server
70
122
  npm install
71
123
  ```
72
124
 
73
- Claude Code (settings.json):
74
- ```json
75
- {
76
- "mcpServers": {
77
- "fin-app": {
78
- "command": "node",
79
- "args": ["/caminho/para/fin-app/mcp-server/index.mjs"],
80
- "env": {
81
- "FIN_BASE_URL": "https://fin.example.com",
82
- "FIN_SERVICE_TOKEN": "seu-token-aqui",
83
- "FIN_USER_ID": "seu-user-id-aqui"
84
- }
85
- }
86
- }
87
- }
88
- ```
89
-
90
- ### Variáveis de ambiente
91
-
92
- | Variável | Descrição |
93
- |----------|-----------|
94
- | `FIN_BASE_URL` | URL base da instância do FIN (ex: `https://fin.example.com` ou `http://localhost:3000`) |
95
- | `FIN_SERVICE_TOKEN` | Service token para autenticação na API Genius |
96
- | `FIN_USER_ID` | UUID do usuário no FIN |
125
+ Aponte o `command` pro `index.mjs` local.
97
126
 
98
127
  ## Resource: Guia do FIN
99
128
 
package/docs/guia.md CHANGED
@@ -269,3 +269,57 @@ As ocorrências são geradas **lazily**: ao consultar um mês (`fin_bills_do_mes
269
269
 
270
270
  - `fixed`: valor fixo todo mês (ex: condomínio R$1.383). O `amount` da ocorrência já vem preenchido.
271
271
  - `variable`: valor muda (ex: Light). O `amount` da ocorrência vem `null` até ser definido no momento do pagamento.
272
+
273
+ ## 10. Câmbio USD/BRL e ajuste de saldo
274
+
275
+ O FIN tem suporte para contas cash em USD (carteira física, Wise, conta no exterior). No v0, contas em USD trabalham com **saldo manual** — você não lança despesas/receitas categorizadas em dólar. Em vez disso, usa duas operações:
276
+
277
+ ### `fin_cambio` — comprar ou vender dólar
278
+
279
+ O FIN não usa cotação automática para câmbio — **você informa a taxa praticada** na operação, porque a taxa real varia por banco/spread/operação. A tool cria atomicamente 2 transações vinculadas (`exchange_pair_id` compartilhado):
280
+
281
+ - **Vender dólar:** `from = conta USD`, `to = conta BRL`
282
+ - **Comprar dólar:** `from = conta BRL`, `to = conta USD`
283
+
284
+ Cada transação fica na sua conta respectiva, com a moeda da conta. A categoria "Câmbio" é criada automaticamente no primeiro uso.
285
+
286
+ **Exemplo — vendi $100 e recebi R$540 no Itaú:**
287
+ ```json
288
+ fin_cambio({
289
+ "from_account_name": "Wise USD",
290
+ "to_account_name": "Itaú",
291
+ "amount_from_cents": 10000,
292
+ "amount_to_cents": 54000,
293
+ "description": "Venda de dólar"
294
+ })
295
+ ```
296
+
297
+ Resultado: despesa de $100 na Wise USD + receita de R$540 no Itaú. Ambas com `exchange_pair_id` igual.
298
+
299
+ ### `fin_ajustar_saldo_conta` — atualizar saldo manualmente
300
+
301
+ Para casos onde o saldo USD muda **sem motivo categórico** (ex: você achou mais $50 na carteira, ou simplesmente quer atualizar pra refletir o saldo real), use `fin_ajustar_saldo_conta`. Ele atualiza o `initial_balance` da conta diretamente, **sem criar transação**.
302
+
303
+ **Importante:** o valor é o **saldo absoluto**, não um delta. Se a conta tinha $400 e você quer somar $50, passe `45000` (não `5000`).
304
+
305
+ ```json
306
+ fin_ajustar_saldo_conta({
307
+ "account_name": "Wise USD",
308
+ "amount_cents": 45000
309
+ })
310
+ ```
311
+
312
+ Funciona para qualquer conta cash (BRL ou USD). Não funciona para cartão de crédito.
313
+
314
+ ### Restrições no v0
315
+
316
+ - ❌ Lançar despesa/receita categorizada em USD via `fin_criar_despesa`/`fin_criar_receita` (continua bloqueado — mensagem: "use fin_cambio ou fin_ajustar_saldo_conta")
317
+ - ❌ Cotação automática (você sempre informa a taxa)
318
+ - ❌ Cartão de crédito em USD
319
+ - ❌ Filtros nos relatórios mensais por moeda (transações de câmbio aparecem no relatório como categoria "Câmbio" — visualmente identificadas)
320
+
321
+ ### Para "comprei algo no exterior por $20"
322
+
323
+ No v0, a recomendação é **registrar como ajuste de saldo**: subtrair $20 do saldo atual da conta USD via `fin_ajustar_saldo_conta`. A categorização (que tipo de gasto foi) pode ser feita depois manualmente pela UI, ou simplesmente ignorada se não for crítica para o seu controle.
324
+
325
+ Lançar despesas categorizadas em USD pode entrar em uma versão futura se virar uma dor real.
package/index.mjs CHANGED
@@ -12,20 +12,47 @@ import { join, dirname } from "path";
12
12
  import { fileURLToPath } from "url";
13
13
 
14
14
  // ── Config ──────────────────────────────────────────────────────────
15
+ //
16
+ // Auth modes (in order of preference):
17
+ //
18
+ // 1. Per-user API key (recommended):
19
+ // FIN_BASE_URL=https://fin-app-wine.vercel.app
20
+ // FIN_API_KEY=fin_live_xxx ← gerada em /settings/api-keys no app
21
+ //
22
+ // 2. Legacy shared service token (backward-compat):
23
+ // FIN_BASE_URL=https://fin-app-wine.vercel.app
24
+ // FIN_SERVICE_TOKEN=<shared secret>
25
+ // FIN_USER_ID=<supabase uuid>
26
+ //
15
27
  const BASE_URL = process.env.FIN_BASE_URL;
16
- const TOKEN = process.env.FIN_SERVICE_TOKEN;
17
- const USER_ID = process.env.FIN_USER_ID;
28
+ const API_KEY = process.env.FIN_API_KEY;
29
+ const LEGACY_TOKEN = process.env.FIN_SERVICE_TOKEN;
30
+ const LEGACY_USER_ID = process.env.FIN_USER_ID;
18
31
 
19
- if (!BASE_URL || !TOKEN || !USER_ID) {
20
- console.error("Missing env: FIN_BASE_URL, FIN_SERVICE_TOKEN, FIN_USER_ID");
32
+ if (!BASE_URL) {
33
+ console.error("Missing env: FIN_BASE_URL");
21
34
  process.exit(1);
22
35
  }
23
36
 
24
- const headers = {
25
- Authorization: `Bearer ${TOKEN}`,
26
- "X-Genius-User-Id": USER_ID,
27
- "Content-Type": "application/json",
28
- };
37
+ if (!API_KEY && !(LEGACY_TOKEN && LEGACY_USER_ID)) {
38
+ console.error(
39
+ "Missing auth config. Set FIN_API_KEY (recomendado) ou " +
40
+ "FIN_SERVICE_TOKEN + FIN_USER_ID (legado).\n" +
41
+ "Gere uma API key em: " + BASE_URL + "/settings/api-keys"
42
+ );
43
+ process.exit(1);
44
+ }
45
+
46
+ const headers = API_KEY
47
+ ? {
48
+ Authorization: `Bearer ${API_KEY}`,
49
+ "Content-Type": "application/json",
50
+ }
51
+ : {
52
+ Authorization: `Bearer ${LEGACY_TOKEN}`,
53
+ "X-Genius-User-Id": LEGACY_USER_ID,
54
+ "Content-Type": "application/json",
55
+ };
29
56
 
30
57
  // ── Resource content ────────────────────────────────────────────────
31
58
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -594,6 +621,92 @@ EXEMPLO — anular estorno que não abate (GitHub R$38,63):
594
621
  required: ["card_name", "cycle_end", "amount_cents"],
595
622
  },
596
623
  },
624
+ // ── Câmbio USD/BRL e ajuste manual de saldo ─────────────────────
625
+ {
626
+ name: "fin_cambio",
627
+ description: `Registra uma operação de câmbio (compra ou venda de dólar) entre uma conta BRL e uma conta USD. Cria 2 transações vinculadas com a categoria "Câmbio".
628
+
629
+ QUANDO USAR:
630
+ - Vender dólar: from = conta USD, to = conta BRL. Ex: "vendi $100 por R$540".
631
+ - Comprar dólar: from = conta BRL, to = conta USD. Ex: "comprei $50 por R$280".
632
+
633
+ NÃO USAR QUANDO:
634
+ - Quiser apenas atualizar o saldo de uma conta sem registrar a operação (use fin_ajustar_saldo_conta).
635
+ - Quiser lançar despesa em USD (não suportado no v0 — só câmbio).
636
+ - Quiser transferir entre contas da mesma moeda (use fin_criar_transferencia).
637
+
638
+ CAMPOS:
639
+ - from_account_name: string — conta de origem (a que fica negativa). Match parcial.
640
+ - to_account_name: string — conta de destino (a que fica positiva). Match parcial.
641
+ - amount_from_cents: number — valor que SAI da from_account, em centavos da moeda da conta. Ex: $100 = 10000 (centavos USD).
642
+ - amount_to_cents: number — valor que ENTRA na to_account, em centavos da moeda da conta. Ex: R$540 = 54000 (centavos BRL).
643
+ - tx_date: string YYYY-MM-DD — data (default: hoje).
644
+ - description: string — descrição livre (opcional).
645
+
646
+ REGRAS DE NEGÓCIO:
647
+ - As contas DEVEM ter moedas diferentes (uma BRL, outra USD).
648
+ - Cria 2 transações: uma despesa na from_account, uma receita na to_account.
649
+ - Ambas transações ficam linkadas via exchange_pair_id.
650
+ - A categoria "Câmbio" é criada automaticamente no primeiro uso.
651
+ - A taxa de câmbio é INFORMADA por você — não usa cotação automática.
652
+ - Operação atômica: se uma transação falhar, a outra é desfeita.
653
+
654
+ EXEMPLO — vender $100 por R$540:
655
+ { "from_account_name": "Wise USD", "to_account_name": "Itaú", "amount_from_cents": 10000, "amount_to_cents": 54000, "description": "Venda de dólar" }
656
+
657
+ EXEMPLO — comprar $50 por R$280:
658
+ { "from_account_name": "Itaú", "to_account_name": "Wise USD", "amount_from_cents": 28000, "amount_to_cents": 5000 }`,
659
+ inputSchema: {
660
+ type: "object",
661
+ properties: {
662
+ from_account_name: { type: "string", description: "Nome da conta de origem" },
663
+ to_account_name: { type: "string", description: "Nome da conta de destino" },
664
+ amount_from_cents: { type: "number", description: "Valor que SAI da origem, em centavos da moeda da conta" },
665
+ amount_to_cents: { type: "number", description: "Valor que ENTRA no destino, em centavos da moeda da conta" },
666
+ tx_date: { type: "string", description: "YYYY-MM-DD (default: hoje)" },
667
+ description: { type: "string", description: "Descrição livre (opcional)" },
668
+ },
669
+ required: ["from_account_name", "to_account_name", "amount_from_cents", "amount_to_cents"],
670
+ },
671
+ },
672
+ {
673
+ name: "fin_ajustar_saldo_conta",
674
+ description: `Atualiza o saldo de uma conta cash (BRL ou USD) diretamente, sem criar transação. Útil para correções e contas em USD que mudam sem motivo categórico.
675
+
676
+ QUANDO USAR:
677
+ - Conta USD: registrar quanto dólar você tem agora (ex: "achei mais $50 na carteira", "agora tenho $537").
678
+ - Conta BRL: corrigir saldo inicial após verificar extrato bancário.
679
+ - Onboarding: definir saldo inicial de uma conta recém-criada.
680
+
681
+ NÃO USAR QUANDO:
682
+ - Quiser registrar uma despesa ou receita categorizada (use fin_criar_despesa / fin_criar_receita).
683
+ - Quiser registrar câmbio entre contas (use fin_cambio).
684
+ - For uma conta de cartão de crédito (não suportado — cartão tem fatura).
685
+
686
+ CAMPOS:
687
+ - account_name: string — nome da conta (match parcial).
688
+ - amount_cents: number — novo saldo ABSOLUTO, em centavos da moeda da conta. Ex: $487 = 48700 (centavos USD); R$1.234,00 = 123400 (centavos BRL).
689
+
690
+ REGRAS DE NEGÓCIO:
691
+ - O valor é o SALDO ABSOLUTO, não um delta. Se a conta tinha $400 e você quer somar $50, passe 45000 (não 5000).
692
+ - Funciona para qualquer conta cash (BRL ou USD).
693
+ - Não cria transação — atualiza diretamente o initial_balance da conta.
694
+ - Não suportado para cartão de crédito.
695
+
696
+ EXEMPLO — atualizar saldo USD pra $537:
697
+ { "account_name": "Wise USD", "amount_cents": 53700 }
698
+
699
+ EXEMPLO — corrigir saldo BRL pra R$1.234,56:
700
+ { "account_name": "Itaú", "amount_cents": 123456 }`,
701
+ inputSchema: {
702
+ type: "object",
703
+ properties: {
704
+ account_name: { type: "string", description: "Nome da conta (match parcial)" },
705
+ amount_cents: { type: "number", description: "Novo saldo absoluto em centavos da moeda da conta" },
706
+ },
707
+ required: ["account_name", "amount_cents"],
708
+ },
709
+ },
597
710
  // ── Bills (Contas a Pagar) ──────────────────────────────────────
598
711
  {
599
712
  name: "fin_listar_bills",
@@ -1014,6 +1127,15 @@ async function handleTool(name, args) {
1014
1127
  case "fin_ajuste_fatura": {
1015
1128
  return ok(await api("POST", `/api/genius/card-invoice-adjustments`, args));
1016
1129
  }
1130
+ case "fin_cambio": {
1131
+ return ok(await api("POST", `/api/genius/exchange`, args));
1132
+ }
1133
+ case "fin_ajustar_saldo_conta": {
1134
+ const { account_name, amount_cents } = args;
1135
+ return ok(
1136
+ await api("POST", `/api/genius/accounts/${encodeURIComponent(account_name)}/balance`, { amount_cents })
1137
+ );
1138
+ }
1017
1139
  case "fin_listar_bills": {
1018
1140
  return ok(await api("GET", `/api/genius/bills`));
1019
1141
  }
@@ -1067,7 +1189,7 @@ async function handleTool(name, args) {
1067
1189
  // ── Server ──────────────────────────────────────────────────────────
1068
1190
 
1069
1191
  const server = new Server(
1070
- { name: "fin-app", version: "2.0.0" },
1192
+ { name: "fin-app", version: "2.2.0" },
1071
1193
  { capabilities: { tools: {}, resources: {} } }
1072
1194
  );
1073
1195
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fin-app-mcp",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "MCP server para o FIN App — finanças pessoais. Expõe tools para agents LLM operarem contas, despesas, faturas de cartão, bills recorrentes e mais.",
5
5
  "type": "module",
6
6
  "main": "index.mjs",