GST e-invoice extraction

Convierte facturas fiscales GST de la India, en PDF o escaneadas, en el payload JSON INV-01 del gobierno. Cada valor que devuelve el modelo se verifica contra el texto del documento, de modo que un campo que no puede corroborar se reporta como ausente en lugar de inventado; un campo obligatorio faltante significa que no hay payload en absoluto. La procedencia por campo y la confianza del OCR viajan junto al payload, nunca dentro de él.

Documentación

gst-einvoice-mcp 0.1.5

pip install gst-einvoice-mcp==0.1.5

Extracción de facturas electrónicas GST

Convierte una factura fiscal GST india en el payload JSON INV-01 del gobierno, y te dice exactamente qué no pudo leer.

Se distribuye como un servidor MCP con tres herramientas, para que un agente pueda analizar un documento, verificar un GSTIN o revalidar un payload que ya tenga.

Produce un payload listo para enviar, no una factura presentada. Aquí no hay IRN. Un Número de Referencia de Factura lo emite el Portal de Registro de Facturas del gobierno después de que envíes el payload. Nada en este repositorio se comunica con el IRP.


La idea

Una herramienta de extracción que adivina en silencio es peor que una que dice que no puede leer un campo. Un dígito alucinado en un GSTIN que aún así pasa su suma de verificación, o una línea de artículo que nunca estuvo en la página, es el fallo que le cuesta dinero real a un contador — y es invisible precisamente porque parece correcto.

Así que el diseño tiene una regla: el modelo puede estructurar texto, nunca puede inventar valores. Eso se aplica dos veces. El prompt lo dice, y luego cada valor que devuelve el modelo se verifica contra el texto del documento antes de conservarse. Un valor sin fuente en el documento se reemplaza con null y se reporta, por plausible que parezca. Si ese campo es obligatorio en INV-01, no se produce ningún payload.

Todo lo que la herramienta sabe sobre su propio trabajo — cómo se leyó cada página, de dónde vino cada campo, qué le generaba incertidumbre — viaja junto al payload en extraction_meta, nunca dentro de él. El payload se mantiene estrictamente puro según la especificación, porque la API del gobierno rechaza claves desconocidas.

Lee LIMITATIONS.md antes de confiar en la salida. Es específico sobre lo que la herramienta no puede corroborar y qué te cuesta eso.


Instalación

Requiere Python 3.11 o superior y Tesseract OCR como dependencia del 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 .

Tesseract no necesita estar en PATH: el módulo OCR lo busca allí primero, luego en la ubicación estándar de instalación de Windows, y genera un error accionable que nombra ambas si ninguna funciona.

Entorno

VariableRequeridaPropósito
GROQ_API_KEYLa etapa 2 lee la tabla de líneas de artículo a través de Groq
GST_MCP_MODELrecomendadaFija el modelo al que tu clave puede acceder
GST_MCP_TRANSPORTnostdio (predeterminado), sse o streamable-http

El modelo predeterminado es openai/gpt-oss-120b, que es contra el que se validó esta versión. Funciona sin configuración.

Aun así, establece GST_MCP_MODEL en un despliegue. Un identificador de modelo codificado expira silenciosamente cuando el proveedor lo retira, y el fallo llega como un HTTP 404 que parece una clave incorrecta en lugar de una constante obsoleta. Fija el modelo al que tienes acceso y verifícalo contra los avisos de desaprobación de Groq.


Configuración del cliente MCP

pip install coloca un comando gst-einvoice-mcp en tu PATH, así que un cliente solo necesita nombrarlo:

{
  "mcpServers": {
    "gst-einvoice": {
      "command": "gst-einvoice-mcp",
      "env": {
        "GROQ_API_KEY": "your-key-here",
        "GST_MCP_MODEL": "openai/gpt-oss-120b"
      }
    }
  }
}

¿Ejecutando desde un clon en lugar de una instalación? Apunta command al intérprete dentro de tu entorno virtual e invoca el módulo directamente:

{
  "mcpServers": {
    "gst-einvoice": {
      "command": "/absolute/path/to/.venv/bin/python",
      "args": ["-m", "gst_einvoice.server"],
      "env": {
        "GROQ_API_KEY": "your-key-here"
      }
    }
  }
}

En Windows, esa ruta de intérprete termina en \.venv\Scripts\python.exe.

Herramientas

