XLSX Tools MCP

Un servidor MCP para leer, analizar y editar con precisión libros de Excel (.xlsx) preservando la estructura existente, los estilos, las fórmulas y la integridad de los datos. Admite operaciones de celdas, gestión de hojas y filas/columnas, formato, agregación de datos, recálculo de fórmulas y acceso seguro a archivos concurrente.

Documentación

xlsx-tools-mcp

Un servidor MCP para leer y escribir archivos Excel (.xlsx) con alta precisión, preservando la estructura, los estilos y las fórmulas existentes del archivo.

CI PyPI Version Downloads/month Listed on mcpservers.org


Descripción general

xlsx-tools-mcp expone 29 herramientas del Protocolo de Contexto de Modelos (MCP) que brindan a un agente LLM acceso preciso de lectura y escritura que preserva la estructura a archivos Excel .xlsx. Se ejecuta como un servidor MCP estándar de stdio: lo instalas y lo registras con un cliente MCP (Claude Code, OpenCode, etc.), y el agente del cliente puede listar hojas, leer rangos de celdas, buscar valores, agregar datos, escribir celdas/fórmulas, gestionar hojas/filas/columnas, aplicar estilos y forzar el recálculo de fórmulas.

Está construido sobre el principio de que editar un libro de trabajo existente no debe destruir lo que no toca.

