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ávelObrigatóriaFinalidade
GROQ_API_KEYsimO Estágio 2 lê a tabela de itens de linha via Groq
GST_MCP_MODELrecomendadaFixar o modelo que sua chave pode acessar
GST_MCP_TRANSPORTnãostdio (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_MODEL em 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

FerramentaRecebeRetorna
parse_invoiceum caminho de arquivo, tolerância opcionalo payload INV-01, campos ausentes, recusas e extraction_meta
validate_gstinum GSTINestrutura, soma de verificação, código do estado, PAN e o motivo da falha
validate_payloadum payload INV-01as 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:

  1. 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.
  2. Sem payload, missing_fields preenchido. 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.
  3. Sem payload, refusals preenchido. 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.

sourceSignificado
regexConfirmado deterministicamente, estruturalmente certo
llmEstruturado pelo modelo, depois verificado contra o texto do documento
derivedSegue por regra a partir de valores que foram lidos; não impresso na página
assumedNem 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óduloO que faz
gstin.pyEstrutura e soma de verificação mod-36
state_codes.pyTabela de códigos de estado, incluindo o descontinuado 25 e o legado 28
schema.pyModelos pydantic INV-01, extra="forbid" em todo lugar
validators.pyAs quatro verificações aritméticas e de divisão de impostos
ingest.pyRoteamento por página e as regras de detectar-e-recusar
ocr.pyTesseract com confiança por palavra mapeada em intervalos de caracteres
extract_rules.pyExtração determinística: GSTINs, partes, número, data, HSN
extract_llm.pyO estágio LLM e a verificação de fundamentação
pipeline.pyMontagem de ponta a ponta
server.pyO 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

Avatar for 411sst from gravatar.com 411sst

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

AlgoritmoDigest do hash
SHA256aa8a2410dc8f55095385f946d05de488a167cad8fe69303dbaaa9eb0953947eeCopiar
MD5df88853d70fce10ce69e2306e37aa293Copiar
BLAKE2b-25646b015ed04dbe6c63661d03201ed40ba2403ea96e0dd10cc5a472656c7e2ac27Copiar

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

AlgoritmoDigest do hash
SHA256556679fc6d332720ff7e2d69729a7941ca8c4463b2ad42aeaf2e19a614362690Copiar
MD52442ea9ce546f9e3e233508ceabb2641Copiar
BLAKE2b-256825504b5a974af3ef69eaa4b395c2e0805b8e2c88d7b36f7caa8294e5003aae5Copiar

Veja mais detalhes sobre o uso de hashes aqui.