fin-app-mcp 2.0.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 +118 -0
  2. package/docs/guia.md +271 -0
  3. package/index.mjs +1111 -0
  4. package/package.json +37 -0
package/index.mjs ADDED
@@ -0,0 +1,1111 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import {
5
+ CallToolRequestSchema,
6
+ ListToolsRequestSchema,
7
+ ListResourcesRequestSchema,
8
+ ReadResourceRequestSchema,
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+ import { readFileSync } from "fs";
11
+ import { join, dirname } from "path";
12
+ import { fileURLToPath } from "url";
13
+
14
+ // ── Config ──────────────────────────────────────────────────────────
15
+ 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;
18
+
19
+ if (!BASE_URL || !TOKEN || !USER_ID) {
20
+ console.error("Missing env: FIN_BASE_URL, FIN_SERVICE_TOKEN, FIN_USER_ID");
21
+ process.exit(1);
22
+ }
23
+
24
+ const headers = {
25
+ Authorization: `Bearer ${TOKEN}`,
26
+ "X-Genius-User-Id": USER_ID,
27
+ "Content-Type": "application/json",
28
+ };
29
+
30
+ // ── Resource content ────────────────────────────────────────────────
31
+ const __dirname = dirname(fileURLToPath(import.meta.url));
32
+ const guiaContent = readFileSync(join(__dirname, "docs", "guia.md"), "utf-8");
33
+
34
+ // ── API helpers ─────────────────────────────────────────────────────
35
+
36
+ async function api(method, path, body) {
37
+ const url = `${BASE_URL}${path}`;
38
+ const opts = { method, headers };
39
+ if (body) opts.body = JSON.stringify(body);
40
+ const res = await fetch(url, opts);
41
+ const data = await res.json();
42
+ if (!res.ok) return { ok: false, status: res.status, ...data };
43
+ return data;
44
+ }
45
+
46
+ function ok(data) {
47
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
48
+ }
49
+
50
+ function err(msg) {
51
+ return { content: [{ type: "text", text: `Erro: ${msg}` }], isError: true };
52
+ }
53
+
54
+ // ── Tool definitions ────────────────────────────────────────────────
55
+
56
+ const tools = [
57
+ // ── Consultas ─────────────────────────────────────────────────────
58
+ {
59
+ name: "fin_resumo_mensal",
60
+ description: `Retorna o resumo financeiro do mês: receitas totais, despesas totais, saldo, taxa de poupança (%) e top categorias de gasto.
61
+
62
+ QUANDO USAR: para ter uma visão geral de como foi o mês financeiramente.
63
+ NÃO USAR QUANDO: precisar de detalhes por transação (usar fin_todas_transacoes) ou breakdown por categoria (usar fin_gastos_por_categoria).
64
+
65
+ CAMPOS:
66
+ - month: string YYYY-MM — mês de referência. Default: mês atual (timezone America/Sao_Paulo).
67
+
68
+ REGRAS DE NEGÓCIO:
69
+ - Todos os valores retornados estão em centavos.
70
+ - savings_rate é percentual (0-100), não decimal.
71
+ - Inclui apenas transações do mês solicitado.`,
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: { month: { type: "string", description: "YYYY-MM" } },
75
+ },
76
+ },
77
+ {
78
+ name: "fin_gastos_por_categoria",
79
+ description: `Retorna o breakdown de gastos agrupados por categoria e subcategoria no mês.
80
+
81
+ QUANDO USAR: para entender onde o dinheiro foi gasto no mês, comparar categorias.
82
+ NÃO USAR QUANDO: quiser o resumo geral (usar fin_resumo_mensal) ou transações individuais.
83
+
84
+ CAMPOS:
85
+ - month: string YYYY-MM — mês de referência.
86
+
87
+ REGRAS DE NEGÓCIO:
88
+ - Valores em centavos.
89
+ - Inclui apenas despesas (expenses) — receitas não aparecem.
90
+ - Agrupa por categoria + subcategoria.`,
91
+ inputSchema: {
92
+ type: "object",
93
+ properties: { month: { type: "string", description: "YYYY-MM" } },
94
+ },
95
+ },
96
+ {
97
+ name: "fin_saldos",
98
+ description: `Retorna os saldos das contas bancárias (tipo cash). Cartões de crédito não têm "saldo" — têm faturas.
99
+
100
+ QUANDO USAR: para saber quanto tem em cada conta corrente/carteira/poupança.
101
+ NÃO USAR QUANDO: quiser ver fatura de cartão de crédito (usar fin_fatura_cartao).
102
+
103
+ CAMPOS:
104
+ - account_name: string — filtro por nome da conta (match parcial, case-insensitive). Opcional.
105
+
106
+ REGRAS DE NEGÓCIO:
107
+ - Saldo = saldo_inicial + receitas - despesas (calculado pelo sistema).
108
+ - Valores em centavos.`,
109
+ inputSchema: {
110
+ type: "object",
111
+ properties: {
112
+ account_name: { type: "string", description: "Nome parcial da conta" },
113
+ },
114
+ },
115
+ },
116
+ {
117
+ name: "fin_buscar_transacoes",
118
+ description: `Busca transações por texto na descrição, data, categoria ou conta. Retorna até 20 resultados.
119
+
120
+ QUANDO USAR: para encontrar transações específicas por descrição ou filtros simples. Bom para verificar se algo já foi lançado.
121
+ NÃO USAR QUANDO: precisar listar TODAS as transações de um período (usar fin_todas_transacoes) ou transações de uma fatura específica (usar fin_fatura_transacoes).
122
+
123
+ CAMPOS:
124
+ - q: string — busca por texto na descrição.
125
+ - date_from / date_to: string YYYY-MM-DD — filtro de período.
126
+ - category: string — nome da categoria.
127
+ - account: string — nome da conta.
128
+ - limit: number — max resultados (default 5, max 20).
129
+
130
+ REGRAS DE NEGÓCIO:
131
+ - Valores em centavos.
132
+ - Compras de supermercado/delivery (ex: Rappi, iFood) podem estar divididas em múltiplos lançamentos por categoria (alimentação, limpeza, higiene) na mesma data, somando o valor total do extrato bancário. Na conciliação por valor, considere somar itens do mesmo estabelecimento/data.
133
+ - Parcelas aparecem como transações separadas com installment_index e installment_count.`,
134
+ inputSchema: {
135
+ type: "object",
136
+ properties: {
137
+ q: { type: "string", description: "Texto na descrição" },
138
+ date_from: { type: "string", description: "YYYY-MM-DD" },
139
+ date_to: { type: "string", description: "YYYY-MM-DD" },
140
+ category: { type: "string", description: "Nome da categoria" },
141
+ account: { type: "string", description: "Nome da conta" },
142
+ limit: { type: "number", description: "Max resultados (default 5, max 20)" },
143
+ },
144
+ },
145
+ },
146
+ {
147
+ name: "fin_fatura_cartao",
148
+ description: `Retorna o resumo de uma fatura de cartão de crédito: total bruto, estornos, total líquido, vencimento, e top categorias.
149
+
150
+ QUANDO USAR: para ver o total de uma fatura e quando vence.
151
+ NÃO USAR QUANDO: precisar ver as transações individuais da fatura (usar fin_fatura_transacoes).
152
+
153
+ CAMPOS:
154
+ - card_name: string — nome do cartão (match parcial).
155
+ - reference_month: string YYYY-MM — mês de referência da fatura.
156
+
157
+ REGRAS DE NEGÓCIO:
158
+ - ATENÇÃO: reference_month = mês do VENCIMENTO da fatura, NÃO o mês dos gastos.
159
+ - Bancos nomeiam faturas pelo mês dos gastos. O FIN nomeia pelo mês do vencimento. Há ~1 mês de diferença.
160
+ - Exemplo: "Fatura de Março" do banco = reference_month 2026-04 no FIN (se vence em abril).
161
+ - Regra prática: some +1 ao mês que o banco informa.
162
+ - net_total já desconta estornos.
163
+ - Valores em centavos.`,
164
+ inputSchema: {
165
+ type: "object",
166
+ properties: {
167
+ card_name: { type: "string", description: "Nome do cartão" },
168
+ reference_month: { type: "string", description: "YYYY-MM" },
169
+ },
170
+ },
171
+ },
172
+ {
173
+ name: "fin_contexto",
174
+ description: `Retorna o contexto financeiro completo: contas, categorias, transações recentes, saldos e resumo do mês atual.
175
+
176
+ QUANDO USAR: no início de uma sessão financeira, para entender a situação geral antes de fazer qualquer operação. Útil para saber quais contas/categorias existem.
177
+ NÃO USAR QUANDO: já souber as contas e categorias e quiser ir direto para operações específicas.
178
+
179
+ REGRAS DE NEGÓCIO:
180
+ - Valores em centavos.
181
+ - Retorna dados do mês atual (timezone America/Sao_Paulo).
182
+ - É uma visão consolidada — para detalhes, use as tools específicas.`,
183
+ inputSchema: { type: "object", properties: {} },
184
+ },
185
+ {
186
+ name: "fin_todas_transacoes",
187
+ description: `Lista TODAS as transações com paginação (até 200 por página). Ideal para conciliação em lote.
188
+
189
+ QUANDO USAR: para listar todas as transações de um período, conta ou tipo. Ideal para conciliação com extrato bancário.
190
+ NÃO USAR QUANDO: quiser buscar por texto na descrição (usar fin_buscar_transacoes) ou ver transações de uma fatura de cartão (usar fin_fatura_transacoes).
191
+
192
+ CAMPOS:
193
+ - account: string — nome da conta (match parcial).
194
+ - date_from / date_to: string YYYY-MM-DD — filtro de período.
195
+ - tx_type: string — 'expense', 'income', ou 'transfer'.
196
+ - category: string — nome da categoria.
197
+ - page: number — página (default 1).
198
+ - per_page: number — itens por página (default 50, max 200).
199
+
200
+ REGRAS DE NEGÓCIO:
201
+ - Retorna total_count para saber o total de resultados.
202
+ - Valores em centavos.
203
+ - Paginação: se total_count > per_page, há mais páginas.`,
204
+ inputSchema: {
205
+ type: "object",
206
+ properties: {
207
+ account: { type: "string", description: "Nome da conta" },
208
+ date_from: { type: "string", description: "YYYY-MM-DD" },
209
+ date_to: { type: "string", description: "YYYY-MM-DD" },
210
+ tx_type: { type: "string", description: "expense | income | transfer" },
211
+ category: { type: "string", description: "Nome da categoria" },
212
+ page: { type: "number", description: "Página (default 1)" },
213
+ per_page: { type: "number", description: "Itens por página (default 50, max 200)" },
214
+ },
215
+ },
216
+ },
217
+ {
218
+ name: "fin_relatorio",
219
+ description: `Relatório financeiro multi-mês: receitas, despesas, saldo e categorias por mês.
220
+
221
+ QUANDO USAR: para análise trimestral, semestral ou anual — comparar meses entre si.
222
+ NÃO USAR QUANDO: quiser apenas um mês (usar fin_resumo_mensal).
223
+
224
+ CAMPOS:
225
+ - from_month: string YYYY-MM — mês inicial (obrigatório).
226
+ - to_month: string YYYY-MM — mês final (obrigatório).
227
+
228
+ REGRAS DE NEGÓCIO:
229
+ - Valores em centavos.
230
+ - Range máximo: 24 meses.`,
231
+ inputSchema: {
232
+ type: "object",
233
+ properties: {
234
+ from_month: { type: "string", description: "YYYY-MM (obrigatório)" },
235
+ to_month: { type: "string", description: "YYYY-MM (obrigatório)" },
236
+ },
237
+ required: ["from_month", "to_month"],
238
+ },
239
+ },
240
+ {
241
+ name: "fin_receitas",
242
+ description: `Lista receitas (income) detalhadas com paginação (até 100 por página).
243
+
244
+ QUANDO USAR: para ver receitas de um período ou conta específica.
245
+ NÃO USAR QUANDO: quiser ver todas as transações incluindo despesas (usar fin_todas_transacoes).
246
+
247
+ CAMPOS:
248
+ - account: string — nome da conta (match parcial).
249
+ - date_from / date_to: string YYYY-MM-DD — filtro de período.
250
+ - page: number — página (default 1).
251
+ - per_page: number — itens por página (default 50, max 100).
252
+
253
+ REGRAS DE NEGÓCIO:
254
+ - Retorna apenas receitas (tx_type = income).
255
+ - Valores em centavos.`,
256
+ inputSchema: {
257
+ type: "object",
258
+ properties: {
259
+ account: { type: "string", description: "Nome da conta" },
260
+ date_from: { type: "string", description: "YYYY-MM-DD" },
261
+ date_to: { type: "string", description: "YYYY-MM-DD" },
262
+ page: { type: "number", description: "Página (default 1)" },
263
+ per_page: { type: "number", description: "Itens por página (default 50, max 100)" },
264
+ },
265
+ },
266
+ },
267
+ {
268
+ name: "fin_fatura_transacoes",
269
+ description: `Lista TODAS as transações de uma fatura de cartão: cobranças, estornos (kind: refund) e pagamentos. Retorna totais e líquido.
270
+
271
+ QUANDO USAR: para ver o detalhamento completo de uma fatura — cada transação, estornos, e o total líquido.
272
+ NÃO USAR QUANDO: quiser apenas o resumo da fatura (usar fin_fatura_cartao).
273
+
274
+ CAMPOS:
275
+ - card_name: string — nome do cartão (obrigatório, match parcial).
276
+ - reference_month: string YYYY-MM — mês de referência (default: mês atual).
277
+
278
+ REGRAS DE NEGÓCIO:
279
+ - ATENÇÃO: reference_month segue a mesma regra de fin_fatura_cartao — é o mês do VENCIMENTO, não dos gastos. Some +1 ao mês que o banco informa.
280
+ - Estornos aparecem com kind: 'refund' e signedAmount negativo.
281
+ - Valores em centavos.`,
282
+ inputSchema: {
283
+ type: "object",
284
+ properties: {
285
+ card_name: { type: "string", description: "Nome do cartão (obrigatório)" },
286
+ reference_month: { type: "string", description: "YYYY-MM (default: mês atual)" },
287
+ },
288
+ required: ["card_name"],
289
+ },
290
+ },
291
+ {
292
+ name: "fin_listar_contas",
293
+ description: `Lista todas as contas bancárias com detalhes: nome, instituição, tipo (cash/credit), saldo inicial, e dados do cartão (closing_day, due_day, credit_limit) quando aplicável.
294
+
295
+ QUANDO USAR: para saber quais contas e cartões existem, seus nomes e configurações.
296
+ NÃO USAR QUANDO: quiser saber os saldos atuais (usar fin_saldos) ou faturas (usar fin_fatura_cartao).
297
+
298
+ REGRAS DE NEGÓCIO:
299
+ - Contas cash: corrente, carteira, poupança.
300
+ - Contas credit: cartões de crédito — incluem closing_day (fechamento) e due_day (vencimento).
301
+ - Valores em centavos.`,
302
+ inputSchema: { type: "object", properties: {} },
303
+ },
304
+ {
305
+ name: "fin_listar_categorias",
306
+ description: `Lista todas as categorias de transação com suas subcategorias. Inclui id, nome, flow (expense/income), status ativo, e subcategorias.
307
+
308
+ QUANDO USAR: para saber quais categorias existem antes de criar transações, ou para encontrar o nome exato de uma categoria/subcategoria.
309
+ NÃO USAR QUANDO: já souber os nomes das categorias.
310
+
311
+ REGRAS DE NEGÓCIO:
312
+ - flow 'expense' = categoria de despesa; flow 'income' = categoria de receita.
313
+ - Categorias inativas (is_active: false) estão arquivadas.
314
+ - Subcategorias herdam o flow da categoria pai.`,
315
+ inputSchema: { type: "object", properties: {} },
316
+ },
317
+ // ── Mutações ──────────────────────────────────────────────────────
318
+ {
319
+ name: "fin_criar_despesa",
320
+ description: `Cria uma despesa. Também usada para lançar ESTORNOS em cartão de crédito (via reversal_of_id). Suporta parcelamento automático.
321
+
322
+ QUANDO USAR:
323
+ - Lançar uma despesa normal (compra, conta, assinatura).
324
+ - Lançar um ESTORNO/CANCELAMENTO em cartão de crédito — usar com reversal_of_id.
325
+ - Lançar uma compra parcelada — usar com installments.
326
+
327
+ NÃO USAR QUANDO:
328
+ - Quiser lançar receita (usar fin_criar_receita).
329
+ - Quiser registrar pagamento de fatura de cartão (usar fin_pagar_fatura).
330
+ - Quiser transferir entre contas (usar fin_criar_transferencia).
331
+
332
+ CAMPOS:
333
+ - description: string — descrição da despesa (obrigatório).
334
+ - amount_cents: number — valor em centavos (obrigatório). Para parcelamento, informar o TOTAL.
335
+ - account_name: string — nome da conta/cartão (match parcial).
336
+ - category_name: string — nome da categoria (deve ser flow 'expense').
337
+ - subcategory_name: string — nome da subcategoria.
338
+ - tx_date: string YYYY-MM-DD — data (default: hoje, timezone America/Sao_Paulo).
339
+ - essential: boolean — se é despesa essencial.
340
+ - installments: number — número de parcelas (2-48). Gera N transações separadas automaticamente.
341
+ - current_installment: number — parcela inicial (default: 1). Útil para entrar no meio de um parcelamento.
342
+ - reversal_of_id: string UUID — ID da transação original para estorno.
343
+ - reversal_kind: string — 'full' ou 'partial' (obrigatório quando reversal_of_id é informado).
344
+
345
+ REGRAS DE NEGÓCIO — ESTORNO:
346
+ - Para estorno em cartão: criar despesa com reversal_of_id = UUID da transação original.
347
+ - O amount_cents é POSITIVO — o sistema inverte automaticamente na fatura.
348
+ - O account_name DEVE ser o mesmo cartão da transação original.
349
+ - NUNCA usar fin_criar_receita para estorno em cartão — receita SOMA na fatura em vez de subtrair.
350
+ - A transação original deve existir, ser expense, e não ser outro estorno.
351
+
352
+ REGRAS DE NEGÓCIO — PARCELAMENTO:
353
+ - installments: N gera N transações separadas, cada uma em um mês consecutivo.
354
+ - O amount_cents informado é o TOTAL — o sistema divide entre as parcelas.
355
+ - Cada parcela cai numa fatura diferente automaticamente.
356
+ - Deletar qualquer parcela deleta TODAS do grupo.
357
+
358
+ REGRAS DE NEGÓCIO — CICLO DE FATURA:
359
+ - invoice_cycle_end: força a transação a pertencer a um ciclo de fatura específico, independente da tx_date. Usar quando o banco lista um cancelamento/estorno em uma fatura diferente da compra original. O valor é a data de fechamento do ciclo (YYYY-MM-DD), NÃO o reference_month.
360
+ - Ver seção 8 do guia (fin://docs/guia) para cenários de conciliação complexa.
361
+
362
+ EXEMPLO — despesa simples:
363
+ { "description": "Almoço", "amount_cents": 3500, "account_name": "Nubank", "category_name": "Alimentação" }
364
+
365
+ EXEMPLO — estorno:
366
+ { "description": "Cancelamento Rappi", "amount_cents": 1474, "account_name": "Latam Itaú", "category_name": "Alimentação", "reversal_of_id": "uuid-da-transacao-original", "reversal_kind": "partial" }
367
+
368
+ EXEMPLO — parcelamento:
369
+ { "description": "Óculos Fozoco", "amount_cents": 36792, "account_name": "Nubank", "category_name": "Saúde", "installments": 4 }
370
+
371
+ EXEMPLO — forçar ciclo:
372
+ { "description": "Cancelamento GoDaddy", "amount_cents": 59322, "account_name": "Latam Itaú", "category_name": "Tecnologia", "reversal_of_id": "uuid-original", "reversal_kind": "full", "invoice_cycle_end": "2026-05-01" }`,
373
+ inputSchema: {
374
+ type: "object",
375
+ properties: {
376
+ description: { type: "string" },
377
+ amount_cents: { type: "number", description: "Valor em centavos" },
378
+ account_name: { type: "string" },
379
+ category_name: { type: "string" },
380
+ subcategory_name: { type: "string" },
381
+ tx_date: { type: "string", description: "YYYY-MM-DD (default: hoje)" },
382
+ essential: { type: "boolean" },
383
+ installments: {
384
+ type: "number",
385
+ description: "Número de parcelas (2-48)",
386
+ },
387
+ current_installment: {
388
+ type: "number",
389
+ description: "Parcela inicial (default: 1). Usar para entrar no meio de parcelamento existente.",
390
+ },
391
+ reversal_of_id: {
392
+ type: "string",
393
+ description: "UUID da transação original — para estornos/créditos em cartão",
394
+ },
395
+ reversal_kind: {
396
+ type: "string",
397
+ description: "'full' ou 'partial' — obrigatório quando reversal_of_id é informado",
398
+ },
399
+ invoice_cycle_end: {
400
+ type: "string",
401
+ description: "YYYY-MM-DD — data de fechamento do ciclo. Força a transação pra uma fatura específica.",
402
+ },
403
+ },
404
+ required: ["description", "amount_cents"],
405
+ },
406
+ },
407
+ {
408
+ name: "fin_criar_receita",
409
+ description: `Cria uma receita (income). Credita numa conta cash.
410
+
411
+ QUANDO USAR: lançar salário, freelance, dividendos, reembolso em conta corrente.
412
+ NÃO USAR QUANDO:
413
+ - Quiser lançar estorno em cartão de crédito — usar fin_criar_despesa com reversal_of_id.
414
+ - Quiser transferir entre contas — usar fin_criar_transferencia.
415
+
416
+ CAMPOS:
417
+ - description: string — descrição (obrigatório).
418
+ - amount_cents: number — valor em centavos (obrigatório).
419
+ - account_name: string — nome da conta destino (match parcial). Deve ser conta cash.
420
+ - category_name: string — nome da categoria (deve ser flow 'income').
421
+ - subcategory_name: string — subcategoria.
422
+ - tx_date: string YYYY-MM-DD — data (default: hoje).
423
+
424
+ REGRAS DE NEGÓCIO:
425
+ - Valores em centavos.
426
+ - NUNCA usar para estorno em cartão de crédito. Receita em cartão SOMA na fatura em vez de subtrair.`,
427
+ inputSchema: {
428
+ type: "object",
429
+ properties: {
430
+ description: { type: "string" },
431
+ amount_cents: { type: "number", description: "Valor em centavos" },
432
+ account_name: { type: "string" },
433
+ category_name: { type: "string" },
434
+ subcategory_name: { type: "string" },
435
+ tx_date: { type: "string", description: "YYYY-MM-DD (default: hoje)" },
436
+ },
437
+ required: ["description", "amount_cents"],
438
+ },
439
+ },
440
+ {
441
+ name: "fin_criar_transferencia",
442
+ description: `Cria uma transferência entre duas contas. Move dinheiro da conta origem para a conta destino.
443
+
444
+ QUANDO USAR: transferência entre contas (ex: da corrente para poupança, de um banco para outro).
445
+ NÃO USAR QUANDO:
446
+ - Quiser registrar pagamento de fatura de cartão — usar fin_pagar_fatura.
447
+ - Quiser lançar despesa ou receita — usar as tools específicas.
448
+
449
+ CAMPOS:
450
+ - amount_cents: number — valor em centavos (obrigatório).
451
+ - from_account_name: string — conta de origem (obrigatório, match parcial).
452
+ - to_account_name: string — conta de destino (obrigatório, match parcial).
453
+ - description: string — descrição.
454
+ - tx_date: string YYYY-MM-DD — data (default: hoje).
455
+
456
+ REGRAS DE NEGÓCIO:
457
+ - Valores em centavos.
458
+ - Ambas as contas devem existir.`,
459
+ inputSchema: {
460
+ type: "object",
461
+ properties: {
462
+ amount_cents: { type: "number", description: "Valor em centavos" },
463
+ from_account_name: { type: "string" },
464
+ to_account_name: { type: "string" },
465
+ description: { type: "string" },
466
+ tx_date: { type: "string", description: "YYYY-MM-DD (default: hoje)" },
467
+ },
468
+ required: ["amount_cents", "from_account_name", "to_account_name"],
469
+ },
470
+ },
471
+ {
472
+ name: "fin_pagar_fatura",
473
+ description: `Registra o pagamento de uma fatura de cartão de crédito. Debita da conta origem e credita no cartão.
474
+
475
+ QUANDO USAR: quando o usuário pagou a fatura do cartão e quer registrar.
476
+ NÃO USAR QUANDO:
477
+ - Quiser transferir entre contas normais — usar fin_criar_transferencia.
478
+ - Quiser lançar despesa no cartão — usar fin_criar_despesa.
479
+
480
+ CAMPOS:
481
+ - amount_cents: number — valor pago em centavos (obrigatório).
482
+ - card_name: string — nome do cartão (obrigatório, match parcial).
483
+ - from_account_name: string — conta de onde saiu o dinheiro (obrigatório, match parcial).
484
+ - tx_date: string YYYY-MM-DD — data do pagamento.
485
+ - installments: number — parcelas do pagamento (2-48), se a fatura for parcelada.
486
+
487
+ REGRAS DE NEGÓCIO:
488
+ - Valores em centavos.
489
+ - Suporta parcelamento do pagamento da fatura (não confundir com parcelamento de compras).`,
490
+ inputSchema: {
491
+ type: "object",
492
+ properties: {
493
+ amount_cents: { type: "number", description: "Valor em centavos" },
494
+ card_name: { type: "string" },
495
+ from_account_name: { type: "string" },
496
+ tx_date: { type: "string", description: "YYYY-MM-DD" },
497
+ installments: { type: "number", description: "Parcelas (2-48)" },
498
+ },
499
+ required: ["amount_cents", "card_name", "from_account_name"],
500
+ },
501
+ },
502
+ {
503
+ name: "fin_editar_transacao",
504
+ description: `Edita uma transação existente. Apenas os campos informados são alterados — campos omitidos permanecem inalterados.
505
+
506
+ QUANDO USAR: corrigir valor, descrição, categoria, conta ou data de uma transação.
507
+ NÃO USAR QUANDO: quiser deletar (usar fin_deletar_transacao) ou criar nova (usar tools de criação).
508
+
509
+ CAMPOS:
510
+ - transaction_id: string UUID — ID da transação (obrigatório).
511
+ - new_amount_cents: number — novo valor em centavos.
512
+ - new_description: string — nova descrição.
513
+ - new_category_name: string — nova categoria (por nome).
514
+ - new_subcategory_name: string — nova subcategoria (por nome).
515
+ - new_account_name: string — nova conta (por nome).
516
+ - new_date: string YYYY-MM-DD — nova data.
517
+ - new_invoice_cycle_end: string YYYY-MM-DD — força a transação pra uma fatura específica (data de fechamento do ciclo).
518
+
519
+ REGRAS DE NEGÓCIO:
520
+ - Valores em centavos.
521
+ - Campos omitidos não são alterados.
522
+ - O transaction_id é um UUID (usar fin_buscar_transacoes ou fin_todas_transacoes para encontrar).
523
+ - new_invoice_cycle_end: usar para mover uma transação entre faturas (ex: estorno que o banco listou em fatura diferente da compra).`,
524
+ inputSchema: {
525
+ type: "object",
526
+ properties: {
527
+ transaction_id: { type: "string", description: "UUID da transação" },
528
+ new_amount_cents: { type: "number" },
529
+ new_description: { type: "string" },
530
+ new_category_name: { type: "string" },
531
+ new_subcategory_name: { type: "string" },
532
+ new_account_name: { type: "string" },
533
+ new_date: { type: "string", description: "YYYY-MM-DD" },
534
+ new_invoice_cycle_end: { type: "string", description: "YYYY-MM-DD — data de fechamento do ciclo da fatura" },
535
+ },
536
+ required: ["transaction_id"],
537
+ },
538
+ },
539
+ {
540
+ name: "fin_deletar_transacao",
541
+ description: `Deleta uma transação pelo ID.
542
+
543
+ QUANDO USAR: remover uma transação lançada por engano ou duplicada.
544
+
545
+ CAMPOS:
546
+ - transaction_id: string UUID — ID da transação (obrigatório).
547
+
548
+ REGRAS DE NEGÓCIO:
549
+ - ATENÇÃO: se a transação faz parte de um parcelamento, deletar UMA parcela deleta TODAS as parcelas do grupo (todas com o mesmo installment_group_id).
550
+ - A operação é irreversível.`,
551
+ inputSchema: {
552
+ type: "object",
553
+ properties: {
554
+ transaction_id: { type: "string", description: "UUID da transação" },
555
+ },
556
+ required: ["transaction_id"],
557
+ },
558
+ },
559
+ {
560
+ name: "fin_ajuste_fatura",
561
+ description: `Cria ou atualiza um ajuste manual no total de uma fatura de cartão de crédito. Ajustes somam (positivo) ou subtraem (negativo) do net_total da fatura.
562
+
563
+ QUANDO USAR:
564
+ - O banco aplicou um crédito/cancelamento de outro ciclo como desconto no pagamento da fatura (cross-cycle credit). Criar ajuste NEGATIVO na fatura que recebeu o crédito.
565
+ - O banco lista um estorno na fatura mas NÃO desconta do total. Criar ajuste POSITIVO pra anular o efeito do refund no net_total.
566
+
567
+ NÃO USAR QUANDO:
568
+ - O estorno/crédito se comporta normalmente (abate do total na mesma fatura que a compra). Nesse caso, basta lançar o estorno com fin_criar_despesa + reversal_of_id.
569
+ - Quiser mover uma transação entre faturas (usar invoice_cycle_end em fin_criar_despesa ou fin_editar_transacao).
570
+
571
+ CAMPOS:
572
+ - card_name: string — nome do cartão de crédito (obrigatório, match parcial).
573
+ - cycle_end: string YYYY-MM-DD — data de fechamento do ciclo da fatura (obrigatório). ATENÇÃO: é a data de fechamento (closing_day), NÃO o reference_month. Use fin_fatura_transacoes pra descobrir o cycle_end de uma fatura.
574
+ - amount_cents: number — valor do ajuste em centavos (obrigatório). Positivo = aumenta o total, Negativo = reduz o total.
575
+
576
+ REGRAS DE NEGÓCIO:
577
+ - Só funciona para cartões de crédito (type: credit).
578
+ - Um ajuste por (cartão, ciclo). Se já existe, o valor é sobrescrito.
579
+ - Ajustes aparecem como kind: 'adjustment' na listagem da fatura.
580
+ - Valores em centavos.
581
+
582
+ EXEMPLO — crédito cross-cycle (GoDaddy R$593,22 aplicado na fatura Abr):
583
+ { "card_name": "Latam Itaú", "cycle_end": "2026-04-01", "amount_cents": -59322 }
584
+
585
+ EXEMPLO — anular estorno que não abate (GitHub R$38,63):
586
+ { "card_name": "Latam Itaú", "cycle_end": "2026-04-01", "amount_cents": 3863 }`,
587
+ inputSchema: {
588
+ type: "object",
589
+ properties: {
590
+ card_name: { type: "string", description: "Nome do cartão de crédito" },
591
+ cycle_end: { type: "string", description: "YYYY-MM-DD — data de fechamento do ciclo da fatura" },
592
+ amount_cents: { type: "number", description: "Valor em centavos (positivo ou negativo)" },
593
+ },
594
+ required: ["card_name", "cycle_end", "amount_cents"],
595
+ },
596
+ },
597
+ // ── Bills (Contas a Pagar) ──────────────────────────────────────
598
+ {
599
+ name: "fin_listar_bills",
600
+ description: `Lista todas as contas a pagar recorrentes ativas (Light, condomínio, internet, etc.).
601
+
602
+ QUANDO USAR: para ver quais contas recorrentes estão cadastradas, seus valores default e dia de vencimento.
603
+ NÃO USAR QUANDO: quiser saber o que falta pagar no mês (usar fin_bills_do_mes).
604
+
605
+ REGRAS DE NEGÓCIO:
606
+ - Retorna apenas bills ativas (is_active: true).
607
+ - amount_type 'fixed' = valor fixo todo mês; 'variable' = valor muda.
608
+ - default_amount_cents: valor padrão da conta (pode ser null se variable).
609
+ - Valores em centavos.`,
610
+ inputSchema: { type: "object", properties: {} },
611
+ },
612
+ {
613
+ name: "fin_bills_do_mes",
614
+ description: `Lista as contas a pagar do mês com status: pendente, atrasada, paga, ou paga em atraso. Responde "o que falta pagar este mês?".
615
+
616
+ QUANDO USAR: no início de sessão financeira para saber quais contas recorrentes já foram pagas e quais estão pendentes/atrasadas.
617
+ NÃO USAR QUANDO: quiser ver transações gerais (usar fin_todas_transacoes) ou faturas de cartão (usar fin_fatura_cartao).
618
+
619
+ CAMPOS:
620
+ - month: string YYYY-MM — mês de referência (default: mês atual).
621
+
622
+ REGRAS DE NEGÓCIO:
623
+ - Status: 'pending' (aguardando vencimento), 'overdue' (vencida e não paga), 'paid' (paga no prazo), 'paid_late' (paga em atraso).
624
+ - Ocorrências são geradas automaticamente para cada bill ativa no mês consultado.
625
+ - amount_cents: valor definido para esta ocorrência (null se ainda não definido — usar default_amount_cents da bill).
626
+ - transaction_id: preenchido quando a conta foi paga (link para a transação de despesa).
627
+ - Valores em centavos.
628
+
629
+ EXEMPLO:
630
+ { "month": "2026-04" }
631
+
632
+ Retorno: [{ bill_name: "Light", status: "pending", due_date: "2026-04-01", amount_cents: null }, ...]`,
633
+ inputSchema: {
634
+ type: "object",
635
+ properties: {
636
+ month: { type: "string", description: "YYYY-MM (default: mês atual)" },
637
+ },
638
+ },
639
+ },
640
+ {
641
+ name: "fin_pagar_bill",
642
+ description: `Marca uma conta a pagar como paga e cria a transação de despesa automaticamente.
643
+
644
+ QUANDO USAR: ao conciliar extrato e encontrar pagamento de uma conta recorrente (Light, condomínio, faxina, etc.). Use fin_bills_do_mes primeiro para obter o occurrence_id.
645
+ NÃO USAR QUANDO: quiser lançar despesa avulsa que não é conta recorrente (usar fin_criar_despesa).
646
+
647
+ CAMPOS:
648
+ - occurrence_id: string UUID — ID da ocorrência (obrigatório, obter via fin_bills_do_mes).
649
+ - amount_cents: number — valor pago em centavos (obrigatório).
650
+ - account_name: string — conta de onde saiu o dinheiro (obrigatório, match parcial).
651
+ - category_name: string — categoria da despesa (opcional, usa default da bill se omitido).
652
+ - subcategory_name: string — subcategoria (opcional).
653
+ - payment_date: string YYYY-MM-DD — data do pagamento (default: hoje).
654
+
655
+ REGRAS DE NEGÓCIO:
656
+ - Se a ocorrência já foi paga, retorna erro.
657
+ - Cria transação de despesa automaticamente (não precisa chamar fin_criar_despesa).
658
+ - Se category_name não for informado, usa a categoria default cadastrada na bill.
659
+ - Valores em centavos.
660
+ - Não suporta multa/juros no momento.
661
+
662
+ EXEMPLO:
663
+ { "occurrence_id": "uuid-da-ocorrencia", "amount_cents": 35000, "account_name": "Itaú", "payment_date": "2026-04-01" }`,
664
+ inputSchema: {
665
+ type: "object",
666
+ properties: {
667
+ occurrence_id: { type: "string", description: "UUID da ocorrência (de fin_bills_do_mes)" },
668
+ amount_cents: { type: "number", description: "Valor pago em centavos" },
669
+ account_name: { type: "string", description: "Nome da conta" },
670
+ category_name: { type: "string", description: "Categoria (opcional, usa default da bill)" },
671
+ subcategory_name: { type: "string", description: "Subcategoria (opcional)" },
672
+ payment_date: { type: "string", description: "YYYY-MM-DD (default: hoje)" },
673
+ },
674
+ required: ["occurrence_id", "amount_cents", "account_name"],
675
+ },
676
+ },
677
+ {
678
+ name: "fin_criar_bill",
679
+ description: `Cria uma nova conta a pagar recorrente (ex: Light, condomínio, streaming).
680
+
681
+ QUANDO USAR: o usuário contratou um novo serviço ou tem uma nova conta fixa mensal.
682
+ NÃO USAR QUANDO: quiser pagar uma conta existente (usar fin_pagar_bill).
683
+
684
+ CAMPOS:
685
+ - name: string — nome da conta (obrigatório, ex: "Light", "Condomínio").
686
+ - due_day: number — dia de vencimento, 1-31 (obrigatório).
687
+ - amount_type: string — 'fixed' (valor fixo) ou 'variable' (muda todo mês). Default: 'variable'.
688
+ - default_amount_cents: number — valor padrão em centavos (para fixed).
689
+ - account_name: string — conta default de pagamento (match parcial).
690
+ - category_name: string — categoria default.
691
+ - subcategory_name: string — subcategoria default.
692
+
693
+ REGRAS DE NEGÓCIO:
694
+ - amount_type 'fixed' = valor fixo todo mês (ex: condomínio R$1.383).
695
+ - amount_type 'variable' = valor muda (ex: Light). Definido no momento do pagamento.
696
+ - Valores em centavos.
697
+
698
+ EXEMPLO:
699
+ { "name": "Light", "due_day": 1, "amount_type": "variable", "category_name": "Moradia", "account_name": "Itaú" }`,
700
+ inputSchema: {
701
+ type: "object",
702
+ properties: {
703
+ name: { type: "string", description: "Nome da conta (ex: Light, Condomínio)" },
704
+ due_day: { type: "number", description: "Dia de vencimento (1-31)" },
705
+ amount_type: { type: "string", description: "'fixed' ou 'variable' (default: variable)" },
706
+ default_amount_cents: { type: "number", description: "Valor padrão em centavos (para fixed)" },
707
+ account_name: { type: "string", description: "Conta default de pagamento" },
708
+ category_name: { type: "string", description: "Categoria default" },
709
+ subcategory_name: { type: "string", description: "Subcategoria default" },
710
+ },
711
+ required: ["name", "due_day"],
712
+ },
713
+ },
714
+ {
715
+ name: "fin_editar_bill",
716
+ description: `Edita uma conta a pagar recorrente: nome, dia de vencimento, valor, categoria, ou desativar.
717
+
718
+ QUANDO USAR: mudar configurações de uma conta existente ou desativar.
719
+
720
+ CAMPOS:
721
+ - bill_id: string UUID — ID da bill (obrigatório, obter via fin_listar_bills).
722
+ - name: string — novo nome.
723
+ - due_day: number — novo dia de vencimento (1-31).
724
+ - amount_type: string — 'fixed' ou 'variable'.
725
+ - default_amount_cents: number — novo valor padrão em centavos.
726
+ - account_name: string — nova conta default.
727
+ - category_name: string — nova categoria default.
728
+ - subcategory_name: string — nova subcategoria default.
729
+ - is_active: boolean — false para desativar (soft delete).
730
+
731
+ REGRAS DE NEGÓCIO:
732
+ - Campos omitidos não são alterados.
733
+ - is_active: false desativa a bill (não gera novas ocorrências, mas mantém histórico).
734
+ - Valores em centavos.`,
735
+ inputSchema: {
736
+ type: "object",
737
+ properties: {
738
+ bill_id: { type: "string", description: "UUID da bill" },
739
+ name: { type: "string", description: "Novo nome" },
740
+ due_day: { type: "number", description: "Novo dia de vencimento (1-31)" },
741
+ amount_type: { type: "string", description: "'fixed' ou 'variable'" },
742
+ default_amount_cents: { type: "number", description: "Novo valor padrão em centavos" },
743
+ account_name: { type: "string", description: "Nova conta default" },
744
+ category_name: { type: "string", description: "Nova categoria default" },
745
+ subcategory_name: { type: "string", description: "Nova subcategoria default" },
746
+ is_active: { type: "boolean", description: "false para desativar" },
747
+ },
748
+ required: ["bill_id"],
749
+ },
750
+ },
751
+ // ── Admin / Setup ─────────────────────────────────────────────────
752
+ {
753
+ name: "fin_criar_conta",
754
+ description: `Cria uma nova conta bancária.
755
+
756
+ QUANDO USAR: o usuário quer adicionar uma conta corrente, carteira, poupança ou cartão de crédito.
757
+
758
+ CAMPOS:
759
+ - name: string — nome da conta (obrigatório, ex: "Nubank", "Inter", "Carteira").
760
+ - institution: string — nome da instituição financeira.
761
+ - type: string — 'cash' (corrente/carteira/poupança) ou 'credit' (cartão de crédito) (obrigatório).
762
+ - initial_balance_cents: number — saldo inicial em centavos (default: 0).
763
+ - closing_day: number — dia de fechamento da fatura, 1-28 (obrigatório para type 'credit').
764
+ - due_day: number — dia de vencimento da fatura, 1-28 (obrigatório para type 'credit').
765
+ - credit_limit_cents: number — limite de crédito em centavos (para type 'credit').
766
+
767
+ REGRAS DE NEGÓCIO:
768
+ - Cartão de crédito (type 'credit') EXIGE closing_day e due_day.
769
+ - closing_day e due_day devem estar entre 1 e 28.
770
+ - Valores em centavos.`,
771
+ inputSchema: {
772
+ type: "object",
773
+ properties: {
774
+ name: { type: "string", description: "Nome da conta (ex: Nubank, Inter)" },
775
+ institution: { type: "string", description: "Nome da instituição financeira" },
776
+ type: { type: "string", description: "'cash' ou 'credit'" },
777
+ initial_balance_cents: { type: "number", description: "Saldo inicial em centavos (default: 0)" },
778
+ closing_day: { type: "number", description: "Dia de fechamento da fatura (1-28, obrigatório para credit)" },
779
+ due_day: { type: "number", description: "Dia de vencimento da fatura (1-28, obrigatório para credit)" },
780
+ credit_limit_cents: { type: "number", description: "Limite de crédito em centavos (para credit)" },
781
+ },
782
+ required: ["name", "type"],
783
+ },
784
+ },
785
+ {
786
+ name: "fin_editar_conta",
787
+ description: `Edita uma conta bancária existente. Campos omitidos permanecem inalterados.
788
+
789
+ QUANDO USAR: corrigir nome, instituição, limites ou dias de fechamento/vencimento.
790
+
791
+ CAMPOS:
792
+ - account_id: string UUID — ID da conta (obrigatório).
793
+ - name: string — novo nome.
794
+ - institution: string — nova instituição.
795
+ - type: string — 'cash' ou 'credit'.
796
+ - initial_balance_cents: number — novo saldo inicial em centavos.
797
+ - closing_day: number — novo dia de fechamento (1-28).
798
+ - due_day: number — novo dia de vencimento (1-28).
799
+ - credit_limit_cents: number — novo limite de crédito em centavos.
800
+
801
+ REGRAS DE NEGÓCIO:
802
+ - Valores em centavos.
803
+ - Campos omitidos não são alterados.`,
804
+ inputSchema: {
805
+ type: "object",
806
+ properties: {
807
+ account_id: { type: "string", description: "UUID da conta" },
808
+ name: { type: "string", description: "Novo nome" },
809
+ institution: { type: "string", description: "Nova instituição" },
810
+ type: { type: "string", description: "'cash' ou 'credit'" },
811
+ initial_balance_cents: { type: "number", description: "Novo saldo inicial em centavos" },
812
+ closing_day: { type: "number", description: "Novo dia de fechamento (1-28)" },
813
+ due_day: { type: "number", description: "Novo dia de vencimento (1-28)" },
814
+ credit_limit_cents: { type: "number", description: "Novo limite de crédito em centavos" },
815
+ },
816
+ required: ["account_id"],
817
+ },
818
+ },
819
+ {
820
+ name: "fin_criar_categoria",
821
+ description: `Cria uma nova categoria de transação.
822
+
823
+ QUANDO USAR: o usuário quer uma nova categoria que não existe.
824
+ NÃO USAR QUANDO: a categoria já existir (verificar com fin_listar_categorias).
825
+
826
+ CAMPOS:
827
+ - name: string — nome da categoria (obrigatório).
828
+ - flow: string — 'expense' (despesa) ou 'income' (receita) (obrigatório).
829
+
830
+ REGRAS DE NEGÓCIO:
831
+ - flow define se a categoria é usada em despesas ou receitas.
832
+ - Não é possível usar uma categoria de flow 'income' em despesas e vice-versa.`,
833
+ inputSchema: {
834
+ type: "object",
835
+ properties: {
836
+ name: { type: "string", description: "Nome da categoria" },
837
+ flow: { type: "string", description: "'expense' ou 'income'" },
838
+ },
839
+ required: ["name", "flow"],
840
+ },
841
+ },
842
+ {
843
+ name: "fin_editar_categoria",
844
+ description: `Edita uma categoria existente: nome, flow, ou status (ativar/arquivar).
845
+
846
+ QUANDO USAR: renomear, mudar flow, ou arquivar uma categoria.
847
+
848
+ CAMPOS:
849
+ - category_id: string UUID — ID da categoria (obrigatório).
850
+ - name: string — novo nome.
851
+ - flow: string — 'expense' ou 'income'.
852
+ - is_active: boolean — true para ativar, false para arquivar.
853
+
854
+ REGRAS DE NEGÓCIO:
855
+ - is_active: false arquiva a categoria (seta archived_at automaticamente).
856
+ - is_active: true reativa (limpa archived_at).
857
+ - Categorias arquivadas não aparecem como opção ao criar transações, mas transações antigas permanecem.`,
858
+ inputSchema: {
859
+ type: "object",
860
+ properties: {
861
+ category_id: { type: "string", description: "UUID da categoria" },
862
+ name: { type: "string", description: "Novo nome" },
863
+ flow: { type: "string", description: "'expense' ou 'income'" },
864
+ is_active: { type: "boolean", description: "true pra ativar, false pra arquivar" },
865
+ },
866
+ required: ["category_id"],
867
+ },
868
+ },
869
+ {
870
+ name: "fin_criar_subcategoria",
871
+ description: `Cria uma nova subcategoria vinculada a uma categoria existente.
872
+
873
+ QUANDO USAR: o usuário quer subdividir uma categoria (ex: "Alimentação" → "Restaurantes", "Mercado").
874
+ NÃO USAR QUANDO: a subcategoria já existir (verificar com fin_listar_categorias).
875
+
876
+ CAMPOS:
877
+ - name: string — nome da subcategoria (obrigatório).
878
+ - category_name: string — nome da categoria pai (obrigatório, resolvido por nome).
879
+
880
+ REGRAS DE NEGÓCIO:
881
+ - A categoria pai deve existir.
882
+ - A subcategoria herda o flow da categoria pai.`,
883
+ inputSchema: {
884
+ type: "object",
885
+ properties: {
886
+ name: { type: "string", description: "Nome da subcategoria" },
887
+ category_name: { type: "string", description: "Nome da categoria pai" },
888
+ },
889
+ required: ["name", "category_name"],
890
+ },
891
+ },
892
+ {
893
+ name: "fin_editar_subcategoria",
894
+ description: `Edita uma subcategoria existente: nome, categoria pai, ou status.
895
+
896
+ QUANDO USAR: renomear, mover para outra categoria pai, ou arquivar.
897
+
898
+ CAMPOS:
899
+ - subcategory_id: string UUID — ID da subcategoria (obrigatório).
900
+ - name: string — novo nome.
901
+ - category_name: string — nome da nova categoria pai.
902
+ - is_active: boolean — true para ativar, false para arquivar.
903
+
904
+ REGRAS DE NEGÓCIO:
905
+ - Mesma lógica de arquivamento que categorias (is_active: false seta archived_at).`,
906
+ inputSchema: {
907
+ type: "object",
908
+ properties: {
909
+ subcategory_id: { type: "string", description: "UUID da subcategoria" },
910
+ name: { type: "string", description: "Novo nome" },
911
+ category_name: { type: "string", description: "Nome da nova categoria pai" },
912
+ is_active: { type: "boolean", description: "true pra ativar, false pra arquivar" },
913
+ },
914
+ required: ["subcategory_id"],
915
+ },
916
+ },
917
+ ];
918
+
919
+ // ── Tool handlers ───────────────────────────────────────────────────
920
+
921
+ async function handleTool(name, args) {
922
+ switch (name) {
923
+ case "fin_resumo_mensal": {
924
+ const qs = args.month ? `?month=${args.month}` : "";
925
+ return ok(await api("GET", `/api/genius/summary${qs}`));
926
+ }
927
+ case "fin_gastos_por_categoria": {
928
+ const qs = args.month ? `?month=${args.month}` : "";
929
+ return ok(await api("GET", `/api/genius/spending${qs}`));
930
+ }
931
+ case "fin_saldos": {
932
+ const qs = args.account_name
933
+ ? `?account_name=${encodeURIComponent(args.account_name)}`
934
+ : "";
935
+ return ok(await api("GET", `/api/genius/balance${qs}`));
936
+ }
937
+ case "fin_buscar_transacoes": {
938
+ const params = new URLSearchParams();
939
+ if (args.q) params.set("q", args.q);
940
+ if (args.date_from) params.set("date_from", args.date_from);
941
+ if (args.date_to) params.set("date_to", args.date_to);
942
+ if (args.category) params.set("category", args.category);
943
+ if (args.account) params.set("account", args.account);
944
+ if (args.limit) params.set("limit", String(args.limit));
945
+ const qs = params.toString() ? `?${params}` : "";
946
+ return ok(await api("GET", `/api/genius/transactions${qs}`));
947
+ }
948
+ case "fin_fatura_cartao": {
949
+ const params = new URLSearchParams();
950
+ if (args.card_name) params.set("card_name", args.card_name);
951
+ if (args.reference_month)
952
+ params.set("reference_month", args.reference_month);
953
+ const qs = params.toString() ? `?${params}` : "";
954
+ return ok(await api("GET", `/api/genius/card-invoice${qs}`));
955
+ }
956
+ case "fin_contexto": {
957
+ return ok(await api("GET", `/api/genius/context`));
958
+ }
959
+ case "fin_todas_transacoes": {
960
+ const params = new URLSearchParams();
961
+ if (args.account) params.set("account", args.account);
962
+ if (args.date_from) params.set("date_from", args.date_from);
963
+ if (args.date_to) params.set("date_to", args.date_to);
964
+ if (args.tx_type) params.set("tx_type", args.tx_type);
965
+ if (args.category) params.set("category", args.category);
966
+ if (args.page) params.set("page", String(args.page));
967
+ if (args.per_page) params.set("per_page", String(args.per_page));
968
+ const qs = params.toString() ? `?${params}` : "";
969
+ return ok(await api("GET", `/api/genius/transactions/all${qs}`));
970
+ }
971
+ case "fin_relatorio": {
972
+ const params = new URLSearchParams();
973
+ params.set("from_month", args.from_month);
974
+ params.set("to_month", args.to_month);
975
+ return ok(await api("GET", `/api/genius/report?${params}`));
976
+ }
977
+ case "fin_receitas": {
978
+ const params = new URLSearchParams();
979
+ if (args.account) params.set("account", args.account);
980
+ if (args.date_from) params.set("date_from", args.date_from);
981
+ if (args.date_to) params.set("date_to", args.date_to);
982
+ if (args.page) params.set("page", String(args.page));
983
+ if (args.per_page) params.set("per_page", String(args.per_page));
984
+ const qs = params.toString() ? `?${params}` : "";
985
+ return ok(await api("GET", `/api/genius/income${qs}`));
986
+ }
987
+ case "fin_fatura_transacoes": {
988
+ const params = new URLSearchParams();
989
+ params.set("card_name", args.card_name);
990
+ if (args.reference_month) params.set("reference_month", args.reference_month);
991
+ return ok(await api("GET", `/api/genius/card-invoice/transactions?${params}`));
992
+ }
993
+ case "fin_criar_despesa": {
994
+ return ok(await api("POST", `/api/genius/expenses`, args));
995
+ }
996
+ case "fin_criar_receita": {
997
+ return ok(await api("POST", `/api/genius/income`, args));
998
+ }
999
+ case "fin_criar_transferencia": {
1000
+ return ok(await api("POST", `/api/genius/transfers`, args));
1001
+ }
1002
+ case "fin_pagar_fatura": {
1003
+ return ok(await api("POST", `/api/genius/card-payments`, args));
1004
+ }
1005
+ case "fin_editar_transacao": {
1006
+ const { transaction_id, ...body } = args;
1007
+ return ok(await api("PATCH", `/api/genius/transactions/${transaction_id}`, body));
1008
+ }
1009
+ case "fin_deletar_transacao": {
1010
+ return ok(
1011
+ await api("DELETE", `/api/genius/transactions/${args.transaction_id}`)
1012
+ );
1013
+ }
1014
+ case "fin_ajuste_fatura": {
1015
+ return ok(await api("POST", `/api/genius/card-invoice-adjustments`, args));
1016
+ }
1017
+ case "fin_listar_bills": {
1018
+ return ok(await api("GET", `/api/genius/bills`));
1019
+ }
1020
+ case "fin_bills_do_mes": {
1021
+ const qs = args.month ? `?month=${args.month}` : "";
1022
+ return ok(await api("GET", `/api/genius/bills/occurrences${qs}`));
1023
+ }
1024
+ case "fin_pagar_bill": {
1025
+ return ok(await api("POST", `/api/genius/bills/pay`, args));
1026
+ }
1027
+ case "fin_criar_bill": {
1028
+ return ok(await api("POST", `/api/genius/bills`, args));
1029
+ }
1030
+ case "fin_editar_bill": {
1031
+ const { bill_id, ...body } = args;
1032
+ return ok(await api("PATCH", `/api/genius/bills/${bill_id}`, body));
1033
+ }
1034
+ // ── Admin / Setup handlers ──────────────────────────────────────
1035
+ case "fin_listar_contas": {
1036
+ return ok(await api("GET", `/api/genius/accounts`));
1037
+ }
1038
+ case "fin_criar_conta": {
1039
+ return ok(await api("POST", `/api/genius/accounts`, args));
1040
+ }
1041
+ case "fin_editar_conta": {
1042
+ const { account_id, ...body } = args;
1043
+ return ok(await api("PATCH", `/api/genius/accounts/${account_id}`, body));
1044
+ }
1045
+ case "fin_listar_categorias": {
1046
+ return ok(await api("GET", `/api/genius/categories`));
1047
+ }
1048
+ case "fin_criar_categoria": {
1049
+ return ok(await api("POST", `/api/genius/categories`, args));
1050
+ }
1051
+ case "fin_editar_categoria": {
1052
+ const { category_id, ...body } = args;
1053
+ return ok(await api("PATCH", `/api/genius/categories/${category_id}`, body));
1054
+ }
1055
+ case "fin_criar_subcategoria": {
1056
+ return ok(await api("POST", `/api/genius/subcategories`, args));
1057
+ }
1058
+ case "fin_editar_subcategoria": {
1059
+ const { subcategory_id, ...body } = args;
1060
+ return ok(await api("PATCH", `/api/genius/subcategories/${subcategory_id}`, body));
1061
+ }
1062
+ default:
1063
+ return err(`Tool desconhecida: ${name}`);
1064
+ }
1065
+ }
1066
+
1067
+ // ── Server ──────────────────────────────────────────────────────────
1068
+
1069
+ const server = new Server(
1070
+ { name: "fin-app", version: "2.0.0" },
1071
+ { capabilities: { tools: {}, resources: {} } }
1072
+ );
1073
+
1074
+ // Tools
1075
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
1076
+
1077
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
1078
+ try {
1079
+ return await handleTool(req.params.name, req.params.arguments ?? {});
1080
+ } catch (e) {
1081
+ return err(e.message);
1082
+ }
1083
+ });
1084
+
1085
+ // Resources
1086
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
1087
+ resources: [
1088
+ {
1089
+ uri: "fin://docs/guia",
1090
+ name: "Guia do FIN",
1091
+ description:
1092
+ "Regras de negócio do FIN: ciclo de fatura, estorno, parcelamento, modelo de dados. Leia antes de operar.",
1093
+ mimeType: "text/markdown",
1094
+ },
1095
+ ],
1096
+ }));
1097
+
1098
+ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
1099
+ if (req.params.uri === "fin://docs/guia") {
1100
+ return {
1101
+ contents: [
1102
+ { uri: "fin://docs/guia", text: guiaContent, mimeType: "text/markdown" },
1103
+ ],
1104
+ };
1105
+ }
1106
+ throw new Error(`Resource não encontrado: ${req.params.uri}`);
1107
+ });
1108
+
1109
+ // Start
1110
+ const transport = new StdioServerTransport();
1111
+ await server.connect(transport);