Características

  • Escrituras que preservan la estructura mediante openpyxl — las escrituras cargan el libro de trabajo existente y lo guardan de nuevo, preservando estilos, celdas combinadas, comentarios y cualquier aspecto que la edición no toque.
  • Resultados de fórmulas nunca obsoletos mediante recálculo con LibreOffice — openpyxl escribe cadenas de fórmulas pero nunca las evalúa. Después de cada escritura de valor/fórmula, el servidor ejecuta una pasada de LibreOffice sin interfaz para recalcular resultados reales y luego devuelve errors_found — cualquier valor de error de Excel (#REF!, #DIV/0!, #N/A, …) producido por el recálculo.
  • Lecturas rápidas mediante python-calamine — un analizador basado en Rust para una inferencia de tipos precisa y rápida, con una alternativa automática de openpyxl cuando necesitas fórmulas/estilos/comentarios o cuando calamine no puede analizar el archivo.
  • Agrupación/agregación y consultas basadas en pandas — aggregate_sheet y query_sheet realizan operaciones sin destruir la estructura de origen, equipadas con validación de expresiones AST para bloquear la inyección de código.
  • Objetos nativos de Excel — crea Tablas formales de Excel (create_table) con autofiltro y estilos, y gráficos nativos (create_chart para barras, líneas, circulares, dispersión).
  • Ciclo de vida y formato de hojas — renombra hojas (rename_sheet), duplica hojas con estilos intactos (copy_sheet), limpia rangos (clear_range) y ajusta automáticamente los anchos de columna (autofit_columns).
  • Fiabilidad multiplataforma (Windows, macOS, Linux) — confinamiento de rutas sin distinción de mayúsculas, delimitadores de entorno ; con coma o punto y coma, reemplazo atómico seguro de archivos con reintento de bloqueo transitorio (defensa contra WinError 32 / antivirus) y recálculo silencioso sin parpadeo de ventana.
  • Bloqueo por archivo — las llamadas de herramientas concurrentes (u otros procesos) que tocan el mismo libro de trabajo se serializan mediante un archivo <path>.lock hermano (filelock), de modo que las escrituras nunca se intercalan y corrompen el archivo.
  • Confinamiento de rutas — XLSX_MCP_ALLOWED_DIRS (separado por comas o punto y coma) restringe todas las lecturas y escrituras a directorios específicos. Si se establece, cualquier intento de acceder a un archivo fuera de estos límites genera AccessDeniedError. Se admiten comparaciones de rutas sin distinción de mayúsculas y rutas de múltiples unidades en Windows.
  • Copia de seguridad y restauración automáticas — las herramientas de escritura crean automáticamente un archivo .bak hermano antes de modificar el libro de trabajo si backup=True (que por defecto es XLSX_MCP_AUTO_BACKUP=true). Usa restore_backup para revertir cambios de forma atómica.
  • Protección contra inyección de fórmulas — write_cells y append_rows rechazan fórmulas que contengan WEBSERVICE, HYPERLINK, INDIRECT, RTD, CALL, REGISTER o comandos DDE (|) por defecto para prevenir la exfiltración de datos o enlaces maliciosos. Pasa allow_external_formulas=True para omitir esta verificación.
  • Protección contra bombas XML — el paquete defusedxml es una dependencia automática; openpyxl lo detecta y usa su analizador XML endurecido, de modo que el XML xlsx hostil no puede expandirse hasta agotar los recursos.
  • Precarga de archivos al inicio — establece XLSX_MCP_FILES para precargar uno o más libros de trabajo (separados por comas o punto y coma); las herramientas pueden llamarse entonces con path omitido o con un alias corto en lugar de una ruta completa del sistema de archivos.

Estadísticas de descargas

Downloads/month Downloads/week

En vivo a través de pypistats.org, descargas sin espejos. Estas cuentan eventos de descarga, no usuarios únicos o instalaciones — un usuario puede generar muchas descargas (CI, reinstalaciones, reconstrucciones de Docker, espejos).


Arquitectura

┌──────────────────────── Supervisor (MCP transport, stdio)
│  src/xlsx_tools_mcp/server.py     29 MCP tools + instructions
│  src/xlsx_tools_mcp/settings.py   env vars, preloaded files, path resolution
│  src/xlsx_tools_mcp/locking.py    per-file <path>.lock serialization
│  src/xlsx_tools_mcp/errors.py     domain error types
│  src/xlsx_tools_mcp/recalc.py     LibreOffice headless recalc + error scanning
│
├─ Read path
│  src/xlsx_tools_mcp/io/reader.py      calamine primary → openpyxl fallback
│  src/xlsx_tools_mcp/io/transform.py   pandas aggregation on read results
│
└─ Write path
   src/xlsx_tools_mcp/io/writer.py      openpyxl → LibreOffice recalc → scan errors

La capa de E/S (io/) está deliberadamente desacoplada del transporte MCP (server.py). Cada herramienta MCP es un envoltorio delgado que resuelve la ruta de destino, toma el bloqueo por archivo y llama a una función de la capa de E/S. Esto mantiene la lógica central independiente de MCP, de modo que puede probarse directamente (ver tests/).

La compensación del recálculo

Después de una escritura que toca valores de celdas o fórmulas, el servidor ejecuta soffice --headless --convert-to xlsx en el archivo para que cada fórmula obtenga un valor calculado real. Este ciclo de ida y vuelta recalcula fórmulas pero re-exporta todo el libro de trabajo — es una compensación, no una garantía de preservación bit a bit. Las características que openpyxl de otro modo preservaría pueden no sobrevivir de forma idéntica: tablas dinámicas, gráficos, validación de datos, algunos formatos y algunos nombres definidos.

Si estás trabajando en un libro de trabajo estructuralmente complejo donde ese riesgo importa, puedes pasar recalculate=False en las herramientas de escritura de valores/fórmulas (write_cells, append_rows, insert_rows, delete_rows, insert_columns, delete_columns) para guardar solo con openpyxl y omitir el ciclo de ida y vuelta por completo.


Requisitos

  • Python ≥ 3.10
  • LibreOffice — opcional pero recomendado. Solo se necesita para el recálculo de fórmulas. Sin él, las escrituras aún tienen éxito (guardadas mediante openpyxl) pero las fórmulas no se recalculan y se devuelve una advertencia en el campo message.

Instala LibreOffice:

# macOS
brew install --cask libreoffice

# Debian / Ubuntu
sudo apt-get install -y libreoffice-calc

# Windows
winget install TheDocumentFoundation.LibreOffice

El servidor encuentra LibreOffice verificando soffice / libreoffice en PATH, la ubicación estándar de instalación de macOS (/Applications/LibreOffice.app/Contents/MacOS/soffice) y las rutas estándar de Windows (%ProgramFiles%\LibreOffice\program\soffice.exe, etc.). En Windows, el recálculo sin interfaz se ejecuta silenciosamente sin parpadeo de ventana (CREATE_NO_WINDOW) y con perfiles de usuario aislados.


Instalación

El servidor habla transporte stdio (MCP estándar): después de la instalación espera a que un cliente MCP se conecte y llame a las herramientas. Normalmente no lo ejecutas tú mismo; lo registras con un cliente.

1. Desde PyPI mediante uvx (recomendado — sin clonar)

uvx xlsx-tools-mcp

uvx obtiene y ejecuta el paquete publicado sin contaminar tu proyecto. Esta es la forma más sencilla de activar un cliente MCP (ver fragmentos de configuración a continuación).

2. Desde el código fuente

git clone https://github.com/ruriazz/xlsx-tools-mcp.git
cd xlsx-tools-mcp
uv sync
# run the server (useful for local dev / debugging):
uv run xlsx-tools-mcp

3. Mediante pip

pip install xlsx-tools-mcp

Esto instala el punto de entrada de consola, de modo que puedes ejecutar el servidor directamente:

xlsx-tools-mcp

Configuración para clientes MCP

El registro más simple para cada cliente usa uvx xlsx-tools-mcp (sin clonar, siempre la versión publicada).

Consejo para Windows: En los archivos de configuración JSON del cliente (.mcp.json, opencode.json), usa barras diagonales (p. ej., "C:/path/to/data.xlsx") o barras invertidas escapadas (p. ej., "C:\\\\path\\\\to\\\\data.xlsx") para evitar errores de secuencias de escape JSON.

Claude Code

claude mcp add xlsx-tools-mcp -- uvx xlsx-tools-mcp

O mediante .mcp.json en tu proyecto:

{
  "mcpServers": {
    "xlsx-tools-mcp": { "command": "uvx", "args": ["xlsx-tools-mcp"] }
  }
}

OpenCode

En opencode.json (proyecto) o ~/.config/opencode/opencode.json (global):

{
  "mcp": {
    "xlsx-tools-mcp": { "type": "local", "command": ["uvx", "xlsx-tools-mcp"], "enabled": true }
  }
}

Cuando se ejecuta desde un clon del código fuente

Si clonaste el repositorio en lugar de instalarlo desde PyPI, apunta el cliente a tu copia local intercambiando uvx xlsx-tools-mcp por la forma dinámica uv run (usa la ruta absoluta al clon):

Claude Code .mcp.json:

{
  "mcpServers": {
    "xlsx-tools-mcp": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/xlsx-reader", "run", "xlsx-tools-mcp"]
    }
  }
}

