Skip to main content
Glama
OrtaMarco

mx-fiscal-mcp-server

by OrtaMarco

CFDI Status at the SAT

cfdi_status
Read-onlyIdempotent

Check if a Mexican CFDI invoice is valid and current by querying the SAT's public status service. Get its status, cancelability, and 69-B validation without credentials.

Instructions

Ask the SAT whether an invoice actually exists and is still live. This queries the public ConsultaCFDIService SOAP endpoint — the same service the QR code printed on every Mexican invoice points at — so it needs no credentials, no CSD and no PAC contract.

Pass either the four values the SAT keys on (issuer RFC, receiver RFC, total, UUID) or the whole xml, in which case they are derived from it with the same reader parse_cfdi uses. Deriving them from the XML is the more reliable route: the total must be formatted exactly the way the printed-representation spec demands (six decimals, trailing zeros trimmed), and a total written in another format can come back as a spurious 'No Encontrado'.

What comes back, each with its meaning spelled out:

  • Estado — Vigente / Cancelado / No Encontrado.

  • EsCancelable — whether the issuer can cancel it unilaterally, needs the receiver's approval, or cannot cancel it at all.

  • EstatusCancelacion — whether a cancellation is in progress, was accepted, was rejected, or lapsed.

  • ValidacionEFOS — whether the issuer, and any third-party RFC the invoice was issued on behalf of (a cuenta de terceros), appears on the SAT's definitive 69-B list of companies that invoice simulated operations. Read with the code table the SAT documents (service documentation v1.4, section 3): 100, 101 and 104 put the issuer on the list; 102 and 103 mean the issuer is NOT on it but a third-party RFC is; 200 and 201 mean the issuer is not on it (201: nor any third party). efos_state speaks of the issuer only and efos_third_party_state of the third parties; an empty field or an undocumented code is unknown, with the raw code kept in validacion_efos.

  • CodigoEstatus — the service's own result code.

Fail-soft by design. The SAT publishes no SLA and no status page, and the endpoint does go down; its documentation states capacity for up to 2 million queries per hour and asks callers not to raise their query volume, so query once per invoice and cache the answer. On timeout, refusal or a malformed answer this returns available: false with the reason instead of raising — a failed lookup is a statement about the SAT, never about the invoice. Never report a document as invalid on the strength of an unreachable service. One retry, 10-second timeout.

Args (either shape):

  • xml (string), OR rfc_emisor + rfc_receptor + total + uuid (all strings).

  • response_format ('markdown' | 'json'): output format (default 'markdown').

Returns: { available, unavailable_reason, endpoint, expression, attempts, elapsed_ms, source, status{codigo_estatus, query_outcome, estado, document_state, document_meaning, es_cancelable, cancellable_state, cancellable_meaning, estatus_cancelacion, cancellation_state, cancellation_meaning, validacion_efos, efos_state, efos_third_party_state, efos_meaning, raw} | null, findings[] }.

