Skip to main content
Glama
Pranavdmg20

pdf-extract-mcp

by Pranavdmg20

pdf-extract-mcp

CI Python License: MIT MCP

Un servidor del Model Context Protocol (MCP) que extrae datos estructurados de documentos PDF no estructurados de forma determinista: extracción de texto plano más coincidencia de campos mediante regex/heurísticas, sin llamadas a la API de LLM en el momento de la extracción.

Características

  • Servidor MCP real — construido sobre el SDK oficial de MCP para Python (2.x), que habla el protocolo a través de stdio, SSE o streamable HTTP. Verificado por una prueba de extremo a extremo que impulsa el servidor real con el cliente oficial.

  • Extracción guiada por esquema — apunta extract_fields a cualquier JSON Schema y obtén JSON estructurado con exactamente los campos que pediste.

  • Determinista e inspeccionable — coincidencia mediante regex/heurísticas, sin llamadas a la API de LLM, sin costes ocultos, sin caja negra. Cada extracción es repetible y auditable.

  • Informes de validación legibles para humanos — validate_against_schema explica por campo por qué pasó, falló o falta.

  • Esquemas predefinidos — invoice, resume y purchase_order se incluyen listos para usar, además de PDFs de muestra sintéticos para que todo sea demostrable sin configuración adicional.

  • Errores controlados — los PDF corruptos, los archivos inexistentes y los esquemas inválidos devuelven errores estructurados, nunca trazas de pila.

Related MCP server: PDF RAG MCP Server

Qué es MCP y por qué es útil

El Model Context Protocol es un estándar abierto que permite a los asistentes de IA (Claude, Cursor, etc.) llamar a herramientas externas a través de una conexión persistente y bidireccional. En lugar de pegar el texto de un PDF en un chat y pedirle al modelo que lo «descifre», un asistente puede llamar a pdf-extract-mcp directamente, recibir JSON estructurado que coincida con un esquema que tú proporcionas, y actuar sobre él. Como la extracción aquí es determinista (regex + heurísticas), no una llamada a un modelo probabilístico, cada resultado es inspeccionable, repetible y barato. Esto lo hace ideal para canalizaciones automatizadas de documentos (facturas a contabilidad, currículos a ATS, órdenes de compra (POs) a adquisiciones) donde necesitas saber por qué un campo se extrajo de determinada manera.

Instalación

cd pdf-extract-mcp
python3 -m venv .venv
source .venv/bin/activate
make install          # pip install -e ".[dev]"  (installs the console script too)

o, con pip simple:

pip install -e ".[dev]"

El servidor utiliza el SDK oficial de MCP para Python (mcp >= 2.x, la línea de versión actual, que proporciona la API MCPServer). pdfplumber se encarga de la extracción de texto, jsonschema de la validación y reportlab genera los PDFs de muestra.

La instalación también proporciona un script de consola pdf-extract-mcp, por lo que puedes ejecutar el servidor desde cualquier lugar con:

pdf-extract-mcp                     # stdio (default)
pdf-extract-mcp --transport streamable-http --host 127.0.0.1 --port 8000

Ejecución

python server.py

Esto sirve MCP a través de stdio (el valor predeterminado, y lo que esperan Claude Code / Claude Desktop). También puedes exponerlo como un servicio de red:

python server.py --transport streamable-http --host 127.0.0.1 --port 8000
python server.py --transport sse --host 127.0.0.1 --port 8001

Conectar con Claude Code / Claude Desktop

Claude Code — añade un .mcp.json a la raíz de tu proyecto:

{
  "mcpServers": {
    "pdf-extract": {
      "command": "python",
      "args": ["/absolute/path/to/pdf-extract-mcp/server.py"],
      "env": {}
    }
  }
}

Claude Desktop — añade el mismo bloque a la configuración de Claude Desktop (claude_desktop_config.json, ubicado en ~/Library/Application Support/Claude/ en macOS):

{
  "mcpServers": {
    "pdf-extract": {
      "command": "python",
      "args": ["/absolute/path/to/pdf-extract-mcp/server.py"]
    }
  }
}

Reinicia el cliente después de guardar. Deberías ver tres nuevas herramientas: extract_fields, validate_against_schema y list_supported_document_types.

