Skip to main content
Glama
benethos-hub

Unofficial Lexware Office MCP Server

by benethos-hub

Servidor MCP no oficial de Lexware Office

CI PyPI Python Coverage License

Aviso legal

  • Este proyecto no está afiliado, respaldado ni patrocinado por Lexware ni por Haufe-Lexware GmbH & Co. KG. "Lexware" y "Lexware Office" son marcas comerciales de sus respectivos propietarios.

  • Utiliza la API pública documentada con una clave de API que usted genera y puede revocar usted mismo. El uso de esa API se rige por los propios términos de Lexware, que acepta independientemente de este proyecto. La API puede cambiar en cualquier momento, y las solicitudes pueden estar sujetas a límites de velocidad o ser bloqueadas.

  • Accede a registros contables reales. El acceso de escritura está desactivado por defecto. Si lo habilita, cualquier cosa creada a través de la API es un registro real y legalmente relevante: un documento finalizado no se puede retirar a través de la API.

  • Los datos pueden estar incompletos o desactualizados. Nada de esto es asesoramiento fiscal, contable o legal. No confíe en ello para declaraciones, auditorías o sus obligaciones contables.

  • Se proporciona "tal cual", sin garantía. Está pensado para uso personal y profesional bajo su propio riesgo. Consulte LICENSE.

  • Para uso comercial, revise los términos de la API de Lexware y sus propias obligaciones de conservación y documentación.

Un servidor MCP que conecta un cliente MCP como Claude Desktop a una cuenta de Lexware Office a través de la API REST pública oficial. Pregunte sobre facturas, contactos, artículos y comprobantes en lenguaje natural, y deje que el cliente los obtenga por usted.

Estado: 0.2.2. El servidor gestiona contactos, comprobantes y documentos: los encuentra, los lee, los crea, los modifica, ve lo que aún está sin pagar, descarga un PDF y sube un recibo. get_profile responde qué cuenta está conectada. Cada herramienta de la tabla siguiente está construida y se probó con una cuenta real. Habla con stdio a un cliente que lo inicia, y HTTP transmisible detrás de un token de portador cuando algo más tiene que alcanzarlo: como una imagen de contenedor publicada, con un archivo Compose para ambos. Consulte SPECS.md para la especificación técnica completa y la hoja de ruta.

Por qué existe esto

Lexware Office contiene la contabilidad diaria de una pequeña empresa. La mayoría de las preguntas al respecto son preguntas de lectura: qué está aún sin pagar, qué pidió este cliente, qué recibo pertenece a ese gasto, y esas son exactamente las preguntas que un asistente responde bien una vez que puede ver los datos. Este servidor lo hace posible sin exportar nada, utilizando una clave de API que el propietario de la cuenta genera y puede revocar.

Related MCP server: lexware-mcp-server

Seguridad ante todo

El servidor apunta a un sistema contable real, por lo que los valores predeterminados son cautelosos.

Ejecútelo en modo de solo lectura a menos que tenga un motivo para no hacerlo. Este servidor puede cambiar registros contables reales: crear un contacto, registrar un comprobante, emitir una factura, adjuntar un recibo, y es el asistente quien decide cuándo llamar a dicha herramienta, no usted. --tools read-only le da todo lo que necesita para responder preguntas sobre los libros, que es para lo que la mayoría de la gente lo quiere: buscar, leer y descargar. Nada en ese conjunto escribe.

Active una herramienta de escritura cuando tenga un trabajo para ella y sepa lo que deja atrás. Esta API no puede eliminar un comprobante contable en absoluto, por lo que uno incorrecto se corrige en la aplicación web en lugar de retirarse aquí, y una factura finalizada es un documento real con un número que se ha utilizado. Si no está seguro de qué herramientas necesita, el modo de solo lectura es el punto de partida honesto: la página de permisos agrega una más tarde con un clic, y un cliente que respete notifications/tools/list_changed, como hace Claude Desktop, lo recoge sin reiniciar.

  • Nada está habilitado hasta que usted lo diga. Una instalación nueva no tiene archivo de políticas, y un servidor sin uno no ofrece ninguna herramienta. Lo que este servidor puede hacer es una decisión que alguien tomó, nunca un valor predeterminado que sucedió.

  • Una bandera por herramienta, en un archivo JSON que usted escribe con --tools, marca a través de setup, o edita a mano. No es un nivel, no es un grupo: create_contact activado y upload_file desactivado es algo normal de querer, y no hay combinación que el archivo no pueda expresar.

  • Lo que una herramienta le cuesta es visible mientras decide. Cada herramienta habilitada se envía al asistente en cada solicitud, y la página de permisos pone ese número en cada fila.

  • El archivo se verifica dos veces, una cuando se construye la lista de herramientas y otra cuando llega una llamada, por lo que una lista de herramientas obsoleta en el cliente no puede pasarlo por alto.

  • La clave de API nunca se registra, nunca se devuelve en un resultado de herramienta y se redacta de los mensajes de error. Pertenece al .env y a ningún otro lugar — no en el archivo de configuración de su cliente, que otro programa posee y reescribe, y que es el que la gente captura de pantalla cuando pide ayuda. Tampoco ninguna ruta desde su máquina llega al asistente.