OpenCode:

{
  "mcp": {
    "xlsx-tools-mcp": {
      "type": "local",
      "command": ["uv", "--directory", "/absolute/path/to/xlsx-reader", "run", "xlsx-tools-mcp"],
      "enabled": true
    }
  }
}

Reemplaza /absolute/path/to/xlsx-reader con la ubicación real de tu clon.


Precarga de archivos (XLSX_MCP_FILES)

Establece la variable de entorno XLSX_MCP_FILES en la sección env de la configuración del servidor MCP (no en tu shell interactivo — el servidor lo lanza el cliente) para precargar libros de trabajo al inicio. Formato: entradas alias=absolute/path separadas por comas o punto y coma, o rutas absolutas simples:

XLSX_MCP_FILES=name=/abs/path/to/name.xlsx,report=/data/report.xlsx

Las rutas simples obtienen un alias que por defecto es el nombre del archivo:

XLSX_MCP_FILES=/abs/path/to/sales.xlsx

Con alias/filename como alias:

  • Un archivo configurado → cada herramienta puede llamarse con path omitido por completo.
  • Múltiples archivos configurados → pasa el alias (o nombre de archivo) como path.
  • list_configured_files() devuelve el mapeo alias → ruta absoluta.
  • Las rutas absolutas y relativas sin procesar aún funcionan para archivos que no precargaste.

Claude Code — .mcp.json con precarga:

{
  "mcpServers": {
    "xlsx-tools-mcp": {
      "command": "uvx",
      "args": ["xlsx-tools-mcp"],
      "env": {
        "XLSX_MCP_FILES": "report=/data/report.xlsx,sales=/data/sales.xlsx"
      }
    }
  }
}

OpenCode con precarga:

{
  "mcp": {
    "xlsx-tools-mcp": {
      "type": "local",
      "command": ["uvx", "xlsx-tools-mcp"],
      "env": { "XLSX_MCP_FILES": "report=/data/report.xlsx,sales=/data/sales.xlsx" },
      "enabled": true
    }
  }
}

Referencia de herramientas

Las 29 herramientas. Salvo que se indique, path acepta una ruta del sistema de archivos, un alias/nombre de archivo precargado, o puede omitirse cuando exactamente un archivo está precargado. create_workbook es la excepción — su path es obligatorio porque un archivo nuevo nunca está precargado.

