es__validate_verifactu_record
Validate VERI*FACTU XML records structurally and against XSD schema to ensure compliance with Spanish tax regulations.
Instructions
Valida un registro VERI*FACTU XML. Realiza validación estructural y, si el XSD v1.0 (HAC/1177/2024) está disponible en specs/verifactu/, también validación de esquema.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| xml | Yes | Registro VERI*FACTU XML en crudo. | |
| schema_version | No | Versión del esquema XSD (por defecto: '1.0'). | 1.0 |
Implementation Reference
- Handler function that validates a VERI*FACTU XML record — parses XML structurally, checks mandatory elements, and optionally runs XSD schema validation if the XSD file is available in specs/verifactu/.
async def handle_es_validate_verifactu_record( arguments: dict[str, Any], ) -> list[types.TextContent]: try: xml_str = arguments.get("xml", "") if not xml_str: return err("xml is required", "MISSING_PARAM") xml_bytes = xml_str.encode() if isinstance(xml_str, str) else xml_str # --- Structural parse --- try: root = etree.fromstring(xml_bytes) except etree.XMLSyntaxError as exc: return ok({ "valid": False, "errors": [f"XML malformado: {exc}"], "warnings": [], "validation_mode": "structural", }) errors: list[str] = [] warnings: list[str] = [] def _req(tag: str) -> None: if not root.xpath(f".//*[local-name()='{tag}']"): errors.append(f"Elemento obligatorio ausente: <{tag}>") # Check mandatory VERI*FACTU elements for tag in [ "IDEmisorFactura", "NumSerieFactura", "FechaExpedicionFactura", "TipoFactura", "CuotaTotal", "ImporteTotal", "FechaHoraHusoGenRegistro", "Huella", ]: _req(tag) # --- XSD validation (if schema available) --- xsd_dir = ( __import__("pathlib").Path(__file__).parent.parent.parent / "specs" / "verifactu" ) xsd_files = list(xsd_dir.glob("*.xsd")) if xsd_dir.exists() else [] validation_mode = "structural" if xsd_files: try: xsd_doc = etree.parse(str(xsd_files[0])) schema = etree.XMLSchema(xsd_doc) schema.validate(root) for e in schema.error_log: errors.append(f"[XSD] {e.message} (línea {e.line})") validation_mode = "xsd" except Exception as exc: warnings.append(f"XSD validation failed to run: {exc}") else: warnings.append( "Validación XSD no disponible — descargue el XSD v1.0 (BOE-A-2024-22138) " "a specs/verifactu/ para habilitar validación de esquema completa." ) return ok({ "valid": len(errors) == 0, "errors": errors, "warnings": warnings, "validation_mode": validation_mode, }) except Exception as exc: logger.exception("es__validate_verifactu_record failed") return err(str(exc)) - Tool definition (types.Tool) with name 'es__validate_verifactu_record', input schema requiring 'xml' string and optional 'schema_version'.
TOOL_ES_VALIDATE_VERIFACTU_RECORD = types.Tool( name="es__validate_verifactu_record", description=( "Valida un registro VERI*FACTU XML. Realiza validación estructural y, si el XSD v1.0 " "(HAC/1177/2024) está disponible en specs/verifactu/, también validación de esquema." ), inputSchema={ "type": "object", "properties": { "xml": {"type": "string", "description": "Registro VERI*FACTU XML en crudo."}, "schema_version": { "type": "string", "description": "Versión del esquema XSD (por defecto: '1.0').", "default": "1.0", }, }, "required": ["xml"], }, - mcp_facturacion_electronica_es/server.py:111-111 (registration)Registration in _TOOL_HANDLERS dict mapping the tool name string to its handler function.
"es__validate_verifactu_record": handle_es_validate_verifactu_record, - mcp_facturacion_electronica_es/server.py:80-80 (registration)Registration in _ALL_TOOLS list (line 80) and imported from verifactu module (line 64).
TOOL_ES_VALIDATE_VERIFACTU_RECORD, - Supporting helper constants for VERI*FACTU endpoints used by the submission tool (not directly by validation, but part of the same tool family).
VERIFACTU_ENDPOINTS: dict[str, str] = { "sandbox": ( "https://prewww2.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/FactSistemaFacturacion" ), "production": ( "https://www2.agenciatributaria.gob.es" "/wlpl/TIKE-CONT/ws/SistemaFacturacion/FactSistemaFacturacion" ), # [NEED: verify production URL against published AEAT technical guide] } #: SII issued-invoice submission endpoints SII_ISSUED_ENDPOINTS: dict[str, str] = { "sandbox": ( "https://www7.aeat.es/wlpl/BURT-JDIT/ws/fe/SiiEndPointFacultativoRecepcion" ), "production": ( "https://www2.agenciatributaria.gob.es" "/wlpl/BURT-JDIT/ws/fe/SiiEndPointFacultativoRecepcion" ), # [NEED: verify sandbox URL — AEAT may use www7 or www10] } #: SII received-invoice submission endpoints SII_RECEIVED_ENDPOINTS: dict[str, str] = { "sandbox": ( "https://www7.aeat.es/wlpl/BURT-JDIT/ws/fr/SiiEndPointFacultativoRecepcion" ), "production": ( "https://www2.agenciatributaria.gob.es" "/wlpl/BURT-JDIT/ws/fr/SiiEndPointFacultativoRecepcion" ), } #: FACe B2B REST API v2 base URLs FACE_BASE_URLS: dict[str, str] = { "sandbox": "https://se-face.redsara.es/factura-face-b2b-api/api/v2", "production": "https://face.gob.es/factura-face-b2b-api/api/v2", # [NEED: verify — FACe may have changed API base path in 2025] } #: TicketBAI provincial submission endpoints TICKETBAI_ENDPOINTS: dict[str, dict[str, str]] = { "araba": { "sandbox": "https://batuz.eus/BATUZ/TicketBAI/alta", "production": "https://batuz.eus/BATUZ/TicketBAI/alta", # [NEED: verify Álava sandbox — may be the same as production with a test NIF] }, "gipuzkoa": { "sandbox": "https://tbai.prep.gipuzkoa.eus/sarrerak/alta/", "production": "https://tbai.egoitza.gipuzkoa.eus/sarrerak/alta/", }, "bizkaia": { "sandbox": "https://pruebasapi.ebizkaia.eus/LROE/facturas/1.0/facturas", "production": "https://api.ebizkaia.eus/LROE/facturas/1.0/facturas", # [NEED: verify Bizkaia LROE path — Bizkaia uses a different submission scheme] }, } #: Facturae XAdES-EPES signature policy (Orden EHA/962/2007) FACTURAE_POLICY_ID = ( "http://www.facturae.es/politica_de_firma_formato_facturae" "/politica_de_firma_formato_facturae_v3_1.pdf" ) #: [NEED: compute SHA-256 of the policy PDF from facturae.gob.es and set here] FACTURAE_POLICY_HASH: str | None = None #: TicketBAI signature policy URIs per province TICKETBAI_POLICY_IDS: dict[str, str] = { "araba": ( "https://www.araba.eus/tbai/2021061/tbai-TBXEko+sinadura+politika+v1.0+esAN.pdf" ), "gipuzkoa": ( "https://www.gipuzkoa.eus/eu/web/ogasuna/tick-et-bai" "/laguntza-teknikoa/argibide-teknikoak" ), "bizkaia": "https://www.bizkaia.eus/ogasun/batuz/TicketBAI/ca_bizkaiako_sinadura_politika_1_0.pdf", # [NEED: verify all three policy URIs from official provincial technical documentation] }