Herramientas

Cada herramienta siguiente está construida y se probó con una cuenta real. Ninguna de ellas está habilitada hasta que el archivo de políticas la nombre.

Herramientas de lectura:

Herramienta

Qué hace

get_profile

Perfil de la empresa y verificación de conexión

search_contacts

Encuentra clientes y proveedores por nombre, correo electrónico, número o rol

get_contact

Un contacto con direcciones, roles y versión

search_articles

Lista artículos, filtrados por número, código de barras o tipo. La API no ofrece búsqueda por título

get_article

Un artículo con su bloque de precios y versión

search_vouchers

La consulta central: filtra la lista de comprobantes por tipo, estado, contacto, rango de fechas y lo que está abierto

get_sales_document

Lee una factura, cotización, nota de crédito, confirmación de pedido, albarán, recordatorio o factura de anticipo en su totalidad

get_voucher

Lee un comprobante contable, por id o por su número de documento

get_payments

Estado de pago y monto abierto de un comprobante

get_recurring_templates

Plantillas que emiten facturas según un cronograma, una o una página de ellas

get_master_data

Países, condiciones de pago, categorías de contabilización y diseños de impresión, con una búsqueda para acotarlos

download_document

Guarda el PDF o XML renderizado de un documento de venta

download_file

Guarda un archivo almacenado, como un recibo subido

read_download

Coloca un archivo descargado en la respuesta, para clientes que no pueden seguir un enlace de recurso

get_deeplink

Construye un enlace permanente a un documento de venta, contacto o comprobante en la aplicación web, sin una llamada a la API

Herramientas de escritura. Estas cambian registros contables reales, así que actívelas una a la vez y contra una cuenta que esté dispuesto a que se modifique:

Herramienta

Qué hace

create_contact

Crea un cliente o proveedor

update_contact

Cambia uno, sin tocar lo que no nombró

create_article

Agrega un artículo al catálogo

update_article

Cambia uno, sin tocar lo que no nombró

create_voucher

Registra un comprobante contable

update_voucher

Cambia uno que ya está registrado

create_sales_document

Crea una factura, cotización, nota de crédito, confirmación de pedido, albarán o recordatorio: un borrador a menos que pida que se emita, lo que el asistente solo puede hacer con su instrucción explícita

upload_file

Sube un recibo, que también crea su comprobante

attach_file_to_voucher

Cuelga un archivo en un comprobante que ya existe

update_contact y update_voucher cuestan dos llamadas a la API en lugar de una. La API reemplaza un registro en lugar de parchearlo, por lo que se lee primero el actual y el cambio se superpone. Sin eso, cambiar solo una dirección de correo electrónico vaciaría las direcciones, la nota y todo lo demás. Ambas también necesitan la version que leyó por última vez: si el registro cambió en el medio, la actualización se rechaza y no se escribe nada.

Una herramienta elimina, y es la única:

Herramienta

Qué hace

delete_article

Elimina un artículo. La API no puede recuperarlo. Requiere confirm: true y no envía nada sin ello

Es el único miembro del paso --tools irreversible hasta ahora, por lo que ese paso es la única forma de activarlo. Un artículo también es lo único que esta API le permite eliminar, que es la otra mitad del punto:

--tools write no es lo mismo que reversible. Nada de lo que activa ese preset elimina un registro, pero dos de sus herramientas crean uno que no se puede quitar después.

Un comprobante contable no se puede eliminar a través de la API. No existe un endpoint para ello, así que un create_voucher erróneo debe corregirse en la aplicación web de Lexware Office, y se contabiliza en el momento en que se crea: la API no acepta ningún estado en el camino. Lo mismo se aplica a upload_file: subir un recibo también crea el comprobante asociado, por lo que deja un registro detrás aunque su nombre solo mencione el archivo.

Las descargas se escriben en el directorio de descargas de la máquina donde se ejecuta el servidor, y se informan de dos maneras: una ruta, que es lo que quieres cuando el cliente y el servidor comparten esa máquina, y un URI de recurso, que el cliente puede leer para obtener los bytes esté donde esté el servidor. El archivo en sí nunca viaja dentro del resultado de la herramienta, porque base64 cuesta aproximadamente 1,37 veces el tamaño del archivo en contexto y ningún modelo puede leer un PDF de todos modos. Un archivo existente nunca se reemplaza: una segunda descarga se guarda junto a la primera con un contador en su nombre.

La lista de recursos se llena desde el directorio de descargas cuando el servidor se inicia, por lo que un URI sigue siendo legible después de un reinicio. Lo que el servidor no puede hacer es anunciar una nueva descarga: el SDK de MCP no le da forma de enviar una notificación de cambio de lista, por lo que un cliente que lista una vez al inicio no verá nada que se obtenga más tarde en la sesión.

Entre eso y que Claude Desktop no sigue los enlaces de recursos en absoluto, read_download es la ruta que siempre funciona. Toma el mismo URI y pone el contenido en la respuesta. Lo que llega depende del archivo:

Archivo

Llega como

XML

texto, por lo que un XRechnung se puede leer

PDF

imágenes de sus páginas, las primeras 10 por defecto

Imagen

la imagen

Cualquier otro

un binario incrustado para que el cliente lo maneje

Un PDF se renderiza en lugar de pasarse tal cual porque Claude Desktop convierte un binario incrustado en un bloque de imagen cuando llama a la API, y application/pdf no es un tipo de imagen permitido allí, por lo que toda la solicitud se rechaza. Renderizar tampoco cuesta una llamada a la API, ya que el archivo ya está en el servidor.

Un enlace a la aplicación web es una herramienta separada. get_deeplink convierte un id en una URL para un navegador, no cuesta ninguna llamada a la API, y es la ruta que sigue funcionando cuando el cliente no puede mostrar ni el archivo ni un enlace de recurso: alguien lo abre por sí mismo. Una descarga no lleva uno: responde dónde están los bytes, que es una pregunta diferente, y los dos se unieron una vez durante el tiempo suficiente para que un enlace roto viajara junto con una descarga que funcionaba.

upload_file acepta PDF, JPEG, PNG y XML, como máximo 5 MiB por archivo, que es lo que acepta la API. Un archivo XML se trata como un XRechnung y se rechaza si no lo es.

Requisitos

  • uv, que trae su propio Python y el comando uvx que usan todos los ejemplos siguientes

  • Python 3.11 o más reciente, si prefieres traer el tuyo. La instalación incluye el SDK de MCP, httpx, platformdirs y pypdfium2, este último para renderizar páginas PDF

  • Una cuenta de Lexware Office con el complemento de API pública habilitado

  • Una clave de API de https://app.lexware.de/addons/public-api

Cómo obtener una clave de API

  1. Inicia sesión en Lexware Office como propietario de la cuenta.

  2. Abre el complemento de API pública en https://app.lexware.de/addons/public-api.

  3. Crea una clave y cópiala una vez: solo se muestra una única vez.

  4. Mantenla fuera de cualquier archivo que vaya al control de versiones. Ponla en config/.env, que está en gitignore, o pásala como variable de entorno. Una clave en config/.env se encuentra sin importar desde qué directorio se inicie el servidor, por lo que un cliente como Claude Desktop no necesita su propia clave en su archivo de configuración.

Una clave se puede revocar en la misma página en cualquier momento, que es la forma más rápida de cortar el acceso si algo parece sospechoso.

Instalación

La forma más sencilla de ejecutar el servidor: sin clonar, sin entorno virtual manual, sin git. uvx lo obtiene y lo ejecuta bajo demanda desde PyPI (publicado como benethos-lexware-office-mcp). Para ejecutarlo en un contenedor en su lugar, consulta En un contenedor.

1. Instala uv, si aún no lo tienes: la página de instalación de uv cubre todas las plataformas. Trae uvx, y eso es lo único que se necesita aquí.

2. Configura el servidor. No hay que instalar nada para esto: uvx obtiene el paquete y lo ejecuta.

uvx benethos-lexware-office-mcp setup

Eso abre la interfaz descrita en Configurarlo en un navegador: clave, ajustes y una casilla por herramienta. Todo lo que hace también se puede hacer a mano: inicia un archivo de ajustes con uvx benethos-lexware-office-mcp --settings-sample > config/.env, pon la clave en él, y usa --tools como se describe abajo.

Comprueba que funciona:

uvx benethos-lexware-office-mcp --help

3. Apunta Claude Desktop a ello en claude_desktop_config.json:

{
  "mcpServers": {
    "benethos-lexware-office-mcp": {
      "command": "uvx",
      "args": ["benethos-lexware-office-mcp"]
    }
  }
}

No aparece ninguna ruta de tu máquina ahí, que es el punto: uvx busca el paquete por nombre. Dos cosas que vale la pena saber sobre esa entrada:

  • Fija una versión para estabilidad: "args": ["benethos-lexware-office-mcp==0.2.2"]. Sin una fijación, uvx toma la versión más reciente que pueda resolver, y un reinicio del cliente es suficiente para cambiar lo que ejecuta.

  • uvx tiene que estar en el PATH que usa el cliente, que no siempre es el de tu terminal: algunos clientes gráficos pasan un entorno reducido. Si el servidor no se inicia, pon la ruta absoluta a uvx en command, y reinicia el cliente por completo en lugar de recargarlo.

¿Prefieres tener un comando propio? uv tool install benethos-lexware-office-mcp te da benethos-lexware-office-mcp sin el uvx delante, lo que vale la pena si cambias permisos desde la línea de comandos a menudo. No compra nada más: la misma versión se puede fijar de cualquier manera, y un arranque en caliente difiere en decenas de milisegundos. Una cosa a saber: uv lo instala en su propio directorio de herramientas, que no está en el PATH de una instalación nueva. Lo dice cuando termina. Ejecuta uv tool update-shell y abre una nueva terminal.