HerramientaRecibeDevuelve
parse_invoiceuna ruta de archivo, tolerancia opcionalel payload INV-01, campos faltantes, rechazos y extraction_meta
validate_gstinun GSTINestructura, suma de verificación, código de estado, PAN y el motivo del fallo
validate_payloadun payload INV-01las cuatro verificaciones de consistencia sobre un payload que ya tienes

parse_invoice tiene tres resultados normales, y solo el primero te da un payload:

  1. Un payload más advertencias. Utilizable, pero las advertencias indican qué campos se leyeron con baja confianza de OCR, cuáles se asignaron por posición en lugar de por una etiqueta, y cuáles se derivaron en lugar de imprimirse.
  2. Sin payload, missing_fields poblado. Un campo obligatorio no pudo leerse. No se inventó nada para llenar el vacío, que es por qué no hay payload.
  3. Sin payload, refusals poblado. Las facturas de exportación, SEZ y moneda extranjera se rechazan por diseño, con un mensaje que indica qué se detectó y qué hacer en su lugar.

Ejemplo práctico

La factura, como PDF con capa 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())

El 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
  }
}

El bloque de advertencias

Esta es la mitad que la mayoría de las herramientas no te dan. Seis entradas, de la ejecución anterior, 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 en esa lista significa que el payload esté mal. Cada una nombra algo que la herramienta no pudo corroborar, para que sepas dónde mirar. Los cuatro validadores aritméticos no generaron nada, que es lo que significa su silencio.

En una factura cuya plantilla omite una columna — una factura intraestatal sin columna IGST, o una que imprime un valor gravable pero no un total bruto separado — también verás una nota pipeline que indica que el campo se derivó en lugar de leerse, y field_provenance lo registrará como "source": "derived". Cinco reglas pueden hacer esto: el encabezado de impuesto que no puede aplicarse se establece en cero a partir de los dos códigos de estado; el total bruto de una fila se completa desde su valor gravable y descuento, y el valor gravable de una fila desde su total bruto, cada uno el inverso exacto del otro y nunca ambos en una misma fila; el total por línea en una factura de una sola línea que imprime su total solo al pie se completa desde la identidad del artículo INV-01; y un total de documento que el modelo omitió se completa desde su contraparte de fila, nuevamente solo en una factura de una sola línea. Las dos últimas se activan solo cuando el resultado concilia con el ValDtls.TotInvVal impreso, y un valor derivado nunca alimenta otra derivación a través del límite fila/totales. En una factura de varias líneas, un TotItemVal o total de documento faltante aún se reporta como faltante y no se produce ningún payload — consulta LIMITATIONS.md para saber por qué.

Procedencia

extraction_meta.field_provenance lleva una entrada para cada campo en el payload — 45 para esta factura — que indica qué etapa lo produjo y, para una página escaneada, la confianza de OCR del texto del que se leyó:

{
  "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 }
}

En una página escaneada, los mismos campos llevan números reales — 0.86 a 0.96 en una renderización limpia de 300 dpi — y la confianza más baja entre las palabras de un campo es la que se registra.

sourceSignificado
regexConfirmado determinísticamente, estructuralmente seguro
llmEstructurado por el modelo, luego verificado contra el texto del documento
derivedSigue por regla a partir de valores que se leyeron; no impreso en la página
assumedNi leído ni derivado — una suposición que la herramienta nombra explícitamente

Desarrollo

pytest -q -W error

1644 pruebas en diez módulos, pasando con advertencias tratadas como errores. La etapa LLM toma un cliente inyectado, así que toda la suite se ejecuta sin clave API y sin red.

La suite pasa tanto en 3.11 como en 3.13; 3.11 es el mínimo porque es la versión más baja en la que se resuelve todo el conjunto de dependencias, y se verificó ejecutando la suite allí en lugar de asumirlo.

Pruebas de cambios locales a través de un cliente MCP

El servidor MCP ejecuta lo que está instalado en site-packages, no tu árbol de trabajo. Un cliente MCP lanza el servidor a través del punto de entrada gst-einvoice-mcp, que se resuelve a la distribución instalada. Editar un archivo en el repositorio no cambia nada de lo que el servidor sirve hasta que reinstales, y no hay ningún error que te lo diga — la llamada a la herramienta tiene éxito y devuelve el comportamiento anterior.