Forma de respuesta (todas las herramientas de escritura): cada herramienta de escritura devuelve {"saved": bool, "recalculated": bool, "errors_found": list, "message": str}. Cuando no está vacío, errors_found es una lista de {"sheet": "...", "cell": "B2", "error": "#DIV/0!"}.

Inspeccionar / Leer

HerramientaDescripción
list_configured_files()Lista los archivos precargados al inicio mediante XLSX_MCP_FILES, como un mapa alias → ruta absoluta. Llama a esto primero si no estás seguro de qué está disponible.
list_sheets(path?)Lista cada hoja del libro de trabajo con recuentos aproximados de filas/columnas (calamine).
get_workbook_info(path?)Metadatos a nivel de libro de trabajo: dimensiones exactas por hoja, max_row/max_column, estado de la hoja, la hoja activa y nombres definidos.
read_sheet(sheet, cell_range?, max_rows?, offset_row?, format?, path?)Lee valores de celdas. cell_range limita el área de lectura. max_rows limita las filas. offset_row omite un número de filas para paginación. format puede ser "array" (lista 2D), "records" (lista de diccionarios usando la primera fila como claves) o "markdown" (cadena de tabla).
get_cell(sheet, cell, path?)Detalle completo de una sola celda: valor (calculado en caché), fórmula, formato de número, fuente (negrita/cursiva/tamaño/color), color de relleno, estado de combinación, comentario.
search_workbook(query, sheet?, match_case?, limit?, path?)Búsqueda de subcadenas en una o todas las hojas. sheet restringe a una hoja; match_case=True la hace sensible a mayúsculas; limit limita las coincidencias. Devuelve {"sheet", "cell", "value"}.
aggregate_sheet(sheet, group_by, agg, cell_range?, has_header?, path?)Agrupa y agrega con pandas. group_by es una lista de nombres de columnas (tomados de la fila de encabezado); agg mapea nombre de columna → función de agregación, p. ej., {"amount": "sum"}. has_header=True (por defecto) lee los nombres de columna de la primera fila. Devuelve {columns, records, row_count}.
profile_sheet(sheet, sample_rows?, path?)Perfila metadatos de la hoja: tipos inferidos, recuentos de nulos, mín/máx, valores de muestra.
query_sheet(sheet, filter_query, columns?, max_rows?, cell_range?, path?)Filtra datos de la hoja usando expresiones pandas con defensa de inyección de código endurecida por AST.

Escritura

HerramientaDescripción
create_workbook(path, sheets?, overwrite?)Crear un nuevo libro de trabajo .xlsx/.xlsm. sheets por defecto es ["Sheet1"]. overwrite=True reemplaza un archivo existente. path es obligatorio (los archivos nuevos nunca se precargan).
write_cells(sheet, cells, create_sheet_if_missing?, recalculate?, allow_external_formulas?, backup?, path?)Escribir valores y/o fórmulas en celdas específicas. cells es una lista de {"cell": "A1", "value": ...} o {"cell": "B1", "formula": "=A1*2"}. Opcionalmente, crea la hoja primero; recalculate=True (por defecto) ejecuta el recálculo de LibreOffice.
append_rows(sheet, rows, create_sheet_if_missing?, recalculate?, allow_external_formulas?, backup?, path?)Añadir filas después de la última fila utilizada. rows es una lista de filas, cada una con una lista de valores de celda en orden de columna.
create_sheet(sheet, index?, path?)Añadir una nueva hoja vacía. index es una posición de inserción basada en cero; omítelo para añadir al final.
delete_sheet(sheet, path?)Eliminar una hoja. Falla si es la única hoja restante.
rename_sheet(old_name, new_name, path?)Renombrar una hoja existente.
copy_sheet(source_sheet, target_sheet, path?)Duplicar una hoja incluyendo su contenido, fórmulas y estilos.
insert_rows(sheet, start_row, count?, recalculate?, path?)Insertar filas en blanco antes de start_row (basado en 1), desplazando las filas existentes hacia abajo. count por defecto es 1.
delete_rows(sheet, start_row, count?, recalculate?, path?)Eliminar filas comenzando en start_row (basado en 1), desplazando las filas inferiores hacia arriba. count por defecto es 1.
insert_columns(sheet, start_column, count?, recalculate?, path?)Insertar columnas en blanco antes de start_column (basado en 1), desplazando las columnas existentes hacia la derecha. count por defecto es 1.
delete_columns(sheet, start_column, count?, recalculate?, path?)Eliminar columnas comenzando en start_column (basado en 1), desplazando las columnas a la derecha hacia la izquierda. count por defecto es 1.
merge_cells(sheet, cell_range, path?)Fusionar un rango rectangular (p. ej., "A1:C1") en una sola celda.
unmerge_cells(sheet, cell_range, path?)Deshacer una fusión en un rango previamente fusionado.
set_cell_style(sheet, cell_range, style, path?)Aplicar formato a un rango (p. ej., "A1:D1"). Claves de style: bold, italic, font_size, font_color (RGB hexadecimal, p. ej., "FF0000"), bg_color (RGB hexadecimal), horizontal, vertical (alineación), border ("thin", "medium", "thick", …), number_format (p. ej., "#,##0.00").
autofit_columns(sheet, min_width?, max_width?, padding?, path?)Ajustar automáticamente los anchos de columna según la longitud máxima del texto para evitar recortes.
clear_range(sheet, cell_range, clear_values?, clear_styles?, path?)Limpiar valores de celda y/o estilos dentro de un rango especificado sin eliminar filas ni columnas.
create_table(sheet, cell_range, table_name, style_name?, show_filter?, show_row_stripes?, path?)Crear una tabla formal de Excel sobre un rango.
create_chart(sheet, chart_type, data_range, categories_range?, title?, target_cell?, path?)Añadir un gráfico nativo de Excel (barras, líneas, circular, dispersión).
recalculate_workbook(path?)Forzar un pase de recálculo sin interfaz de LibreOffice e informar cualquier error de fórmula encontrado.
restore_backup(backup_path, target_path)Restaurar un archivo de respaldo .bak sobre un libro de trabajo objetivo de forma atómica.