Desde las fuentes en su lugar, para desarrollar o ejecutar algo no publicado:

git clone https://github.com/benethos-hub/lexware-office-mcp
cd lexware-office-mcp
uv sync
uv run benethos-lexware-office-mcp setup

Un cliente entonces necesita el intérprete del entorno virtual de ese checkout, command apuntando a .venv/Scripts/python.exe en Windows o .venv/bin/python en otros lugares, con args de ["-m", "benethos_lexware_office_mcp"].

Sin clave ahí, a propósito. El servidor la encuentra en el .env. El archivo de configuración de un cliente es el lugar equivocado para una credencial: no es tuyo: otro programa lo posee, decide dónde vive y cuándo lo reescribe. Es el archivo que la gente captura de pantalla cuando pide ayuda con una configuración de MCP, es legible en la vista de ajustes del propio cliente, y viaja a la siguiente máquina con el resto de la configuración de ese cliente. El .env es al menos un archivo que este proyecto documenta, que nada sincroniza en tu nombre, y que la interfaz de configuración escribe sin mostrarte nunca la clave de vuelta.

Ese .env ya es la parte con la que hay que tener cuidado. Contiene una credencial para un sistema contable en vivo, así que mantenlo fuera del control de versiones, fuera de carpetas compartidas y fuera de copias de seguridad que otras personas puedan leer. Cuando dejes de usar el servidor, elimínalo y revoca la clave en Extensiones, API pública: revocar es el único paso que realmente termina el acceso.

4. Reinicia Claude Desktop por completo: ciérralo desde la bandeja en lugar de cerrar la ventana. Eso es para el archivo de configuración que acabas de editar, que un cliente lee una vez al inicio, y también es lo que necesita un ajuste cambiado en el .env: el servidor también los lee al inicio. No es necesario para los permisos: cámbialos más tarde y el cliente en ejecución es informado, consulta Desactivar herramientas individuales.

Configurarlo en un navegador

uvx benethos-lexware-office-mcp setup

Tres páginas en 127.0.0.1, cerradas con Ctrl+C. Escriben los mismos archivos que la línea de comandos, así que puedes usar cualquiera o ambos. Las pantallas están en alemán, porque Lexware Office se vende solo para empresas alemanas, y cada una se nombra abajo por lo que hace con su etiqueta entre corchetes.

Resumen (Übersicht): qué .env y qué tools.json están realmente en efecto, a qué se resuelve cada ajuste y de dónde viene ese valor, si cada archivo existe aún, cuántas herramientas están activadas y cuánto cuestan. Una prueba de conexión en el botón, nunca al cargar la página.

Credenciales (Zugangsdaten): la clave de API, verificada contra la API antes de guardarse a menos que digas lo contrario, y los ajustes que no son secretos. La clave nunca se te muestra de vuelta, nunca se registra y nunca se exporta. Si una variable de entorno la está estableciendo, la página lo dice, porque eso anularía lo que guardes.

Permisos (Rechte): una casilla por herramienta, agrupadas, con los presets como botones. En una instalación nueva sin archivo de política aún, las herramientas de lectura vienen pre-marcadas como punto de partida: una propuesta en un formulario, no un permiso: todavía no hay archivo y por lo tanto todavía no hay herramienta hasta que pulses guardar, y la página lo dice. Cada fila lleva lo que esa herramienta cuesta al asistente en contexto, y el total sigue tus marcas: cada herramienta habilitada se envía al modelo en cada solicitud, así que activar una es una decisión de presupuesto además de una de permiso. Las herramientas de escritura están marcadas, y las que la API no puede retractar están marcadas por separado: nur App para un contacto, que Lexware Office elimina sin ceremonia, y nur App · Buchhaltung para un registro que entra en los libros. Ninguna significa que esté atascado: nada está fijado cuando se crea, y una leyenda en la página nombra las cuatro cosas que sí vinculan un registro más tarde.

Los perfiles también viven aquí. Guarda la selección actual bajo un nombre, cárgala más tarde. Cargar solo llena las casillas: nada llega a tools.json hasta que pulses guardar. Un nombre que ya está en uso se rechaza en lugar de reemplazar silenciosamente lo que hay: mayúsculas y espacios no crean un segundo perfil, y reemplazar uno es su propio botón junto a la lista. Se almacenan en tool_profiles.json junto al archivo de política.

El archivo de política en sí se puede descargar y volver a leer desde la misma página: el archivo tal como es, por lo que funciona en otra instalación con o sin esta interfaz, y un tools.json escrito por --tools se lee aquí. Leer uno solo marca las casillas, y guardar sigue siendo una pulsación separada. Una herramienta que el archivo no menciona permanece apagada y la página dice cuántas son, que es lo que hace --tools sync en la línea de comandos.

Dos cosas que vale la pena saber. Se vincula a 127.0.0.1 y nada más: las páginas no tienen contraseña, lo que solo es defendible mientras no se puedan alcanzar desde otra máquina, por lo que no hay opción para cambiarlo. Y es un comando separado: el servidor MCP nunca sirve HTTP, y un cliente como Claude Desktop inicia ese, no este.