Es fácil perder una hora en esto. Durante el trabajo de 0.1.2, el repositorio llevaba una nueva regla de derivación mientras el servidor aún servía 0.1.1, así que una llamada a la herramienta contra el código nuevo ejercitaba silenciosamente la ruta antigua y parecía mostrar que el cambio no había funcionado.

O reinstala después de cada cambio:

pip install -e .        # editable, so later edits are picked up on server restart

o apunta el command del cliente MCP al virtualenv del propio repositorio en lugar de una instalación a nivel de usuario, para que el servidor y las pruebas ejecuten el mismo código:

// in your MCP client config
"command": "C:\\path\\to\\repo\\.venv\\Scripts\\gst-einvoice-mcp.exe"

De cualquier manera, reinicia el servidor MCP después de cambiar código — un servidor en ejecución mantiene los módulos que importó al inicio. Si un cambio parece no tener efecto, verifica qué copia se está sirviendo antes de buscar el error en tu código.

MóduloQué hace
gstin.pyEstructura y suma de verificación mod-36
state_codes.pyTabla de códigos de estado, incluidos el descontinuado 25 y el heredado 28
schema.pyModelos pydantic INV-01, extra="forbid" en todo
validators.pyLas cuatro verificaciones aritméticas y de división de impuestos
ingest.pyEnrutamiento por página y las reglas de detectar y rechazar
ocr.pyTesseract con confianza por palabra mapeada a intervalos de caracteres
extract_rules.pyExtracción determinística: GSTIN, partes, número, fecha, HSN
extract_llm.pyLa etapa LLM y la verificación de fundamentación
pipeline.pyEnsamblaje de extremo a extremo
server.pyEl servidor MCP

Nota de licencia

Este proyecto depende de PyMuPDF, que es AGPL-3.0. Esa es una elección deliberada, hecha porque PyMuPDF abre archivos de imagen directamente como documentos de una página y dio una detección de capa de texto más confiable que las alternativas. Si tienes la intención de distribuir esta herramienta como parte de un producto de código cerrado, revisa esa licencia primero.

Fechas clave

Datos de PyPI

1 mantenedor

Datos de PyPI

Avatar for 411sst from gravatar.com 411sst

Descarga el archivo para tu plataforma. Si no estás seguro de cuál elegir, aprende más sobre instalación de paquetes.

Distribución de fuente

gst_einvoice_mcp-0.1.5.tar.gz (187.3 kB ver detalles)

Distribución compilada

Si no estás seguro sobre el formato del nombre de archivo, aprende más sobre nombres de archivos wheel.

gst_einvoice_mcp-0.1.5-py3-none-any.whl (90.4 kB ver detalles)

Detalles para el archivo gst_einvoice_mcp-0.1.5.tar.gz.

Metadatos del archivo

  • URL de descarga: gst_einvoice_mcp-0.1.5.tar.gz
  • Fecha de carga: 16 de septiembre de 2026
  • Tamaño: 187.3 kB
  • Etiquetas: Fuente
  • Cargado usando Publicación de Confianza? No
  • Cargado vía: twine/7.0.0 CPython/3.13.4

Hashes del archivo

AlgoritmoResumen hash
SHA256aa8a2410dc8f55095385f946d05de488a167cad8fe69303dbaaa9eb0953947eeCopiar
MD5df88853d70fce10ce69e2306e37aa293Copiar
BLAKE2b-25646b015ed04dbe6c63661d03201ed40ba2403ea96e0dd10cc5a472656c7e2ac27Copiar

Ver más detalles sobre el uso de hashes aquí.

Detalles para el archivo gst_einvoice_mcp-0.1.5-py3-none-any.whl.

Metadatos del archivo

  • URL de descarga: gst_einvoice_mcp-0.1.5-py3-none-any.whl
  • Fecha de carga: 16 de septiembre de 2026
  • Tamaño: 90.4 kB
  • Etiquetas: Python 3
  • Cargado usando Publicación de Confianza? No
  • Cargado vía: twine/7.0.0 CPython/3.13.4

Hashes del archivo

AlgoritmoResumen hash
SHA256556679fc6d332720ff7e2d69729a7941ca8c4463b2ad42aeaf2e19a614362690Copiar
MD52442ea9ce546f9e3e233508ceabb2641Copiar
BLAKE2b-256825504b5a974af3ef69eaa4b395c2e0805b8e2c88d7b36f7caa8294e5003aae5Copiar

Ver más detalles sobre el uso de hashes aquí.