Ejemplo de carga útil — write_cells

Una llamada que escribe una fórmula y un valor:

{
  "sheet": "Sheet1",
  "cells": [
    { "cell": "A1", "value": 100 },
    { "cell": "B1", "formula": "=A1*2" }
  ],
  "recalculate": true,
  "path": "/data/budget.xlsx"
}

Respuesta correspondiente:

{
  "saved": true,
  "recalculated": true,
  "errors_found": [],
  "message": "Recalculated with LibreOffice headless."
}

Si una fórmula afectada produjo un error, errors_found se vería así:

{
  "saved": true,
  "recalculated": true,
  "errors_found": [
    { "sheet": "Sheet1", "cell": "C5", "error": "#DIV/0!" }
  ],
  "message": "Recalculated with LibreOffice headless."
}

Ejemplo de carga útil — query_sheet

{
  "sheet": "Sales",
  "filter_query": "Revenue > 500 and Status == 'PAID'",
  "columns": ["Customer", "Revenue"],
  "max_rows": 10
}

Ejemplo de carga útil — create_table y create_chart

{
  "sheet": "Summary",
  "cell_range": "A1:D10",
  "table_name": "SalesTable",
  "style_name": "TableStyleMedium9"
}
{
  "sheet": "Summary",
  "chart_type": "bar",
  "data_range": "B1:B10",
  "categories_range": "A2:A10",
  "title": "Monthly Revenue",
  "target_cell": "F2"
}

Variables de entorno

VariablePredeterminadoDescripción
XLSX_MCP_FILESvacíoLista separada por comas o punto y coma de alias=path o path para precargar al inicio.
XLSX_MCP_ALLOWED_DIRSvacíoDirectorios separados por comas o punto y coma para limitar el acceso a archivos (las rutas fuera generan AccessDeniedError). Admite distinción de mayúsculas y minúsculas en Windows.
XLSX_MCP_AUTO_BACKUPtrueCrear automáticamente una copia .bak antes de modificar un libro de trabajo.
XLSX_MCP_BACKUP_DIRvacíoDirectorio personalizado para archivos de respaldo (por defecto, junto al archivo objetivo).
XLSX_MCP_RECALC_TIMEOUT60Tiempo de espera en segundos para el recálculo de fórmulas de LibreOffice sin interfaz.
XLSX_MCP_LOCK_TIMEOUT10Tiempo de espera en segundos para adquirir el bloqueo por archivo antes de fallar.