--port N lo mueve, --no-browser solo imprime la dirección, y --env-file y --tools-file dicen qué archivos edita. A diferencia de en cualquier otro lugar, esos archivos no tienen que existir aún.

Si tu cliente inicia el servidor con --tools-file, dale a setup el mismo argumento: de lo contrario edita un archivo diferente e informa éxito. Ambos procesos fijan sus archivos cuando se inician y nunca los cambian después, y ninguno puede ver cómo se inició el otro. El resumen imprime la línea "args" que hace que tu cliente coincida con los archivos que la interfaz está manejando, que es la dirección más fácil.

Desactivar herramientas individuales

Un archivo JSON decide lo que este servidor ofrece, y nada más. O marca las casillas bajo setup arriba, o inicia el archivo con

uvx benethos-lexware-office-mcp --tools read-only

que escribe cada herramienta en tools.json, dejando las de lectura activadas y el resto desactivadas, e imprime lo que ha hecho. Tres ajustes predefinidos, cada uno de los cuales incluye el anterior:

activa

--tools read-only

solo las consultas

--tools write

y la creación y actualización

--tools irreversible

y el borrado de un artículo

--tools sync

no cambia ninguna marca, solo añade las herramientas que el archivo no conocía

--tools show solo informa. --tools-file PATH indica dónde escribir, y funciona con todos ellos: --tools write --tools-file ./tools.json crea el archivo allí.

Un ajuste predefinido sobrescribe todo el archivo, por lo que las ediciones manuales se pierden. Úsalo para crear un archivo, no para actualizar uno. Cuando una actualización trae herramientas nuevas, ejecuta --tools sync: las escribe como desactivadas, deja intactas todas las marcas que hayas establecido y nunca activa nada. Esa última parte es la razón por la que es el único de estos que es seguro ejecutar desde un script.

El tercer paso es independiente porque es una decisión propia: lo que se borra desaparece, por lo que debe elegirse nombrándolo en lugar de escoger la opción más amplia. Exactamente una herramienta tiene ese efecto, delete_article, y eso no es algo temporal: un artículo es lo único que esta API puede borrar, y tampoco hay forma de reservar, finalizar o anular nada a posteriori.

Sin --tools-file, el archivo se busca exactamente igual que el .env, con la precedencia más baja primero:

  1. el directorio de configuración por usuario

  2. config/ de un checkout, cuando se ejecuta desde las fuentes

  3. config/ y luego la raíz del directorio de trabajo

Gana el último que se encuentre, y un archivo que nadie ha creado todavía se resuelve al primero. Después, edítalo:

{
 "create_contact": false,
 "search_contacts": true,
 "upload_file": false
}

Una herramienta establecida en false no aparece en la lista y no se puede llamar. Una herramienta que el archivo no menciona también está desactivada: el silencio es una negativa, así que una herramienta que llega con una actualización espera a que la actives en lugar de aparecer por sí sola. Que no haya ningún archivo significa que no hay ninguna herramienta, por eso --tools forma parte de la configuración del servidor.

El archivo se lee cuando se construye la lista de herramientas y de nuevo en cada llamada, por lo que una edición surte efecto de inmediato en ambas direcciones: sin reiniciar. El servidor también informa al cliente cuando cambia el conjunto de herramientas activadas, por lo que este vuelve a obtener la lista por sí mismo: Claude Desktop detecta el cambio mientras está en ejecución. Nada depende de ello en ningún caso, ya que una herramienta que se ha desactivado no se puede llamar sea cual sea la lista que el cliente siga mostrando. Si el tuyo no lo detecta, reinícialo: en Claude Desktop, saliendo desde la bandeja.

Cada herramienta también declara qué es: de lectura o escritura, a qué grupo pertenece y si lo que escribe se puede eliminar de nuevo. Esa clasificación es la que --tools read-only activa y la que la interfaz del navegador agrupa y marca. Nunca decide una llamada: solo lo hace el archivo.

Configuración

De dónde viene un valor y cuál gana

Se aplica un único .env, nunca varios. Estos son los lugares donde se busca, de menor a mayor precedencia, y el de mayor precedencia que exista es el archivo: los demás no se leen:

  1. .env en el directorio de configuración por usuario

  2. config/.env del checkout desde el que se ejecuta el servidor, si se ejecuta desde uno

  3. config/.env y luego .env en el directorio de trabajo

--env-file lo nombra en su lugar, y entonces no se realiza ninguna búsqueda. Esa es la misma regla que sigue --tools-file para el archivo de políticas, por lo que ambas marcas significan lo mismo: este archivo, y nada más.

Dos cosas quedan fuera de ese archivo, una por debajo y otra por encima:

  • el valor predeterminado integrado, para un ajuste que ningún archivo menciona

  • una variable de entorno real, que prevalece sobre lo que diga el archivo

La última es la que sorprende a la gente. Un ajuste exportado en tu shell, puesto en el bloque env de un cliente o fijado en un archivo Compose no se puede cambiar editando un .env: ni a mano, ni mediante setup. El valor se escribe, el archivo es correcto y no ocurre nada.

