next-finance-mcp 0.9.0 → 0.9.2

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.
package/dist/client.js CHANGED
@@ -490,8 +490,15 @@ export async function listarContas(filtro = {}) {
490
490
  }
491
491
  export async function listarPlanoDeContas(filtro = {}) {
492
492
  // POST /api/PlanoDeContas/Filtrar aceita body vazio e retorna a lista completa
493
- const raw = await apiPost("/api/PlanoDeContas/Filtrar", {});
494
- let planos = Array.isArray(raw) ? raw : Object.values(raw).find(v => Array.isArray(v)) ?? [];
493
+ let raw;
494
+ try {
495
+ raw = await apiPost("/api/PlanoDeContas/Filtrar", {});
496
+ }
497
+ catch {
498
+ throw new Error("Plano de contas indisponível no momento (API REST fora do ar). Verifique os logs do servidor.");
499
+ }
500
+ const rawArr = raw;
501
+ let planos = Array.isArray(rawArr) ? rawArr : (Object.values(rawArr).find(v => Array.isArray(v)) ?? []);
495
502
  if (filtro.busca?.trim()) {
496
503
  const termo = filtro.busca.trim().toLowerCase();
497
504
  planos = planos.filter(p => {
@@ -516,8 +523,14 @@ export async function listarPlanoDeContas(filtro = {}) {
516
523
  }
517
524
  // ── Centro de Custos ─────────────────────────────────────────────────────────
518
525
  export async function listarCentrosDeCusto(busca) {
519
- const raw = await apiGet("/api/CentroDeCustos");
520
- let centros = Array.isArray(raw) ? raw : Object.values(raw).find(v => Array.isArray(v)) ?? [];
526
+ let raw;
527
+ try {
528
+ raw = await apiGet("/api/CentroDeCustos");
529
+ }
530
+ catch {
531
+ throw new Error("Centros de custo indisponíveis no momento (API REST fora do ar). Verifique os logs do servidor.");
532
+ }
533
+ let centros = Array.isArray(raw) ? raw : (Object.values(raw).find(v => Array.isArray(v)) ?? []);
521
534
  if (busca?.trim()) {
522
535
  const t = busca.trim().toLowerCase();
523
536
  centros = centros.filter(c => {
@@ -587,17 +600,37 @@ export async function buscarComprasProduto(opts = {}) {
587
600
  };
588
601
  }
589
602
  export async function listarTiposConta() {
590
- const raw = await apiGet("/api/TipoConta");
591
- const arr = Array.isArray(raw) ? raw : Object.values(raw).find(v => Array.isArray(v)) ?? [];
592
- // Exibe apenas o nome — ID é interno
603
+ let raw;
604
+ try {
605
+ raw = await apiGet("/api/TipoConta");
606
+ }
607
+ catch {
608
+ // Fallback web: TipoContaAjax/ListarTodos
609
+ const r = await webGet("/TipoContaAjax/ListarTodos");
610
+ if (r.status !== 200)
611
+ throw new Error("Tipos de conta indisponíveis no momento.");
612
+ raw = JSON.parse(r.text);
613
+ }
614
+ const arr = Array.isArray(raw) ? raw : (Object.values(raw).find(v => Array.isArray(v)) ?? []);
593
615
  const tipos = arr.map(t => ({ nome: String(t["Nome"] ?? t["Descricao"] ?? "") })).filter(t => t.nome);
594
616
  return { tipos };
595
617
  }
596
618
  // ── Moedas ────────────────────────────────────────────────────────────────────
597
619
  // Usa GET /api/Moeda
598
620
  export async function listarMoedas(busca) {
599
- const raw = await apiGet("/api/Moeda");
600
- let moedas = Array.isArray(raw) ? raw : Object.values(raw).find(v => Array.isArray(v)) ?? [];
621
+ let raw;
622
+ try {
623
+ raw = await apiGet("/api/Moeda");
624
+ }
625
+ catch {
626
+ // Fallback web: MoedaAjax/ListarTodos
627
+ const r = await webGet("/MoedaAjax/ListarTodos");
628
+ if (r.status !== 200)
629
+ throw new Error("Moedas indisponíveis no momento.");
630
+ raw = JSON.parse(r.text);
631
+ }
632
+ const rawArr = raw;
633
+ let moedas = Array.isArray(rawArr) ? rawArr : Object.values(rawArr).find(v => Array.isArray(v)) ?? [];
601
634
  if (busca?.trim()) {
602
635
  const t = busca.trim().toLowerCase();
603
636
  moedas = moedas.filter(m => {
@@ -753,7 +786,8 @@ export async function buscarItensDespesa(opts = {}) {
753
786
  };
754
787
  }
755
788
  function slimLancamento(l) {
756
- const divs = l["ListaLancamentoDivisao"] ?? [];
789
+ // Ambos os endpoints (REST e web legado) usam ListaLancamentoDivisao
790
+ const divs = (l["ListaLancamentoDivisao"] ?? l["LancamentoDivisoes"] ?? l["Divisoes"]) ?? [];
757
791
  // Divisões: cada uma tem seu próprio valor (+/-), plano de contas e centro de custo
758
792
  const divisoes = divs.map(d => ({
759
793
  valor: Number(d["Valor"] ?? 0),
@@ -763,11 +797,12 @@ function slimLancamento(l) {
763
797
  // Raiz: plano e centro só existem quando não há divisões
764
798
  const plano = String(l["NomePlanoDeContas"] ?? "").trim();
765
799
  const centro = String(l["NomeCentroDeCustos"] ?? "").trim();
766
- const conta = String(l["NomeConta"] ?? l["NomeContaFinanceira"] ?? l["Conta"] ?? "").trim();
800
+ // NomeConta pode vir de vários campos dependendo do endpoint (REST vs web legado)
801
+ const conta = String(l["NomeConta"] ?? l["NomeContaFinanceira"] ?? l["Conta"] ?? l["ContaNome"] ?? "").trim();
767
802
  return {
768
- data: String(l["Data"] ?? l["DataLancamento"] ?? "").slice(0, 10),
803
+ data: String(l["Data"] ?? l["DataLancamento"] ?? l["DataCompetencia"] ?? "").slice(0, 10),
769
804
  valor: Number(l["Valor"] ?? 0),
770
- descricao: String(l["Descricao"] ?? l["Historico"] ?? "").trim(),
805
+ descricao: String(l["Descricao"] ?? l["Historico"] ?? l["Observacao"] ?? "").trim(),
771
806
  conta,
772
807
  plano_de_contas: plano,
773
808
  centro_de_custo: centro,
@@ -775,7 +810,7 @@ function slimLancamento(l) {
775
810
  despesa: Boolean(l["Despesa"]),
776
811
  receita: Boolean(l["Receita"]),
777
812
  previsao: Boolean(l["Previsao"]),
778
- transferencia: !!l["IdLancamentoTransferencia"],
813
+ transferencia: !!(l["IdLancamentoTransferencia"] ?? l["Transferencia"]),
779
814
  observacoes: l["Observacoes"] ? String(l["Observacoes"]).trim() : undefined,
780
815
  numero_documento: l["NumeroDocumento"] ? String(l["NumeroDocumento"]).trim() : undefined,
781
816
  };
@@ -826,27 +861,37 @@ export async function buscarLancamentos(opts) {
826
861
  params.set("filtro[ListarOrdenadoPelaDataDeModificacao]", "false");
827
862
  const r = await webPost("/LancamentoAjax/Filtrar", params.toString(), "application/x-www-form-urlencoded");
828
863
  if (r.status !== 200)
829
- throw new Error(`Erro ${r.status} ao buscar lançamentos.`);
864
+ throw new Error(`Erro ${r.status} ao buscar lançamentos (endpoint legado).`);
830
865
  const raw = JSON.parse(r.text);
831
- lancamentos = Array.isArray(raw) ? raw
832
- : (Object.values(raw).find(v => Array.isArray(v)) ?? []);
866
+ // O endpoint legado retorna { Lancamentos: [...], SaldosPorMes: [...], ... }
867
+ // Extraímos explicitamente o campo Lancamentos para não confundir com SaldosPorMes
868
+ if (Array.isArray(raw)) {
869
+ lancamentos = raw;
870
+ }
871
+ else if (Array.isArray(raw["Lancamentos"])) {
872
+ lancamentos = raw["Lancamentos"];
873
+ }
874
+ else {
875
+ lancamentos = (Object.values(raw).find(v => Array.isArray(v)) ?? []);
876
+ }
833
877
  }
834
878
  // Helpers para verificar plano/centro tanto no lançamento raiz quanto nas divisões
835
879
  // (lançamentos rateados têm NomePlanoDeContas vazio no raiz e preenchido em ListaLancamentoDivisao)
880
+ function getDivs(l) {
881
+ return (l["ListaLancamentoDivisao"] ?? l["LancamentoDivisoes"] ?? l["Divisoes"]) ?? [];
882
+ }
836
883
  function nomesPlano(l) {
837
884
  const raiz = String(l["NomePlanoDeContas"] ?? "");
838
- const divs = l["ListaLancamentoDivisao"] ?? [];
839
- return [raiz, ...divs.map(d => String(d["NomePlanoDeContas"] ?? ""))].filter(Boolean);
885
+ return [raiz, ...getDivs(l).map(d => String(d["NomePlanoDeContas"] ?? ""))].filter(Boolean);
840
886
  }
841
887
  function nomesCentro(l) {
842
888
  const raiz = String(l["NomeCentroDeCustos"] ?? "");
843
- const divs = l["ListaLancamentoDivisao"] ?? [];
844
- return [raiz, ...divs.map(d => String(d["NomeCentroDeCustos"] ?? ""))].filter(Boolean);
889
+ return [raiz, ...getDivs(l).map(d => String(d["NomeCentroDeCustos"] ?? ""))].filter(Boolean);
845
890
  }
846
891
  // Filtros client-side (a API não filtra por plano/centro/tipo)
847
892
  const excluirTransf = opts.excluirTransferencias !== false; // default: true
848
893
  if (excluirTransf)
849
- lancamentos = lancamentos.filter(l => !l["IdLancamentoTransferencia"]);
894
+ lancamentos = lancamentos.filter(l => !l["IdLancamentoTransferencia"] && !l["Transferencia"]);
850
895
  if (opts.excluirPrevisoes)
851
896
  lancamentos = lancamentos.filter(l => !l["Previsao"]);
852
897
  if (opts.apenasDespesas)
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
8
8
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
9
  import * as client from "./client.js";
10
10
  import { openLoginUI } from "./login-server.js";
11
- const server = new Server({ name: "next-finance-mcp", version: "0.9.0" }, { capabilities: { tools: {} } });
11
+ const server = new Server({ name: "next-finance-mcp", version: "0.9.2" }, { capabilities: { tools: {} } });
12
12
  // ── Ferramentas ─────────────────────────────────────────────────────────────
13
13
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
14
14
  tools: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-finance-mcp",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "mcpName": "io.github.paivapiovesan/next-finance",
5
5
  "description": "MCP Server para o NEXT Finance (finance.net.br) — login via browser, listagem de carteiras, contas e lançamentos",
6
6
  "type": "module",