Seguridad y concurrencia

  • Limitación de rutas — XLSX_MCP_ALLOWED_DIRS (separado por comas o punto y coma) restringe todas las lecturas y escrituras a directorios específicos. Si se establece, cualquier intento de acceder a un archivo fuera de estos límites genera AccessDeniedError. Se admiten comparaciones de rutas sin distinción de mayúsculas y minúsculas y rutas de múltiples unidades en Windows.
  • Respaldo automático y restauración — Las herramientas de escritura crearán automáticamente un archivo hermano .bak antes de modificar el libro de trabajo si backup=True (que por defecto es XLSX_MCP_AUTO_BACKUP=true). Usa XLSX_MCP_BACKUP_DIR para guardar respaldos en una carpeta específica. Usa restore_backup para revertir cambios de forma atómica.
  • Protección contra inyección de fórmulas — write_cells y append_rows rechazan fórmulas que contengan WEBSERVICE, HYPERLINK, INDIRECT, RTD, CALL, REGISTER o comandos DDE (|) por defecto para prevenir la exfiltración de datos o la ejecución de comandos. Pasa allow_external_formulas=True para omitir esta verificación.
  • Validación segura de consultas AST — query_sheet usa un analizador AST para permitir estrictamente solo comparaciones, lógica booleana y operadores seguros, bloqueando búsquedas de variables @, llamadas de ejecución de código y atributos dunder privados.
  • Protección contra bombas XML — defusedxml es una dependencia automática de este paquete. openpyxl lo detecta automáticamente y usa su analizador XML endurecido, por lo que un .xlsx malicioso (un zip de XML) no puede provocar agotamiento de recursos por expansión de entidades. No se necesita configuración.
  • Bloqueo por archivo — cada lectura/escritura adquiere un archivo hermano <path>.lock (a través de filelock). Las llamadas de herramientas concurrentes u otros procesos que toquen el mismo libro de trabajo se serializan para que las escrituras nunca se intercalen y corrompan el archivo.
  • Resiliencia ante bloqueos transitorios — en Windows NTFS, los intercambios atómicos de archivos pueden encontrar temporalmente violaciones de uso compartido (WinError 32 / 33) mientras los escáneres antivirus o indexadores de búsqueda inspeccionan archivos recién guardados. El servidor realiza un reintento con retroceso exponencial para tener éxito de forma transparente una vez liberado, o proporciona un mensaje de orientación claro si el archivo está abierto permanentemente en Microsoft Excel.
  • Tiempo de espera de recálculo — XLSX_MCP_RECALC_TIMEOUT (segundos, por defecto 60) limita cuánto tiempo puede ejecutarse el pase de recálculo de LibreOffice.
  • Tiempo de espera de bloqueo — XLSX_MCP_LOCK_TIMEOUT (segundos, por defecto 10) limita cuánto tiempo esperará una herramienta para adquirir el bloqueo por archivo antes de fallar.

Solución de problemas

  • errors_found está vacío aunque mi fórmula está rota — probablemente el recálculo no se ejecutó. Verifica el campo message: si dice que no se encontró LibreOffice, el archivo se guardó a través de openpyxl tal cual y las fórmulas no se recalcularon (los valores en caché pueden estar desactualizados). Instala LibreOffice (consulta Requisitos).
  • El recálculo es lento o agota el tiempo — aumenta XLSX_MCP_RECALC_TIMEOUT (por defecto 60s). En caso de tiempo de espera agotado, el archivo aún se guarda, pero recalculated será false y message indica que el recálculo agotó el tiempo.
  • LockTimeoutError en acceso concurrente — otra operación mantiene el bloqueo. Aumenta XLSX_MCP_LOCK_TIMEOUT (por defecto 10s), o reintenta cuando la otra operación termine.
  • Workbook is locked by another process / WinError 32 — en Windows, Microsoft Excel y algunas aplicaciones bloquean archivos abiertos de forma exclusiva. Cierra el libro de trabajo en Excel y reintenta. Los bloqueos transitorios de escáneres antivirus o indexadores de búsqueda se reintentan automáticamente por el servidor antes de fallar.
  • "Hoja no encontrada" — el mensaje de error lista los nombres de hojas disponibles, para que puedas elegir la correcta.
  • path requerido / no hay archivo configurado — llamaste a una herramienta sin path pero no hay (o hay múltiples) archivos precargados. Precarga un archivo a través de XLSX_MCP_FILES, pasa un alias explícito o pasa una ruta directa.

Desarrollo / Contribución

Consulta CONTRIBUTING.md. Ejecuta la suite de pruebas con:

uv run pytest