La interfaz de configuración lo indica en lugar de dejar que lo descubras: cada ajuste lleva una insignia que nombra su origen, y uno que está siendo retenido por una variable de entorno se marca como tal. Cuando algo que has guardado parece ignorarse, esa insignia es la respuesta.

En un contenedor esto no es un caso límite. compose.yaml fija el transporte, la dirección de enlace, el puerto y los hosts permitidos como variables de entorno reales, porque pertenecen al contenedor y no a la instalación que hay dentro de él. Todo lo demás (la clave de API, el token HTTP, los límites) se deja al volumen de configuración, que es lo que permite a la interfaz de configuración cambiarlo.

El mismo orden se aplica al archivo de políticas, y LXO_MCP_TOOL_POLICY y --tools-file lo nombran directamente. La interfaz fija el archivo que encontró al iniciarse, por lo que la página no puede cambiar su propio sujeto sin que te des cuenta.

Nombrando los archivos

--env-file PATH nombra un archivo de ajustes en lugar de buscarlo, y se combina con --tools-file para que una sola entrada en la configuración de un cliente lleve su propia cuenta y sus propios permisos:

"args": ["--env-file", "/path/to/test.env",
         "--tools-file", "/path/to/test-tools.json"]

Una ruta que no existe se rechaza en lugar de recurrir silenciosamente a la búsqueda, excepto bajo setup, que existe en parte para crear una.

setup escribe este archivo por ti.

Los ajustes

Variable

Significado

Valor predeterminado

LXO_MCP_API_KEY

Tu clave de API de Lexware Office. Obligatoria.

—

LXO_MCP_TOOL_POLICY

Archivo de activación/desactivación por herramienta, ver más abajo

tools.json en el directorio de configuración

LXO_MCP_BASE_URL

URL base de la API

https://api.lexware.io

LXO_MCP_APP_BASE_URL

Base de la aplicación web para enlaces profundos

https://app.lexware.de

LXO_MCP_DOWNLOAD_DIR

Dónde se guardan los documentos descargados

directorio de caché del usuario

LXO_MCP_TIMEOUT

Tiempo de espera HTTP en segundos

30

LXO_MCP_RATE

Peticiones por segundo, global en todos los endpoints

1.5

LXO_MCP_BURST

Capacidad del cubo de tokens. El cubo propio de la cuenta contiene 4

2

LXO_MCP_PAGE_SIZE

Filas por página que una búsqueda solicita y devuelve

25

LXO_MCP_PDF_PAGES

Páginas de un PDF que read_download renderiza por defecto

10

LXO_MCP_LOG_LEVEL

Nivel de registro en stderr

INFO

LXO_MCP_TRANSPORT

stdio, streamable-http o sse

stdio

LXO_MCP_BEARER_TOKEN

Secreto compartido que toda petición HTTP debe llevar. Obligatorio para un transporte HTTP

—

LXO_MCP_HTTP_HOST

Dirección a la que enlazar para un transporte HTTP

127.0.0.1

LXO_MCP_HTTP_PORT

Puerto al que enlazar

8770

LXO_MCP_HTTP_PATH

Ruta URL en la que sirve el transporte

/mcp

LXO_MCP_ALLOWED_HOSTS

Valores de Host a aceptar además de loopback, separados por comas

—

LXO_MCP_GENERATE_BEARER_TOKEN

Crear un token al iniciar si no hay ninguno y escribirlo en el archivo de ajustes

desactivado

LXO_MCP_EXIT_ON_CONFIG_CHANGE

Terminar el proceso cuando cambie el archivo de ajustes, para algo que lo reinicie

desactivado

Todos los ajustes anteriores están en uso. LXO_MCP_PAGE_SIZE está limitado a 250, que es el tamaño de página más bajo que acepta cualquier endpoint, y un valor mayor se rechaza al iniciar en lugar de convertirse en un error de API más tarde.

Transporte

stdio es el valor predeterminado y el que usan Claude Desktop y clientes locales similares: el cliente inicia el servidor como su propio proceso hijo, y nada más puede hablar con él.

streamable-HTTP y SSE sirven las mismas herramientas en un puerto, para un contenedor o una máquina propia:

uvx benethos-lexware-office-mcp --transport streamable-http --port 8770

Dos cosas se interponen ante ese puerto, y ninguna es opcional. Un token de portador que toda petición debe llevar como Authorization: Bearer <token>: sin LXO_MCP_BEARER_TOKEN el servidor se niega a iniciar un transporte HTTP por completo, porque cualquiera que pueda alcanzar el puerto podría gastar tus credenciales de Lexware. Y la protección contra el rebinding de DNS del SDK, que comprueba Host y Origin contra una lista de permitidos de los nombres de loopback, ampliada por --allowed-hosts cuando un contenedor o un proxy pone otro nombre delante.

Ninguna de las dos hace que el puerto sea seguro para publicar en una red. Lo hacen sobrevivible en una máquina compartida con otros procesos. --host enlaza en un lugar distinto de loopback, lo que un contenedor tiene que hacer: consulta En un contenedor para saber por qué eso no es la relajación que parece.