Example: "Is this invoice still valid?" -> cfdi_status(xml="<cfdi:Comprobante …>").

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
xmlNoThe complete CFDI XML. When given, the four query fields are derived from it and any values passed alongside are ignored.
uuidNoThe fiscal folio (UUID) from the Timbre Fiscal Digital. Required unless `xml` is given.
totalNoInvoice total exactly as written in the XML, e.g. '1160.00'. Required unless `xml` is given.
rfc_emisorNoIssuer's RFC. Required unless `xml` is given.
rfc_receptorNoReceiver's RFC. Required unless `xml` is given.
response_formatNoOutput format: 'markdown' for a human-readable summary (default) or 'json' for the full structured payload.markdown

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
sourceYes
statusYes
attemptsYes
endpointYes
findingsYes
availableYes
elapsed_msYes
expressionYes
unavailable_reasonYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv1.0.2
    • changedOutput schema / properties / status / anyOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "cancellable_meaning": {
      -        "type": "string"
      -      },
      -      "cancellable_state": {
      -        "enum": [
      -          "sin_aceptacion",
      -          "con_aceptacion",
      -          "no_cancelable",
      -          "unknown"
      -        ],
      -        "type": "string"
      -      },
      -      "cancellation_meaning": {
      -        "type": "string"
      -      },
      -      "cancellation_state": {
      -        "enum": [
      -          "cancelado_sin_aceptacion",
      -          "cancelado_con_aceptacion",
      -          "plazo_vencido",
      -          "en_proceso",
      -          "solicitud_rechazada",
      -          "ninguno"
      -        ],
      -        "type": "string"
      -      },
      -      "codigo_estatus": {
      -        "type": "string"
      -      },
      -      "document_meaning": {
      -        "type": "string"
      -      },
      -      "document_state": {
      -        "enum": [
      -          "vigente",
      -          "cancelado",
      -          "no_encontrado"
      -        ],
      -        "type": "string"
      -      },
      -      "efos_meaning": {
      -        "type": "string"
      -      },
      -      "efos_state": {
      -        "enum": [
      -          "not_listed",
      -          "listed",
      -          "unknown"
      -        ],
      -        "type": "string"
      -      },
      -      "es_cancelable": {
      -        "type": "string"
      -      },
      -      "estado": {
      -        "type": "string"
      -      },
      -      "estatus_cancelacion": {
      -        "type": "string"
      -      },
      -      "query_outcome": {
      -        "enum": [
      -          "found",
      -          "not_found"
      -        ],
      -        "type": "string"
      -      },
      -      "raw": {
      -        "additionalProperties": {
      -          "type": "string"
      -        },
      -        "propertyNames": {
      -          "type": "string"
      -        },
      -        "type": "object"
      -      },
      -      "validacion_efos": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "codigo_estatus",
      -      "query_outcome",
      -      "estado",
      -      "document_state",
      -      "document_meaning",
      -      "es_cancelable",
      -      "cancellable_state",
      -      "cancellable_meaning",
      -      "estatus_cancelacion",
      -      "cancellation_state",
      -      "cancellation_meaning",
      -      "validacion_efos",
      -      "efos_state",
      -      "efos_meaning",
      -      "raw"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "cancellable_meaning": {
      +        "type": "string"
      +      },
      +      "cancellable_state": {
      +        "enum": [
      +          "sin_aceptacion",
      +          "con_aceptacion",
      +          "no_cancelable",
      +          "unknown"
      +        ],
      +        "type": "string"
      +      },
      +      "cancellation_meaning": {
      +        "type": "string"
      +      },
      +      "cancellation_state": {
      +        "enum": [
      +          "cancelado_sin_aceptacion",
      +          "cancelado_con_aceptacion",
      +          "plazo_vencido",
      +          "en_proceso",
      +          "solicitud_rechazada",
      +          "ninguno"
      +        ],
      +        "type": "string"
      +      },
      +      "codigo_estatus": {
      +        "type": "string"
      +      },
      +      "document_meaning": {
      +        "type": "string"
      +      },
      +      "document_state": {
      +        "enum": [
      +          "vigente",
      +          "cancelado",
      +          "no_encontrado"
      +        ],
      +        "type": "string"
      +      },
      +      "efos_meaning": {
      +        "type": "string"
      +      },
      +      "efos_state": {
      +        "enum": [
      +          "not_listed",
      +          "listed",
      +          "unknown"
      +        ],
      +        "type": "string"
      +      },
      +      "efos_third_party_state": {
      +        "enum": [
      +          "listed",
      +          "not_listed",
      +          "not_reported",
      +          "unknown"
      +        ],
      +        "type": "string"
      +      },
      +      "es_cancelable": {
      +        "type": "string"
      +      },
      +      "estado": {
      +        "type": "string"
      +      },
      +      "estatus_cancelacion": {
      +        "type": "string"
      +      },
      +      "query_outcome": {
      +        "enum": [
      +          "found",
      +          "not_found"
      +        ],
      +        "type": "string"
      +      },
      +      "raw": {
      +        "additionalProperties": {
      +          "type": "string"
      +        },
      +        "propertyNames": {
      +          "type": "string"
      +        },
      +        "type": "object"
      +      },
      +      "validacion_efos": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "codigo_estatus",
      +      "query_outcome",
      +      "estado",
      +      "document_state",
      +      "document_meaning",
      +      "es_cancelable",
      +      "cancellable_state",
      +      "cancellable_meaning",
      +      "estatus_cancelacion",
      +      "cancellation_state",
      +      "cancellation_meaning",
      +      "validacion_efos",
      +      "efos_state",
      +      "efos_third_party_state",
      +      "efos_meaning",
      +      "raw"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
  2. First observedv1.0.0

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already mark it read-only, idempotent and non-destructive. The description goes well beyond: no credentials needed, fail-soft design returning available:false instead of raising, one retry with a 10-second timeout, the SAT's 2M queries-per-hour capacity and no-SLA context, and the total-formatting pitfall that produces a spurious 'No Encontrado'. It even warns that a failed lookup is a statement about the SAT, never about the invoice. Nothing contradicts the annotations.

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?

Front-loaded with purpose and structured with headers, bullets and emphasis. It is long, but the semantic complexity (EFOS code table, cancellation states, fail-soft behavior) justifies the length. Minor redundancy: the raw return-shape literal is spelled out even though an output schema exists.

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

Completeness5/5

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

For a complex, externally-dependent tool, everything needed is present: purpose, endpoint, credential requirements, input modes and precedence, formatting gotchas, per-field return interpretation, failure semantics, retry/timeout, rate-limit etiquette, and a usage example. The output schema covers the return structure while the description covers interpretation.

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 100%, so the baseline is 3. The description adds meaning beyond the schema: the total must follow the printed-representation spec (six decimals, trailing zeros trimmed) and a mis-format causes a false 'No Encontrado', plus guidance that XML derivation is more reliable than hand-passing the four values. These are interpretations that affect correct invocation.

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 opening sentence states a specific verb and resource — 'Ask the SAT whether an invoice actually exists and is still live' — then names the exact endpoint (ConsultaCFDIService SOAP). This distinguishes it from parse_cfdi (local XML parsing) and the other sibling validators, so an agent can route correctly without opening the schema.

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 explicit guidance on the two input shapes, states which route is more reliable ('Deriving them from the XML is the more reliable route'), and adds operational rules (query once, cache, one retry, never declare invalid on an unreachable service). It does not explicitly name when to prefer this over siblings like parse_cfdi or sat_catalog_lookup, but the purpose statement plus sibling names make the boundary clear.

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