@conflaro/mcp 1.9.0 → 1.11.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.
package/README.md CHANGED
@@ -96,7 +96,7 @@ faltou, sem erro de versão duplicada.
96
96
  recusam quando divergem. O servidor lê a dele do `package.json`.
97
97
 
98
98
  O npm publica por **Trusted Publisher** (OIDC): o vínculo é com
99
- `conflaro/conttrole` + `mcp-publish.yml`, configurado nas Settings do pacote no
99
+ `conflaro/app` + `mcp-publish.yml`, configurado nas Settings do pacote no
100
100
  npmjs.com. Renomear o workflow quebra a publicação.
101
101
  🔴 Pacote novo não aceita Trusted Publisher antes de existir: a 1ª versão do
102
102
  `@conflaro/mcp` sai à mão (`npm publish` no terminal, com a passkey), que depois é apagado (ver o
package/dist/index.js CHANGED
@@ -77,13 +77,24 @@ const server = new McpServer({ name: "conflaro", version });
77
77
  // ── Empresa ────────────────────────────────────────────────────────────────
78
78
  server.tool("get_company", "Mostra os dados da empresa dona da chave de API configurada (razão social, CNPJ, IE/IM, regime tributário, endereço).", {}, tool(() => api("GET", "/v1/company")));
79
79
  // ── Documentos fiscais ─────────────────────────────────────────────────────
80
+ // Sem `companyId`: a empresa da chave. Com `companyId`: uma empresa da
81
+ // CARTEIRA do escritório (chave `ok_`) — escrever exige ainda que a empresa
82
+ // tenha liberado a emissão pelo contador.
83
+ const FD_COMPANY = z
84
+ .string()
85
+ .optional()
86
+ .describe("SÓ no modo contador (chave do escritório `ok_`): a empresa da carteira (id de list_portfolio_companies). Omita com a chave de uma empresa.");
87
+ const fdBase = (companyId) => companyId
88
+ ? `/v1/accounting/companies/${encodeURIComponent(String(companyId))}/fiscal-documents`
89
+ : "/v1/fiscal-documents";
80
90
  server.tool("list_fiscal_documents", "Lista os documentos fiscais (NF-e/NFC-e/NFS-e) da empresa, com paginação e filtros por status e tipo.", {
81
91
  page: z.number().int().positive().optional().describe("Página (default 1)"),
82
92
  pageSize: z.number().int().positive().max(100).optional().describe("Itens por página (default 20, máx 100)"),
83
93
  status: z.string().optional().describe("Filtra por status (ex.: AUTHORIZED, DRAFT, REJECTED)"),
84
94
  type: z.string().optional().describe("Filtra por tipo (NFE, NFCE, NFSE)"),
85
- }, tool((a) => api("GET", "/v1/fiscal-documents", { query: a })));
86
- server.tool("get_fiscal_document", "Detalha um documento fiscal pelo id (status, número, chave de acesso, itens, etc.).", { id: z.string().describe("ID do documento fiscal") }, tool((a) => api("GET", `/v1/fiscal-documents/${a.id}`)));
95
+ companyId: FD_COMPANY,
96
+ }, tool(({ companyId, ...query }) => api("GET", fdBase(companyId), { query })));
97
+ server.tool("get_fiscal_document", "Detalha um documento fiscal pelo id (status, número, chave de acesso, itens, etc.).", { id: z.string().describe("ID do documento fiscal"), companyId: FD_COMPANY }, tool((a) => api("GET", `${fdBase(a.companyId)}/${a.id}`)));
87
98
  /**
88
99
  * 🔴 Estes shapes são um RECORTE da API pública, não um espelho dela.
89
100
  *
@@ -310,6 +321,7 @@ const itemShape = z.object({
310
321
  }).passthrough();
311
322
  server.tool("create_fiscal_document", "Cria um documento fiscal (rascunho). Use emit=true para já enfileirar a emissão. Impostos são resolvidos pela regra tributária quando não informados.", {
312
323
  type: z.enum(["NFE", "NFCE", "NFSE"]).describe("Tipo do documento"),
324
+ companyId: FD_COMPANY,
313
325
  clientId: z
314
326
  .string()
315
327
  .optional()
@@ -472,22 +484,24 @@ server.tool("create_fiscal_document", "Cria um documento fiscal (rascunho). Use
472
484
  // `z.object` fechado, então campo não declarado seria DESCARTADO em
473
485
  // silêncio — o pior desfecho, porque a nota sai sem ele e nada avisa.
474
486
  // O declarado vence o `extra` quando os dois trazem a mesma chave.
475
- const { extra, ...campos } = a;
487
+ const { extra, companyId, ...campos } = a;
476
488
  const declarados = Object.fromEntries(Object.entries(campos).filter(([, v]) => v !== undefined));
477
- return api("POST", "/v1/fiscal-documents", {
489
+ return api("POST", fdBase(companyId), {
478
490
  body: { ...(extra ?? {}), ...declarados },
479
491
  });
480
492
  }));
481
- server.tool("emit_fiscal_document", "Dispara a emissão (assíncrona) de um documento fiscal já criado. Acompanhe o status com get_fiscal_document.", { id: z.string().describe("ID do documento fiscal") }, tool((a) => api("POST", `/v1/fiscal-documents/${a.id}/emit`)));
493
+ server.tool("emit_fiscal_document", "Dispara a emissão (assíncrona) de um documento fiscal já criado. Acompanhe o status com get_fiscal_document.", { id: z.string().describe("ID do documento fiscal"), companyId: FD_COMPANY }, tool((a) => api("POST", `${fdBase(a.companyId)}/${a.id}/emit`)));
482
494
  server.tool("cancel_fiscal_document", "Cancela um documento fiscal AUTORIZADO, com justificativa.", {
483
495
  id: z.string().describe("ID do documento fiscal"),
484
496
  reason: z.string().min(15).describe("Justificativa do cancelamento (mín. 15 caracteres)"),
485
- }, tool((a) => api("POST", `/v1/fiscal-documents/${a.id}/cancel`, { body: { reason: a.reason } })));
497
+ companyId: FD_COMPANY,
498
+ }, tool((a) => api("POST", `${fdBase(a.companyId)}/${a.id}/cancel`, { body: { reason: a.reason } })));
486
499
  server.tool("correct_fiscal_document", "Emite uma Carta de Correção (CC-e) para um documento fiscal autorizado.", {
487
500
  id: z.string().describe("ID do documento fiscal"),
488
501
  text: z.string().min(15).describe("Texto da correção (mín. 15 caracteres)"),
489
- }, tool((a) => api("POST", `/v1/fiscal-documents/${a.id}/correction`, { body: { text: a.text } })));
490
- server.tool("get_fiscal_document_xml", "Baixa o XML autorizado de um documento fiscal (retorna o XML como texto).", { id: z.string().describe("ID do documento fiscal") }, tool((a) => api("GET", `/v1/fiscal-documents/${a.id}/xml`)));
502
+ companyId: FD_COMPANY,
503
+ }, tool((a) => api("POST", `${fdBase(a.companyId)}/${a.id}/correction`, { body: { text: a.text } })));
504
+ server.tool("get_fiscal_document_xml", "Baixa o XML autorizado de um documento fiscal (retorna o XML como texto).", { id: z.string().describe("ID do documento fiscal"), companyId: FD_COMPANY }, tool((a) => api("GET", `${fdBase(a.companyId)}/${a.id}/xml`)));
491
505
  // ── GNRE por operação (docs/plans/gnre.md §8) ──
492
506
  server.tool("get_fiscal_document_gnre", "GNRE da nota (guia de recolhimento do DIFAL, FCP e ICMS-ST devidos a OUTRA UF): o que a NF-e autorizada exige (`assessment`, pelo XML), as guias geradas (status, valor, vencimento, `digitableLine` para pagar) e a cota do mês. `noGuideReason` explica quando NÃO há guia devida. Valores em string decimal.", { id: z.string().describe("ID do documento fiscal (NF-e)") }, tool((a) => api("GET", `/v1/fiscal-documents/${encodeURIComponent(String(a.id))}/gnre`)));
493
507
  server.tool("generate_fiscal_document_gnre", "Gera (ou regera) as guias GNRE da nota no Portal GNRE — fala com o fisco, consome a cota do plano e cria obrigação de pagamento: confirme com o usuário antes. Idempotente (guia emitida não é refeita). O resultado sai em ≥ 30 s: depois consulte get_fiscal_document_gnre (ou refresh_fiscal_document_gnre). `blocked` = falta dado que só a pessoa resolve; o motivo fica na guia. Exige o módulo GNRE (403 `feature_not_enabled`).", { id: z.string().describe("ID do documento fiscal (NF-e autorizada)") }, tool((a) => api("POST", `/v1/fiscal-documents/${encodeURIComponent(String(a.id))}/gnre`)));
@@ -794,6 +808,14 @@ server.tool("get_simples_das_forecast", "Previsão do DAS do MÊS CORRENTE se o
794
808
  : "/v1/simples/forecast";
795
809
  return api("GET", path);
796
810
  }));
811
+ server.tool("get_simples_monophasic_recovery", "PIS/COFINS monofásico que a empresa do Simples PODE ter pago a mais no DAS nos últimos 60 meses: revenda de NCM monofásico emitida sem o CST 04 de PIS, com o PIS/COFINS da alíquota efetiva do Anexo I de cada mês. É INDÍCIO a confirmar com o contador (só houve pagamento a mais se o PGDAS-D não segregou a receita) — diga isso. `monthsWithoutRate` ficou FORA do total. Escopo `simples:read`.", {
812
+ companyId: RD_COMPANY,
813
+ }, tool((a) => {
814
+ const path = a.companyId
815
+ ? `/v1/accounting/companies/${encodeURIComponent(String(a.companyId))}/simples/monophasic-recovery`
816
+ : "/v1/simples/monophasic-recovery";
817
+ return api("GET", path);
818
+ }));
797
819
  server.tool("get_product_tax_in_price", "Quanto do preço de um produto é imposto (empresa do Simples): alíquota efetiva do DAS do anexo do produto, imposto no preço, margem e o PREÇO MÍNIMO para a margem desejada. Valores em string decimal (\"40.00\"). `minimumPrice: null` = imposto + despesas + margem passam de 100%. Nada é gravado. Escopo `simples:read`.", {
798
820
  productId: z.string().describe("Id do produto (list_products)"),
799
821
  cost: z.string().describe("Custo unitário, decimal em string"),
@@ -814,6 +836,13 @@ server.tool("list_portfolio_companies", "MODO CONTADOR: as empresas-cliente com
814
836
  page: z.number().int().positive().optional(),
815
837
  pageSize: z.number().int().positive().max(100).optional(),
816
838
  }, tool((a) => api("GET", "/v1/accounting/companies", { query: a })));
839
+ server.tool("list_portfolio_company_clients", "MODO CONTADOR: os clientes cadastrados numa empresa da carteira — o `clientId` para criar nota por ela (create_fiscal_document com `companyId`).", {
840
+ companyId: z.string().describe("Empresa da carteira (id de list_portfolio_companies)"),
841
+ search: z.string().optional().describe("Nome, documento, email ou nome fantasia"),
842
+ page: z.number().int().positive().optional(),
843
+ pageSize: z.number().int().positive().max(100).optional(),
844
+ }, tool(({ companyId, ...query }) => api("GET", `/v1/accounting/companies/${encodeURIComponent(String(companyId))}/clients`, { query })));
845
+ server.tool("get_portfolio_nfse_readiness", "MODO CONTADOR: a empresa da carteira consegue emitir NFS-e agora? (certificado, configuração do município, cota do plano) — sem criar nota. Use antes de propor uma NFS-e.", { companyId: z.string().describe("Empresa da carteira (id de list_portfolio_companies)") }, tool((a) => api("GET", `/v1/accounting/companies/${encodeURIComponent(String(a.companyId))}/nfse-readiness`)));
817
846
  server.tool("list_closing_eligible_companies", "MODO CONTADOR: empresas da carteira com nota de PRODUÇÃO autorizada ou cancelada no mês (BRT) — as que o fechamento vai processar. Mês é 1–12.", {
818
847
  year: z.number().int().describe("Ano (ex.: 2026)"),
819
848
  month: z.number().int().min(1).max(12).describe("Mês, 1–12"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conflaro/mcp",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "description": "Servidor MCP da Conflaro — emissão fiscal (NF-e, NFC-e, NFS-e) por IA",
5
5
  "mcpName": "io.conttrole/mcp",
6
6
  "type": "module",