MCP server for parsing and analyzing Portuguese SAF-T XML files
The SAF-T MCP server presents a well-organized domain-specific toolkit with consistent naming patterns and reasonable descriptions. All 13 tools follow verb_noun naming conventions (saft_load, saft_validate, saft_query_*). However, the server has significant gaps in output schema documentation, error handling guidance, and parameter validation depth. Tool descriptions range from adequate (100-150 chars) to good (200-300 chars), meeting the 10-1024 char baseline. Input schemas are present and properly typed for all tools. The main deficiency is the absence of documented return/output schemas, critical for helping LLMs understand what data they receive and how to chain tools. Parameter descriptions are generally present but sometimes lack constraint details (e.g., saft_query_invoices doc_type values are documented in description but not as enum; date formats are mentioned but no pattern validation shown). Error handling is minimal, no recovery guidance, no categorization of retryable vs. fatal errors, and no suggestion for next steps when a tool fails.
Compute accounts receivable aging from invoices and payments.
Detect suspicious patterns in the loaded SAF-T file.
Compare the loaded SAF-T file against a second file.
Compute aggregate statistics (revenue, invoice count, VAT, payment status) on the loaded SAF-T file.
Export query results to a CSV file.
Get full detail for a single invoice including all line items.
Load and parse a SAF-T PT XML file.
No documented output schemas for any tool. Tool descriptions state what is returned (e.g., 'Returns revenue totals, invoice counts, VAT breakdown...') but LLMs cannot see the actual response structure, types, or field names. This prevents proper chaining and forces agents to explore output via trial-and-error.
Enum constraints not formally declared in schemas. saft_query_invoices doc_type parameter lists allowed values in description ('FT', 'FR', 'NC', 'ND', 'FS') but input schema shows type: string with no enum field. saft_tax_summary group_by lists 'rate', 'month', 'doc_type' in description but not as enum. Same for saft_query_products product_type. LLMs cannot reliably parse unstructured lists in descriptions, enums are machine-parseable and self-documenting.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 68 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 48 | - | v1 |
Search and filter customers in the loaded SAF-T file.
Search and filter invoices in the loaded SAF-T file.
Search and filter products in the loaded SAF-T file.
Generate an executive summary of the loaded SAF-T file.
Generate a VAT analysis of the loaded SAF-T file.
Validate the loaded SAF-T file against XSD and Portuguese business rules.
No error handling or recovery guidance. All tools catch SaftError and return {error: str(e), suggestion: ...} for saft_load, but other tools do not show error responses. No categorization of retryable vs. permanent errors, no invalid-input guidance, and no suggestions when resources are missing. E.g., if an invoice is not found, should the agent query_invoices first? The tool does not say.
Parameter constraints not formally specified. Date parameters (date_from, date_to) mention ISO format YYYY-MM-DD in description but no format/pattern in schema. Numeric parameters (min_amount, max_amount, limit, offset) lack minValue/maxValue bounds. e.g., limit defaults to 50 with max 500, but this constraint is only in description text, not in the schema. Validation must be schema-enforced so LLMs and clients understand boundaries.
saft_export is marked WRITE but description does not explicitly state it modifies the file system. Tool description should say: 'Creates a CSV file at the specified path. This is a write operation and cannot be undone, ensure the path is correct before calling.'
Parameter relationships not documented. E.g., saft_compare requires file_path but does not state whether the file must exist before calling saft_load. saft_export filters parameter is object type with no schema for its structure, agents cannot know what sub-keys it accepts.