En un contenedor

La imagen se publica para linux/amd64 y linux/arm64, por lo que no se necesita nada de este repositorio para ejecutar una:

docker pull ghcr.io/benethos-hub/lexware-office-mcp:latest

Fija una versión para cualquier cosa de la que dependas: :0.2.2 para una versión exacta, :0.2 para seguir sus versiones de parche. :latest se mueve con cada versión, y :edge se construye bajo demanda desde lo que contenga main y no es una versión en absoluto.

Con Compose

docker compose up -d                      # the server, on 127.0.0.1:8770
docker compose --profile setup up -d      # add the configuration interface
docker compose rm -f -s setup             # take the interface away again

No uses docker compose --profile setup down. Ese es todo el proyecto: derriba el servidor con él. rm -f -s setup detiene y elimina el único servicio y deja el servidor en ejecución. docker compose stop setup también funciona y conserva el contenedor detenido para la próxima vez.

Tal como se distribuye, compose.yaml se construye desde este checkout. Dos líneas comentadas en cada uno de sus dos servicios lo cambian a la imagen publicada, y ese archivo es entonces lo único que necesitas de aquí.

Como contenedores individuales

docker run -d --name lexware-office-mcp \
  --restart unless-stopped \
  -p 127.0.0.1:8770:8770 \
  -v lxo-config:/config -v lxo-downloads:/downloads \
  ghcr.io/benethos-hub/lexware-office-mcp:latest

El token que generó para sí mismo está en el volumen de configuración, que es de donde lo lees:

docker exec lexware-office-mcp grep LXO_MCP_BEARER_TOKEN /config/.env

Esa única línea en lugar de todo el archivo: una vez que se ha introducido una clave, la clave de API también está ahí dentro, y no tiene por qué desplazarse por una terminal de la que podrías hacer una captura de pantalla.

La interfaz de configuración es la misma imagen con su otro comando, apuntando al mismo volumen:

docker run --rm -d --name lexware-office-mcp-setup \
  -p 127.0.0.1:8771:8771 \
  -v lxo-config:/config -v lxo-downloads:/downloads \
  ghcr.io/benethos-hub/lexware-office-mcp:latest \
  setup --no-browser --host 0.0.0.0 --port 8771 \
        --env-file /config/.env --tools-file /config/tools.json

Se inició con --rm, por lo que detenerlo también es su fin:

docker stop lexware-office-mcp-setup

--restart unless-stopped no es decoración aquí. El contenedor termina su proceso cuando cambia el archivo de ajustes, que es lo que lleva un ajuste guardado a un servidor en ejecución. Sin una política de reinicio, termina y permanece terminado.

Apaga la interfaz cuando hayas terminado

Abre http://127.0.0.1:8771/, introduce la clave, marca las herramientas... y luego detenla. Nada la detiene por ti. No tiene inicio de sesión, acepta una clave de API y seguirá sirviendo esa página felizmente mientras la máquina esté encendida.

docker compose rm -f -s setup             # Compose
docker stop lexware-office-mcp-setup      # a single container
docker ps --filter name=setup             # nothing listed means it is off

El servidor está pensado para ejecutarse. La interfaz está pensada para ejecutarse durante los minutos que configuras en ella, por eso un simple docker compose up la deja fuera y por eso no tiene política de reinicio: una vez detenida, permanece detenida hasta que la vuelves a solicitar.

No hay que preparar nada de antemano. En su primer arranque, el servidor genera un bearer token, lo escribe en el volumen de configuración y lo anuncia — la interfaz lo muestra, y ese es el valor que necesita un cliente. No está incrustado en la imagen, donde cada copia lo compartiría.

El contenedor se vincula a 0.0.0.0, y eso no es una relajación. Un proceso en el loopback del propio contenedor no se puede alcanzar a través de un puerto publicado en absoluto. El aislamiento es el namespace de red, y quién puede alcanzar el puerto lo decide la publicación, que mapea solo 127.0.0.1.

Una configuración guardada en el navegador llega al servidor en ejecución. La configuración se lee una sola vez al arrancar, así que se le indica al contenedor que termine cuando su archivo de configuración cambia y Compose lo inicia de nuevo un segundo después. Lo que Compose fija como variables de entorno reales — el transporte, la dirección de vinculación, el puerto, los hosts permitidos — pertenece al contenedor y no se puede cambiar desde el volumen; ver Configuración.

Ejemplos de prompts

Una vez que el servidor está conectado, prompts como estos son el uso previsto:

  • "¿Qué facturas siguen abiertas y cuáles están vencidas?"

  • "Muéstrame todo lo que facturamos al cliente Muster GmbH este trimestre."

  • "¿Qué contiene la factura RE-2024-0142 y se ha pagado?"

  • "Busca el artículo con número A-1007 y dime su precio actual."

  • "Descarga el PDF de la última nota de crédito que emitimos."

  • "Dame un enlace para abrir el comprobante X en Lexware Office."

Límites de tasa