Herramientas

Tool

Propósito

extract_fields(pdf_path, schema)

Extrae campos estructurados de un PDF que coincida con un JSON Schema -> {"ok": true, "data": {...}}

validate_against_schema(data, schema)

Comprueba los datos extraídos contra un esquema -> informe de aprobado/fallido/faltante con razones legibles para humanos

list_supported_document_types()

Enumera los tipos de documento que se incluyen con esquemas predefinidos

El argumento schema de extract_fields acepta un objeto JSON Schema, un nombre de esquema integrado (p. ej. "invoice"), o una ruta a un archivo de esquema .json. Los esquemas integrados se encuentran en schemas/:

  • invoice — vendor_name, invoice_number, total_amount, due_date (obligatorio) + issue_date, customer_name

  • resume — name, email (obligatorio) + phone, skills

  • purchase_order — po_number, vendor_name, total_amount (obligatorio) + issue_date, customer_name

Ejemplo práctico

Primero genera los PDFs de muestra (ya presentes en el repositorio; regenéralos en cualquier momento con):

python sample_pdfs/generate_samples.py

Ahora llama a extract_fields sobre la factura de muestra usando el nombre del esquema integrado invoice. En Claude Code puedes simplemente decir "extrae los campos de sample_pdfs/invoice.pdf usando el esquema invoice"; por debajo, emite una llamada a herramienta equivalente a:

{
  "name": "extract_fields",
  "arguments": {
    "pdf_path": "/absolute/path/to/pdf-extract-mcp/sample_pdfs/invoice.pdf",
    "schema": "invoice"
  }
}

Resultado real esperado:

{
  "ok": true,
  "data": {
    "vendor_name": "Acme Widgets Corp",
    "invoice_number": "INV-2024-0087",
    "total_amount": 1750.0,
    "due_date": "April 1, 2024",
    "issue_date": "March 1, 2024",
    "customer_name": "Globex Industries"
  },
  "text_length": 372
}

Introduciendo los datos en validate_against_schema con el mismo esquema:

{
  "ok": true,
  "valid": true,
  "passed": ["customer_name", "due_date", "invoice_number", "issue_date", "total_amount", "vendor_name"],
  "failed": [],
  "missing": [],
  "summary": "Valid: all 6 present field(s) conform to the schema.",
  "error": null
}

Ejecuta estos ejemplos desde Python directamente para verlo en vivo:

import json
from tools.extract import extract_fields
from tools.validate import validate_against_schema

schema = json.load(open("schemas/invoice.json"))
result = extract_fields("sample_pdfs/invoice.pdf", schema)
print(result["data"])
print(validate_against_schema(result["data"], schema))

Cómo funciona el registro de herramientas MCP en server.py

Esta es la parte central del proyecto, por lo que vale la pena entender exactamente qué hace el SDK por ti.

1. Crea el objeto del servidor.

from mcp.server.mcpserver import MCPServer

mcp = MCPServer(
    "pdf-extract-mcp",
    title="PDF Extract MCP",
    description="Deterministic structured-data extraction from PDF documents",
    version="0.2.0",
)

MCPServer es la clase de servidor del SDK de mcp 2.x. Implementa el protocolo de red MCP: sabe cómo responder a los mensajes JSON-RPC que un cliente envía durante el protocolo de enlace (handshake) de MCP (initialize, tools/list, tools/call, etc.). Los argumentos del constructor son metadatos: el nombre del servidor (requerido para el protocolo de enlace) más título/descripción/versión opcionales que los clientes pueden mostrar al usuario.

2. Registra cada herramienta con un decorador.

@mcp.tool()
def extract_fields(pdf_path: str, schema: dict) -> dict:
    """Extract structured fields from an unstructured PDF ..."""
    return _extract_fields(pdf_path, schema)

