Skip to main content
Glama
cmendezs

mcp-facturacion-electronica-es

es__generate_facturae_xml

Generate Facturae 3.2.2 XML invoices for Spanish B2G electronic invoicing to the FACe portal. The document is unsigned and must be signed before submission.

Instructions

Genera una factura XML conforme a Facturae 3.2.2 para envío B2G al portal FACe. El documento generado está sin firmar; use es__sign_facturae_xades para firmarlo.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
invoiceYesInvoiceDocument con seller, buyer, vat_summary y lines.
schema_versionNoVersión del esquema Facturae (por defecto: '3.2.2').3.2.2

Implementation Reference

  • Handler function for the es__generate_facturae_xml tool. Parses invoice data, builds Facturae 3.2.2 XML via build_facturae_xml(), and returns the XML string.
    async def handle_es_generate_facturae_xml(
        arguments: dict[str, Any],
    ) -> list[types.TextContent]:
        try:
            invoice_data = arguments.get("invoice")
            if not invoice_data:
                return err("invoice is required", "MISSING_PARAM")
    
            invoice = parse_invoice(invoice_data)
            xml_bytes = build_facturae_xml(invoice)
    
            logger.info("Facturae 3.2.2 XML generated for invoice %s", invoice.number)
            return ok({
                "xml": xml_bytes.decode("utf-8"),
                "schema_version": "3.2.2",
                "invoice_number": invoice.number,
                "next_step": "Use es__sign_facturae_xades to apply XAdES-EPES signature before submitting to FACe.",
            })
    
        except EInvoicingError as exc:
            return err(str(exc))
        except Exception as exc:
            logger.exception("es__generate_facturae_xml failed")
            return err(str(exc))
  • Tool definition/schema for es__generate_facturae_xml, including name, description, and inputSchema (invoice object required, optional schema_version).
    TOOL_ES_GENERATE_FACTURAE_XML = types.Tool(
        name="es__generate_facturae_xml",
        description=(
            "Genera una factura XML conforme a Facturae 3.2.2 para envío B2G al portal FACe. "
            "El documento generado está sin firmar; use es__sign_facturae_xades para firmarlo."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "invoice": {
                    "type": "object",
                    "description": "InvoiceDocument con seller, buyer, vat_summary y lines.",
                },
                "schema_version": {
                    "type": "string",
                    "description": "Versión del esquema Facturae (por defecto: '3.2.2').",
                    "default": "3.2.2",
                },
            },
            "required": ["invoice"],
        },
    )
  • Registration of the tool name es__generate_facturae_xml to its handler in the _TOOL_HANDLERS dict on the server.
    _TOOL_HANDLERS: dict[str, Any] = {
        # VERI*FACTU
        "es__generate_verifactu_record":      handle_es_generate_verifactu_record,
        "es__validate_verifactu_record":      handle_es_validate_verifactu_record,
        "es__submit_verifactu_to_aeat":       handle_es_submit_verifactu_to_aeat,
        "es__generate_qr_verifactu":          handle_es_generate_qr_verifactu,
        "es__cancel_verifactu_record":        handle_es_cancel_verifactu_record,
        # Facturae / FACe
        "es__generate_facturae_xml":          handle_es_generate_facturae_xml,
        "es__sign_facturae_xades":            handle_es_sign_facturae_xades,
        "es__submit_to_face":                 handle_es_submit_to_face,
        "es__get_face_invoice_status":        handle_es_get_face_invoice_status,
        "es__validate_facturae_schema":       handle_es_validate_facturae_schema,
        # SII
        "es__build_sii_invoice_record":       handle_es_build_sii_invoice_record,
        "es__submit_sii_batch":               handle_es_submit_sii_batch,
        "es__query_sii_status":               handle_es_query_sii_status,
        "es__generate_sii_correction":        handle_es_generate_sii_correction,
        # TicketBAI
        "es__generate_ticketbai_xml":         handle_es_generate_ticketbai_xml,
        "es__submit_ticketbai":               handle_es_submit_ticketbai,
        "es__validate_ticketbai_schema":      handle_es_validate_ticketbai_schema,
        # Crea y Crece / B2B
        "es__generate_b2b_einvoice_es":       handle_es_generate_b2b_einvoice_es,
        "es__check_b2b_mandate_applicability": handle_es_check_b2b_mandate_applicability,
        # Utilities
        "es__detect_regional_regime":         handle_es_detect_regional_regime,
        "es__get_compliance_status":          handle_es_get_compliance_status,
        "es__parse_aeat_response":            handle_es_parse_aeat_response,
    }
  • Registration of TOOL_ES_GENERATE_FACTURAE_XML in the _ALL_TOOLS list on the server.
    _ALL_TOOLS: list[types.Tool] = [
        # VERI*FACTU
        TOOL_ES_GENERATE_VERIFACTU_RECORD,
        TOOL_ES_VALIDATE_VERIFACTU_RECORD,
        TOOL_ES_SUBMIT_VERIFACTU_TO_AEAT,
        TOOL_ES_GENERATE_QR_VERIFACTU,
        TOOL_ES_CANCEL_VERIFACTU_RECORD,
        # Facturae / FACe
        TOOL_ES_GENERATE_FACTURAE_XML,
        TOOL_ES_SIGN_FACTURAE_XADES,
        TOOL_ES_SUBMIT_TO_FACE,
        TOOL_ES_GET_FACE_INVOICE_STATUS,
        TOOL_ES_VALIDATE_FACTURAE_SCHEMA,
        # SII
        TOOL_ES_BUILD_SII_INVOICE_RECORD,
        TOOL_ES_SUBMIT_SII_BATCH,
        TOOL_ES_QUERY_SII_STATUS,
        TOOL_ES_GENERATE_SII_CORRECTION,
        # TicketBAI
        TOOL_ES_GENERATE_TICKETBAI_XML,
        TOOL_ES_SUBMIT_TICKETBAI,
        TOOL_ES_VALIDATE_TICKETBAI_SCHEMA,
        # Crea y Crece / B2B
        TOOL_ES_GENERATE_B2B_EINVOICE_ES,
        TOOL_ES_CHECK_B2B_MANDATE_APPLICABILITY,
        # Utilities
        TOOL_ES_DETECT_REGIONAL_REGIME,
        TOOL_ES_GET_COMPLIANCE_STATUS,
        TOOL_ES_PARSE_AEAT_RESPONSE,
    ]
  • Core XML builder helper function build_facturae_xml() that constructs the Facturae 3.2.2 XML tree from an InvoiceDocument.
    def build_facturae_xml(invoice: InvoiceDocument) -> bytes:
        """Build a Facturae 3.2.2 XML document from an InvoiceDocument.
    
        Args:
            invoice: Validated core InvoiceDocument.
    
        Returns:
            UTF-8 encoded Facturae 3.2.2 XML bytes (unsigned).
        """
        nsmap = {
            None: _FACTURAE_NS,
            "ds": _DS_NS,
            "xades": _XADES_NS,
        }
        root = etree.Element("Facturae", nsmap=nsmap)
    
        # FileHeader
        fh = _sub(root, "FileHeader")
        _sub(fh, "SchemaVersion", "3.2.2")
        _sub(fh, "Modality", "I")  # I = individual invoice
        _sub(fh, "InvoiceIssuerType", "EU")  # EU = private seller
    
        # Compute totals
        tax_base = sum((v.taxable_base for v in invoice.vat_summary), Decimal("0"))
        tax_total = sum((v.vat_amount for v in invoice.vat_summary), Decimal("0"))
        grand_total = tax_base + tax_total
    
        batch = _sub(fh, "Batch")
        _sub(batch, "BatchIdentifier", invoice.number)
        _sub(batch, "InvoicesCount", "1")
        ta = _sub(batch, "TotalInvoicesAmount")
        _sub(ta, "TotalAmount", fmt_amount(grand_total))
        oa = _sub(batch, "TotalOutstandingAmount")
        _sub(oa, "TotalAmount", fmt_amount(grand_total))
        ea = _sub(batch, "TotalExecutableAmount")
        _sub(ea, "TotalAmount", fmt_amount(grand_total))
        _sub(batch, "InvoiceCurrencyCode", invoice.currency.upper())
    
        # Parties
        parties = _sub(root, "Parties")
        seller_elem = _sub(parties, "SellerParty")
        _build_tax_id_block(seller_elem, invoice.seller)
        _build_legal_block(seller_elem, invoice.seller)
    
        buyer_elem = _sub(parties, "BuyerParty")
        _build_tax_id_block(buyer_elem, invoice.buyer)
        _build_legal_block(buyer_elem, invoice.buyer)
    
        # Invoices
        invoices_elem = _sub(root, "Invoices")
        inv = _sub(invoices_elem, "Invoice")
    
        ih = _sub(inv, "InvoiceHeader")
        _sub(ih, "InvoiceNumber", invoice.number)
        _sub(ih, "InvoiceDocumentType", "FC")  # FC = complete invoice
        _sub(ih, "InvoiceClass", "OO")          # OO = original
    
        iid = _sub(inv, "InvoiceIssueData")
        _sub(iid, "IssueDate", invoice.date)
        _sub(iid, "InvoiceCurrencyCode", invoice.currency.upper())
        _sub(iid, "TaxCurrencyCode", invoice.currency.upper())
        _sub(iid, "LanguageName", "es")
    
        # TaxesOutputs
        taxes = _sub(inv, "TaxesOutputs")
        for vat in invoice.vat_summary:
            tax = _sub(taxes, "Tax")
            _sub(tax, "TaxTypeCode", "01")  # 01 = IVA
            _sub(tax, "TaxRate", fmt_amount(vat.vat_rate))
            tb = _sub(tax, "TaxableBase")
            _sub(tb, "TotalAmount", fmt_amount(vat.taxable_base))
            ta_elem = _sub(tax, "TaxAmount")
            _sub(ta_elem, "TotalAmount", fmt_amount(vat.vat_amount))
    
        # InvoiceTotals
        totals = _sub(inv, "InvoiceTotals")
        _sub(totals, "TotalGrossAmount", fmt_amount(tax_base))
        _sub(totals, "TotalGeneralDiscounts", "0.00")
        _sub(totals, "TotalGeneralSurcharges", "0.00")
        _sub(totals, "TotalGrossAmountBeforeTaxes", fmt_amount(tax_base))
        _sub(totals, "TotalTaxOutputs", fmt_amount(tax_total))
        _sub(totals, "TotalTaxesWithheld", "0.00")
        _sub(totals, "InvoiceTotal", fmt_amount(grand_total))
        _sub(totals, "TotalOutstandingAmount", fmt_amount(grand_total))
        _sub(totals, "TotalExecutableAmount", fmt_amount(grand_total))
    
        # Items
        if invoice.lines:
            items = _sub(inv, "Items")
            for line in invoice.lines:
                il = _sub(items, "InvoiceLine")
                _sub(il, "ItemDescription", line.description[:2500])
                if line.quantity is not None:
                    _sub(il, "Quantity", fmt_amount(line.quantity))
                _sub(il, "UnitPriceWithoutTax", fmt_amount(line.unit_price))
                _sub(il, "TotalCost", fmt_amount(line.total_price))
                _sub(il, "GrossAmount", fmt_amount(line.total_price))
                line_taxes = _sub(il, "TaxesOutputs")
                lt = _sub(line_taxes, "Tax")
                _sub(lt, "TaxTypeCode", "01")
                _sub(lt, "TaxRate", fmt_amount(line.vat_rate))
                ltb = _sub(lt, "TaxableBase")
                _sub(ltb, "TotalAmount", fmt_amount(line.total_price))
                lta = _sub(lt, "TaxAmount")
                _sub(lta, "TotalAmount", fmt_amount(line.total_price * line.vat_rate / 100))
    
        # PaymentDetails (if provided)
        if invoice.payment:
            pd = _sub(inv, "PaymentDetails")
            installment = _sub(pd, "Installment")
            _sub(installment, "InstallmentDueDate", invoice.payment.due_date or invoice.date)
            _sub(installment, "InstallmentAmount", fmt_amount(invoice.payment.amount))
            _sub(installment, "PaymentMeans", invoice.payment.payment_method_code or "01")
            if invoice.payment.iban:
                _sub(installment, "AccountToBeCredited")
                # AccountToBeCredited needs full bank details; simplified here
                # [NEED: add IBAN and BIC fields per Facturae 3.2.2 schema]
    
        return etree.tostring(root, xml_declaration=True, encoding="UTF-8", pretty_print=True)
Behavior3/5

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

No annotations are provided, so the description must supply behavioral details. It states the generated document is unsigned, which is critical. However, it does not disclose other behaviors like validation requirements, side effects, or output format.

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?

Two concise sentences front-load the key action and specs. Every sentence adds value: the first specifies the standard and target, the second warns about signing requirements.

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

Completeness3/5

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

The tool has a nested object parameter and no output schema. The description explains the purpose and unsigned nature but does not describe the return value (e.g., whether it returns the XML string or a file). This is a gap for agent invocation.

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

Parameters3/5

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

Schema coverage is 100%, so the baseline is 3. The description adds no extra meaning beyond the schema field descriptions; it simply references the invoice object without elaborating on its structure.

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 clearly states the tool generates a Facturae XML for B2G submission to FACe, specifying the exact schema version (3.2.2). It distinguishes itself from sibling tools like es__sign_facturae_xades by noting the output is unsigned.

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 explicitly tells the agent when to use the tool (for B2G Facturae generation) and what subsequent action to take (signing with es__sign_facturae_xades). It does not explicitly list when not to use it, but the context of siblings implies other tools are for different scenarios.

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

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cmendezs/mcp-facturacion-electronica-es'

If you have feedback or need assistance with the MCP directory API, please join our Discord server