La API de Lexware permite dos solicitudes por segundo, aplicadas con un token bucket. Ese presupuesto es global — cubre todos los endpoints de la API al mismo tiempo, así que leer un contacto y leer una factura se descuentan de la misma asignación.

El servidor refleja esto con un único token bucket compartido por cada solicitud en el proceso, rellenándose ligeramente por debajo de la tasa documentada por defecto. Lexware señala que aplicar el límite exactamente, sin margen, tiende a producir 429 de todos modos una vez que la fluctuación de la red desplaza los tiempos de llegada, así que el valor predeterminado deja margen. Las solicitudes se serializan a través de ese bucket en lugar de lanzarse en paralelo, lo que significa que una pregunta amplia que toca muchos documentos se vuelve más lenta en lugar de bloquearse.

Dos cosas que vale la pena saber:

  • El presupuesto pertenece a tu cuenta, no a este proceso. Una segunda instancia del servidor, otra integración o un script que ejecutes tú mismo gastan de los mismos dos por segundo.

  • Lexware advierte que un cliente que sigue insistiendo después de un 429 puede permanecer bloqueado permanentemente. Por lo tanto, el servidor retrocede exponencialmente y se rinde después de algunos intentos en lugar de reintentar con más fuerza.

El bucket de la cuenta se midió el 2026-08-21 y contiene cuatro: cinco solicitudes lanzadas a la vez lograron pasar cuatro y una fue rechazada. El valor predeterminado de 2 deja la mitad de eso para todo lo demás que consume de la misma cuenta — la aplicación web, otra integración, una segunda instancia de este servidor. Súbelo a 4 solo si sabes que este servidor es el único consumidor.

Ambos valores del limitador son configurables mediante LXO_MCP_RATE y LXO_MCP_BURST si tu cuenta se comporta de manera diferente.

Desarrollo

uv sync --extra dev
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv run mypy

La suite de pruebas es totalmente offline. Simula la capa HTTP y no necesita clave de API, así que se ejecuta en cualquier lugar. Dos tipos de prueba salen del proceso sin salir de la máquina: tres inician el servidor como un subproceso real y le hablan por MCP a través de stdio, lo que también demuestra que nada escribe en stdout en la ruta de arranque, y la interfaz de configuración se maneja a través de un servidor HTTP loopback real con una cookie jar real, porque sus protecciones CSRF solo merecen probarse como las encuentra un navegador.

No se incluye ninguna clave de API en este repositorio y ninguna pertenece a CI, así que un checkout nunca puede hablar con Lexware por sí solo. Verificar el servidor contra la API real es, por lo tanto, siempre una ejecución local deliberada con una clave que tú proporcionas, separada de la suite anterior y nunca parte de ella:

uv run python tests/smoke.py
uv run python tests/smoke.py --env-file path/to/.env

Lee tu cuenta y no escribe nada en ella. El servidor que construye recibe el preset de solo lectura, así que las herramientas de escritura no están disponibles para ser llamadas en absoluto. Imprime lo que verificó, para qué no tenía nada la cuenta y qué falló, y enmascara los ids de registro para que el informe se pueda pegar en algún lugar. pytest nunca lo ejecuta. Consulta SPECS.md sección 14.1 para saber por qué una verificación en vivo no es una puerta de entrada.

Las contribuciones y los problemas son bienvenidos. SPECS.md es donde se registran las decisiones de diseño, con el razonamiento y las mediciones que las respaldan.

Licencia

MIT. Ver LICENSE.

Marcas comerciales y afiliación

Este proyecto no está afiliado, respaldado ni patrocinado por Lexware, Haufe-Lexware GmbH & Co. KG, ni por ninguna de sus subsidiarias. "Lexware" y "Lexware Office" son marcas comerciales de sus respectivos propietarios y se utilizan aquí solo para nombrar la API con la que se integra este software, en un sentido descriptivo.

El software habla exclusivamente con la API pública documentada utilizando credenciales que el propietario de la cuenta proporciona y puede revocar. El uso de esa API se rige por los propios términos de Lexware, que aceptas independientemente de este proyecto.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    MCP server for DACH accounting automation. Connect AI assistants to sevDesk and Lexoffice — create invoices, manage contacts, handle bookings and vouchers for German-speaking businesses.
    15
    43 npm
    -
  • A
    license
    B
    quality
    A
    maintenance
    MCP server for the Lexware Office API that enables management of invoices, contacts, articles, vouchers, and more through the Model Context Protocol.
    66
    460 npm
    6
    Functional Source , Version 1.1, MIT Future
  • A
    license
    A
    quality
    B
    maintenance
    Enables MCP-capable assistants to query and manage Lexware Office contacts, sales documents, vouchers, files, payments, webhooks, and reference data via the Lexware Office public API. Adds bank reconciliation tools for matching bank statement CSVs against Lexware vouchers or scanned receipt PDFs.
    4
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for Lexware Office that enables querying and managing contacts, sales documents, vouchers, files, payments, and webhooks through a sandboxed two-tool interface (search/execute) with read-only-by-default write safety.
    2
    MIT