GST e-invoice extraction
Converte faturas fiscais indianas GST, em PDF ou digitalizadas, no payload JSON INV-01 do governo. Cada valor retornado pelo modelo é verificado novamente contra o texto do documento, de modo que um campo que não possa ser corroborado é reportado como ausente, em vez de inventado; um campo obrigatório ausente significa nenhum payload. A proveniência por campo e a confiança do OCR acompanham o payload, nunca dentro dele.
Documentação
gst-einvoice-mcp 0.1.5
pip install gst-einvoice-mcp==0.1.5
GST e-invoice extraction
Transforma uma fatura fiscal GST indiana no payload JSON INV-01 do governo e informa exatamente o que não conseguiu ler.
Distribuído como um servidor MCP com três ferramentas, permitindo que um agente analise um documento, verifique um GSTIN ou revalide um payload que já possui.
Ele produz um payload pronto para envio, não uma fatura registrada. Não há IRN aqui. Um Número de Referência de Fatura é emitido pelo Portal de Registro de Faturas do governo após você enviar o payload a ele. Nada neste repositório se comunica com o IRP.
A ideia
Uma ferramenta de extração que adivinha silenciosamente é pior do que uma que diz que não consegue ler um campo. Um dígito alucinado em um GSTIN que ainda passa na soma de verificação, ou um item de linha que nunca esteve na página, é a falha que custa dinheiro real a um contador — e é invisível justamente porque parece correto.
Portanto, o design tem uma regra: o modelo pode estruturar texto, mas nunca inventar valores. Isso é aplicado duas vezes. O prompt diz isso, e então cada valor retornado pelo modelo é verificado novamente contra o texto do documento antes de ser mantido. Um valor sem origem no documento é substituído por null e relatado, por mais plausível que pareça. Se esse campo for obrigatório no INV-01, nenhum payload é produzido.
Tudo o que a ferramenta sabe sobre seu próprio trabalho — como cada página foi lida, de onde veio cada campo, sobre o que estava incerta — viaja ao lado do payload em extraction_meta, nunca dentro dele. O payload permanece estritamente puro em relação à especificação, pois a API do governo rejeita chaves desconhecidas.
Leia LIMITATIONS.md antes de confiar na saída. Ele é específico sobre o que a ferramenta não consegue corroborar e o que isso custa a você.
Instalação
Requer Python 3.11 ou mais recente e Tesseract OCR como dependência do sistema.
# Tesseract (Windows)
winget install UB-Mannheim.TesseractOCR
# Tesseract (Debian/Ubuntu)
sudo apt-get install -y tesseract-ocr
# Tesseract (macOS)
brew install tesseract
# From PyPI -- installs the \`gst-einvoice-mcp\` command the MCP client configuration below names
pip install gst-einvoice-mcp
# Or from a clone, for development (editable)
python -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # Linux / macOS
pip install -e .
O Tesseract não precisa estar em PATH: o módulo de OCR procura lá primeiro, depois no local de instalação padrão do Windows, e gera um erro acionável nomeando ambos se nenhum funcionar.
Ambiente
| Variável | Obrigatória | Finalidade |
|---|---|---|
GROQ_API_KEY | sim | O Estágio 2 lê a tabela de itens de linha via Groq |
GST_MCP_MODEL | recomendada | Fixar o modelo que sua chave pode acessar |
GST_MCP_TRANSPORT | não | stdio (padrão), sse ou streamable-http |
O modelo padrão é openai/gpt-oss-120b, que é o que esta versão foi validada. Ele funciona sem configuração.
Ainda assim, defina
GST_MCP_MODELem uma implantação. Um identificador de modelo codificado expira silenciosamente quando o provedor o descontinua, e a falha chega como um HTTP 404 que parece uma chave inválida em vez de uma constante desatualizada. Fixe o modelo ao qual você tem acesso e verifique-o contra os avisos de descontinuação da Groq.
Configuração do cliente MCP
pip install coloca um comando gst-einvoice-mcp no seu PATH, então um cliente só precisa nomeá-lo:
{
"mcpServers": {
"gst-einvoice": {
"command": "gst-einvoice-mcp",
"env": {
"GROQ_API_KEY": "your-key-here",
"GST_MCP_MODEL": "openai/gpt-oss-120b"
}
}
}
}
Executando a partir de um clone em vez de uma instalação? Aponte command para o interpretador dentro do seu ambiente virtual e invoque o módulo diretamente:
{
"mcpServers": {
"gst-einvoice": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["-m", "gst_einvoice.server"],
"env": {
"GROQ_API_KEY": "your-key-here"
}
}
}
}
No Windows, esse caminho de interpretador termina em \.venv\Scripts\python.exe.
Ferramentas
| Ferramenta | Recebe | Retorna |
|---|---|---|
parse_invoice | um caminho de arquivo, tolerância opcional | o payload INV-01, campos ausentes, recusas e extraction_meta |
validate_gstin | um GSTIN | estrutura, soma de verificação, código do estado, PAN e o motivo da falha |
validate_payload | um payload INV-01 | as quatro verificações de consistência sobre um payload que você já possui |
parse_invoice tem três resultados normais, e apenas o primeiro fornece um payload:
- Um payload com avisos. Utilizável, mas os avisos indicam quais campos foram lidos com baixa confiança de OCR, quais foram atribuídos por posição em vez de por um rótulo, e quais foram derivados em vez de impressos.
- Sem payload,
missing_fieldspreenchido. Um campo obrigatório não pôde ser lido. Nada foi inventado para preencher a lacuna, e é por isso que não há payload. - Sem payload,
refusalspreenchido. Faturas de exportação, SEZ e moeda estrangeira são recusadas por design, com uma mensagem indicando o que foi detectado e o que fazer em vez disso.
Exemplo prático
A fatura, como PDF com camada de texto:
TAX INVOICE
Seller: Nimbus Components Pvt Ltd
GSTIN 27AAPFU0939F1ZV
Plot 14 MIDC Andheri East
Mumbai 400093
Invoice No: INV-2026-0042
Invoice Date: 17/04/2026
Buyer: Kanchan Electricals LLP
GSTIN 27AABCB5507N1ZJ
221 Laxmi Road Shivajinagar
Pune 411005
Sl No 01 Laptop Stand
Qty 4 NOS Rate 1500.00
Taxable 6000.00 GST 18%
CGST 540.00 SGST 540.00 IGST 0.00
HSN/SAC: 8471
Goods once sold will not be taken back or exchanged
Total Invoice Value 7080.00
from gst_einvoice.extract_llm import make_client
from gst_einvoice.pipeline import extract_invoice
# model defaults to openai/gpt-oss-120b; pass model=... to override
result = extract_invoice("sample_invoice.pdf", client=make_client())
O payload
{
"Version": "1.1",
"TranDtls": { "TaxSch": "GST", "SupTyp": "B2B" },
"DocDtls": { "Typ": "INV", "No": "INV-2026-0042", "Dt": "17/04/2026" },
"SellerDtls": {
"Gstin": "27AAPFU0939F1ZV",
"LglNm": "Nimbus Components Pvt Ltd",
"Addr1": "Plot 14 MIDC Andheri East",
"Loc": "Mumbai",
"Pin": 400093,
"Stcd": "27"
},
"BuyerDtls": {
"Gstin": "27AABCB5507N1ZJ",
"LglNm": "Kanchan Electricals LLP",
"Addr1": "221 Laxmi Road Shivajinagar",
"Loc": "Pune",
"Pin": 411005,
"Stcd": "27",
"Pos": "27"
},
"ItemList": [
{
"SlNo": "01",
"PrdDesc": "Laptop Stand",
"IsServc": "N",
"HsnCd": "8471",
"Qty": 4.0,
"Unit": "NOS",
"UnitPrice": 1500.0,
"TotAmt": 6000.0,
"Discount": 0.0,
"AssAmt": 6000.0,
"GstRt": 18.0,
"CgstAmt": 540.0,
"SgstAmt": 540.0,
"IgstAmt": 0.0,
"CesAmt": 0.0,
"StateCesAmt": 0.0,
"OthChrg": 0.0,
"TotItemVal": 7080.0
}
],
"ValDtls": {
"AssVal": 6000.0, "CgstVal": 540.0, "SgstVal": 540.0, "IgstVal": 0.0,
"CesVal": 0.0, "StCesVal": 0.0, "RndOffAmt": 0.0, "TotInvVal": 7080.0
}
}
O bloco de avisos
Esta é a metade que a maioria das ferramentas não fornece. Seis entradas, da execução acima, todas info:
[info] extract_llm ItemList[0].HsnCd
"8471" is not printed as a code of its own in this row's text: it was grounded by
the HSN/SAC codes stage 1 confirmed, or by a longer number elsewhere on the page.
Which code belongs to which row is therefore the model's judgement and could not
be corroborated against the document. A code on the wrong row changes that row's
tax classification, so confirm it against the invoice.
[info] extract_llm ItemList[0].Discount
ItemList[0].Discount was not found in the document: the model returned no value
for it, so it is left empty rather than filled with a guess.
[info] extract_llm ItemList[0].CesAmt (same wording)
[info] extract_llm ValDtls.CesVal (same wording)
[info] extract_llm ValDtls.RndOffAmt (same wording)
[info] pipeline BuyerDtls.Pos
BuyerDtls.Pos (place of supply) was not read from the document -- build 2 does
not extract it -- so it was assumed equal to the buyer's registered state code
(27). A genuine bill-to/ship-to supply, where the goods go to a different state
from the one the buyer is registered in, has a different place of supply, and
the CGST/SGST-versus-IGST split follows the place of supply. Confirm it against
the document before filing.
Nada nessa lista significa que o payload está errado. Cada uma nomeia algo que a ferramenta não conseguiu corroborar, para que você saiba onde verificar. Os quatro validadores aritméticos não levantaram nada, que é o que o silêncio deles significa.
Em uma fatura cujo modelo omite uma coluna — uma fatura intraestadual sem coluna IGST, ou uma que imprime um valor tributável mas sem bruto separado — você também verá uma nota pipeline dizendo que o campo foi derivado em vez de lido, e field_provenance o registrará como "source": "derived". Cinco regras podem fazer isso: a rubrica de imposto que não se aplica é definida como zero a partir dos dois códigos de estado; o bruto de uma linha é preenchido a partir do seu valor tributável e desconto, e o valor tributável de uma linha a partir do seu bruto, cada um o inverso exato do outro e nunca ambos em uma linha; o total por linha em uma fatura de linha única que imprime seu total apenas no rodapé é preenchido a partir da identidade do item INV-01; e um total de documento que o modelo descartou é preenchido a partir do seu equivalente de linha, novamente apenas em uma fatura de linha única. As duas últimas disparam apenas quando o resultado reconcilia com o ValDtls.TotInvVal impresso, e um valor derivado nunca alimenta outra derivação através do limite linha/totais. Em uma fatura de várias linhas, um TotItemVal ou total de documento ausente ainda é relatado como ausente e nenhum payload é produzido — veja LIMITATIONS.md para saber por quê.
Proveniência
extraction_meta.field_provenance carrega uma entrada para cada campo no payload — 45 para esta fatura — dizendo qual estágio o produziu e, para uma página digitalizada, a confiança de OCR do texto do qual foi lido:
{
"SellerDtls.Gstin": { "source": "regex", "ocr_confidence": null },
"SellerDtls.Stcd": { "source": "derived", "ocr_confidence": null },
"ItemList[0].PrdDesc": { "source": "llm", "ocr_confidence": null },
"ItemList[0].IsServc": { "source": "derived", "ocr_confidence": null },
"BuyerDtls.Pos": { "source": "assumed", "ocr_confidence": null }
}
Em uma página digitalizada, os mesmos campos carregam números reais — 0,86 a 0,96 em uma renderização limpa de 300 dpi — e a confiança mais baixa entre as palavras de um campo é a registrada.
source | Significado |
|---|---|
regex | Confirmado deterministicamente, estruturalmente certo |
llm | Estruturado pelo modelo, depois verificado contra o texto do documento |
derived | Segue por regra a partir de valores que foram lidos; não impresso na página |
assumed | Nem lido nem derivado — uma suposição que a ferramenta nomeia explicitamente |
Desenvolvimento
pytest -q -W error
1644 testes em dez módulos, passando com avisos tratados como erros. O estágio LLM recebe um cliente injetado, então toda a suíte roda sem chave de API e sem rede.
A suíte passa tanto em 3.11 quanto em 3.13; 3.11 é o mínimo porque é a versão mais baixa em que todo o conjunto de dependências resolve, e foi verificado executando a suíte lá em vez de assumido.
Testando mudanças locais através de um cliente MCP
O servidor MCP executa o que está instalado em site-packages, não sua árvore de trabalho. Um cliente MCP inicia o servidor através do ponto de entrada gst-einvoice-mcp, que resolve para a distribuição instalada. Editar um arquivo no repositório não muda nada que o servidor sirva até você reinstalar, e não há erro para avisá-lo — a chamada de ferramenta é bem-sucedida e retorna o comportamento antigo.
É fácil perder uma hora com isso. Durante o trabalho da 0.1.2, o repositório carregava uma nova regra de derivação enquanto o servidor ainda servia a 0.1.1, então uma chamada de ferramenta contra o novo código silenciosamente exercitava o caminho antigo e parecia mostrar que a mudança não tinha funcionado.
Ou reinstale após cada mudança:
pip install -e . # editable, so later edits are picked up on server restart
ou aponte o command do cliente MCP para o virtualenv do próprio repositório em vez de uma instalação em nível de usuário, para que o servidor e os testes executem o mesmo código:
// in your MCP client config
"command": "C:\\path\\to\\repo\\.venv\\Scripts\\gst-einvoice-mcp.exe"
De qualquer forma, reinicie o servidor MCP após mudar o código — um servidor em execução mantém os módulos que importou na inicialização. Se uma mudança parece não ter efeito, verifique qual cópia está sendo servida antes de procurar o bug no seu código.
| Módulo | O que faz |
|---|---|
gstin.py | Estrutura e soma de verificação mod-36 |
state_codes.py | Tabela de códigos de estado, incluindo o descontinuado 25 e o legado 28 |
schema.py | Modelos pydantic INV-01, extra="forbid" em todo lugar |
validators.py | As quatro verificações aritméticas e de divisão de impostos |
ingest.py | Roteamento por página e as regras de detectar-e-recusar |
ocr.py | Tesseract com confiança por palavra mapeada em intervalos de caracteres |
extract_rules.py | Extração determinística: GSTINs, partes, número, data, HSN |
extract_llm.py | O estágio LLM e a verificação de fundamentação |
pipeline.py | Montagem de ponta a ponta |
server.py | O servidor MCP |
Nota de licença
Este projeto depende de PyMuPDF, que é AGPL-3.0. Essa é uma escolha deliberada, feita porque o PyMuPDF abre arquivos de imagem diretamente como documentos de uma página e deu detecção de camada de texto mais confiável do que as alternativas. Se você pretende distribuir esta ferramenta como parte de um produto de código fechado, verifique essa licença primeiro.
Datas-chave
Dados do PyPI
1 mantenedor
Dados do PyPI
Baixe o arquivo para sua plataforma. Se não tiver certeza de qual escolher, saiba mais sobre instalação de pacotes.
Distribuição de origem
gst_einvoice_mcp-0.1.5.tar.gz (187,3 kB ver detalhes)
Distribuição compilada
Se não tiver certeza sobre o formato do nome do arquivo, saiba mais sobre nomes de arquivos wheel.
gst_einvoice_mcp-0.1.5-py3-none-any.whl (90,4 kB ver detalhes)
Detalhes para o arquivo gst_einvoice_mcp-0.1.5.tar.gz.
Metadados do arquivo
- URL de download: gst_einvoice_mcp-0.1.5.tar.gz
- Data de envio: 16 de setembro de 2026
- Tamanho: 187,3 kB
- Tags: Source
- Enviado usando Publicação Confiável? Não
- Enviado via:
twine/7.0.0 CPython/3.13.4
Hashes do arquivo
| Algoritmo | Digest do hash | |
|---|---|---|
| SHA256 | aa8a2410dc8f55095385f946d05de488a167cad8fe69303dbaaa9eb0953947ee | Copiar |
| MD5 | df88853d70fce10ce69e2306e37aa293 | Copiar |
| BLAKE2b-256 | 46b015ed04dbe6c63661d03201ed40ba2403ea96e0dd10cc5a472656c7e2ac27 | Copiar |
Veja mais detalhes sobre o uso de hashes aqui.
Detalhes para o arquivo gst_einvoice_mcp-0.1.5-py3-none-any.whl.
Metadados do arquivo
- URL de download: gst_einvoice_mcp-0.1.5-py3-none-any.whl
- Data de envio: 16 de setembro de 2026
- Tamanho: 90,4 kB
- Tags: Python 3
- Enviado usando Publicação Confiável? Não
- Enviado via:
twine/7.0.0 CPython/3.13.4
Hashes do arquivo
| Algoritmo | Digest do hash | |
|---|---|---|
| SHA256 | 556679fc6d332720ff7e2d69729a7941ca8c4463b2ad42aeaf2e19a614362690 | Copiar |
| MD5 | 2442ea9ce546f9e3e233508ceabb2641 | Copiar |
| BLAKE2b-256 | 825504b5a974af3ef69eaa4b395c2e0805b8e2c88d7b36f7caa8294e5003aae5 | Copiar |