El decorador hace tres tareas por ti:

  • Registro del nombre — el nombre de la función extract_fields se convierte en el nombre de la herramienta que un cliente usa para invocarla. (Puedes sobrescribirlo con @mcp.tool(name="...").

  • Inferencia del esquema — el SDK inspecciona las anotaciones de tipo de la función (pdf_path: str, schema: dict) y genera automáticamente el esquema JSON de entrada de la herramienta. Por eso el cliente MCP sabe, antes de llamar, que pdf_path es una cadena y schema es un objeto. Este es el mismo patrón que usa FastAPI: los tipos son el contrato.

  • Descripción — el docstring se convierte en la descripción de la herramienta, que Claude lee para decidir cuándo llamar a la herramienta y con qué argumentos.

3. El cuerpo de la función es solo Python.

Cuando un cliente llama a la herramienta (tools/call con argumentos), el SDK deserializa los argumentos JSON, llama a tu función con ellos y serializa el valor de retorno de vuelta a través del cable. El valor de retorno es lo que ve el cliente — por eso las herramientas siempre devuelven diccionarios JSON simples y nunca lanzan excepciones: una excepción se convertiría en un error de protocolo opaco, mientras que un diccionario estructurado {"ok": false, "error": "..."} es algo que Claude puede leer y al que puede reaccionar. La lógica real de extracción/validación se encuentra en tools/extract.py y tools/validate.py para que siga siendo comprobable mediante pruebas unitarias sin un cliente MCP.

4. Ejecútalo.

if __name__ == "__main__":
    main()   # argparse -> mcp.run(transport="stdio")

mcp.run(transport="stdio") inicia el bucle del protocolo: lee peticiones JSON-RPC delimitadas por saltos de línea desde stdin, las envía a las herramientas registradas y escribe las respuestas en stdout. Ese es todo el servidor — sin framework HTTP, sin rutas, sin manejo manual de peticiones. (Para streamable-http / sse, la misma llamada run() inicia una aplicación ASGI interna.)

Un detalle más que vale la pena señalar: extract_fields usa una pequeña función auxiliar _load_schema que acepta un dict de esquema, un nombre de esquema integrado o una ruta de archivo — de modo que la misma herramienta funciona con "invoice" o con un objeto de esquema completo. La función de extracción real se mantiene estricta (solo dict) y la capa del servidor maneja las conversiones de conveniencia.

Cómo funciona la extracción (determinista e inspeccionable)

  1. Extracción de texto — pdfplumber abre el PDF y extrae el texto plano de cada página.

  2. Coincidencia de campos — para cada propiedad de tu esquema, se prueba una lista ordenada de regex; la primera coincidencia gana (tools/extract.py -> _FIELD_PATTERNS). Los patrones se ordenan de más específicos a menos específicos, y los nombres de campo desconocidos recurren a una coincidencia genérica "Field Name: value", además de una tabla de sinónimos (_FIELD_ALIASES).

  3. Coerción de tipos — las cadenas coincidentes se convierten al tipo del JSON Schema (p. ej. "$1,750.00" -> 1750.0 para "type": "number"; división por comas para arrays). Los fallos de coerción recurren a la cadena original en lugar de perder datos.

  4. Validación — validate_against_schema vuelve a comprobar los datos extraídos con el paquete jsonschema e informa, por campo, si pasó, falló (con una razón legible para humanos) o si falta por completo.

Debido a que cada paso es código simple, puedes rastrear exactamente por qué un campo se extrajo o no — no hay caja negra.

Manejo de errores

Las tres herramientas devuelven JSON estructurado en todos los caminos — nunca lanzan una traza de pila a través del límite MCP:

  • PDF corrupto/ilegible -> {"ok": false, "error": "Could not read PDF ..."}

  • Archivo inexistente -> {"ok": false, "error": "PDF not found: ..."}

  • PDF sin texto extraíble -> {"ok": false, "error": "... contains no extractable text."}

  • Esquema inválido (vacío, sin propiedades o JSON Schema inválido) -> clave de error estructurado

  • Campos obligatorios faltantes -> listados en "missing"; valores malformados -> listados en "failed" con razones

Pruebas

pytest tests/ -v

19 pruebas que cubren:

  • Extracción exitosa para los tres tipos de documento (invoice, resume, purchase_order)

  • Un PDF al que le faltan campos obligatorios (extracción negativa)

  • Validación de esquema que detecta un tipo de campo malformado, campos obligatorios faltantes, violaciones de enum/patrón

  • Rutas de error: PDF corrupto, archivo inexistente, PDF sin texto, esquema inválido

  • Una prueba MCP real de extremo a extremo (tests/test_mcp_end_to_end.py) que lanza server.py como subproceso, se conecta a través de stdio con el cliente oficial de MCP y llama a las tres herramientas a través del cable — demostrando que es un servidor MCP genuino, no una biblioteca que pretende serlo

Los PDFs de muestra se regeneran automáticamente mediante tests/conftest.py si faltan.

Estructura del repositorio

pdf-extract-mcp/
  server.py                    # MCP server: MCPServer + tool registration + transports
  tools/
    __init__.py
    extract.py                 # pdfplumber text extraction + regex field matching
    validate.py                # jsonschema validation with structured reports
  schemas/
    invoice.json               # pre-built schema: invoice
    resume.json                # pre-built schema: resume
    purchase_order.json        # pre-built schema: purchase_order
  sample_pdfs/
    generate_samples.py        # reportlab generator for the 4 sample PDFs
    invoice.pdf
    invoice_missing_fields.pdf
    resume.pdf
    purchase_order.pdf
  tests/
    conftest.py                # auto-generates sample PDFs if missing
    test_tools.py              # unit tests for extract/validate
    test_mcp_end_to_end.py     # end-to-end test over the real MCP stdio transport
  README.md
  requirements.txt

Solución de problemas

  • ModuleNotFoundError: No module named 'mcp' — no estás en el virtualenv: ejecuta source .venv/bin/activate (o usa ./.venv/bin/python server.py).

  • Errores de importación de FastMCP — server.py apunta a la API de mcp 2.x (MCPServer). Si tu entorno tiene mcp 1.x, reinstala con pip install -U "mcp>=2.0".

  • Las herramientas no aparecen en Claude — reinicia el cliente después de editar la configuración y asegúrate de que "args" apunta a la ruta absoluta de server.py, usando el python del venv como comando si es necesario.

  • La extracción no encuentra un campo — añade un patrón para él en _FIELD_PATTERNS en tools/extract.py (o confía en el respaldo genérico "Field Name: value" y en la tabla de sinónimos).

Available Tools

3 tools
extract_fieldsB

Extract structured fields from an unstructured PDF using regex/heuristics.

Args: pdf_path: Path to the PDF file. schema: JSON Schema describing the fields to extract (must have a non-empty 'properties' object). A built-in schema name (e.g. "invoice") or a path to a .json schema file is also accepted.

Returns: {"ok": True, "data": {...}, "text_length": N} on success, or {"ok": False, "error": "..."} on failure (corrupt PDF, bad schema, ...).

NameRequiredDescriptionDefault
schemaYes
pdf_pathYes

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full disclosure burden. It does disclose failure modes (corrupt PDF, bad schema), the exact return contract ({ok: True/False}), and the non-empty properties requirement. However, it omits the side-effect profile (e.g., read-only disk access) and edge behaviors such as empty extraction results, so disclosure is solid but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in a single line, followed by a clean Args/Returns structure that is scannable. Slightly padded by trailing ellipses and the '...' after the failure list, but overall efficient with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no annotations and no output schema, the description covers the return format, failure modes, and semantic details of both parameters—enough to call the tool correctly. Gaps are minor: no sibling-routing guidance and the meaning of the returned 'text_length' field is undefined.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must supply meaning. It adds substantial context: schema may be a JSON Schema object, a built-in name like 'invoice', or a .json file path, and must have a non-empty 'properties'; pdf_path is explained as a path to the PDF. This goes well beyond the bare type declarations.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb (extract), a resource (structured fields), and a source/method (unstructured PDF using regex/heuristics). This clearly distinguishes extraction behavior from list_supported_document_types, though it doesn't explicitly name the differentiation from validate_against_schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to choose this tool versus its siblings. It doesn't mention when validate_against_schema or list_supported_document_types would be the better pick, and states no preconditions or exclusions beyond the inline schema-constraint note in Args. The agent must infer usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_supported_document_typesB

List document types with pre-built schemas (e.g. invoice, resume, purchase_order).

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It weakly implies a read-only operation (listing) but does not explicitly state side effects, authentication requirements, rate limits, or response behavior. The existence of an output schema covers return format, but not behavioral traits. A simple 'List' implies read-only, but for a complete definition more context is expected.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence that leads with the action and then clarifies with concrete examples. There is no filler, and every word contributes to understanding the tool's purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has no parameters, an output schema exists, and the purpose is simple, the description covers the essentials. It explains what the tool does and gives representative examples. It could add a note about usage direction (e.g., that these types can be used with extract_fields or validate_against_schema), but such cross-referencing is not required for basic invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, and the input schema is an empty object. According to the rubric, a parameterless tool gets a baseline 4. The description adds no parameter-specific information, but none is needed; the schema is sufficient.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb 'List' and a clear resource 'document types with pre-built schemas', including helpful examples (invoice, resume, purchase_order). It clearly conveys the tool's function, though it does not explicitly distinguish it from siblings like extract_fields or validate_against_schema, so it falls just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus the sibling tools. The description does not mention alternatives, typical usage scenarios, or any conditions that would make this tool the right choice. This is a clear gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_against_schemaA

Validate extracted data against a JSON Schema.

Args: data: The data to check (e.g. the "data" from extract_fields). schema: JSON Schema to validate against.

Returns: A structured report with passed/failed/missing field lists and a human-readable reason for each failure. Never raises: invalid schemas are reported via the "error" key instead.

NameRequiredDescriptionDefault
dataYes
schemaYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses key behavior: never raises exceptions, invalid schemas are reported via an 'error' key, and the return is a structured report with passed/failed/missing lists and human-readable reasons. This goes beyond a generic 'validates' and gives the agent a clear expectation of failure modes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently formatted with an Args and Returns section, front-loaded with the purpose sentence. It is not verbose—every line contributes to the agent's understanding. It could be tightened (the Returns section is slightly redundant with the behavioral note), but it's well-structured and clear.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given two required parameters, no output schema, and no annotations, the description covers the essential context: what the tool does, what each parameter is, how errors are handled, and what the response contains. It references the sibling workflow (extract_fields) which aids routing. The only minor gap is that it doesn't specify the expected shape of 'data' beyond being an object, but the example mitigates this.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must explain the parameters. It does: 'data' is explicitly the output of extract_fields, and 'schema' is a JSON Schema. This adds meaning beyond the bare 'object' type in the schema and gives the agent the necessary context for both arguments. It could add more detail about expected structures, but it is sufficient for a two-parameter tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear verb+resource: 'Validate extracted data against a JSON Schema.' It names the specific tool and its input, and the example referencing extract_fields ties it to the workflow. It is clearly distinct from the siblings (list_supported_document_types and extract_fields) because validation is an entirely separate operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to use it: after extraction, with the 'data' from extract_fields. It implies the pipeline (extract → validate) without explicitly saying 'use this when...' but the example makes the usage obvious. It does not mention when not to use it or name alternatives, so it lacks an explicit exclusion but is not misleading.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.2.0
    • First observedextract_fields
    • First observedlist_supported_document_types
    • First observedvalidate_against_schema

TDQS

A3.8/5.0

Scored across 3 tools

Disambiguation5/5

Each tool serves a clearly distinct purpose: listing supported schemas, extracting fields from a PDF, and validating extracted data against a schema. There is no overlap or ambiguity in tool responsibilities.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case: list_supported_document_types, extract_fields, validate_against_schema. The convention is uniform and predictable.

Tool Count4/5

With only 3 tools, the set is at the lower end of the well-scoped range but still appropriate for a focused PDF extraction and validation server. Each tool earns its place without redundancy.

Completeness4/5

The tool surface covers the core workflow: discovering available schemas, extracting fields, and validating results. Minor gaps exist (e.g., no tool to add custom schemas or handle batch processing), but they are not critical for the primary purpose.

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Extracts structured JSON data from unstructured text using predefined schemas for receipts, invoices, resumes, and emails. It allows users to transform messy text into organized data through built-in or custom-defined fields.
    1
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables RAG over messy PDFs — extract, chunk, embed, and search scanned, multi-column, and table-heavy documents.
    6
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Extracts text and tables from PDFs for AI agents via MCP, enabling structured data retrieval from invoices, reports, and statements.
    75 PyPI
    1
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables reading entire PDF documents into validated structured JSON, including tables, key-values, and markdown, with read-only extraction tools and layout reconstruction.
    -