fiscal-mcp
Documento fiscal brasileiro como ferramenta de agente. Valide NF-e e NFS-e antes de transmitir — sem certificado, sem cadastro, sem enviar nada para lugar nenhum.
pip install "fiscal-mcp[xsd]"
fiscal-mcp validar nota.xml
[erro] ibs-cclasstrib-prefixo-cst
cClassTrib não corresponde ao CST do item
item 2: imposto/IBSCBS/cClassTrib = '000123' não começa por
imposto/IBSCBS/CST = '200'
→ Os três primeiros dígitos do cClassTrib são o CST do item. Este é o erro
mais comum ao ligar o módulo de IBS/CBS num ERP.
[erro] schema-elemento-fora-de-ordem
cEAN apareceu onde o leiaute espera xProd
item 1, linha 62 do XML
→ O schema da NF-e exige a sequência exata do leiaute — trocar a ordem
reprova mesmo com todos os campos presentes.
2 erro(s), 0 aviso(s)
Três camadas, num laudo só: schema XSD oficial, regras fiscais e chave de acesso. Tudo local — o processo não abre socket, e há teste que prova.
Por que existe
Rejeição da SEFAZ chega tarde, custa uma transmissão e vem com mensagem críptica. Boa parte dos motivos é aritmética simples ou campo fora de formato — coisa que dá para pegar antes de enviar, na sua própria máquina.
E agora tem prazo: desde 3 de agosto de 2026, documentos fiscais do regime regular precisam trazer os campos de IBS e CBS, e notas sem eles podem ser rejeitadas (CGIBS).
Enquanto isso, um agente de IA consegue mexer no seu Notion e no seu GitHub, mas não sabe ler uma nota fiscal.
O que ele faz
| Ferramenta | O que faz |
|---|---|
validar_nfe |
schema XSD oficial, regras fiscais (IBS/CBS incluso), totais e chave — com o que fazer em cada achado |
explicar_nfe |
resumo estruturado do XML, em vez do documento inteiro |
validar_nfse |
NFS-e do padrão nacional: estrutura, DPS embutida, prestador, serviço |
explicar_nfse |
resumo estruturado da NFS-e |
explicar_rejeicao |
código da SEFAZ → significado → ação, e se é reversível |
validar_chave_acesso |
decompõe os 44 dígitos da NF-e e confere o dígito verificador |
validar_chave_nfse |
decompõe os 50 dígitos da NFS-e nacional |
listar_rejeicoes_conhecidas |
o que o catálogo cobre |
Nenhuma delas assina, transmite, emite ou cancela documento. Não existe caminho, nesta versão, para causar efeito fiscal — e um teste verifica isso a cada mudança.
Como usar
Na linha de comando
fiscal-mcp validar nota.xml # NF-e ou NFS-e, ele descobre sozinho
fiscal-mcp validar nota.xml --json # para script e CI
fiscal-mcp validar nota.xml --sem-schema
fiscal-mcp explicar nota.xml # resumo estruturado
fiscal-mcp chave 4326081234... # 44 dígitos (NF-e) ou 50 (NFS-e)
fiscal-mcp rejeicao 539 # traduz o código da SEFAZ
fiscal-mcp rejeicao # lista o catálogo
fiscal-mcp tabelas # qual tabela oficial está embarcada
Sai com código 1 quando encontra erro, então serve direto em CI.
Como servidor MCP
pip install "fiscal-mcp[servidor,xsd]"
Copie e cole no seu cliente. Claude Code:
claude mcp add fiscal -- fiscal-mcp-servidor
Claude Desktop (claude_desktop_config.json), Cursor
(.cursor/mcp.json), VS Code e a maioria dos outros:
{
"mcpServers": {
"fiscal": { "command": "fiscal-mcp-servidor" }
}
}
Sem instalar Python — útil para quem trabalha com ERP em Delphi ou C#:
{
"mcpServers": {
"fiscal": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/josetorquato/fiscal-mcp"]
}
}
}
Aí é só perguntar ao agente: "esse XML está pronto para transmitir?"
O que ele não faz
Escrito antes das perguntas, porque prometer demais é o jeito mais rápido de perder a confiança de quem trabalha com fiscal:
- Não emite, não assina, não transmite. Sem certificado digital envolvido.
- Passar aqui não garante autorização. É validação local: pega o erro previsível, não substitui a SEFAZ.
- A validação por schema exige o extra
[xsd]. Sem ele, o laudo diz que essa camada não rodou — nunca finge que rodou. - NFS-e só no padrão nacional. Município com padrão próprio não é reconhecido (ADR-0006).
- Não verifica dígito verificador de NFS-e — o algoritmo não foi confirmado, e chutar produziria acusação falsa.
- Não valida assinatura digital. Documento não assinado nunca passa no XSD oficial; o laudo diz isso como informação, não como erro.
- Não dá conselho tributário. CFOP, CST e alíquota são do seu contador.
Estado da validação, por documento
| Camadas | Regras | Testado contra | |
|---|---|---|---|
| NF-e / NFC-e | schema XSD + regras + chave | 28 | XML sintético e 17 amostras de formato real da nfelib |
| NFS-e nacional | regras + chave | 10 | ✅ uma nota autorizada de verdade |
⚠️ A NF-e ainda não passou por uma nota real de contribuinte. As 17 amostras
são públicas e MIT, com estrutura de documento real — 15 passam com zero erros, e
as duas que não passam são explicáveis: uma é inválida no XSD oficial de
propósito, a outra tem valores de preenchimento incoerentes entre si (vIBS = 0
com vIBSUF = 16). Nenhuma regra de tabela acusa nenhuma delas, e há teste
que trava isso.
Das 28 regras de NF-e, 17 são da Camada A de IBS/CBS: CST e cClassTrib
conferidos contra a tabela oficial embarcada da SVRS — 18 CST e 164
classificações, versionadas no repositório com URL de origem, data e sha256
(procedência).
Mais aritmética por item, exclusividade de regime, presença condicional e as
alíquotas de transição de 2026.
fiscal-mcp tabelas diz qual versão da tabela está embarcada. Quem valida contra
tabela precisa saber contra qual.
A regra de IBS/CBS emite aviso, não erro: a NT 2025.002 v1.51 reclassificou a regra de rejeição correspondente (UB12-10) como implementação futura, então a nota não é recusada por isso hoje. Ela carrega data de reavaliação, e um teste falha quando essa data passa. Acusar errado é pior que não acusar.
Tem um XML real que pode compartilhar? É a contribuição mais valiosa possível agora — abra uma issue com os dados trocados por fictícios.
Escrever uma regra
Regras são dados, não código. Absorver uma nota técnica deveria ser editar um YAML — e é:
- id: tot-produtos-confere
tipo: soma_itens
severidade: erro # erro | aviso | informacao
campo_item: prod/vProd
campo_total: total/ICMSTot/vProd
tolerancia: "0.01"
mensagem: O total de produtos não bate com a soma dos itens
acao: >
Some o vProd de cada item e compare com total/ICMSTot/vProd.
Tipos disponíveis: existe, nao_vazio, valor_em, formato, soma_itens,
condicional.
Escopo. Por padrão a regra roda uma vez, na raiz da nota. Com escopo: item
ela roda uma vez por item, com os caminhos relativos ao det — e o achado diz
qual item, pelo nItem:
- id: ibs-grupo-ausente
tipo: existe
escopo: item # documento (padrão) | item
campo: imposto/IBSCBS
Caminho absoluto. Num escopo: item, o caminho que começa com / vale a
partir da raiz da nota. É o que permite uma regra olhar o item e algo fora dele
ao mesmo tempo:
- id: ibs-totais-presentes
tipo: condicional
escopo: item
quando_campo: imposto/IBSCBS/CST # relativo ao item
campo: /total/IBSCBSTot # a partir da raiz
Vigência. Regra que ainda não estabilizou declara quando será reavaliada. Não é comentário: um teste falha quando a data passa, e é assim que a manutenção deixa de depender de memória.
vigencia:
reavaliar_em: "2026-09-01"
fonte: "Ato Técnico Conjunto RFB/CGIBS nº 1, de 31/07/2026"
Todo achado precisa de acao. Quem lê é um agente que vai tentar de novo —
erro sem ação vira loop de retry ou nota duplicada. Um teste falha se alguma
regra não tiver.
Contribuir
O que mais ajuda, em ordem:
- XML real anonimizado — principalmente NF-e, e municípios de NFS-e diferentes.
- Código de rejeição que você levou e não está no catálogo.
- Regra nova em
regras/, com as duas fixtures. - Leitura da seção 7 da NT 2025.002-RTC v1.51 — o leiaute de IBS/CBS já está mapeado; o que falta confirmar em fonte primária são os códigos de rejeição, e nenhum entra aqui sem leitura humana.
Antes de abrir PR, leia o CONTRIBUTING. Há uma regra sem exceção: PR com dado fiscal real identificável é fechado sem merge.
Como isso vai crescer
O produto é a validação: o validador mais fundo que existe para NF-e, que roda offline e que você pode conferir antes de transmitir. Emissão está suspensa, com gatilho escrito — ver ADR-0011. O que vem depois, e por que nesta ordem, está escrito:
| Documento | Para quê |
|---|---|
| ROADMAP.md | as fases e o critério de saída de cada uma |
| BACKLOG.md | as tarefas, priorizadas |
| docs/adr/ | as decisões e por que foram tomadas assim |
| docs/spec/ | o produto em detalhe |
Três decisões que explicam o resto:
- ADR-0011 — validação é o produto; emissão sai do caminho crítico e só volta se um gatilho nomeado disparar.
- ADR-0008 — não escrevo integração com SEFAZ antes de saber que existe quem pague pela manutenção.
- ADR-0005 — certificado digital de cliente não passa pela minha infra. O A1 é a identidade jurídica da empresa.
Licença
MIT — código e regras.
Feito por José Torquato, que também mantém o Cilada.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fiscal_mcp-0.2.0.tar.gz.
File metadata
- Download URL: fiscal_mcp-0.2.0.tar.gz
- Upload date:
- Size: 214.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c562dc619da27c434119db64228f78d40f18ee009a9fcd950133f0d8837a62f
|
|
| MD5 |
cd84e5567ed502e37612ca08ad76bc08
|
|
| BLAKE2b-256 |
fafa7494776a6235af01ffb1a7e74ac7ec64dee4bff5fe4b39afa0b86beef4d6
|
Provenance
The following attestation bundles were made for fiscal_mcp-0.2.0.tar.gz:
Publisher:
publicar.yml on JoseTorquato/fiscal-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fiscal_mcp-0.2.0.tar.gz -
Subject digest:
2c562dc619da27c434119db64228f78d40f18ee009a9fcd950133f0d8837a62f - Sigstore transparency entry: 2607049447
- Sigstore integration time:
-
Permalink:
JoseTorquato/fiscal-mcp@d5c08a268f0edf918870305b403de436f3f726e9 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/JoseTorquato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publicar.yml@d5c08a268f0edf918870305b403de436f3f726e9 -
Trigger Event:
release
-
Statement type:
File details
Details for the file fiscal_mcp-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fiscal_mcp-0.2.0-py3-none-any.whl
- Upload date:
- Size: 92.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2c9dc38131146821bd20bbd0d7868d9cf91dbfe62e77f644ebd4c53192d5b3d
|
|
| MD5 |
eb57bc1308ab9aef08011a89e6d85a89
|
|
| BLAKE2b-256 |
8bfaf72162743a3c98eb718bf012ae1f7f73044dd854dcb524c384fa82ad966b
|
Provenance
The following attestation bundles were made for fiscal_mcp-0.2.0-py3-none-any.whl:
Publisher:
publicar.yml on JoseTorquato/fiscal-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fiscal_mcp-0.2.0-py3-none-any.whl -
Subject digest:
f2c9dc38131146821bd20bbd0d7868d9cf91dbfe62e77f644ebd4c53192d5b3d - Sigstore transparency entry: 2607050256
- Sigstore integration time:
-
Permalink:
JoseTorquato/fiscal-mcp@d5c08a268f0edf918870305b403de436f3f726e9 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/JoseTorquato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publicar.yml@d5c08a268f0edf918870305b403de436f3f726e9 -
Trigger Event:
release
-
Statement type: